Altifigence Academy

63 / 63 · Conceito

Prática integradora: transmissor UART e temporização de quadros

Combine um contador divisor, um registrador de deslocamento e controle de fluxo para gerar quadros 8N1 e verificar os limites entre bits.

Fixar primeiro a especificação do quadro

Conhecimentos prévios: contadores, registradoresRegistrador Circuito que armazena vários bits de estado. Os registradores síncronos deste curso capturam a entrada especificada na borda de clock. Saiba mais de deslocamento, valid/ready e resetReset Controle que retorna o estado a um valor inicial especificado. É preciso definir se é síncrono ou assíncrono e sua prioridade em relação a outros controles. Saiba mais síncrono.

O formato UARTUART Dispositivo de comunicação serial que usa bits de início, dados e parada sem uma linha de clock separada. Transmissor e receptor devem concordar com a taxa de baud e o formato do quadro. 8N1 contém um bit de início, oito bits de dados, nenhum bit de paridade e um bit de parada. O repouso e a parada valem 1; o início vale 0. Os dados são enviados começando pelo LSBLSB Bit de menor peso. É preciso conferir em conjunto o sentido da notação e as regras de indexação. Saiba mais. Todos os bits têm a mesma duração. Esta prática trata somente da transmissão e supõe que o fornecedor de dados usa o mesmo clock.

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\%

Com um clock de 48 MHz e uma meta de 115200 baud, pode-se escolher o divisor inteiro D=417. A taxa real é de aproximadamente 115108 baud, com erro de cerca de −0,080%. A tolerância total, incluindo o receptor, deve ser conferida nas especificações dos dispositivos.

Desdobrar 0x53 na ordem real de transmissão

0x53 é escrito como 01010011, mas a linha transmite a partir do bit de menor peso: 1, 1, 0, 0, 1, 0, 1, 0. No diagrama, uma coluna representa um período de bit de D ciclos do clock do sistema. Uma coluna de repouso é mostrada antes e depois para facilitar a comparação.

UART 8N1 · 0x53 · LSB primeiro
Ver os dados da forma de onda
Dados da forma de onda: cada caractere é um intervalo; um ponto mantém o estado anterior; p representa um ciclo de clock.
SinalForma de ondaValores do barramento
tx101.0.10101.
fase234523452345repouso → início → d0 → d1 → d2 → d3 → d4 → d5 → d6 → d7 → parada → repouso

Do início ao fim do bit de parada, passam exatamente 10D ciclos de clock. Uns ou zeros consecutivos também ocupam um período de bit cada: a ausência de uma transição na linha não significa que um bit desapareceu.

Capturar cada byte uma única vez

A implementação abaixo exige que CLKS_PER_BIT seja uma constante maior ou igual a 1. Enquanto busy está ativo, nenhuma entrada é aceita, e somente os dados capturados no início do quadro são usados. Não se cria um novo clock de bits: um contador determina quando avançar dentro do domínio de clock 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

Se E0 é a borda de aceitação, tx passa ao bit de início, 0. Depois, avança para o próximo bit em intervalos exatos de D ciclos. Em E0+9D, começa o bit de parada; em E0+10D, busy baixa e done é ativado por um ciclo. O último bit de parada também precisa durar D períodos completos.

Como in_ready=0 até imediatamente antes da borda de conclusão, esta implementação só pode aceitar o próximo quadro, no mínimo, na borda posterior. O intervalo entre aceitações de quadros consecutivos é, portanto, de 10D+110D+1 ciclos. Esse ciclo de repouso faz parte do comportamento correto.

Prever manualmente a sequência de bits

Os dados de 8'h53, começando pelo LSB, são 1,1,0,0,1,0,1,0. O quadro completo segue esta ordem:

Ordemstartd0d1d2d3d4d5d6d7stop
tx0110010101

A verificação não deve se limitar a contar mudanças de tx, pois dois bits consecutivos iguais não produzem transição. Use o início do quadro como referência, amostre o centro de cada período de bit e compare com o valor esperado. Confira também a duração dos bits de início e parada. Para testes unitários rápidos, use divisores pequenos, como D=1, 2 e 3.

Estender o projeto com uma FIFO

Uma FIFOFIFO Fila na qual o primeiro dado a entrar é o primeiro a sair. As condições de cheio e vazio e as regras de leitura e escrita simultâneas fazem parte do contrato. Saiba mais com leitura registrada atualiza dout e dout_valid logo após a borda que aceita pop. O circuito seguinte do mesmo clock amostra a resposta na próxima borda de subidaBorda de subida Instante em que o clock passa de 0 para 1. É diferente do nível alto, que abrange todo o intervalo com CLK=1. Saiba mais. Enviar à UART o dout da própria borda que aceita pop pode usar o valor anterior. Adicione um buffer de uma posição para guardar o byte retornado e um estado pending, entregando o byte exatamente uma vez quando a UART estiver ready.

A verificação final inclui 00, FF, 01, 80, 55 e 53, mudanças de entrada durante busy, quadros consecutivos e reset durante um quadro. A política de reset aborta a transmissão e devolve tx ao repouso, 1. O receptor ainda pode ver um quadro válido; portanto, o formato do quadro UART, por si só, não garante a detecção de uma transmissão interrompida. Se os dados afetados tiverem de ser descartados, defina um contrato de sistema que coordene o reset do receptor com o transmissor ou forneça um sinal separado de interrupção da transmissão ao receptor.

Leitura adicional: Microchip — EUSART and 8N1 framing

Experimente

Com CLKS_PER_BIT=4, um dado é aceito em E0. Determine as bordas em que d0 e stop começam e em que done é gerado. Se houver um novo in_valid=1 em E40, o próximo byte será aceito nessa borda?

Ler a explicação

d0 começa logo após E4, stop após E36 e done após E40. Imediatamente antes de E40, busy=1 e in_ready=0, portanto o novo byte ainda não é aceito. O transmissor deve manter valid e data; o próximo byte pode ser aceito em E41. Cada quadro dura 40 ciclos de clock, e o intervalo mínimo entre aceitações é de 41 ciclos.

Sua escolha vale neste navegador. Altere-a a qualquer momento no rodapé.