Menu Close

UART 组装与实验

上几节内容介绍了UART的组成部分发送(tx.v), 接收(rx.v)以及波特率发生器(bd_rate_en.v)等原理及代码,本节内容介绍如何将各个模块组装在一起形成一个完整的UART收发系统UART.v。组装后的结果如图1 所示。根据图1的结构,下面将进行程序组装,并提供仿真与实验结果。

图1

一  rx采用中心对齐的模块。

  1. UART组装的程序
`timescale 1ns / 1ps
module uart
#(
    parameter BAUD_TICK = 115200,
    parameter STOP_BIT = 1,
    parameter PARITY = "NONE"
)
(
    input        inclk,
    
    input        tx_req,
    input  [7:0] tx_data,
    output       tx_ack,
    
    output       tx,
    input        rx,      //rx 端在本设计中没有实现
    
    output       rx_rdy,
    output [7:0] rx_data,
    
    output       sys_clk,  //将系统时钟输出,便于测试程序使用
    output       rst
);
 
wire   tx_bd_en;
wire   rx_bd_en;
wire   locked;
assign rst=!locked;
 
uart_pll   uart_pll_inst
(
    .areset  (1'b0),
    .inclk0  (inclk),           // inclk  ----50M,
    .c0      (sys_clk),         // sys_clk----72M
    .locked  (locked)         
);
 
baud_rate_en
#(
    .BAUD_RATE (BAUD_TICK),     // 波特率:115200
    .FREQUENCY (72000000)       // 时钟参数:72M
)   
bau_rate_en_inst
(
    .clk        (sys_clk),

    .baud_tick  (BAUD_TICK),
    .bt_en      (0),
    
    .tx_bd_en   (tx_bd_en),
    .rx_bd_en   (rx_bd_en),
    
    .rst        (rst)
);
 
 
//========================================================================= 
tx
#(
    .PARITY     (PARITY),
    .STOP_BIT   (STOP_BIT)
)
txd_inst
(
    .clk       (sys_clk),
    
    .tx_bd_en  (tx_bd_en),
    
    .tx_data   (tx_data),
    .tx_req    (tx_req),
    .tx_ack    (tx_ack),
    
    .tx        (tx),
    .rst       (rst)
);

`define CENTER_ALIGNMENT

`ifdef CENTER_ALIGNMENT

    rx   #(
       .PARITY      (PARITY),    //"ODD"--odd parity, "EVEN"--even parity, others--no parity
       .STOP_BIT    (STOP_BIT)
    )
    rx_inst
    (
        .sys_clk    (sys_clk),

        .bd8_rate   (rx_bd_en),

        .rx_data    (rx_data),
        .rx_rdy     (rx_rdy),

        .rx         (rx),
        .rst        (rst)
    );
    
    
`else
 
    rx_more_one
    #(
       .PARITY      (PARITY),    //"ODD"--odd parity, "EVEN"--even parity, others--no parity
       .STOP_BIT    (STOP_BIT)
    )
    rx_more_one_inst
    (
        .sys_clk    (sys_clk),

        .bd8_rate   (rx_bd_en),
        .rx_data    (rx_data),
        .rx_rdy     (rx_rdy),

        .rx         (rx),
        .rst        (rst)
    );
`endif

//========================================================================= 
 
 
endmodule

 

2. test_uart.v 程序

  • 本测试程序将仿真程序和板载测试程序合二为一。
  • 仿真数据由本程序产生,通过tx module 串行发送到 tx pin 上。 通过回环方式由rx module 进行串行接收,即:rx <= tx。为了方便测试,在仿真中每包只有8个数据。
  • 板载测试数据由计算机产生,通过rx module 进行串行接收。将接收到的并行数据回环到tx module。

图2

从图2 中可以看出:

1)初始化使用变量,

如果是SIM_DEBUG 模式, 等待16000时钟周期后,tx_data = 0; tx_req = 1; 发送第一笔数据;

如果是正常接收模式,等待rx_rdy_r == 1, tx_data = rx_data_r; tx_req = 1; rx_ack = 1; 发送从接收端得到的数据。

2)rx_ack = 0; 等待发送模块tx.v 返回tx_ack 信号, 如果tx_ack == 1 ,  发送请求信号 tx_req = 0;

3)等待tx_ack 是否为0, 如果tx_ack == 0 ,

如果是SIM_DEBUG 模式, 查看目前是否已经发送过 8个数据了(tx_data = 0~7), 如果已经发送过8个数据,返回状态0

如果是正常接收模式,跳转状态3

4)tx_cnt 清零,

如果是SIM_DEBUG 模式, 数据 + 1; 发送tx_req = 1; 返回到状态1

如果是正常接收模式,等待是否有新的数据被接收, 发送tx_req = 1; 同时将rx_ack = 1 ; 通知rx  rx_data_r数据已经被接收到了, 返回到状态1

 

`timescale 1ns / 1ps
module test_uart #
(
    parameter SIM_DEBUG = "FALSE"  //  "TRUE"  "FALSE"
)
(
    input   inclk,
    output  tx,
    input   rx
);
 
