You dont have javascript enabled! Please enable it!

Caso práctico: monitor UART pasivo con ULX3S

Caso práctico: monitor UART pasivo con ULX3S — hero

Objetivo y caso de uso

Lo que construirás: Un monitor UART práctico en la Radiona ULX3S (Lattice ECP5-85F) que se conecta pasivamente a una línea TX real de 3.3 V, 115200 baud, 8N1, decodifica cada byte en lógica FPGA y reenvía líneas legibles como RX 48 OK a un PC a través de una segunda UART. El diseño también hace parpadear un LED integrado cuando hay tráfico y es lo bastante limpio como para pasar lint con Verilator y sintetizar con Yosys.

Por qué importa / Casos de uso

  • Depura dispositivos embebidos sin cambiar su firmware observando de forma no invasiva un flujo UART en vivo de 3.3 V.
  • Convierte tráfico serie sin procesar en salida de monitor legible por humanos para puesta en marcha, prueba de fábrica y diagnóstico en campo.
  • Practica un diseño serie FPGA fiable con temporización concreta: 115200 baud significa aproximadamente 86.8 µs por trama de byte en 8N1, por lo que el monitor debe muestrear y formatear los datos correctamente a la velocidad de línea.
  • Útil al validar registros de arranque, controladores de sensores, módulos GPS o mensajes de depuración de MCU que ya transmiten por UART.

Resultado esperado

  • La ULX3S recibe bytes de una fuente UART externa de 3.3 V y los decodifica correctamente a 115200 baud, 8N1.
  • Por cada byte recibido, la FPGA emite una línea legible como RX 48 OK a un adaptador USB-UART conectado a un terminal de PC.
  • Un LED integrado parpadea brevemente con cada carácter, proporcionando confirmación visual inmediata del tráfico.
  • El RTL pasa el lint de Verilator y sintetiza con Yosys para la ECP5-85F, con una carga de FPGA muy baja en relación con la lógica disponible y sin uso significativo de GPU (0% GPU).

Público: Estudiantes de FPGA, ingenieros embebidos y depuradores de hardware que trabajan con sistemas basados en UART; Nivel: principiante a intermedio

Arquitectura/flujo: TX de dispositivo de 3.3 V -> decodificador RX UART de ULX3S -> formateador -> TX UART de ULX3S -> adaptador USB-UART -> terminal de PC

Diagrama conceptual de bloques

Vista de alto nivel: qué entra en el sistema, qué procesa cada bloque y qué sale.

Arquitectura funcional

TX de dispositivo de 3.3 V

Decodificador RX UART de ULX3S

formateador

TX UART de ULX3S

adaptador USB-UART

terminal de PC

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Código fuente

Verilator

Yosys

Implementación de hardware

Resumen conceptual de las herramientas usadas para verificar el material publicado.

Prerrequisitos

Nota educativa de validación

Antes de la publicación, este caso pasó la compuerta de validación automatizada de Prometeo con estado PASS. Para este perfil FPGA/ULX3S, los bloques Verilog sintetizables se verificaron con Yosys (read_verilog) y el conjunto de diseño/prueba Verilog se analizó con Verilator. El validador también comprobó la estructura de los bloques de código, las opciones de comandos ASCII seguras para copiar/pegar, las pilas no compatibles y la disponibilidad de la cadena de herramientas ULX3S/ECP5 (yosys, nextpnr-ecp5, ecppack, openFPGALoader).

Esta validación confirma la sintaxis y la compatibilidad de herramientas para el código publicado, pero no sustituye las pruebas físicas en la revisión exacta de tu placa ULX3S, el archivo de restricciones de pines y el cableado real.

Necesitas:

  • Una placa ULX3S de la familia Lattice ECP5-85F
  • Un dispositivo fuente UART de 3.3 V
  • Un adaptador USB-UART
  • Cables puente
  • Un entorno de shell con estas herramientas instaladas:
  • verilator
  • yosys
  • nextpnr-ecp5
  • ecppack
  • openFPGALoader
  • Un programa de terminal serie como picocom o screen

Comprobación rápida de herramientas:

verilator --version
yosys -V
nextpnr-ecp5 --version
ecppack --help | head -n 1
openFPGALoader --version

