Objetivo y caso de uso
Qué construirás: Un probador de salida de registro de desplazamiento SPI basado en hardware en la Radiona ULX3S que controla un CI 74HC595 externo y una barra de LED. Esta herramienta de prueba dedicada genera patrones de prueba SPI confiables para verificar la integridad de componentes externos y aislar fallas físicas de hardware de errores de firmware.
Por qué es importante / Casos de uso
- Validación de ensamblaje de PCB: Prueba placas personalizadas para garantizar que todos los pines del 74HC595 estén soldados correctamente sin cortocircuitos ni puentes.
- Comprobación de integridad de componentes: Verifica que un lote de CI de registro de desplazamiento sea completamente funcional antes de soldarlos en un producto final.
- Depuración de señales SPI: Proporciona una señal maestra SPI continua y predecible (MOSI, SCK, CS) a frecuencias conocidas (por ejemplo, 1–10 MHz) para calibrar osciloscopios o analizadores lógicos.
- Rodaje automatizado (Burn-in): Ejecuta patrones de bits alternos continuos en módulos de matriz de LED externos para probar la estabilidad del hardware a largo plazo.
Resultado esperado
- Un flujo de bits maestro SPI verificado y en buen estado que se ejecuta en la FPGA ULX3S con una temporización predecible.
- Confirmación visual de la integridad del hardware a través de un gráfico de barras LED externo controlado activamente que muestra patrones de prueba alternos.
- Aislamiento claro de fallas entre el firmware de la FPGA, la temporización del protocolo SPI y el hardware físico de la PCB.
Audiencia: Ingenieros de hardware, desarrolladores de FPGA y diseñadores de PCB; Nivel: Intermedio
Arquitectura/flujo: La FPGA ULX3S genera una señal maestra SPI continua (SCK, MOSI, CS) enrutada a través de pines GPIO hacia un registro de desplazamiento 74HC595 externo, el cual decodifica los datos en serie en una salida paralela para controlar la barra de LED.
Nota educativa de validación
Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. Para este perfil FPGA/ULX3S, los bloques de Verilog sintetizable se comprobaron con Yosys (read_verilog) y el conjunto Verilog de diseño/test se revisó con Verilator. El validador también comprobó la estructura de los bloques de código, que los comandos usen opciones copiables con guiones ASCII, que no aparezcan stacks no soportados y que esté disponible la toolchain ULX3S/ECP5 (yosys, nextpnr-ecp5, ecppack, openFPGALoader).
Evidencia de validación publicada
- Resultado automático: PASS.
- Estructura parseada: 3 apartados, 1 tablas y 5 bloques de código detectados antes de publicar.
- Código comprobado: 3 Verilog/Yosys-Verilator, 1 Bash/copy-paste checks.
- Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
- Hallazgos del informe: sin hallazgos bloqueantes.
Esta validación confirma compatibilidad sintáctica y de herramientas para el código publicado, pero no sustituye la prueba física sobre tu revisión exacta de ULX3S, tu archivo de restricciones de pines y tu cableado real.
Nota educativa de seguridad
Este proyecto es un prototipo educativo, no un producto certificado. Antes de energizar la configuración, verifique la disposición de pines (pinout) de la revisión exacta de su placa ULX3S, mantenga las señales de E/S de la FPGA a 3.3 V, nunca conecte 5 V directamente a los pines de E/S, desconecte la alimentación antes de cambiar el cableado y use fuentes externas adecuadas para cargas, motores o servos, compartiendo la tierra solo cuando el cableado lo requiera.
Requisitos previos
- Comprensión básica de lógica digital (relojes, máquinas de estado, registros).
- Familiaridad con el protocolo de Interfaz Periférica Serial (SPI) (específicamente MOSI, SCK y Chip Select/Latch).
- Una instalación funcional de la cadena de herramientas de código abierto para FPGA Lattice ECP5: Yosys, nextpnr-ecp5, Project Trellis (ecppack) y openFPGALoader.
- Verilator instalado para análisis (linting) y simulación.
Materiales
- Placa FPGA: Radiona ULX3S (variante Lattice ECP5-85F).
- Registro de desplazamiento: 1x CI de registro de desplazamiento de 8 bits 74HC595 (encapsulado DIP para protoboard).
- Pantalla: 1x gráfico de barras LED de 10 segmentos (usaremos 8 segmentos).
- Componentes pasivos: 8x resistencias de 330Ω.
- Prototipado: 1x protoboard y un conjunto de cables puente macho-macho y macho-hembra.
Configuración/Conexión
El 74HC595 requiere que pines de control específicos estén conectados a niveles lógicos para funcionar correctamente. La ULX3S opera a una lógica de 3.3V, que es totalmente compatible con el VCC del 74HC595. Conecte los componentes de acuerdo con la siguiente tabla.
| Pin ULX3S | Puerto ULX3S | Pin 74HC595 | Descripción |
|---|---|---|---|
| 3.3V | 3.3V | 16 (VCC) y 10 (SRCLR) | Alimentación y borrado del registro de desplazamiento activo en bajo (conectado a alto) |
| GND | GND | 8 (GND) y 13 (OE) | Tierra y habilitación de salida activa en bajo (conectado a bajo) |
| B11 | gp[0] | 14 (SER) | SPI MOSI (Datos en serie) |
| C11 | gp[1] | 11 (SRCLK) | SPI SCK (Reloj en serie) |
| A10 | gp[2] | 12 (RCLK) | Latch SPI (Reloj del registro) |
Para las salidas a la barra de LED:
1. Conecte los pines 15, 1, 2, 3, 4, 5, 6, 7 (Q0 a Q7) del 74HC595 a los ánodos de los primeros 8 segmentos de la barra de LED.
2. Conecte los 8 cátodos correspondientes de la barra de LED al riel de tierra a través de resistencias limitadoras de corriente de 330Ω.
Código validado
El proyecto está dividido en tres archivos Verilog sintetizables, un banco de pruebas (testbench) y un archivo de restricciones físicas. Manténgalos en el mismo directorio de trabajo.
spi_master.v
Este módulo maneja la serialización física de los datos de 8 bits. Espera una señal start, desplaza los datos empezando por el MSB (bit más significativo) en la línea MOSI, alterna la línea SCK y, finalmente, emite un pulso en la línea LATCH.
Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.
/* spi_master.v */
module spi_master (
input wire clk, // System clock
input wire rst, // Active high reset
input wire start, // Pulse high to start transmission
input wire [7:0] data_in,// Data to transmit
output reg mosi, // Master Out Slave In
output reg sck, // Serial Clock
output reg latch, // Register Latch (RCLK)
output reg busy // High while transmitting
);
reg [3:0] bit_cnt;
reg [7:0] shift_reg;
reg [2:0] state;
localparam IDLE = 3'd0;
localparam LOAD = 3'd1;
localparam LOW = 3'd2;
localparam HIGH = 3'd3;
localparam LATCH = 3'd4;
always @(posedge clk or posedge rst) begin
if (rst) begin
mosi <= 1'b0;
sck <= 1'b0;
latch <= 1'b0;
busy <= 1'b0;
bit_cnt <= 4'd0;
shift_reg <= 8'd0;
state <= IDLE;
end else begin
case (state)
IDLE: begin
latch <= 1'b0;
sck <= 1'b0;
if (start) begin
shift_reg <= data_in;
busy <= 1'b1;
// .../* spi_master.v */
module spi_master (
input wire clk, // System clock
input wire rst, // Active high reset
input wire start, // Pulse high to start transmission
input wire [7:0] data_in,// Data to transmit
output reg mosi, // Master Out Slave In
output reg sck, // Serial Clock
output reg latch, // Register Latch (RCLK)
output reg busy // High while transmitting
);
reg [3:0] bit_cnt;
reg [7:0] shift_reg;
reg [2:0] state;
localparam IDLE = 3'd0;
localparam LOAD = 3'd1;
localparam LOW = 3'd2;
localparam HIGH = 3'd3;
localparam LATCH = 3'd4;
always @(posedge clk or posedge rst) begin
if (rst) begin
mosi <= 1'b0;
sck <= 1'b0;
latch <= 1'b0;
busy <= 1'b0;
bit_cnt <= 4'd0;
shift_reg <= 8'd0;
state <= IDLE;
end else begin
case (state)
IDLE: begin
latch <= 1'b0;
sck <= 1'b0;
if (start) begin
shift_reg <= data_in;
busy <= 1'b1;
bit_cnt <= 4'd8;
state <= LOAD;
end else begin
busy <= 1'b0;
end
end
LOAD: begin
mosi <= shift_reg[7];
shift_reg <= {shift_reg[6:0], 1'b0};
sck <= 1'b0;
state <= HIGH;
end
HIGH: begin
sck <= 1'b1;
bit_cnt <= bit_cnt - 4'd1;
state <= LOW;
end
LOW: begin
sck <= 1'b0;
if (bit_cnt == 4'd0) begin
state <= LATCH;
end else begin
state <= LOAD;
end
end
LATCH: begin
latch <= 1'b1;
state <= IDLE;
end
default: state <= IDLE;
endcase
end
end
endmodule
tester_top.v
Este es el módulo de nivel superior. Divide el reloj de 25 MHz de la ULX3S para crear un tick de actualización para los patrones y un reloj de ~3 MHz para el maestro SPI (resultando en un SCK de ~1.5 MHz). También elimina el rebote (debounce) del botón de usuario y alterna entre cuatro patrones de prueba.
Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.
/* tester_top.v */
module tester_top #(
parameter TIMER_BITS = 24 // Can be reduced for simulation speed
)(
input wire clk_25mhz,
input wire [2:1] btn,
output wire spi_mosi,
output wire spi_sck,
output wire spi_latch
);
wire rst = btn[1];
wire mode_btn = btn[2];
// Clock dividers
reg [TIMER_BITS-1:0] timer;
reg [3:0] spi_clk_div;
wire spi_clk = spi_clk_div[3];
always @(posedge clk_25mhz) begin
timer <= timer + 1;
spi_clk_div <= spi_clk_div + 4'd1;
end
wire update_tick = (timer == 0);
// Button Debouncer
reg [15:0] debounce_shift;
reg mode_btn_clean;
reg mode_btn_last;
wire mode_pulse;
always @(posedge clk_25mhz) begin
debounce_shift <= {debounce_shift[14:0], mode_btn};
if (debounce_shift == 16'hFFFF) mode_btn_clean <= 1'b1;
else if (debounce_shift == 16'h0000) mode_btn_clean <= 1'b0;
mode_btn_last <= mode_btn_clean;
end
assign mode_pulse = mode_btn_clean & ~mode_btn_last;
// Pattern Generator State Machine
reg [1:0] mode;
reg [7:0] current_pattern;
reg [2:0] walk_idx;
always @(posedge clk_25mhz or posedge rst) begin
if (rst) begin
mode <= 2'd0;
walk_idx <= 3'd0;
// .../* tester_top.v */
module tester_top #(
parameter TIMER_BITS = 24 // Can be reduced for simulation speed
)(
input wire clk_25mhz,
input wire [2:1] btn,
output wire spi_mosi,
output wire spi_sck,
output wire spi_latch
);
wire rst = btn[1];
wire mode_btn = btn[2];
// Clock dividers
reg [TIMER_BITS-1:0] timer;
reg [3:0] spi_clk_div;
wire spi_clk = spi_clk_div[3];
always @(posedge clk_25mhz) begin
timer <= timer + 1;
spi_clk_div <= spi_clk_div + 4'd1;
end
wire update_tick = (timer == 0);
// Button Debouncer
reg [15:0] debounce_shift;
reg mode_btn_clean;
reg mode_btn_last;
wire mode_pulse;
always @(posedge clk_25mhz) begin
debounce_shift <= {debounce_shift[14:0], mode_btn};
if (debounce_shift == 16'hFFFF) mode_btn_clean <= 1'b1;
else if (debounce_shift == 16'h0000) mode_btn_clean <= 1'b0;
mode_btn_last <= mode_btn_clean;
end
assign mode_pulse = mode_btn_clean & ~mode_btn_last;
// Pattern Generator State Machine
reg [1:0] mode;
reg [7:0] current_pattern;
reg [2:0] walk_idx;
always @(posedge clk_25mhz or posedge rst) begin
if (rst) begin
mode <= 2'd0;
walk_idx <= 3'd0;
current_pattern <= 8'h01;
end else begin
if (mode_pulse) begin
mode <= mode + 2'd1;
walk_idx <= 3'd0;
end
if (update_tick) begin
case (mode)
2'd0: begin // Mode 0: Walking One
current_pattern <= (8'h01 << walk_idx);
walk_idx <= walk_idx + 3'd1;
end
2'd1: begin // Mode 1: Alternating
current_pattern <= (walk_idx[0]) ? 8'hAA : 8'h55;
walk_idx <= walk_idx + 3'd1;
end
2'd2: begin // Mode 2: All On
current_pattern <= 8'hFF;
end
2'd3: begin // Mode 3: All Off
current_pattern <= 8'h00;
end
endcase
end
end
end
// SPI Master Instantiation
wire spi_busy;
reg spi_start;
reg spi_start_d;
// Generate a single-cycle start pulse in the spi_clk domain
always @(posedge spi_clk) begin
spi_start_d <= update_tick;
spi_start <= update_tick & ~spi_start_d;
end
spi_master u_spi (
.clk(spi_clk),
.rst(rst),
.start(spi_start),
.data_in(current_pattern),
.mosi(spi_mosi),
.sck(spi_sck),
.latch(spi_latch),
.busy(spi_busy)
);
endmodule
tb_tester.v
Un banco de pruebas Verilog puro utilizado por Verilator para simular la FSM (máquina de estados finitos) y la generación de salida SPI. El parámetro TIMER_BITS se anula para acelerar la simulación.
/* tb_tester.v */
`timescale 1ns/1ps
module tb_tester;
reg clk;
reg [2:1] btn;
wire mosi, sck, latch;
// Instantiate with a small timer for fast simulation
tester_top #(.TIMER_BITS(6)) dut (
.clk_25mhz(clk),
.btn(btn),
.spi_mosi(mosi),
.spi_sck(sck),
.spi_latch(latch)
);
initial begin
$dumpfile("tester_sim.vcd");
$dumpvars(0, tb_tester);
clk = 0;
btn = 2'b10; // Assert reset (btn[1])
#100;
btn = 2'b00; // Release reset
// Simulate enough time to observe the SPI transmission
#15000;
$display("Simulation complete. Check VCD for SPI waveforms.");
$finish;
end
always #20 clk = ~clk; // 25MHz clock (40ns period)
endmodule
ulx3s.lpf
El archivo de restricciones físicas que mapea los puertos lógicos a los pines del ECP5 en la ULX3S.
# ulx3s.lpf
LOCATE COMP "clk_25mhz" SITE "G2";
IOBUF COMP "clk_25mhz" IO_TYPE=LVCMOS33;
# Buttons
LOCATE COMP "btn[1]" SITE "R1";
IOBUF COMP "btn[1]" IO_TYPE=LVCMOS33;
LOCATE COMP "btn[2]" SITE "T1";
IOBUF COMP "btn[2]" IO_TYPE=LVCMOS33;
# SPI Outputs to 74HC595
LOCATE COMP "spi_mosi" SITE "B11";
IOBUF COMP "spi_mosi" IO_TYPE=LVCMOS33;
LOCATE COMP "spi_sck" SITE "C11";
IOBUF COMP "spi_sck" IO_TYPE=LVCMOS33;
LOCATE COMP "spi_latch" SITE "A10";
IOBUF COMP "spi_latch" IO_TYPE=LVCMOS33;
Comandos de compilación y programación
Utilice los siguientes comandos del flujo canónico de código abierto para simular, compilar y programar el diseño en su ULX3S.
# 1. Lint and Simulate using Verilator
verilator --lint-only -Wno-DECLFILENAME spi_master.v tester_top.v
verilator --binary --trace -Wno-DECLFILENAME spi_master.v tester_top.v tb_tester.v
./obj_dir/Vtb_tester
# 2. Synthesize using Yosys
yosys -p "synth_ecp5 -top tester_top -json tester.json" spi_master.v tester_top.v
# 3. Place and Route using nextpnr-ecp5 (targeting ECP5-85F)
nextpnr-ecp5 --85k --package CABGA381 --json tester.json --lpf ulx3s.lpf --textcfg tester_out.config
# 4. Pack the bitstream using ecppack
ecppack tester_out.config tester.bit
# 5. Program the ULX3S using openFPGALoader
openFPGALoader -b ulx3s tester.bit
Encuentra este producto y/o libros sobre este tema en Amazon
Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.




