verilog~UART通信受信機(RX)編~

初めに

お久しぶりです、Keymaleです。先週はUARTの送信機について紹介しました。結構検索して見ていただいたみたいなので、今回は受信機のほうを紹介していきます。受信機のコード内に分周器が出てきますが、詳細は過去記事を見てください。

UART規格

規格についても詳細はこちらを見てください。

通信レート(ボーレート)は9600, 19200, 38400, 76800, 115200 bpsなどがあります。今回は前回の送信機の レートと合わせて 9600 bpsとします。メインクロックは50 MHzとして、9600 bpsにするには5208で分周しますが、受信機側は送信側クロックの4倍のクロックで受信データをたたきます。4回たたいて、3回以上HIGHなら1、LOWなら0と多数決論理にて、受信ビットを決定していきます。そのため、4倍のクロックになるように、50 MHzで1302で分周します。

UART_RX module

それでは早速UARTのRXモジュールを見ていきましょう。以下にコードを記載しました。


module UART_RX(
  input clk, //50MHz
  input reset_n,
  input UART_RXD,
  output reg[7:0]get_data );

//////////UART RX clk generate/////////
reg  [15:0]cnt_p;
reg  [15:0]cnt_n;
reg  out_p;
reg  out_n;
parameter rate_div = 1302;/////(50M/9600)/4 = 1302

assign uart_rx_clk = out_p ^ out_n;
initial begin
    cnt_p = 16'd1;
    cnt_n = 16'd1;
    out_p = 0;
    out_n = 0;
end
always@(posedge clk)begin
    cnt_p <= cnt_n + 16'd1;
    if(cnt_n == rate_div)begin
        cnt_p <= 16'd1;
        out_p <= ~out_p; 
    end
end
always@(negedge clk)begin
    cnt_n <= cnt_p + 16'd1;
    if(cnt_p == rate_div)begin
        cnt_n <= 16'd1;
        out_n <= ~out_n;
    end
end
//////////UART RX clk generate end/////////
//////////UART RX module generate/////////
reg[4:0] state;
reg[2:0] clk_cnt;
reg[2:0] get_startbit;
initial begin
    get_data        = '0;
    state           = '0;
    clk_cnt         = '0;
    get_startbit    = '1;
end
always@(posedge uart_rx_clk)begin
    if(!reset_n)begin
        state <= 0;
        get_data <= 0;
        clk_cnt <= 0;
        get_startbit <= 1;
    end else begin
        get_startbit <= {get_startbit[1:0], UART_RXD};
        if(state == 0)begin
            if(get_startbit == 3'b000)begin
                state <= 5'd1;
            end
        end else if(state <= 5'd8)begin
            if(clk_cnt == 3'd3)begin
                state   <= state + 5'd1;
                clk_cnt     <= 0;
                get_data[state-1] <= UART_RXD;
            end else begin
                clk_cnt <= clk_cnt + 1;
            end
        end else if(state == 5'd9)begin
            if(clk_cnt == 3'd3)begin
                state   <= 0;
                clk_cnt     <= 0;
            end else begin
                clk_cnt <= clk_cnt + 1;
            end
        end
    end
end
endmodule

以上になります。CASE文で書く方法もあるのですが、重複が多くなり無駄に行数が多くなってしまうので、多少深くなってしまいますが、IF文でごり押しで書いてみました。

ModelSimでの検証結果

一応modelsimで動作を確認しましたので、以下に結果を載せておきます。

00111011のデータを前回作った送信機から送信して、今回作った受信機側で同じで00111011を受信できていることが確認できるかと思います。

最後まで見てくださってありがとうございます。最近は育児ブログも書いているのでそちらのほうもよかったら見てください。verilog記事はいろいろ書いてきましたが、次はsystem-verilogで実装されたenumlateの使い方について説明していこうかと思います。後はwordpressでのTeX,LaTexの使い方とか、pythonでのグラフ描画(Pyplot)などを掲載してく予定です。

お手すきでしたらコメントもよろしくお願いします。

それでは次回も見てくださいね。