Caso práctico: Probador SPI para 74HC595 con ULX3S

Caso práctico: Probador SPI para 74HC595 con ULX3S — hero

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 ULX3SPuerto ULX3SPin 74HC595Descripción
3.3V3.3V16 (VCC) y 10 (SRCLR)Alimentación y borrado del registro de desplazamiento activo en bajo (conectado a alto)
GNDGND8 (GND) y 13 (OE)Tierra y habilitación de salida activa en bajo (conectado a bajo)
B11gp[0]14 (SER)SPI MOSI (Datos en serie)
C11gp[1]11 (SRCLK)SPI SCK (Reloj en serie)
A10gp[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;
// ...

🔒 Este contenido es premium. Con la suscripción mensual (7 días gratis) podrás desbloquear el material didáctico completo y el pack PDF listo para imprimir.🔓 Desbloquéalo — 7 días gratis
/* 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;
// ...

🔒 Este contenido es premium. Con la suscripción mensual (7 días gratis) podrás desbloquear el material didáctico completo y el pack PDF listo para imprimir.🔓 Desbloquéalo — 7 días gratis
/* 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

Ir a 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.

Quiz rápido

Pregunta 1: ¿Qué dispositivo principal se utiliza para construir el probador de salida de registro de desplazamiento SPI?




Pregunta 2: ¿Qué circuito integrado (CI) externo controla el probador de salida de registro de desplazamiento SPI?




Pregunta 3: ¿Cuál es el propósito principal de esta herramienta de prueba dedicada?




Pregunta 4: ¿Qué permite verificar la validación de ensamblaje de PCB en este contexto?




Pregunta 5: ¿Para qué sirve la comprobación de integridad de componentes mencionada en el texto?




Pregunta 6: ¿Qué señales proporciona el probador para la depuración de señales SPI?




Pregunta 7: ¿En qué rango de frecuencias se generan las señales SPI para calibrar osciloscopios o analizadores lógicos?




Pregunta 8: ¿Qué componente visual se utiliza junto con el CI 74HC595 en este proyecto?




Pregunta 9: ¿En qué consiste el rodaje automatizado (Burn-in) descrito en el caso de uso?




Pregunta 10: ¿Qué tipo de fallas ayuda a aislar esta herramienta de prueba?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:
Scroll al inicio