03_シリアルポートRS232

1. 理論的研究

1.1 シリアルポート用語の概要

1.1.1 非同期通信

UART と SPI および IIC の違いは、非同期通信インターフェイスであることです。非同期通信では、受信側はデータがいつ
到着するかわからないため、双方が独自のクロックを持っている必要があります。データ送信プロセス中にクロックは必要ありません。送信側の送信時間間隔は不均一であり、受信側はデータのスタート ビットとストップ ビットを利用して情報の同期を実現します。

1.1.2 同期通信

SPI と IIC は同期通信インターフェイスです (詳細は後の章で説明します)。
同期通信では、双方が同じ周波数のクロックを使用します。データ送信中に、クロックもデータと一緒に送信されます。送信側と受信側で使用されるクロックはホストによって提供されます。

1.1.3 全二重

全二重、つまりデータを同時に送信および受信できます。

1.1.4 半二重

データ送信とデータ受信はどちらか一方のみを同時に実行できます。

1.1.5 UART通信

UART通信には信号線が2本しかなく、1つはtxという送信データポート線、もう1つはrxという受信データポート線です。PCの場合、そのtxをFPGAのrxに接続する必要があります。同様に、PCのrxもFPGAのtxに接続する必要があります。txが2つまたはrxが2つ接続されている場合、データは正常に送受信できませんので、rxとtxは本体に対する相対的なものであることを覚えておいてください。UART は全二重を実現できます。つまり、データの送信と受信を同時に行うことができます。
ここに画像の説明を挿入

1.2 RS-232信号線

RS-232 信号線ポート規格は、コンピュータ、ルータ、モデム (MODEN、通称
「キャット」) 間の通信によく使用され、この通信システムでは、機器はデータ端末装置 DTE (コンピュータ、ルータ) とデータ通信装置 DCE (モデム) に分けられます。この通信モデルを使用して、信号線の接続と各信号線の役割を説明します。古いデスクトップ コンピュータには通常、RS-232 標準 COM ポート (DB9 インターフェイスとも呼ばれます) があり、
ここに画像の説明を挿入
DB9 インターフェイスの信号線は
ここに画像の説明を挿入
ここに画像の説明を挿入
「ストレート」シリアル ポート接続を示します。
ここに画像の説明を挿入

1.3 RS232 通信プロトコルの概要

1) RS232 は UART の一種で、クロック ラインはなく、データ ラインは 2 本 (rx と tx) のみで、どちらも 1 ビット幅です。ここで、rx はデータを受信するための回線、tx はデータを送信するための回線です。
2) rx ビット幅は 1 ビットで、PC がシリアル ポート デバッグ アシスタントを通じて 8 ビット データを FPGA に送信すると、FPGA はシリアル ポート rx を通じて最下位ビットから最上位ビットまでビットごとに受信し、最終的にビットは FPGA 内で 8 ビット データに結合されます。(受信したデータは最終的に 8 ビット データ、1 ビット データ
に結合されます) 3) tx ビット幅は 1 ビットで、FPGA がシリアル ポートを介して 8 ビット データを PC に送信する場合、FPGA は 8 ビット データを 1 つずつ tx ラインを介して PC に送信し、最下位ビットから最上位ビットに順番に送信します。 RS232 プロトコルに従ってシリアル ポート アシスタントを介して。
4) シリアル ポート データの送受信はフレーム構造に基づいており、フレームごとにデータを送受信します。中央に 8 ビットの有効なデータが含まれることに加えて、各フレームには、各フレームの先頭に 0 に固定されたスタート ビットがなければならず、各フレームの終わりには 1 に固定されたストップ ビットもなければなりません。つまり、最も基本的なフレーム構造 (パリティなどを除く) は 10 ビットですデータの送受信がない場合、rx と tx はアイドル状態になります。このとき、rx ラインと tx ラインは
ハイ レベルに維持されます。データ フレームの送信がある場合は、最初にスタート ビット、次に 8 ビットのデータ ビット、次に 1 ビットのストップ ビットがあります。その後、rx と tx は引き続きアイドル状態になり、次のデータ送信を待ちます。図に示すのは、基本的な RS232 フレーム構造です。
ここに画像の説明を挿入

