Altifigence Academy

52 / 52 · Concept

Capstone: a UART transmitter

분주 카운터·시프트 레지스터·흐름 제어를 결합해 8N1 프레임을 만들고 비트 경계를 검증합니다.

La traduction n’est pas encore disponible. La leçon originale est affichée. (한국어)

프레임 명세를 먼저 고정합니다

선수 지식: 카운터, 시프트 레지스터, valid/ready, 동기식 리셋.

UART의 8N1 형식은 시작 비트 1개, 데이터 8개, parity 없음, 정지 비트 1개입니다. idle과 stop은 1, start는 0이며 데이터는 LSB부터 보냅니다. 모든 비트는 동일한 길이로 유지합니다. 이 실습은 송신만 다루며 같은 클록의 데이터 제공자를 가정합니다.

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

48MHz에서 115200 baud를 목표로 하면 정수 분주값 D=417을 선택할 수 있습니다. 실제 속도는 약 115108 baud, 오차는 약 -0.080%입니다. 수신 측까지 포함한 허용 오차는 장치 사양으로 검토해야 합니다.

0x53을 실제 전송 순서로 펼치기

0x53의 표기는 01010011이지만, 선로에는 낮은 비트부터 1, 1, 0, 0, 1, 0, 1, 0 순서로 나갑니다. 그림에서 한 칸은 시스템 클록 한 주기가 아니라 D클록으로 이루어진 비트 기간입니다. 앞뒤 idle 구간은 비교를 위해 한 칸씩 표시했습니다.

UART 8N1 · 0x53 · LSB first
View waveform data
Wave data: each character is one interval; a dot holds the previous state; p is a clock cycle.
SignalWaveBus values
tx101.0.10101.
phase234523452345idle → start → d0 → d1 → d2 → d3 → d4 → d5 → d6 → d7 → stop → idle

start부터 stop까지는 정확히 10D클록입니다. 연속된 1이나 0도 각각 한 비트 기간을 차지하며, 선로에 전이가 없다고 비트가 사라지는 것은 아닙니다.

데이터 한 바이트를 한 번만 캡처합니다

아래 구현은 CLKS_PER_BIT가 1 이상의 상수라는 계약입니다. busy일 때 입력은 수락하지 않고, 프레임 시작 시 캡처한 data만 사용합니다. bit-clock을 새 클록으로 만들지 않고 원래 클록 안에서 카운터로 이동 시점을 제어합니다.

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

입력 수락 에지를 E0라고 하면 tx가 start=0으로 바뀝니다. 이후 정확히 D개 클록 간격으로 다음 비트로 이동합니다. E0+9D에서 stop 비트가 시작되고 E0+10D에서 busy가 내려가며 done이 한 사이클 발생합니다. 마지막 stop 구간도 D주기를 채워야 합니다.

이 구현은 종료 에지 직전까지 in_ready=0이므로 다음 프레임은 가장 빨라도 그 다음 에지에 수락합니다. 따라서 연속 프레임 수락 간격은 10D+110D+1클록입니다. 이 한 클록의 idle 간격도 정상 동작의 일부입니다.

비트열을 손으로 예측합니다

8'h53의 데이터 비트는 LSB부터 1,1,0,0,1,0,1,0입니다. 전체 프레임은 다음 순서입니다.

순서startd0d1d2d3d4d5d6d7stop
tx0110010101

검증에서는 tx가 바뀐 횟수만 세면 안 됩니다. 연속된 같은 값의 비트에서는 전환이 없기 때문입니다. 프레임 시작을 기준으로 각 비트 구간의 중앙을 샘플링해 예상 비트와 대조하고, start·stop의 길이도 확인합니다. 빠른 단위 검증에는 D=1, 2, 3 같은 작은 값도 사용하세요.

FIFO와 연결하는 확장 설계

등록형 읽기 FIFO는 pop 수락 에지 직후 dout과 dout_valid를 갱신합니다. 같은 클록의 다음 회로는 그 다음 상승 에지에서 응답을 샘플링합니다. pop을 수락하는 바로 그 에지의 dout을 UART에 보내면 이전 값일 수 있습니다. 반환된 바이트를 보관하는 1칸 버퍼와 pending 상태를 두고, UART가 ready일 때 정확히 한 번 전달하도록 연결하세요.

최종 검증에는 00·FF·01·80·55·53, busy 중 바뀌는 입력, 연속 프레임, 프레임 중 reset을 포함합니다. reset은 전송을 중단하고 tx를 idle=1로 되돌린다는 정책입니다. 수신기는 중단된 프레임을 정상 데이터로 취급하면 안 됩니다.

추가 읽기: Microchip — EUSART and 8N1 framing

Essayez vous-même

CLKS_PER_BIT=4이고 E0에서 데이터를 수락했습니다. d0 시작, stop 시작, done 발생 에지를 각각 구하세요. E40에서 새 in_valid=1이면 그 에지에 다음 바이트가 수락됩니까?

Lire l’explication

d0는 E4, stop은 E36, done은 E40 직후입니다. E40 직전에는 busy=1이어서 in_ready=0이므로 새 바이트는 아직 수락되지 않습니다. 송신자는 valid와 data를 유지해야 하며, 다음 바이트는 E41에서 수락할 수 있습니다. 각 프레임은 40클록이고 수락 간격은 최소 41클록입니다.

Votre choix s’applique à ce navigateur. Modifiez-le à tout moment en bas de page.