【开源骚客】《轻松设计SDRAM控制器》第三讲—串口发送模块设计及收发整合

作者: Kevin 分类: 微课堂 发布时间: 2017-02-19 12:32

相信很多朋友在学习完第二讲中所讲到的串口接收模块后,一定非常想学习如何设计串口的发送模块,Kevin也非常明白大家的好学之心,所以在第三讲,Kevin会带着大家一起来完成串口发送模块的设计,并且会将第二讲中已经设计好的串口接收模块与这一讲即将完成的串口发送模块进行整合,来实现一个完整的串口收发工程。

 

本讲主要内容如下:

  • 串口发送模块时序设计;
  • 串口发送模块代码编写;
  • 串口发送模块仿真验证;
  • 串口收发模块整合及板级验证。

 

一、串口发送模块时序设计

对于串口发送模块的时序,其实与接收模块的时序大同小异,只是发送模块是要构造出RS232协议的波形,而接收模块是如何在接收到RS232协议的波形后正确的检测出数据。

uart_01

图1  串口发送模块时序图

下面对时序图中的各个信号作简单的介绍:

  • Tx_trig表示串口发送模块工作的触发信号,也就是相当于让FPGA开始通过串口发送数据的一个触发信号;
  • Tx_data为即将发送的数据;
  • Tx_data_reg是保存待发送数据的寄存器,而该寄存器的值只会在tx_trig为高时进行改变;
  • Tx_flag是串口发送模块处于工作状态的一个标志信号,为高则表示FPGA正在发送数据。
  • Baud_cnt依然是进行5208个时钟周期的计数,大家一定要注意,该计数器只会在tx_flag为高时进行工作,其余状态为0;
  • Bit_flag在串口发送模块中,是在baud_cnt计数到最大值(5207)时拉高,与串口接收模块中稍微有些差别;
  • Bit_cnt在串口接收模块中是用来计数已接收到串口数据的bit数,而在这(串口发送模块)则表示已发送bit数量;
  • Rs232_tx则表示串口的发送端,如何构造RS232_tx发送的数据呢?我们完全可以利用bit_cnt来完成,在bit_cnt为不同的值时,则发送不同的数据位。

由于串口发送模块的时序与接收模块的时序类似,Kevin就不作详细的解释了,相信大家也是可以完全理解的。如果有疑问,也可以配合视频教程来一起学习。

二、串口发送模块代码编写

不知道大家有没有发现,Kevin在视频中写代码,一般都是对照着时序图来进行的。因为在我们进行时序设计时,其实就是在整理自己思路的一个过程,时序图画出来了,代码可以写得似行云流水般飞快。

所以呢,Kevin也建议大家先不看下边Kevin贴出来的代码,先根据给出的时序图将代码写出来。

根据设计好的时序图,写出的代码如下:

// *********************************************************************************
// Project Name : 
// Author       : Kevin
// Email        : deng.kanwen@163.com
// Create Time  : 2016/8/29 14:18:53
// File Name    : uart_tx.v
// Module Name  :
// Called By    : 
// Abstract     :
//
// CopyRight(c) 2016, OpenSoc Studio..
// All Rights Reserved
//
// *********************************************************************************

module  uart_tx(
        // system signals
        input                   sclk                    ,
        input                   s_rst_n                 ,
        // UART Interface
        output  reg             rs232_tx                ,
        // RFIFO
        input                   rfifo_empty             ,
        output  reg             rfifo_rd_en             ,
        input           [ 7:0]  rfifo_rd_data           
);