Materiales

ElementoModelo/familia exactosPropósito
Placa FPGARadiona ULX3S, Lattice ECP5-85FEjecuta el monitor UART
Fuente serieDispositivo UART de 3.3 VSeñal que se está observando
Adaptador USB-UARTAdaptador compatible con 3.3 VEnvía la salida del monitor al PC
Cable USBPara ULX3SAlimentación y programación
Cable USBPara adaptadorConexión serie del PC
Cables puenteSegún sea necesarioCableado TX y GND

Nota educativa de seguridad

Solo electrónica digital de baja tensión.

  • No conecte RS-232 niveles de tensión directamente a los pines de la FPGA.
  • No conecte UART de 5 V directamente a las E/S de ULX3S.
  • Comparta GND entre el dispositivo externo, ULX3S y el adaptador USB-UART.
  • Este proyecto asume únicamente señalización UART de 3.3 V UART.

Cableado

Señales utilizadas por el diseño FPGA:

  • mon_rx: entrada UART monitorizada desde el TX del dispositivo externo
  • host_tx: salida UART desde la FPGA hacia el RX del adaptador USB-UART
  • led0: LED de actividad

Conecte:

  1. Dispositivo externo TX -> pin de ULX3S asignado a mon_rx
  2. Dispositivo externo GND -> ULX3S GND
  3. Pin de ULX3S asignado a host_tx -> adaptador USB-UART RX
  4. Adaptador USB-UART GND -> ULX3S GND
  5. USB de ULX3S -> PC
  6. USB del adaptador USB-UART -> PC

Archivos del proyecto

Cree estos archivos:

  • uart_monitor_top.v
  • tb_uart_monitor_top.v
  • ulx3s_uart_monitor.lpf

Verilog: uart_monitor_top.v

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