5) ボーレート: 情報伝送チャネルでは、データ情報を運ぶ信号単位をシンボルと呼びます (シリアルポートの送信は 1 ビットであるため、シンボルは 2 進数を表します)。1 秒あたりに信号を通じて送信されるシンボルの数はシンボルの伝送速度と呼ばれ、ボーレートと呼ばれ、一般的に使用される記号「ボー」、その単位は「ボー/秒 (Bps)」です。シリアル ポートの一般的なボー レートは 4800、9600、115200 などです。シリアル ポートの章を説明するために 9600 のボー レートを選択します。

6) ビットレート: 1 秒あたりに通信チャネルによって送信される情報の量はビット送信レートと呼ばれ、ビットレートと呼ばれ、その単位は「ビット/秒 (bps)」です。ビット レートはボー レートから計算できます。式は次のとおりです。ビット レート = ボー レート * 単一の変調状態に対応する 2 進数。ボーレートが 9600 の場合、シリアル ポートのビット レートは 9600Bps 1 ビット = 9600bps となります。

7) ボーレートとビットレートの違い
ビットレートとボーレートの関係を議論するには、デコード要素とビットの関係を理解する必要があります。先ほどの例で挙げたバス、地下鉄、タクシーが乗車人数に応じて異なるのと同様に、異なるコード要素を異なる桁のビットで表すこともできます。シンボルに必要なビット数は、シンボルがサポートする状態の数によって決まります。
ここに画像の説明を挿入
ここに画像の説明を挿入
8) 計算: ボーレートが 9600 の場合、シリアル ポートが 1 ビット データを送受信するのにかかる時間は 1 ボー、つまり 1/9600 秒です。50MHz (周期 20ns) のシステム クロックを使用してカウントすると、カウント数は cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208 システム クロック サイクルとなります。つまり、データの各ビット間の間隔は、50MHz のクロック周波数で 5208 回ダウンカウントする必要があります。
9) ホスト コンピュータがシリアル ポート経由で 8 ビット データを送信する場合、8 ビット有効データを送信する前にボー タイムのスタート ビットが自動的に送信され、8 ビット有効データを送信した後はストップ ビットが自動的に送信されます。同様に、シリアル ポート アシスタントは、ホスト コンピュータから送信されたデータを受信する前に、ボー タイムのスタート ビットを検出してデータの受信を開始する必要があり、8 ビット データを受信した後、ボー タイムのストップ ビットを受信します。

2. 準安定状態

2.1 最初のビートを取る

ここに画像の説明を挿入
1) なぜ最初のビートを取るのかというと、受信 rx 信号とクロック信号が同期しておらず、rx 信号がクロック信号と同期している必要があるためです。
2) PC のボーレートと rx 信号は同期しており、rx 信号と FPGA のシステム クロック sys_clk は非同期であるため、この時点で行う必要があるのは、低速クロック ドメイン (PC のボー レート) システムの rx 信号を高速クロック ドメイン (FPGA の sys_clk) システムに同期させることです。

2.2 連続ビート

1) メタステーブル状態とは何ですか?
なぜなら、シリアルポートでデータを送信するときに、オシロスコープを使用して方形パルスの立ち上がりエッジまたは立ち下がりエッジを増幅すると、その立ち上がりエッジと立ち下がりエッジが瞬時にプルアップまたはプルダウンされるのではなく、オペアンプの「スルーレート」と呼ばれる傾き変化プロセスがあることがわかります。このとき、FPGA の第 1 レベル レジスタの出力端子は、クロック エッジが到着してから長期間不定な状態にあり、シリアル ポート入力の決定された rx 値に等しくなるのではなく、0 と 1 の間で発振状態になります。
ここに画像の説明を挿入
ここに画像の説明を挿入2) 準安定状態はどのようにして生成されるのでしょうか?
セットアップ時間Tus、ホールド時間Th、RSセットアップ時間、セーブ時間は条件を満たしておらず、レジスタ遅延Tco、判定時間Tmetは条件を満たしておらず、信号入力レジスタはセットアップ時間、ホールド時間を保証するものではありません。
ここに画像の説明を挿入