//====================================================================\
// ********** Define Parameter and Internal Signals *************
//====================================================================/
`ifndef SIM
localparam      BAUD_END        =       5207                    ;
`else
localparam      BAUD_END        =       28                      ;
`endif
localparam      BAUD_M          =       BAUD_END/2 - 1          ;
localparam      BIT_END         =       9                       ;

reg     [ 7:0]                  tx_data_r                       ;
reg                             tx_flag                         ;
reg     [12:0]                  baud_cnt                        ;
reg                             bit_flag                        ;
reg     [ 3:0]                  bit_cnt                         ;
wire                            tx_trig                         ;
reg                             tx_trig_r1                      ;
//=================================================================================
// ***************      Main    Code    ****************
//=================================================================================
// rfifo_rd_en
always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                rfifo_rd_en     <=      1'b0;
        else if(rfifo_empty == 1'b0 && tx_flag == 1'b0 && rfifo_rd_en == 1'b0)
                rfifo_rd_en     <=      1'b1;
        else
                rfifo_rd_en     <=      1'b0;
end

// tx_trig_r1
always  @(posedge sclk) begin
        tx_trig_r1      <=      tx_trig;
end 

// tx_data_r
always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                tx_data_r       <=      'd0;
        else if(tx_trig_r1 == 1'b1 && tx_flag == 1'b1)
                tx_data_r       <=      rfifo_rd_data;
end

// tx_flag
always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                tx_flag <=      1'b0;
        else if(tx_trig == 1'b1)
                tx_flag <=      1'b1;
        else if(bit_cnt == BIT_END && bit_flag == 1'b1)
                tx_flag <=      1'b0;
end

//baud_cnt
always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                baud_cnt        <=      'd0;
        else if(baud_cnt == BAUD_END)
                baud_cnt        <=      'd0;
        else if(tx_flag == 1'b1)
                baud_cnt        <=      baud_cnt + 1'b1;
        else
                baud_cnt        <=      'd0;
end

// bit_flag
always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                bit_flag        <=      1'b0;
        else if(baud_cnt == BAUD_END)
                bit_flag        <=      1'b1;
        else
                bit_flag        <=      1'b0;
end

//bit_cnt
always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                bit_cnt <=      'd0;
        else if(bit_flag == 1'b1 && bit_cnt == BIT_END)
                bit_cnt <=      'd0;
        else if(bit_flag == 1'b1)
                bit_cnt <=      bit_cnt + 1'b1;
end

// rs232_tx
always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                rs232_tx        <=      1'b1;
        else if(tx_flag == 1'b1)
                case(bit_cnt)
                        0:      rs232_tx        <=      1'b0;           // start bit
                        1:      rs232_tx        <=      tx_data_r[0];
                        2:      rs232_tx        <=      tx_data_r[1];
                        3:      rs232_tx        <=      tx_data_r[2];
                        4:      rs232_tx        <=      tx_data_r[3];
                        5:      rs232_tx        <=      tx_data_r[4];
                        6:      rs232_tx        <=      tx_data_r[5];
                        7:      rs232_tx        <=      tx_data_r[6];
                        8:      rs232_tx        <=      tx_data_r[7];  
                        default:rs232_tx        <=      1'b1;
                endcase
        else
                rs232_tx        <=      1'b1;
end

assign  tx_trig =       rfifo_rd_en;

endmodule

三、串口发送模块仿真验证

对于串口发送模块的仿真,要比对串口接收模块的仿真要简单得多,下边是对串口发送模块进行仿真的TestBench代码。

TestBench文件源代码如下:

`timescale      1ns/1ns

module  tb_uart_tx;

reg             sclk;
reg             s_rst_n;
reg             tx_trig;
reg     [ 7:0]  tx_data;

wire            rs232_tx;

//------------- generate system signals ------------------------------------
initial begin
        sclk    =       1;
        s_rst_n <=      0;
        #100
        s_rst_n <=      1;
end

always  #5      sclk    =       ~sclk;

//--------------------------------------------------------------------------
initial begin
        tx_data <=      8'd0;
        tx_trig <=      0;
        #200
        tx_trig <=      1'b1;
        tx_data <=      8'h55;
        #10
        tx_trig <=      1'b0;
end

uart_tx         uart_tx_inst(
        // system signals
        .sclk                   (sclk                   ),
        .s_rst_n                (s_rst_n                ),
        // UART Interface       
        .rs232_tx               (rs232_tx               ),
        // others               
        .tx_trig                (tx_trig                ),
        .tx_data                (tx_data                )
);

endmodule

下图是对串口发送模块进行仿真的波形,待发送的数据位8’h55,大家可以看到rs232_tx这个信号也是发送的8’h55,这说明串口发送模块是设计正确的。

uart_02

四、串口收发模块整合及板级验证

 

// ****************************************************************************
// Project Name : 
// Author       : Kevin
// Email        : deng.kanwen@163.com
// Creat Time   : 2016/8/29 14:18:53
// File Name    : .v
// Module Name  :
// Called By    : 
// Abstract     :
//
// CopyRight(c) 2016, OpenSoc Studio..
// All Rights Reserved
//
// ****************************************************************************
// Modification History:
// 1. initial
// ***************************************************************************/
// *************************
// MODULE DEFINITION
// *************************

module  top(
        // system signals
        input                   sclk                    ,
        input                   s_rst_n                 ,
        // UART Interface
        input                   rs232_rx                ,
        output  wire            rs232_tx
);

//====================================================================\
// ********** Define Parameter and Internal Signals *************
//====================================================================/
wire    [ 7:0]                  rx_data                         ;
wire                            tx_trig                         ;
//=============================================================================
// ***************      Main    Code    ****************
//=============================================================================
uart_rx         uart_rx_inst(
        // system signals
        .sclk                    (sclk                  ),
        .s_rst_n                 (s_rst_n               ),
        // UART Interface        
        .rs232_rx                (rs232_rx              ),
        // others                
        .rx_data                 (rx_data               ),
        .po_flag                 (tx_trig               )
);

uart_tx         uart_tx_inst(
        // system signals
        .sclk                    (sclk                  ),
        .s_rst_n                 (s_rst_n               ),
        // UART Interface
        .rs232_tx                (rs232_tx              ),
        // others
        .tx_trig                 (tx_trig               ),
        .tx_data                 (rx_data               )
);

endmodule

好了,这样我们就在顶层中把串口的发送和接收模块都例化好了,大家快点新建一个工程把这3个文件添加进去吧,马上就可以享受到上位机与FPGA进行串口通信的乐趣了!!!

对于串口上位机的使用,大家可以观看视频教程进行学习,Kevin就不在此赘述。

好了,第三讲到这就结束了,从第四讲开始,我们就会进入到SDRAM相关内容的部分了,第四讲的内容一定会让你对【开源骚客】的持续关注得到更高的回报哦,敬请期待!!!

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注