module uart_rx #(
    parameter integer CLK_HZ = 25000000,
    parameter integer BAUD   = 115200
)(
    input  wire clk,
    input  wire rst,
    input  wire rx,
    output reg  [7:0] data,
    output reg  valid,
    output reg  framing_error
);
    localparam integer CLKS_PER_BIT  = CLK_HZ / BAUD;
    localparam integer HALF_BIT_CLKS = CLKS_PER_BIT / 2;

    reg rx_sync_0;
    reg rx_sync_1;
    reg [15:0] clk_count;
    reg [3:0] bit_index;
    reg [7:0] rx_shift;
    reg [1:0] state;

    localparam [1:0] S_IDLE  = 2'd0;
    localparam [1:0] S_START = 2'd1;
    localparam [1:0] S_DATA  = 2'd2;
    localparam [1:0] S_STOP  = 2'd3;

    always @(posedge clk) begin
        if (rst) begin
            rx_sync_0 <= 1'b1;
            rx_sync_1 <= 1'b1;
        end else begin
            rx_sync_0 <= rx;
            rx_sync_1 <= rx_sync_0;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            data <= 8'h00;
            valid <= 1'b0;
            framing_error <= 1'b0;
            clk_count <= 16'd0;
            bit_index <= 4'd0;
            rx_shift <= 8'h00;
            state <= S_IDLE;
        end else begin
            valid <= 1'b0;

            case (state)
                S_IDLE: begin
                    framing_error <= 1'b0;
                    clk_count <= 16'd0;
                    bit_index <= 4'd0;
                    if (rx_sync_1 == 1'b0) begin
                        state <= S_START;
                    end
                end

                S_START: begin
                    if (clk_count == HALF_BIT_CLKS - 1) begin
                        clk_count <= 16'd0;
                        if (rx_sync_1 == 1'b0) begin
                            state <= S_DATA;
                        end else begin
                            state <= S_IDLE;
                        end
                    end else begin
                        clk_count <= clk_count + 16'd1;
                    end
                end

                S_DATA: begin
                    if (clk_count == CLKS_PER_BIT - 1) begin
                        clk_count <= 16'd0;
                        rx_shift[bit_index] <= rx_sync_1;
                        if (bit_index == 4'd7) begin
                            bit_index <= 4'd0;
                            state <= S_STOP;
                        end else begin
                            bit_index <= bit_index + 4'd1;
                        end
                    end else begin
                        clk_count <= clk_count + 16'd1;
                    end
                end

                S_STOP: begin
                    if (clk_count == CLKS_PER_BIT - 1) begin
                        clk_count <= 16'd0;
                        data <= rx_shift;
                        valid <= 1'b1;
                        framing_error <= (rx_sync_1 != 1'b1);
                        state <= S_IDLE;
                    end else begin
                        clk_count <= clk_count + 16'd1;
                    end
                end

                default: begin
                    state <= S_IDLE;
                end
            endcase
        end
    end
endmodule

module uart_tx #(
    parameter integer CLK_HZ = 25000000,
    parameter integer BAUD   = 115200
)(
    input  wire clk,
    input  wire rst,
    input  wire [7:0] data,
    input  wire start,
    output reg  tx,
    output reg  busy
);
    localparam integer CLKS_PER_BIT = CLK_HZ / BAUD;

    reg [15:0] clk_count;
    reg [3:0] bit_index;
    reg [9:0] shifter;

    always @(posedge clk) begin
        if (rst) begin
            tx <= 1'b1;
            busy <= 1'b0;
            clk_count <= 16'd0;
            bit_index <= 4'd0;
            shifter <= 10'b1111111111;
        end else begin
            if (!busy) begin
                tx <= 1'b1;
                clk_count <= 16'd0;
                bit_index <= 4'd0;
                if (start) begin
                    shifter <= {1'b1, data, 1'b0};
                    busy <= 1'b1;
                    tx <= 1'b0;
                end
            end else begin
                if (clk_count == CLKS_PER_BIT - 1) begin
                    clk_count <= 16'd0;
                    bit_index <= bit_index + 4'd1;
                    shifter <= {1'b1, shifter[9:1]};
                    tx <= shifter[1];
                    if (bit_index == 4'd9) begin
                        busy <= 1'b0;
                        tx <= 1'b1;
                    end
                end else begin
                    clk_count <= clk_count + 16'd1;
                end
            end
        end
    end
endmodule
// ... continúa para miembros en el código completo validado ...

🔒 Parte del código validado es premium. Con el pase de 7 días o la suscripción mensual podrás consultar el archivo completo validado.

module uart_rx #(
    parameter integer CLK_HZ = 25000000,
    parameter integer BAUD   = 115200
)(
    input  wire clk,
    input  wire rst,
    input  wire rx,
    output reg  [7:0] data,
    output reg  valid,
    output reg  framing_error
);
    localparam integer CLKS_PER_BIT  = CLK_HZ / BAUD;
    localparam integer HALF_BIT_CLKS = CLKS_PER_BIT / 2;

    reg rx_sync_0;
    reg rx_sync_1;
    reg [15:0] clk_count;
    reg [3:0] bit_index;
    reg [7:0] rx_shift;
    reg [1:0] state;

    localparam [1:0] S_IDLE  = 2'd0;
    localparam [1:0] S_START = 2'd1;
    localparam [1:0] S_DATA  = 2'd2;
    localparam [1:0] S_STOP  = 2'd3;

    always @(posedge clk) begin
        if (rst) begin
            rx_sync_0 <= 1'b1;
            rx_sync_1 <= 1'b1;
        end else begin
            rx_sync_0 <= rx;
            rx_sync_1 <= rx_sync_0;
        end
    end

    always @(posedge clk) begin
        if (rst) begin
            data <= 8'h00;
            valid <= 1'b0;
            framing_error <= 1'b0;
            clk_count <= 16'd0;
            bit_index <= 4'd0;
            rx_shift <= 8'h00;
            state <= S_IDLE;
        end else begin
            valid <= 1'b0;

            case (state)
                S_IDLE: begin
                    framing_error <= 1'b0;
                    clk_count <= 16'd0;
                    bit_index <= 4'd0;
                    if (rx_sync_1 == 1'b0) begin
                        state <= S_START;
                    end
                end

                S_START: begin
                    if (clk_count == HALF_BIT_CLKS - 1) begin
                        clk_count <= 16'd0;
                        if (rx_sync_1 == 1'b0) begin
                            state <= S_DATA;
                        end else begin
                            state <= S_IDLE;
                        end
                    end else begin
                        clk_count <= clk_count + 16'd1;
                    end
                end

                S_DATA: begin
                    if (clk_count == CLKS_PER_BIT - 1) begin
                        clk_count <= 16'd0;
                        rx_shift[bit_index] <= rx_sync_1;
                        if (bit_index == 4'd7) begin
                            bit_index <= 4'd0;
                            state <= S_STOP;
                        end else begin
                            bit_index <= bit_index + 4'd1;
                        end
                    end else begin
                        clk_count <= clk_count + 16'd1;
                    end
                end

                S_STOP: begin
                    if (clk_count == CLKS_PER_BIT - 1) begin
                        clk_count <= 16'd0;
                        data <= rx_shift;
                        valid <= 1'b1;
                        framing_error <= (rx_sync_1 != 1'b1);
                        state <= S_IDLE;
                    end else begin
                        clk_count <= clk_count + 16'd1;
                    end
                end

                default: begin
                    state <= S_IDLE;
                end
            endcase
        end
    end
endmodule

module uart_tx #(
    parameter integer CLK_HZ = 25000000,
    parameter integer BAUD   = 115200
)(
    input  wire clk,
    input  wire rst,
    input  wire [7:0] data,
    input  wire start,
    output reg  tx,
    output reg  busy
);
    localparam integer CLKS_PER_BIT = CLK_HZ / BAUD;

    reg [15:0] clk_count;
    reg [3:0] bit_index;
    reg [9:0] shifter;

    always @(posedge clk) begin
        if (rst) begin
            tx <= 1'b1;
            busy <= 1'b0;
            clk_count <= 16'd0;
            bit_index <= 4'd0;
            shifter <= 10'b1111111111;
        end else begin
            if (!busy) begin
                tx <= 1'b1;
                clk_count <= 16'd0;
                bit_index <= 4'd0;
                if (start) begin
                    shifter <= {1'b1, data, 1'b0};
                    busy <= 1'b1;
                    tx <= 1'b0;
                end
            end else begin
                if (clk_count == CLKS_PER_BIT - 1) begin
                    clk_count <= 16'd0;
                    bit_index <= bit_index + 4'd1;
                    shifter <= {1'b1, shifter[9:1]};
                    tx <= shifter[1];
                    if (bit_index == 4'd9) begin
                        busy <= 1'b0;
                        tx <= 1'b1;
                    end
                end else begin
                    clk_count <= clk_count + 16'd1;
                end
            end
        end
    end
endmodule

module uart_monitor_top(
    input  wire clk_25mhz,
    input  wire btn_rst,
    input  wire mon_rx,
    output wire host_tx,
    output reg  led0
);
    wire rst;
    wire [7:0] rx_data;
    wire rx_valid;
    wire rx_ferr;

    reg [7:0] tx_data;
    reg tx_start;
    wire tx_busy;

    reg [7:0] msg_mem [0:17];
    reg [4:0] msg_len;
    reg [4:0] msg_idx;
    reg sending;
    reg [23:0] led_count;
    integer i;

    assign rst = btn_rst;

    uart_rx #(
        .CLK_HZ(25000000),
        .BAUD(115200)
    ) u_rx (
        .clk(clk_25mhz),
        .rst(rst),
        .rx(mon_rx),
        .data(rx_data),
        .valid(rx_valid),
        .framing_error(rx_ferr)
    );

    uart_tx #(
        .CLK_HZ(25000000),
        .BAUD(115200)
    ) u_tx (
        .clk(clk_25mhz),
        .rst(rst),
        .data(tx_data),
        .start(tx_start),
        .tx(host_tx),
        .busy(tx_busy)
    );

    function [7:0] hexchar;
        input [3:0] nib;
        begin
            if (nib < 4'd10) begin
                hexchar = 8'h30 + {4'b0000, nib};
            end else begin
                hexchar = 8'h41 + ({4'b0000, nib} - 8'd10);
            end
        end
    endfunction

    always @(posedge clk_25mhz) begin
        if (rst) begin
            tx_data <= 8'h00;
            tx_start <= 1'b0;
            msg_len <= 5'd0;
            msg_idx <= 5'd0;
            sending <= 1'b0;
            led0 <= 1'b0;
            led_count <= 24'd0;
            for (i = 0; i < 18; i = i + 1) begin
                msg_mem[i] <= 8'h20;
            end
        end else begin
            tx_start <= 1'b0;

            if (led_count != 24'd0) begin
                led_count <= led_count - 24'd1;
                led0 <= 1'b1;
            end else begin
                led0 <= 1'b0;
            end

            if (rx_valid && !sending) begin
                led_count <= 24'd5000000;

                msg_mem[0] <= "R";
                msg_mem[1] <= "X";
                msg_mem[2] <= " ";
                msg_mem[3] <= hexchar(rx_data[7:4]);
                msg_mem[4] <= hexchar(rx_data[3:0]);
                msg_mem[5] <= " ";

                if (!rx_ferr) begin
                    msg_mem[6] <= "O";
                    msg_mem[7] <= "K";
                    msg_mem[8] <= 8'h0A;
                    msg_len <= 5'd9;
                end else begin
                    msg_mem[6]  <= "F";
                    msg_mem[7]  <= "R";
                    msg_mem[8]  <= "A";
                    msg_mem[9]  <= "M";
                    msg_mem[10] <= "I";
                    msg_mem[11] <= "N";
                    msg_mem[12] <= "G";
                    msg_mem[13] <= "_";
                    msg_mem[14] <= "E";
                    msg_mem[15] <= "R";
                    msg_mem[16] <= "R";
                    msg_mem[17] <= 8'h0A;
                    msg_len <= 5'd18;
                end

                msg_idx <= 5'd0;
                sending <= 1'b1;
            end

            if (sending && !tx_busy) begin
                if (msg_idx < msg_len) begin
                    tx_data <= msg_mem[msg_idx];
                    tx_start <= 1'b1;
                    msg_idx <= msg_idx + 5'd1;
                end else begin
                    sending <= 1'b0;
                end
            end
        end
    end
endmodule

Banco de pruebas: tb_uart_monitor_top.v

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

`timescale 1ns/1ps

module tb_uart_monitor_top;
    reg clk;
    reg btn_rst;
    reg mon_rx;
    wire host_tx;
    wire led0;

    localparam integer CLK_HALF_NS = 20;
    localparam integer BIT_NS = 8680;

    integer fd;
    integer i;
    reg [9:0] frame;

    uart_monitor_top dut (
        .clk_25mhz(clk),
        .btn_rst(btn_rst),
        .mon_rx(mon_rx),
        .host_tx(host_tx),
        .led0(led0)
    );

    always #CLK_HALF_NS clk = ~clk;

    task uart_send_byte;
        input [7:0] b;
        integer j;
        begin
            mon_rx = 1'b0;
            #(BIT_NS);
            for (j = 0; j < 8; j = j + 1) begin
                mon_rx = b[j];
                #(BIT_NS);
            end
            mon_rx = 1'b1;
            #(BIT_NS);
// ... continúa para miembros en el código completo validado ...

🔒 Parte del código validado es premium. Con el pase de 7 días o la suscripción mensual podrás consultar el archivo completo validado.

`timescale 1ns/1ps

module tb_uart_monitor_top;
    reg clk;
    reg btn_rst;
    reg mon_rx;
    wire host_tx;
    wire led0;

    localparam integer CLK_HALF_NS = 20;
    localparam integer BIT_NS = 8680;

    integer fd;
    integer i;
    reg [9:0] frame;

    uart_monitor_top dut (
        .clk_25mhz(clk),
        .btn_rst(btn_rst),
        .mon_rx(mon_rx),
        .host_tx(host_tx),
        .led0(led0)
    );

    always #CLK_HALF_NS clk = ~clk;

    task uart_send_byte;
        input [7:0] b;
        integer j;
        begin
            mon_rx = 1'b0;
            #(BIT_NS);
            for (j = 0; j < 8; j = j + 1) begin
                mon_rx = b[j];
                #(BIT_NS);
            end
            mon_rx = 1'b1;
            #(BIT_NS);
        end
    endtask

    initial begin
        clk = 1'b0;
        btn_rst = 1'b1;
        mon_rx = 1'b1;
        fd = $fopen("sim_host_tx_bits.txt", "w");

        #500;
        btn_rst = 1'b0;

        #(BIT_NS * 3);
        uart_send_byte(8'h48);
        #(BIT_NS * 2);
        uart_send_byte(8'h45);
        #(BIT_NS * 2);
        uart_send_byte(8'h4C);

        #(BIT_NS * 250);
        $fclose(fd);
        $finish;
    end

    initial begin
        forever begin
            @(negedge host_tx);
            #(BIT_NS/2);
            frame[0] = host_tx;
            for (i = 1; i < 10; i = i + 1) begin
                #(BIT_NS);
                frame[i] = host_tx;
            end
            $fwrite(fd, "frame bits: %b\n", frame);
        end
    end
endmodule

Restricciones: ulx3s_uart_monitor.lpf

Edite los valores de SITE para que coincidan con el pinout exacto de su ULX3S.

BLOCK RESETPATHS;
BLOCK ASYNCPATHS;

FREQUENCY PORT "clk_25mhz" 25 MHz;

LOCATE COMP "clk_25mhz" SITE "ULX3S_PIN_CLK25";
IOBUF PORT "clk_25mhz" IO_TYPE=LVCMOS33;

LOCATE COMP "btn_rst" SITE "ULX3S_PIN_BTN";
IOBUF PORT "btn_rst" IO_TYPE=LVCMOS33 PULLMODE=UP;

LOCATE COMP "mon_rx" SITE "ULX3S_PIN_MON_RX";
IOBUF PORT "mon_rx" IO_TYPE=LVCMOS33;

LOCATE COMP "host_tx" SITE "ULX3S_PIN_HOST_TX";
IOBUF PORT "host_tx" IO_TYPE=LVCMOS33;

LOCATE COMP "led0" SITE "ULX3S_PIN_LED0";
IOBUF PORT "led0" IO_TYPE=LVCMOS33;

Compilar y ejecutar

1) Lint de Verilator

verilator -Wall -Wno-DECLFILENAME --lint-only uart_monitor_top.v tb_uart_monitor_top.v

2) Ejecutar simulación

verilator -Wall -Wno-DECLFILENAME --binary uart_monitor_top.v tb_uart_monitor_top.v
./obj_dir/Vtb_uart_monitor_top

Evidencia esperada:

  • La simulación finaliza normalmente.
  • Se crea un archivo llamado sim_host_tx_bits.txt.
  • Ese archivo contiene muestras de tramas UART generadas por el transmisor FPGA.

Este es el método de validación para la afirmación RTL de que los bytes recibidos activan una salida UART formateada.

3) Sintetizar

yosys -p "read_verilog uart_monitor_top.v; synth_ecp5 -top uart_monitor_top -json uart_monitor_top.json"

4) Colocar y rutear

nextpnr-ecp5 --85k --json uart_monitor_top.json --lpf ulx3s_uart_monitor.lpf --textcfg uart_monitor_top.config

5) Empaquetar bitstream

ecppack uart_monitor_top.config uart_monitor_top.bit

6) Programar la placa

openFPGALoader -b ulx3s uart_monitor_top.bit

7) Abrir un terminal serie en el adaptador USB-UART

picocom ejemplo:

picocom -b 115200 /dev/ttyUSB0

screen ejemplo:

screen /dev/ttyUSB0 115200

Validación de hardware

Validar el comportamiento en reposo

Con el dispositivo serie externo desconectado:

  • El terminal debe permanecer en silencio.
  • El LED debe permanecer apagado después del reset.
  • mon_rx no debe ser excitado por ninguna tensión fuera de rango.

Validar con una fuente UART conocida

Configura el dispositivo externo de 3.3 V para enviar repetidamente HELLO a 115200 8N1.

Evidencia esperada en el terminal:

RX 48 OK
RX 45 OK
RX 4C OK
RX 4C OK
RX 4F OK
RX 0D OK
RX 0A OK

Este es el método de validación para la afirmación de precisión de que el monitor decodifica bytes correctamente: compara la cadena conocida transmitida con los valores hexadecimales de bytes impresos por la FPGA.

Validar el manejo de errores de trama

Mantén el monitor FPGA en 115200 8N1, pero configura el dispositivo fuente a una velocidad en baudios diferente, como 9600.

Evidencia esperada:

  • La salida se vuelve escasa, incorrecta o inexistente.
  • Algunas líneas recibidas pueden mostrar FRAMING_ERR.

Solución de problemas

Sin salida en el terminal

Comprobar:

  1. host_tx va al adaptador RX
  2. Las masas están compartidas
  3. Se abre el dispositivo serie correcto en el PC
  4. El dispositivo fuente realmente está transmitiendo
  5. La asignación de pines del LPF coincide con la placa real

El LED parpadea pero no hay texto en el PC

Causas probables:

  • Asignación incorrecta del pin host_tx
  • Cableado incorrecto del adaptador USB-UART
  • Dispositivo de terminal incorrecto en el PC

Lint o la síntesis fallan

Compruebe que:

  • Los nombres de archivo coinciden exactamente con los comandos
  • Solo uart_monitor_top.v se pasa a la síntesis de Yosys
  • El LPF usa los mismos nombres de señal de nivel superior que el Verilog

Errores de trama en cada byte

Normalmente causado por:

  • Desajuste de baudios
  • Nivel de voltaje incorrecto
  • Cableado ruidoso
  • Asignación incorrecta del pin de reloj

Capturar registros del terminal

Para guardar una sesión del monitor:

script -c "picocom -b 115200 /dev/ttyUSB0" uart_monitor_session.txt

Lista de comprobación final

  • [ ] Usé una Radiona ULX3S (Lattice ECP5-85F).
  • [ ] Mi señal serie observada es UART de 3.3 V, no RS-232 ni UART de 5 V.
  • [ ] Todas las tierras están conectadas entre sí.
  • [ ] Actualicé ulx3s_uart_monitor.lpf con pines ULX3S válidos.
  • [ ] La verificación lint de Verilator se completa correctamente.
  • [ ] La síntesis de Yosys se completa.
  • [ ] nextpnr se completa.
  • [ ] El bitstream se programa correctamente.
  • [ ] El terminal del PC está configurado a 115200 baudios.
  • [ ] El terminal muestra las líneas esperadas del monitor para un flujo de bytes conocido.

Esto te proporciona un monitor de banco UART reutilizable basado en FPGA para una línea de transmisión en la plataforma ULX3S.

        <div class="amazon-affiliate">
          <p><strong>Find this product and/or books on this topic on Amazon</strong></p>
          <p><a class="amazon-affiliate-btn" href="https://amzn.to/4mt8r4C" target="_blank" rel="nofollow sponsored noopener">Go to Amazon</a></p>
          <p class="amazon-affiliate-disclaimer">As an Amazon Associate, I earn from qualifying purchases. If you buy through this link, you help keep this project running.</p>
        </div>

Cuestionario rápido

Pregunta 1: ¿Qué placa FPGA se utiliza para el proyecto de monitor UART descrito en el texto?




Pregunta 2: ¿Cuál es la velocidad en baudios de la línea UART que se está interviniendo en este proyecto?




Pregunta 3: ¿Qué sucede en la placa FPGA cuando se recibe un carácter?




Pregunta 4: ¿Qué herramienta se utiliza para sintetizar el diseño RTL para la ECP5-85F?




Pregunta 5: ¿Cuál es el tiempo aproximado por trama de byte en 8N1 a 115200 baudios?




Pregunta 6: ¿Cuál es el voltaje de la línea UART que se está interviniendo en este proyecto?




Pregunta 7: ¿Qué tipo de salida envía el FPGA al terminal del PC por cada byte recibido?




Pregunta 8: ¿Cómo interactúa el monitor UART con el firmware del dispositivo embebido?




Pregunta 9: ¿Qué herramienta se menciona para hacer linting del diseño RTL?




Pregunta 10: ¿Cuál es uno de los casos de uso mencionados para este monitor UART?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Ingeniero en Electrónica e Informática

Ingeniero Técnico de Telecomunicación, especialidad en Electrónica, e Ingeniero en Informática (títulos oficiales en España).

Sígueme:
Scroll al inicio