Altifigence Academy

63 / 63 · Concepto

Práctica integradora: transmisor UART y temporización de tramas

Combine un contador divisor, un registro de desplazamiento y control de flujo para generar tramas 8N1 y verificar los límites entre bits.

Fijar primero la especificación de la trama

Conocimientos previos: contadores, registrosRegistro Circuito que almacena varios bits de estado. Los registros síncronos de este curso capturan la entrada especificada en el flanco de reloj. Más información de desplazamiento, valid/ready y resetReset Control que devuelve el estado a un valor inicial especificado. Deben definirse su carácter síncrono o asíncrono y su prioridad frente a otros controles. Más información síncrono.

El formato UARTUART Dispositivo de comunicación serie que usa bits de inicio, datos y parada sin una línea de reloj independiente. Emisor y receptor deben acordar la tasa de baudios y el formato de trama. 8N1 contiene un bit de inicio, ocho bits de datos, ningún bit de paridad y un bit de parada. El reposo y la parada valen 1; el inicio vale 0. Los datos se envían empezando por el LSBLSB Bit de menor peso. Deben comprobarse conjuntamente el sentido de la notación y las reglas de indexación. Más información. Todos los bits duran lo mismo. Esta práctica trata solo la transmisión y supone que el proveedor de datos usa el mismo reloj.

Bactual=fclkD,ϵ=Bactual−BtargetBtarget×100%B_{\mathrm{actual}}=\frac{f_{\mathrm{clk}}}{D},\qquad \epsilon=\frac{B_{\mathrm{actual}}-B_{\mathrm{target}}}{B_{\mathrm{target}}}\times100\%

Con un reloj de 48 MHz y un objetivo de 115200 baudios, puede elegirse el divisor entero D=417. La tasa real es de unos 115108 baudios, con un error aproximado del −0,080 %. La tolerancia total, incluido el receptor, debe comprobarse en las especificaciones de los dispositivos.

Desplegar 0x53 en el orden real de transmisión

0x53 se escribe 01010011, pero la línea transmite desde el bit de menor peso: 1, 1, 0, 0, 1, 0, 1, 0. En el diagrama, una columna representa un período de bit de D ciclos de reloj del sistema. Se muestra una columna de reposo antes y después para facilitar la comparación.

UART 8N1 · 0x53 · Primero el LSB
Ver los datos de la forma de onda
Datos de la forma de onda: cada carácter es un intervalo; un punto conserva el estado anterior; p representa un ciclo de reloj.
SeñalForma de ondaValores del bus
tx101.0.10101.
fase234523452345reposo → inicio → d0 → d1 → d2 → d3 → d4 → d5 → d6 → d7 → parada → reposo

Desde el inicio hasta el final del bit de parada transcurren exactamente 10D ciclos de reloj. Los unos o ceros consecutivos también ocupan un período de bit cada uno: que la línea no cambie no significa que desaparezca un bit.

Capturar cada byte una sola vez

La implementación siguiente exige que CLKS_PER_BIT sea una constante mayor o igual que 1. Mientras busy está activo no se aceptan entradas y se usan solo los datos capturados al iniciar la trama. No se crea un nuevo reloj de bits: un contador determina cuándo avanzar dentro del dominio de reloj original.