parameter PARITY = "NONE";
parameter STOP_BIT = 1;

 
wire       rst;
wire       sys_clk;
 
wire tx_ack;
reg [7:0] tx_data = 0;
reg tx_req = 0;

wire [7:0] rx_data;
wire rx_rdy;


reg rx_rdy_r = 0;
reg [7:0] rx_data_r = 0;
reg rx_ack = 0;
always @ (posedge sys_clk)
if(rst) 
begin
    rx_rdy_r <= 0;
end
else
begin
    if(rx_ack) rx_rdy_r <= 0;
    else if(rx_rdy) 
    begin
        rx_rdy_r <= 1;
        rx_data_r <= rx_data;
    end
end



reg [31:0] tx_cnt = 0;
reg [2:0] tx_st = 0;
always @ (posedge sys_clk)
if(rst) 
begin
    rx_ack <= 0;
    tx_cnt <= 0;
    tx_data <= 0;
    tx_req <= 0;
    tx_st <= 0;
end
else case (tx_st)
0:
begin
    rx_ack <= 0;
    tx_data <= 0;
    tx_req <= 0;
    
    if(SIM_DEBUG == "TRUE") 
    begin
        tx_cnt <= tx_cnt + 1;
        if(tx_cnt == 16000)
        begin
            tx_req <= 1;
            tx_st <= 1;
        end
    end
    else if(rx_rdy_r)
    begin
        tx_data <= rx_data_r;
        tx_req <= 1;
        rx_ack <= 1;
        tx_st <= 1;
    end
end
1:
begin
    rx_ack <= 0;
    if(tx_ack)
    begin
        tx_req <= 0;
        tx_st <= 2;
    end
end
2:
begin
    if(!tx_ack)
    begin
        if((tx_data == 7) && (SIM_DEBUG == "TRUE") )
            tx_st <= 0;
        else
            tx_st <= 3;
    end
end
3:
begin
    tx_cnt <= 0;

    if(SIM_DEBUG == "TRUE")
    begin
        tx_data <= tx_data + 1;
        tx_req <= 1;
        tx_st <= 1;
    end
    else if(rx_rdy_r)
    begin
        tx_data <= rx_data_r;
        tx_req <= 1;
        rx_ack <= 1;
        tx_st <= 1;
    end
end
default: tx_st <= 0;
endcase

 
uart
#(
    .STOP_BIT   (STOP_BIT),
    .PARITY     (PARITY)
)
uart_inst
(
    .inclk    (inclk),
    
    .tx_req   (tx_req),
    .tx_data  (tx_data),
    .tx_ack   (tx_ack),
    
    .tx       (tx),
    .rx       (rx),
 
    .rx_rdy   (rx_rdy),
    .rx_data  (rx_data),
    
    .sys_clk  (sys_clk),
    .rst      (rst)
);

 
endmodule

 

 

2.  仿真

仿真时采用回环的方式,即TX与RX信号在tb.v内实现回环,如图2所示。

图3

  • 仿真程序
`timescale 1ns / 1ps
 
module tb
(
);
 
 
 
 
parameter PERIOD = 20   ; //50Mhz
 
reg clk; 
initial 
begin
    clk = 0;
    #(PERIOD/2);
 
    forever 
        #(PERIOD/2) clk = ~clk;
end

initial
begin
    #2000000;
    $stop;
end


wire txrx;
test_uart #
(
    .SIM_DEBUG ( "TRUE")  //  "TRUE"  "FALSE"
)
test_uart_inst
(
    .inclk  (clk),
    .tx     (txrx),
    .rx     (txrx)
);

endmodule

 

  • 仿真波形(Modelsim)

图4

从图4的波形可以看出,经过回环测试,接收数据可以正确接收数据。

二  RXD部分用判多方式

`timescale 1ns / 1ps
module rx_more_one
#(
   parameter PARITY = "ODD",    //"ODD"--odd parity, "EVEN"--even parity, others--no parity
   parameter STOP_BIT = 1
)
(
   input sys_clk,
   input rst,
   
   input rx,
   
   input bd8_rate,

   output reg [7:0] rx_data = 0,
   output reg rx_rdy = 0
);