3) 準安定状態を解決するにはどうすればよいですか?
マルチレベルレジスタを使用して、システムに対する準安定性の害を軽減します。基本的には2拍子になります

ここに画像の説明を挿入

3. 実験の目的

シリアル ポート RS232 に基づいてデータ受信および送信モジュールを設計および実現し、受信および送信モジュールを使用してシリアル ポート データ ループバック実験を完了します。
ここに画像の説明を挿入

4. ハードウェアリソース

MAX3232はRS232トランシーバチップです。RS-232レベル規格の信号はコントローラが直接認識できないため、「レベル変換チップ」を介してコントローラが認識できる「TTL」レベルの信号に変換して通信を実現します。
ここに画像の説明を挿入

5. モジュール設計

5.1 最上位モジュールのブロック図

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

5.2 シリアルポートデータ受信モジュール

ここに画像の説明を挿入
ここに画像の説明を挿入
なぜパラレルデータに付随してバリッドフラグ信号を出力する必要があるかというと、パラレルデータを使用する場合、後段のモジュールやシステムではその時点でサンプリングされたデータが安定して有効であるかどうかが分からない可能性があり、データバリッドフラグ信号の到着はその時点でデータが安定して有効であることを示す指標となるためです。データ有効フラグ信号が High の場合、パラレル データは後続のモジュールまたはシステムで使用できます。
87654321 を送信 —> 87654321
ここに画像の説明を挿入
ここに画像の説明を挿入

5.3 シリアルデータ送信モジュール

ここに画像の説明を挿入
ここに画像の説明を挿入

6. 波形図

6.1 シリアルデータ受信モジュール

start_nedge: 立ち下がりエッジ、スタート フラグ信号、場合によっては rex_reg 信号にも 0、1 ジャンプがあり、データ ビットの立ち下がり遅延が発生します。この状況を回避するには、イネーブル信号 work_en を設定すると、work_en 信号は、この時点で表示される start_nedge フラグ信号が必要なシリアル ポート フレームの最初の立ち下がりエッジではないと判断できるため、フィルターで除外できます。
ここに画像の説明を挿入
baud_cnt:
データ フレーム内の 10 ビットと 10 ビット データ bit_flag を区別してデータを抽出し、最も安定した状態を見つけます。
ここに画像の説明を挿入
8 ビットデータ (有効データ 1 ~ 8) を抽出します。
ここに画像の説明を挿入
有効信号が終了した後、work_en が Low になり、カウンタも 0 にクリアされ、
ここに画像の説明を挿入
その後データの結合操作が実行され、最後にデータが出力され、信号 po_flag が High になります。
ここに画像の説明を挿入

6.2 シリアルデータ送信モジュール

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

7.RTL

7.1 uart_rx

`timescale  1ns/1ns




module  uart_rx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
    input   wire            sys_clk     ,   //系统时钟50MHz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire            rx          ,   //串口接收数据

    output  reg     [7:0]   po_data     ,   //串转并后的8bit数据
    output  reg             po_flag         //串转并后的数据有效标志信号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg         rx_reg1     ;
reg         rx_reg2     ;
reg         rx_reg3     ;
reg         start_nedge ;
reg         work_en     ;
reg [12:0]  baud_cnt    ;
reg         bit_flag    ;
reg [3:0]   bit_cnt     ;
reg [7:0]   rx_data     ;
reg         rx_flag     ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg1 <= 1'b1;
    else
        rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg2 <= 1'b1;
    else
        rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;

//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        start_nedge <= 1'b0;
    else    if((~rx_reg2) && (rx_reg3))
        start_nedge <= 1'b1;
    else
        start_nedge <= 1'b0;

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)
        baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag <= 1'b0;
    else    if(baud_cnt == BAUD_CNT_MAX/2 - 1)
        bit_flag <= 1'b1;
    else
        bit_flag <= 1'b0;

//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        bit_cnt <= 4'b0;
     else    if(bit_flag ==1'b1)
         bit_cnt <= bit_cnt + 1'b1;

//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_data <= 8'b0;
    else    if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
        rx_data <= {
    
    rx_reg3, rx_data[7:1]};

//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;

//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <= 8'b0;
    else    if(rx_flag == 1'b1)
        po_data <= rx_data;

//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <= 1'b0;
    else
        po_flag <= rx_flag;

endmodule

7.2 uart_tx

`timescale  1ns/1ns




