LoginSignup
17
11

More than 5 years have passed since last update.

Verilogでシリアル通信入力

Last updated at Posted at 2017-11-25

FPGAでシリアル通信を受信する回路です。
ソースを行方不明にしてしまい毎回作ってい気がするので記事として投稿します。

条件

  • シリアル通信フォーマット
    • データ長  :8bit
    • ストップbit :1bit
    • パリティ  :なし
  • ボーレートとクロック
    • ボーレート:115200
    • クロック :20MHz

仕様

  • 8bitデータをパラレル出力
  • 受信完了するとデータと同時にイネーブルをアサート
  • イネーブルは1サイクル幅でネゲート

テストベンチ

テストベンチトップ

テストベンチのトップモジュールです。
UART模擬入力を生成するタスクをインクルードし、
検証対象のモジュール(UART受信回路)をインスタンスしています。

UART_RX_tb.v
`timescale 1ns/1ps

`default_nettype none

module UART_RX_tb();

    reg         i_rst_n     =1'b0   ;//リセット(L有意)
    reg         i_clk       =1'b0   ;//クロック
    wire[7:0]   o_dat               ;//受信データ
    wire        o_ena               ;//受信データイネーブル


    //UART模擬タスクをインクルード
    `include "./TB/task_UART.v"

    //クロック
    always #25 i_clk=~i_clk;

    //テストシナリオ
    initial begin
        #120 i_rst_n=1'b1;//リセット解除

        //適当にウェイトを挟んでUART模擬入力を入れる
        #5000;
        T_UART_RX (8'h65);
        #50000;
        T_UART_RX (8'h39);
    end

    //検証対象(UART受信回路)
    UART_RX
    DUT
    (.i_rst_n   (i_rst_n    )//リセット(L有意)
    ,.i_clk     (i_clk      )//クロック
    ,.i_UART_RX (uart_rx_in )//UART受信入力
    ,.o_dat     (o_dat      )//受信データ
    ,.o_ena     (o_ena      )//受信データイネーブル
    );

endmodule

UART入力模擬タスク

UART入力信号を模擬するタスクです。

  • パラメータ(RATE)を書き換えることで異なるボーレートに対応
  • timescaleが 1ns である前提でき記述してあります
  • 8bitをLABファーストで出力
task_UART.v
reg uart_rx_in=1'b1;//UART入力模擬信号

//-------------------------------------------
//UART受信入力模擬タスク
//  FPGAがRXとしてFPGAに入れる模擬入力
//-------------------------------------------
task T_UART_RX;
    parameter   RATE=115200 ;//ボーレート[bps]
    input[7:0]  dat         ;//送るデータ  
    integer     i           ;//ループ用変数
    time        BIT_CYC     ;//1ビットの周期

    begin
        $display("UART input start : 0x%2X",dat);
        //ボーレートからbit周期[ns]を算出
        BIT_CYC=1000000000/RATE;//timescaleが1nsの前提
        //スタートビット
        uart_rx_in  =1'b0;
        #BIT_CYC;
        //データ:LSBファースト
        for(i=0; i<8; i=i+1)begin
            uart_rx_in  =dat[i];
            #BIT_CYC;
        end
        //ストップビット
        uart_rx_in  =1'b1;
        #BIT_CYC;
    end
endtask

UART受信回路

回路構成

  • 微分回路で立下り(スタートビット)を検出
  • スタートビット検出したら"busy"状態にする
  • busyの間分周カウンタを回してデータの中央でサンプリング
  • 10bit受信したbusyを解除
  • busy解除した次のサイクルでスタートbitをストップbitが正常であれば8bitデータを出力する

ソースコード

UART_RX.v"
//-------------------------------------------------------------------
//UART受信回路
//  ・クロック         20[MHz]     => 50[ns]   
//  ・ボーレート      115200[bps] => 8.68[us]
//  ・分周カウント数    174(0xAE)[カウント/bit]
//  ・パリティ             なし
//  ・ストップbit      1bit
//  ・metastable対策 3段FF
//  ・ノイズ対策      実装しない(バッファICと接続を想定)
//-------------------------------------------------------------------
module UART_RX #
    (parameter  DIV_WID=8       //分周カウンタbit幅
    ,parameter  DIV_CNT=8'hAE   //データサンプル周期(クロックサイクル数)
    )
    (input  wire        i_rst_n     //リセット(L有意)
    ,input  wire        i_clk       //クロック
    ,input  wire        i_UART_RX   //UART受信入力
    ,output wire[7:0]   o_dat       //受信データ
    ,output wire        o_ena       //受信データイネーブル
    );

    reg[2:0]            rx      ;//エッジ検出微分FF
    wire                start   ;//開始パルス
    wire                fin     ;//終了パルス
    reg                 busy    ;//処理中ビジー
    reg[DIV_WID-1:0]    div     ;//分周カウンタ
    reg[4:0]            cnt     ;//ビットカウント
    wire                dt_get  ;//データラッチトリガ
    reg[9:0]            sp_ff   ;//シリパラFF
    reg                 chk_trg ;//データチェックトリガ
    reg[7:0]            dat     ;//受信データ
    reg                 ena     ;//受信データイネーブル

    //エッジ検出微分FF
    always@(posedge i_clk or negedge i_rst_n)begin
        if(~i_rst_n)
            rx  <=3'b111;
        else
            rx  <={rx[1:0],i_UART_RX};
    end

    //開始パルス:立下り∧非ビジー
    assign start    =(rx[2:1]==2'b10)&(busy==1'b0) ? 1'b1 : 1'b0;

    //終了パルス:10bit目のサンプルタイミング
    assign  fin     =(cnt==5'd9)&(dt_get==1'b1) ? 1'b1 : 1 'b0;

    //受信中ビジーフラグ
    always@(posedge i_clk or negedge i_rst_n)begin
        if(~i_rst_n)
            busy    <=1'b0;
        else if(start)
            busy    <=1'b1;
        else if(fin)
            busy    <=1'b0;
    end

    //分周カウンタ
    always@(posedge i_clk or negedge i_rst_n)begin
        if(~i_rst_n)
            div <=8'd0;
        else if(start)
            div <={ 1'b0,DIV_CNT[DIV_WID-1:1] };//スタートは1/2周期で設定
        else if(busy)
            begin
                if(div==8'd0)
                    div <=DIV_CNT;
                else
                    div <=div-1;
            end
        else
            div <=8'd0;
    end

    //ビットカウンタ
    always@(posedge i_clk or negedge i_rst_n)begin
        if(~i_rst_n)
            cnt <=5'd0;
        else if(start)
            cnt <=5'd0;//開始時ゼロクリア
        else if(dt_get)
            cnt <=cnt+1;//データゲット毎にインクリメント
    end

    //データラッチトリガ
    assign dt_get   =(busy==1'b1)&(div==8'd0) ? 1'b1 : 1'b0;

    //シリパラFF
    always@(posedge i_clk or negedge i_rst_n)begin
        if(~i_rst_n)
            sp_ff   <=10'h3FF;
        else if(dt_get)
            sp_ff   <={ rx[2],sp_ff[9:1] };//LSBファーストを受信
    end

    //データチェックトリガ
    always@(posedge i_clk or negedge i_rst_n)begin
        if(~i_rst_n)
            chk_trg <=1'b0;
        else
            chk_trg <=fin;//終了トリガの次のサイクルでデータチェック
    end

    //受信データ&イネーブル
    always@(posedge i_clk or negedge i_rst_n)begin
        if(~i_rst_n)
            begin
                dat <=8'h00;    //リセット
                ena <=1'b0;
            end
        else if ((chk_trg==1'b1)    //データチェックトリガが来たとき
                &(sp_ff[0]==1'b0)   //スタートbitが0で
                &(sp_ff[9]==1'b1))  //ストップbitが1ならOK
            begin
                dat <=sp_ff[8:1];
                ena <=1'b1;
            end
        else
            begin
                dat <=dat   ;//保持
                ena <=1'b0  ;//1サイクル幅パルスにする
            end
    end

    //出力ポート接続
    assign  o_dat   =dat;
    assign  o_ena   =ena;

endmodule

シミュレーション結果

テストシナリオで設定した値が受信できています

image.png

17
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
11