Altifigence Academy

63 / 63 · 概念

総合実習:UART 送信器とフレームのタイミング

分周カウンタ・シフトレジスタ・フロー制御を組み合わせて 8N1 フレームを作り、ビット境界を検証する。

先にフレーム仕様を固定する

前提知識:カウンタ、シフトレジスタレジスタ 複数ビットの状態を記憶する回路です。このコースの同期式レジスタは、クロックエッジで定められた入力を記憶します。 詳しく見る、valid/ready、同期リセットリセット 状態を仕様で定めた初期値に戻す制御です。同期式か非同期式か、他の制御より優先されるかを合わせて定めます。 詳しく見る。

UARTUART 別のクロック線を使わず、スタートビット・データ・ストップビットでシリアル通信する装置です。送受信側でボーレートとフレーム形式を合わせる必要があります。 の 8N1 形式は、開始ビット 1 個、データ 8 ビット、パリティなし、停止ビット 1 個で構成する。idle と stop は 1、start は 0 で、データは LSBLSB 最も低い桁の重みを持つビットです。表記方向とインデックス規則を合わせて確認します。 詳しく見る から送る。すべてのビットを同じ長さだけ保持する。この実習は送信だけを扱い、同じクロックを使うデータ供給側を仮定する。

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

48 MHz で 115200 baud を目標にするなら、整数の分周値 D=417 を選べる。実際の速度は約 115108 baud、誤差は約 -0.080% である。受信側まで含めた許容誤差は、デバイス仕様で確認する必要がある。

0x53 を実際の送信順に展開する

0x53 の表記は 01010011 だが、線路には下位ビットから 1、1、0、0、1、0、1、0 の順に出る。図の 1 マスはシステムクロックの 1 周期ではなく、D クロックからなるビット期間である。前後の idle 区間は、比較のために 1 マスずつ示している。

UART 8N1・0x53・LSB から送信
波形データを表示
波形データ:各文字は一つの区間、ドットは前の状態の保持、p はクロック 1 周期を表します。
信号波形バスの値
tx101.0.10101.
区間234523452345待機 → 開始 → d0 → d1 → d2 → d3 → d4 → d5 → d6 → d7 → 停止 → 待機

start から stop までは正確に 10D クロックである。連続する 1 や 0 もそれぞれ一つのビット期間を占める。線路に遷移がないからといって、ビットが消えるわけではない。

データ 1 バイトを一度だけ取り込む

以下の実装では、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 が 1 サイクル発生する。最後の stop 区間も D 周期を満たす必要がある。

この実装では終了エッジ直前まで in_ready=0 なので、次のフレームを受け付けるのは最速でもその次のエッジである。したがって、連続フレームの受け付け間隔は 10D+110D+1 クロックになる。この 1 クロックの idle 間隔も正常動作の一部である。

ビット列を手で予測する

8'h53 のデータビットは、LSB から 1,1,0,0,1,0,1,0 である。フレーム全体は次の順序になる。

順序startd0d1d2d3d4d5d6d7stop
tx0110010101

検証では tx が変化した回数だけを数えてはならない。同じ値のビットが続くと遷移しないためである。フレーム開始を基準に各ビット区間の中央をサンプリングし、期待するビットと照合する。start・stop の長さも確認する。高速な単体検証には D=1、2、3 のような小さい値も使う。

FIFO につなぐ拡張設計

レジスタ付き読み出しの FIFOFIFO 先に入れたデータを先に取り出すキューです。満杯・空・同時読出しと書込みの許可規則も動作の取り決めに含まれます。 詳しく見る は、pop 受け付けエッジ直後に dout と dout_valid を更新する。同じクロックを使う次の回路は、その次の立ち上がりエッジ立上りエッジ クロックが 0 から 1 に変わる瞬間です。CLK=1 の区間全体を指すレベルとは区別します。 詳しく見るで応答をサンプリングする。pop を受け付けるまさにそのエッジの dout を UART へ送ると、以前の値である可能性がある。返されたバイトを保持する 1 要素バッファと pending 状態を設け、UART が ready のときに正確に一度だけ渡すよう接続する。

最終検証には、00・FF・01・80・55・53、busy 中に変わる入力、連続フレーム、フレーム途中の reset を含める。reset は送信を中断し、tx を idle=1 に戻す方針である。それでも受信器には有効なフレームに見える場合があるため、UART のフレーム形式だけでは転送の中断を必ず検出できるとは限らない。該当データを必ず破棄する必要がある場合は、受信器の reset を送信器と連動させるか、転送中断を通知する別の信号を受信器に送るシステム上の取り決めを明示する。

参考資料:Microchip — EUSART and 8N1 framing

自分で考えてみましょう

CLKS_PER_BIT=4 とし、E0 でデータを受け付けた。d0 の開始、stop の開始、done の発生エッジをそれぞれ求めよ。E40 で新しい in_valid=1 がある場合、そのエッジで次のバイトを受け付けるか。

解説を見る

d0 は E4 直後、stop は E36 直後、done は E40 直後に発生する。E40 直前は busy=1 で in_ready=0 のため、新しいバイトはまだ受け付けられない。送信側は valid と data を保持する必要があり、次のバイトは E41 で受け付けられる。各フレームは 40 クロック、受け付け間隔は最小 41 クロックである。

選択はこのブラウザに適用されます。フッターからいつでも変更できます。