localparam [ 3: 0 ]
           IDLE0     = 0,
           IDLE1     = 1,
           START     = 2,
           FIRST_BIT = 3,
           SEC_BIT   = 4,
           THIRD_BIT = 5,
           FOUTH_BIT = 6,
           FIF_BIT   = 7,
           SIX_BIT   = 8,
           SEVN_BIT  = 9,
           EIGTH_BIT = 10,
           PAR_BIT   = 11,
           STOP1_BIT = 12,
           STOP2_BIT = 13;

localparam EXTRA_COUNT = (PARITY == "ODD"||PARITY == "EVEN") ? 8 : 0;
localparam EXTRA_STOP_COUNT = (STOP_BIT == 2) ? 8 : 0;


reg [ 1: 0 ] rx_r;
reg rx_nedge; //
reg [ 7: 0 ] rx_data_tmp;
reg rx_rdy_tmp;
reg [ 7: 0 ] sample_bit;

wire [ 3: 0 ] count_one;
wire rx_bit;



reg [ 3: 0 ] sample_st_cs, sample_st_ns;
reg [ 6: 0 ] sample_count_cs, sample_count_ns;
reg [ 6: 0 ] sample_count_cs_r;


assign count_one = sample_bit[ 0 ] + sample_bit[ 1 ] + sample_bit[ 2 ] + sample_bit[ 3 ] + sample_bit[ 4 ] + sample_bit[ 5 ] + sample_bit[ 6 ] + sample_bit[ 7 ];

assign rx_bit = ( count_one > 4 ) ? 1'b1 : 1'b0;

always@( posedge sys_clk or posedge rst )
if ( rst )
begin
    rx_r <= 0;
    rx_nedge <= 0;
end
else
begin
    if ( bd8_rate )
    begin
        rx_r <= { rx_r[ 0 ], rx };         // rx shift left
        rx_nedge <= (rx_r == 2'b10) ? 1'b1 : 1'b0;  // capture the falling edge
    end
end


always@( posedge sys_clk or posedge rst )
if ( rst )
begin
    sample_st_cs <= IDLE0;
end
else
begin
    sample_st_cs <= sample_st_ns;
end


always@( posedge sys_clk or posedge rst )
if ( rst )
begin
    sample_count_cs <= 0;
end
else
begin
    sample_count_cs <= sample_count_ns;
end


always@( * )
begin
    sample_count_ns = sample_count_cs;
    sample_st_ns = sample_st_cs;

    if ( bd8_rate )
    begin
        case ( sample_st_cs )
        IDLE0:
        begin     //idle state, waiting for ta least 10bits 1
            if ( sample_count_cs == (80 + EXTRA_COUNT + EXTRA_STOP_COUNT ) )
                sample_st_ns = IDLE1;
            else if ( rx_r[ 1 ] )
                sample_count_ns = sample_count_cs + 1;
            else
                sample_count_ns = 0;
        end
        IDLE1:
        begin //waiting for start_bit's edge
            sample_count_ns = 0;
            if ( rx_nedge )
                sample_st_ns = START;
        end
        START:
        begin
            sample_count_ns = sample_count_cs + 1;
            if ( sample_count_cs == 5 )
            begin
                sample_count_ns = 0;
                if ( rx_bit ) // sample times bigger than 4
                    sample_st_ns = IDLE1; // detect start flag
                else
                    sample_st_ns = FIRST_BIT;
            end
        end
        FIRST_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = SEC_BIT;
            end
        end
        SEC_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = THIRD_BIT;
            end
        end
        THIRD_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = FOUTH_BIT;
            end
        end
        FOUTH_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = FIF_BIT ;
            end
        end
        FIF_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = SIX_BIT;
            end
        end
        SIX_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = SEVN_BIT;
            end
        end
        SEVN_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = EIGTH_BIT;
            end
        end
        EIGTH_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;

                if ( ( PARITY == "ODD" ) || ( PARITY == "EVEN" ) )
                    sample_st_ns = PAR_BIT;
                else
                    sample_st_ns = STOP1_BIT;
            end
        end
        PAR_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = STOP1_BIT;
            end
        end
        STOP1_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 6 )
            begin
                sample_count_ns = 0;

                if ( STOP_BIT == 2 )
                    sample_st_ns = STOP2_BIT;
                else
                    sample_st_ns = IDLE1;
            end
        end
        STOP2_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = IDLE1;
            end
        end
        endcase
    end