SystemVerilog
module uart_tx #(
    parameter int unsigned CLKS_PER_BIT = 417,
    localparam int CW = (CLKS_PER_BIT <= 1) ? 1 : $clog2(CLKS_PER_BIT)
) (
    input  logic       clk, rst,
    input  logic       in_valid,
    output logic       in_ready,
    input  logic [7:0] in_data,
    output logic       tx, busy, done
);
    logic [9:0] frame;
    logic [3:0] bits_left;
    logic [CW-1:0] ticks_left;
    assign in_ready = !busy;

    always_ff @(posedge clk) begin
        if (rst) begin
            tx         <= 1'b1;
            busy       <= 1'b0;
            done       <= 1'b0;
            bits_left  <= 4'd0;
            ticks_left <= '0;
        end else begin
            done <= 1'b0;
            if (!busy) begin
                if (in_valid) begin
                    frame      <= {1'b1, in_data, 1'b0};
                    tx         <= 1'b0;
                    busy       <= 1'b1;
                    bits_left  <= 4'd10;
                    ticks_left <= CW'(CLKS_PER_BIT-1);
                end
            end else if (ticks_left != 0) begin
                ticks_left <= ticks_left - 1'b1;
            end else if (bits_left == 4'd1) begin
                tx   <= 1'b1;
                busy <= 1'b0;
                done <= 1'b1;
            end else begin
                frame      <= {1'b1, frame[9:1]};
                tx         <= frame[1];
                bits_left  <= bits_left - 1'b1;
                ticks_left <= CW'(CLKS_PER_BIT-1);
            end
        end
    end
endmodule

Si E0 es el flanco de aceptación, tx pasa al bit de inicio, 0. Después avanza al siguiente bit a intervalos exactos de D ciclos. En E0+9D comienza el bit de parada y en E0+10D baja busy y se activa done durante un ciclo. El último bit de parada también debe durar D períodos completos.

Como in_ready=0 hasta justo antes del flanco de finalización, esta implementación puede aceptar la siguiente trama, como pronto, en el flanco posterior. El intervalo entre aceptaciones de tramas consecutivas es, por tanto, de 10D+110D+1 ciclos. Ese ciclo de reposo forma parte del comportamiento correcto.

Predecir a mano la secuencia de bits

Los datos de 8'h53, empezando por el LSB, son 1,1,0,0,1,0,1,0. La trama completa sigue este orden:

Ordenstartd0d1d2d3d4d5d6d7stop
tx0110010101

La verificación no debe limitarse a contar los cambios de tx, porque dos bits consecutivos iguales no producen una transición. Tome el inicio de la trama como referencia, muestree el centro de cada período de bit y compare con el valor esperado. Compruebe también la duración de los bits de inicio y parada. Para pruebas unitarias rápidas, use divisores pequeños como D=1, 2 y 3.

Extender el diseño con una FIFO

Una FIFOFIFO Cola en la que el primer dato que entra es el primero que sale. Las condiciones de lleno y vacío y las reglas de lectura y escritura simultáneas forman parte del contrato. Más información con lectura registrada actualiza dout y dout_valid justo después del flanco que acepta pop. El siguiente circuito del mismo reloj muestrea esa respuesta en el próximo flanco de subidaFlanco ascendente Instante en que el reloj pasa de 0 a 1. Se distingue del nivel alto, que abarca todo el intervalo con CLK=1. Más información. Si se envía al UART el dout del mismo flanco en que se acepta pop, puede usarse el valor anterior. Añada un búfer de una posición para conservar el byte devuelto y un estado pending, y entregue el byte exactamente una vez cuando el UART esté ready.

La verificación final incluye 00, FF, 01, 80, 55 y 53, cambios de entrada durante busy, tramas consecutivas y reset durante una trama. La política de reset aborta la transmisión y devuelve tx al reposo, 1. El receptor puede ver una trama válida pese a la interrupción, por lo que el formato de trama UART por sí solo no garantiza detectar que la transferencia se ha abortado. Si es obligatorio descartar los datos afectados, defina un acuerdo del sistema que coordine el reset del receptor con el transmisor o proporcione una señal de aborto independiente al receptor.

Lectura adicional: Microchip — EUSART and 8N1 framing

Inténtalo tú

Con CLKS_PER_BIT=4, se acepta un dato en E0. Determine los flancos en que comienzan d0 y stop y en que se genera done. Si hay un nuevo in_valid=1 en E40, ¿se acepta el siguiente byte en ese flanco?

Leer la explicación

d0 comienza justo después de E4, stop después de E36 y done después de E40. Justo antes de E40, busy=1 e in_ready=0, por lo que aún no se acepta el nuevo byte. El emisor debe mantener valid y data; el siguiente byte puede aceptarse en E41. Cada trama dura 40 ciclos de reloj y el intervalo mínimo entre aceptaciones es de 41 ciclos.

Tu elección se aplica a este navegador. Puedes cambiarla en el pie de página.