module  uart_tx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
     input   wire            sys_clk     ,   //系统时钟50MHz
     input   wire            sys_rst_n   ,   //全局复位
     input   wire    [7:0]   pi_data     ,   //模块输入的8bit数据
     input   wire            pi_flag     ,   //并行数据有效标志信号
 
     output  reg             tx              //串转并后的1bit数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg [12:0]  baud_cnt;
reg         bit_flag;
reg [3:0]   bit_cnt ;
reg         work_en ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            work_en <= 1'b0;
        else    if(pi_flag == 1'b1)
            work_en <= 1'b1;
        else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
            work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            baud_cnt <= 13'b0;
        else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
            baud_cnt <= 13'b0;
        else    if(work_en == 1'b1)
            baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            bit_flag <= 1'b0;
        else    if(baud_cnt == 13'd1)
            bit_flag <= 1'b1;
        else
            bit_flag <= 1'b0;

//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (work_en == 1'b1))
        bit_cnt <= bit_cnt + 1'b1;

//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            tx <= 1'b1; //空闲状态时为高电平
        else    if(bit_flag == 1'b1)
            case(bit_cnt)
                0       : tx <= 1'b0;
                1       : tx <= pi_data[0];
                2       : tx <= pi_data[1];
                3       : tx <= pi_data[2];
                4       : tx <= pi_data[3];
                5       : tx <= pi_data[4];
                6       : tx <= pi_data[5];
                7       : tx <= pi_data[6];
                8       : tx <= pi_data[7];
                9       : tx <= 1'b1;
                default : tx <= 1'b1;
            endcase

endmodule

7.3 RS232

`timescale  1ns/1ns




module  rs232
(
    input   wire    sys_clk     ,   //系统时钟50MHz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    rx          ,   //串口接收数据

    output  wire    tx              //串口发送数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   UART_BPS    =   14'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率

//wire  define
wire    [7:0]   po_data;
wire            po_flag;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------ uart_rx_inst ------------------------
uart_rx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_rx_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .rx         (rx         ),  //input             rx
            
    .po_data    (po_data    ),  //output    [7:0]   po_data
    .po_flag    (po_flag    )   //output            po_flag
);

//------------------------ uart_tx_inst ------------------------
uart_tx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_tx_inst
(
    .sys_clk    (sys_clk    ),  //input             sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input             sys_rst_n
    .pi_data    (po_data    ),  //input     [7:0]   pi_data
    .pi_flag    (po_flag    ),  //input             pi_flag
                
    .tx         (tx         )   //output            tx
);

endmodule

8. テストベンチ

8.1 tb_uart_rx

`timescale  1ns/1ns


module  tb_uart_rx();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg             sys_clk;
reg             sys_rst_n;
reg             rx;

//wire  define
wire    [7:0]   po_data;
wire            po_flag;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位和输入信号
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        rx        <= 1'b1;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送8次数据,分别为0~7
initial begin
        #200
        rx_bit(8'd0);  //任务的调用,任务名+括号中要传递进任务的参数
        rx_bit(8'd1);
        rx_bit(8'd2);
        rx_bit(8'd3);
        rx_bit(8'd4);
        rx_bit(8'd5);
        rx_bit(8'd6);
        rx_bit(8'd7);
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//定义一个名为rx_bit的任务,每次发送的数据有10位
//data的值分别为0~7由j的值传递进来
//任务以task开头,后面紧跟着的是任务名,调用时使用
task rx_bit(
    //传递到任务中的参数,调用任务的时候从外部传进来一个8位的值
        input   [7:0]   data
);
        integer i;      //定义一个常量
//用for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1
//不可以写成C语言i=i++的形式
        for(i=0; i<10; i=i+1) begin
            case(i)
                0: rx <= 1'b0;
                1: rx <= data[0];
                2: rx <= data[1];
                3: rx <= data[2];
                4: rx <= data[3];
                5: rx <= data[4];
                6: rx <= data[5];
                7: rx <= data[6];
                8: rx <= data[7];
                9: rx <= 1'b1;
            endcase
            #(5208*20); //每发送1位数据延时5208个时钟周期
        end
endtask         //任务以endtask结束

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------uart_rx_inst------------------------
uart_rx uart_rx_inst(
        .sys_clk    (sys_clk    ),  //input           sys_clk
        .sys_rst_n  (sys_rst_n  ),  //input           sys_rst_n
        .rx         (rx         ),  //input           rx
                
        .po_data    (po_data    ),  //output  [7:0]   po_data
        .po_flag    (po_flag    )   //output          po_flag
);

endmodule


8.2 tb_uart_tx

`timescale  1ns/1ns
/



module  tb_uart_tx();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg         sys_clk;
reg         sys_rst_n;
reg [7:0]   pi_data;
reg         pi_flag;

//wire  define
wire        tx;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送7次数据,分别为0~7
initial begin
        pi_data <= 8'b0;
        pi_flag <= 1'b0;
        #200
        //发送数据0
        pi_data <= 8'd0;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
//每发送1bit数据需要5208个时钟周期,一帧数据为10bit
//所以需要数据延时(5208*20*10)后再产生下一个数据
        #(5208*20*10);
        //发送数据1
        pi_data <= 8'd1;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据2
        pi_data <= 8'd2;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据3
        pi_data <= 8'd3;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据4
        pi_data <= 8'd4;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据5
        pi_data <= 8'd5;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据6
        pi_data <= 8'd6;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据7
        pi_data <= 8'd7;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------uart_rx_inst------------------------
uart_tx uart_tx_inst(
        .sys_clk    (sys_clk    ),  //input           sys_clk
        .sys_rst_n  (sys_rst_n  ),  //input           sys_rst_n
        .pi_data    (pi_data    ),  //output  [7:0]   pi_data
        .pi_flag    (pi_flag    ),  //output          pi_flag

        .tx         (tx         )   //input           tx
);

endmodule

8.3 tb_rs232

`timescale  1ns/1ns




module  tb_rs232();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire  define
wire    tx          ;

//reg   define
reg     sys_clk     ;
reg     sys_rst_n   ;
reg     rx          ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位和输入信号
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    rx        <= 1'b1;
    #20;
    sys_rst_n <= 1'b1;
end

//调用任务rx_byte
initial begin
    #200
    rx_byte();
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//创建任务rx_byte,本次任务调用rx_bit任务,发送8次数据,分别为0~7
task    rx_byte();  //因为不需要外部传递参数,所以括号中没有输入
    integer j;
    for(j=0; j<8; j=j+1)    //调用8次rx_bit任务,每次发送的值从0变化7
        rx_bit(j);
endtask

//创建任务rx_bit,每次发送的数据有10位,data的值分别为0到7由j的值传递进来
task    rx_bit(
    input   [7:0]   data
);
    integer i;
    for(i=0; i<10; i=i+1)   begin
        case(i)
            0: rx <= 1'b0;
            1: rx <= data[0];
            2: rx <= data[1];
            3: rx <= data[2];
            4: rx <= data[3];
            5: rx <= data[4];
            6: rx <= data[5];
            7: rx <= data[6];
            8: rx <= data[7];
            9: rx <= 1'b1;
        endcase
        #(5208*20); //每发送1位数据延时5208个时钟周期
    end
endtask

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------ rs232_inst ------------------------
rs232   rs232_inst
(
    .sys_clk    (sys_clk    ),  //input         sys_clk
    .sys_rst_n  (sys_rst_n  ),  //input         sys_rst_n
    .rx         (rx         ),  //input         rx

    .tx         (tx         )   //output        tx
);

endmodule



おすすめ

転載: blog.csdn.net/HeElLose/article/details/131404467