end


always@( posedge sys_clk or posedge rst )
if ( rst )
begin
    sample_bit <= 0;
    rx_rdy_tmp <= 1'b0;
    rx_data_tmp <= 0;
end
else
begin
    rx_rdy_tmp <= 1'b0;

    if ( sample_st_cs == IDLE1 )
        sample_bit <= rx_r[ 1 ];

    case ( sample_st_cs )
    START, FIRST_BIT, SEC_BIT, THIRD_BIT, FOUTH_BIT, FIF_BIT, SIX_BIT, SEVN_BIT, EIGTH_BIT, PAR_BIT, STOP1_BIT, STOP2_BIT :
    begin
        if ( bd8_rate )
            sample_bit <= { sample_bit[ 6: 0 ], rx_r[ 1 ] };
    end
    endcase

    sample_count_cs_r <= sample_count_cs;

    if (( sample_count_cs_r == 7 ) & bd8_rate )
    begin //delay one clock
        case ( sample_st_cs )
        FIRST_BIT:  rx_data_tmp[ 0 ] <= rx_bit;
        SEC_BIT:    rx_data_tmp[ 1 ] <= rx_bit;
        THIRD_BIT:  rx_data_tmp[ 2 ] <= rx_bit;
        FOUTH_BIT:  rx_data_tmp[ 3 ] <= rx_bit;
        FIF_BIT:    rx_data_tmp[ 4 ] <= rx_bit;
        SIX_BIT:    rx_data_tmp[ 5 ] <= rx_bit;
        SEVN_BIT:   rx_data_tmp[ 6 ] <= rx_bit;
        EIGTH_BIT:
        begin
            rx_data_tmp[ 7 ] <= rx_bit;
            rx_rdy_tmp <= 1'b1;
        end
        default: ;
        endcase
    end
end


always@( posedge sys_clk )
begin
    if ( rx_rdy_tmp )
    begin
        rx_data <= rx_data_tmp;
        rx_rdy <= 1'b1;
    end
    else rx_rdy <= 1'b0;
end

endmodule

 

加载rx_more_one 变量到仿真波形中,观察其中一位的接收,移位,求和,判多的结果看看是否与预期的设计相符,图5,图6显示了停止位的解码过程。

图5

 

图6

图6的红框标注了sample_bit,count_one,rx_bit与状态机及sample_count_cs之间的逻辑与时序关系。最终测试在停止位解码时,count_one得到8个1,提高了判决的冗余度。

练习题:

  1. 将奇偶校验设为奇校验,重新仿真,并验证接收数据的正确性。
  2. 分析对照仿真波形rx_r[1],sample_st_cs, sample_count_cs,sample_bit,count_one,rx_bit的时序关系,并分析在判多时为什么要如下语句出现:
sample_count_cs_r <= sample_count_cs;


if(sample_count_cs_r == 7)
begin //delay one clock
    case(sample_st_cs)
    FIRST_BIT: rx_data_tmp[0] = rx_bit;
    SEC_BIT:   rx_data_tmp[1] = rx_bit;
    THIRD_BIT: rx_data_tmp[2] = rx_bit;
    FOUTH_BIT: rx_data_tmp[3] = rx_bit;
    FIF_BIT:   rx_data_tmp[4] = rx_bit;
    SIX_BIT:   rx_data_tmp[5] = rx_bit;
    SEVN_BIT:  rx_data_tmp[6] = rx_bit;
    EIGTH_BIT: 
    begin
        rx_data_tmp[7] = rx_bit;
        rx_rdy_tmp = 1'b1;    
    end
    default:;
    endcase

 

而不是直接采用if(sample_count_cs == 7)begin 的方式实现 ?

工程代码如下:

[content_control]

uart_demo_prj.zip

[/content_control]

Posted in FPGA, FPGA 教材教案, Quartus II, Quartus II, Verilog, Verilog, 开发工具, 开发语言, 教材与教案, 文章

3 Comments

  1. 6班李红梅

    关于老师最后的练习题,我是这样理解的:因为sample_count_cs == 7 时,sample_bit的最后一个bit刚刚移位进去,此时立刻抓取会导致最后一个bit 还不够稳定,如果延迟一个时钟再抓取此时的smple_bit的每一个bit 已经是稳稳的抓取到了。还有关于老师代码中还加了&bd8_rate, 没怎么想明白,我暂时理解的是处于对每一个bit包络线的考虑吗?

发表回复

相关链接