通过verilog实现UART,串口收发模块

一.UART介绍

UART:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),是一种异步收发传输器。

异步:通过uart通信协议进行数据交互的双方没有时钟线来进行同步,想要正确的进行数据交互只能按照事先约定的数据传输速率进行传输。对于这个问题我想到了大多数通信方式,无论是异步还是同步在进行通信时都需要一种机制去保证数据传输是正确的,对于同步通信来说,通信双方有着时钟线来保证收发一致,根据时钟信号来决定数据发送和采样时刻,对于异步通信来说,可以像串口一样事先规定波特率,或者把数据通过特定的方式进行编码后再传输,接收方接收数据后再进行解码,这样传输的编码既包含了码元信息又包含了时钟信息。例如,曼彻斯特编码和差分曼彻斯特编码,如图。这样在没有时钟的情况下也可以正确的进行数据传输。

曼彻斯特编码的编码规则是,电平信号由高跳变到低表示逻辑‘1’,电平信号由低跳变到高表示逻辑‘0’;,按照这样的规则,数据接收方可以通过检测电平的跳变来知道传输的码元个数和码元的值。差分曼彻斯特编码的规则是,在每个时钟周期的开始出电平和上一时刻一致则表示逻辑‘1’,否则表示逻辑‘0’;不难发现的是无论是曼彻斯特编码还是差分曼彻斯特编码,编码后的信号都会在时钟周期的中间时刻变化一次。

物理连接线:通过uart通信的双发一般只需要TXD,RXD,GND三根线即可通信。

通信方式:全双工,通信双方可以同时收发数据,因为uart的数据发送和接收是通过不同的数据线实现的,并且在通信双方的RXD和TXD连接是相反的。

传输速率:uart的传输速率也叫波特率,常用的波特率有9600bps,115200bps,38400bps等。

数据格式:uart的数据传输格式为一位起始位,8位数据位(有其他位数),可选的一位校验位,一位停止位。

二.串口回环设计

        (一).框图设计

                              

该设计包括一个按键消抖模块,一个串口接收模块,一个串口发送模块,一个fifoip核,一个数码管驱动模块,主要功能是,通过pc端的串口调试助手向开发板发送数据,接收模块把串行数据转成并行数据存进fifo,接收的数据会在数码管上进行显示来观察接收模块数据是否正确,再通过按键一次性把fifo中暂存的数据连续读出直至fifo为空,读出的数据通过串口发送模块返回到串口调试助手,此外可以通过开发板上的按键调整串口收发模块的波特率。

                                                                RTL视图

(二).代码实现

串口接收模块代码:通过状态机实现

module uart_rx #(parameter DATA_WIDE = 8 )(

   input                                clk         , //system clock 50MHz

   input                                rst_n       , //reset, low valid

   input                                key         , //波特率调节按键

   input                                port_rx     , //数据接收端口

   input                                fifo_full   , //fifo满信号

   output                               recive_vld  , //数据接收有效信号

   output     reg [DATA_WIDE - 1:00]    data_rx   //接收的数据,送给fifo

);

//Parameter Declarations

localparam   //状态机独热码参数

            IDLE        =  4'b0001      , //空闲状态

            START       =  4'b0010      , //起始位

            DATA        =  4'b0100      , //数据位

            STOP        =  4'b1000      ; //停止位

localparam   SYS_CLK     =  50_000_000   ;//系统时钟

localparam   BPS9600     =  9600         ,//9600波特率参数

            BPS115200   =  115200       ;//115200波特率参数

//Internal wire/reg Declarations

reg         [03:00]    state_c ,state_n ;//状态机现态,次态

reg         [23:00]    bps_param        ;//波特率变量

reg         [15:00]    cnt_base         ;//计时一个bit所需时间

wire                   add_cnt_base     ;

wire                   end_cnt_base     ;

wire        [15:00]    end_cnt_base_para;//cnt_base计数最大值

reg         [03:00]    cnt_bit          ;//比特计数器

wire                   add_cnt_bit      ;

wire                   end_cnt_bit     ;

reg                    port_rx_r        ;

wire                   neg_rx           ;//下降沿检测

reg                    cnt_key          ;//按键计数器

wire                   idle2start       ;//初始状态跳转到起始位状态条件

wire                   start2data       ;//起始位状态跳转到数据接收状态条件

wire                   data2stop        ;//数据接收状态跳转到停止位状态条件

wire                   stop2idle        ;//停止位状态跳转到空闲状态条件                

//Logic Description

//三段式状态机//

/状态转移描述//

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        state_c <= IDLE ; //空闲状态

    end

    else begin

        state_c <= state_n ;//下一状态

    end

end

//状态转移条件描述/

always @(*) begin

    case (state_c)

        IDLE :begin

                if(idle2start)begin

                    state_n = START ;

                end

                else begin

                    state_n = IDLE  ;

                end

            end

        START:begin

                if(start2data)begin

                    state_n = DATA ;

                end

                else begin

                    state_n = START ;

                end

            end

        DATA :begin

                if(data2stop)begin

                    state_n = STOP ;

                end

                else begin

                    state_n = DATA ;

                end

            end

        STOP :begin

                if(stop2idle)begin

                    state_n = IDLE ;

                end

                else begin

                    state_n = STOP ;

                end

            end

        default: begin

                if(idle2start)begin

                    state_n = START ;

                end

                else begin

                    state_n = IDLE  ;

                end

            end

    endcase

end

assign  idle2start = state_c == IDLE && neg_rx        ; //数据检测到下降沿

assign  start2data = state_c == START && cnt_bit == 1 ;//发送一位起始位后跳转到数据发送状态

assign  data2stop  = state_c == DATA &&  cnt_bit == 8 && end_cnt_base ;//数据位最后一位发送完成跳转到停止位状态

assign  stop2idle  = state_c == STOP &&  end_cnt_bit ;//停止位发送完毕回到空闲状态

///输出信号控制///

///data_rx/

always @(posedge clk or negedge rst_n)begin

  if(!rst_n)begin

       data_rx <= 8'b0;

  end

  else begin

      case (state_c)

        DATA : if((cnt_bit != 0 && cnt_bit != 9 && cnt_base == end_cnt_base_para/2 -1 ))begin

            data_rx[cnt_bit - 1 ] <= port_rx ;

        end

        else begin

            data_rx <= data_rx ;

        end

        default: data_rx <= 8'b0 ;

      endcase

  end

end

///其他信号产生

/cnt_key,计数按键按下次数来改变波特率/

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        cnt_key <= 1'b0 ;

    end

    else if( key == 1 )begin

        cnt_key <= ~cnt_key ;

    end

    else begin

        cnt_key <= cnt_key ;

    end

end

bps_param;波特率选择/

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        bps_param <= BPS9600 ;

    end

    else if(cnt_key)begin

        bps_param <= BPS115200 ;

    end

    else begin

        bps_param <= BPS9600 ;

    end

end

//end_cnt_base_para///

assign end_cnt_base_para = SYS_CLK / bps_param ;

//cnt_base

always @(posedge clk or negedge rst_n)begin

   if(!rst_n)begin

       cnt_base <= 0;

   end

   else if(add_cnt_base)begin

       if(end_cnt_base)begin

           cnt_base <= 0;

       end

       else begin

           cnt_base <= cnt_base + 1;

       end

   end

end

assign add_cnt_base = state_c != IDLE ;

assign end_cnt_base = add_cnt_base && cnt_base == end_cnt_base_para - 1'b1  ;

//cnt_bit//

always @(posedge clk or negedge rst_n)begin

   if(!rst_n)begin

       cnt_bit <= 0;

   end

   else if(add_cnt_bit)begin

       if(end_cnt_bit)begin

           cnt_bit <= 0;

       end

       else begin

           cnt_bit <= cnt_bit + 1;

       end

   end

end

assign add_cnt_bit = state_c != IDLE && end_cnt_base ;

assign end_cnt_bit = add_cnt_bit && cnt_bit == 10 - 1'b1  ;

assign recive_vld =  (add_cnt_base && cnt_base == end_cnt_base_para/2 - 1)&&  cnt_bit == 8 && fifo_full == 0;

port_rx_r

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        port_rx_r <= 1'b0 ;

    end

    else begin

        port_rx_r <= port_rx ;

    end

end

assign neg_rx = (~port_rx) && port_rx_r ;//下降沿检测

endmodule

串口发送模块代码:通过状态机实现

module uart_tx #(parameter DATA_WIDE = 8 )(

   input                                clk             , //system clock 50MHz

   input                                rst_n           , //reset, low valid

   input  [01:00]                       key             , //波特率调节按键key[0],发送数据按键key[1]

   input  [DATA_WIDE - 1:00]            data_tx         , //要发送的数据,来源是fifo

   input  [07:00]                       usedw           , //fifo数据余量

   input                                fifo_empty      , //fifo空信号

   output   [23:00]                     bps             , //波特率输出

   output                               send_vld        , //发送有效信号

   output                               rd_req          , //fifo读请求

   output                               led             , //led指示空信号

   output     reg                       sclr            , //fifo同步复位信号

   output     reg                       port_tx           //数据发送端口

);      

//Parameter Declarations

parameter   //状态机独热码参数

            IDLE        =  4'b0001      , //空闲状态

            START       =  4'b0010      , //起始位

            DATA        =  4'b0100      , //数据位

            STOP        =  4'b1000      ; //停止位

parameter   SYS_CLK     =  50_000_000   ;//系统时钟

parameter   BPS9600     =  9600         ,//9600波特率参数

            BPS115200   =  115200       ;//115200波特率参数

//Internal wire/reg Declarations

reg         [03:00]    state_c ,state_n ;//状态机现态,次态

reg         [23:00]    bps_param        ;//波特率变量

reg         [15:00]    cnt_base         ;//计时一个bit所需时间

wire                   add_cnt_base     ;

wire                   end_cnt_base     ;

wire        [15:00]    end_cnt_base_para;//cnt_base计数最大值

reg         [03:00]    cnt_bit          ;//比特计数器

wire                   add_cnt_bit      ;

wire                   end_cnt_bit      ;

reg         [31:00]    cnt_byte         ;//字节计数器

wire                   add_cnt_byte     ;

wire                   end_cnt_byte     ;

reg                    tx_en            ;//发送使能

reg                    tx_en_r          ;//发送使能打拍;寻找发送使能下降沿。当下降沿到来时说明数据发送完毕,复位fifoa

reg                    cnt_key          ;//按键计数器

wire                   idle2start       ;//初始状态跳转到起始位状态条件

wire                   start2data       ;//起始位状态跳转到数据发送状态条件

wire                   data2stop        ;//数据发送状态跳转到停止位状态条件

wire                   stop2idle        ;//停止位状态跳转到空闲状态条件                  

//Logic Description

//三段式状态机//

/状态转移描述//

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        state_c <= IDLE ; //空闲状态

    end

    else begin

        state_c <= state_n ;//下一状态

    end

end

//状态转移条件描述/

always @(*) begin

    case (state_c)

        IDLE :begin

                if(idle2start)begin

                    state_n = START ;

                end

                else begin

                    state_n = IDLE  ;

                end

            end

        START:begin

                if(start2data)begin

                    state_n = DATA ;

                end

                else begin

                    state_n = START ;

                end

            end

        DATA :begin

                if(data2stop)begin

                    state_n = STOP ;

                end

                else begin

                    state_n = DATA ;

                end

            end

        STOP :begin

                if(stop2idle)begin

                    state_n = IDLE ;

                end

                else begin

                    state_n = STOP ;

                end

            end

        default: begin

                if(idle2start)begin

                    state_n = START ;

                end

                else begin

                    state_n = IDLE  ;

                end

            end

    endcase

end

assign  idle2start = state_c == IDLE && tx_en ; //数据发送使能有效

assign  start2data = state_c == START && cnt_bit == 1 ;//发送一位起始位后跳转到数据发送状态

assign  data2stop  = state_c == DATA &&  cnt_bit == 8 && end_cnt_base ;//数据位最后一位发送完成跳转到停止位状态

assign  stop2idle  = state_c == STOP &&  end_cnt_bit ;//停止位发送完毕回到空闲状态

///输出信号控制///

///port_tx///

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        port_tx <= 1'b1 ;

    end

    else begin

            case (state_c)

                    IDLE :begin

                            port_tx <= 1'b1 ;

                        end

                    START:begin

                            port_tx <= 1'b0 ;

                        end

                    DATA :begin

                            port_tx <= data_tx[cnt_bit -1] ;

                        end

                    STOP :begin

                            port_tx <= 1'b1 ;

                        end

                default: port_tx <= 1'b1 ;

            endcase

    end

end

///其他信号产生

/cnt_key,计数按键按下次数来改变波特率/

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        cnt_key <= 1'b0 ;

    end

    else if( key[0] == 1 )begin

        cnt_key <= ~cnt_key ;

    end

    else begin

        cnt_key <= cnt_key ;

    end

end

bps_param;波特率选择/

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        bps_param <= BPS9600 ;

    end

    else if(cnt_key)begin

        bps_param <= BPS115200 ;

    end

    else begin

        bps_param <= BPS9600 ;

    end

end

//end_cnt_base_para///

assign end_cnt_base_para = SYS_CLK / bps_param ;

//cnt_base

always @(posedge clk or negedge rst_n)begin

   if(!rst_n)begin

       cnt_base <= 0;

   end

   else if(add_cnt_base)begin

       if(end_cnt_base)begin

           cnt_base <= 0;

       end

       else begin

           cnt_base <= cnt_base + 1;

       end

   end

end

assign add_cnt_base = state_c != IDLE ;

assign end_cnt_base = add_cnt_base && cnt_base == end_cnt_base_para - 1'b1  ;

//cnt_bit//

always @(posedge clk or negedge rst_n)begin

   if(!rst_n)begin

       cnt_bit <= 0;

   end

   else if(add_cnt_bit)begin

       if(end_cnt_bit)begin

           cnt_bit <= 0;

       end

       else begin

           cnt_bit <= cnt_bit + 1;

       end

   end

end

assign add_cnt_bit = state_c != IDLE && end_cnt_base ;

assign end_cnt_bit = add_cnt_bit && cnt_bit == 10 - 1'b1  ;

assign bps = bps_param ;

/cnt_byte

always @(posedge clk or negedge rst_n)begin

   if(!rst_n)begin

       cnt_byte <= 0;

   end

   else if( end_cnt_byte )begin

           cnt_byte <= 0;

       end

   else if( add_cnt_byte )begin

            if(end_cnt_byte)begin

                cnt_byte <= 0;

            end

            else begin

                cnt_byte <= cnt_byte + 1;

            end

           

   end

end

assign add_cnt_byte = end_cnt_bit ;

assign end_cnt_byte =  add_cnt_byte && cnt_byte == 8 - 1;

///tx_en/

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        tx_en <= 1'b0 ;

    end

    else if( fifo_empty == 1 )begin

        tx_en <= 1'b0 ;

    end

    else if(usedw >= 8 && tx_en == 0)begin

        tx_en <= 1'b1 ;

    end

    else if(add_cnt_byte && usedw == 0 )begin

        tx_en <= 1'b0 ;

    end

    else begin

        tx_en <= tx_en ;

    end

end

//tx_en_r///

always @(posedge clk or negedge rst_n)begin

  if(!rst_n)begin

       tx_en_r <= 1'b0;

  end

  else begin

       tx_en_r <= tx_en ;

  end

end

send_vld/

assign send_vld = end_cnt_bit ;

assign rd_req = tx_en &&  start2data && fifo_empty == 0;

///sclr///

always @(posedge clk or negedge rst_n)begin

  if(!rst_n)begin

       sclr <= 1'b0 ;

  end

  /* else if(  tx_en == 0 && tx_en_r != 0 )begin

      sclr <= 1'b0 ;

  end  */

  else begin

      sclr <= 1'b1 ;

  end

end

assign led = fifo_empty ;

endmodule

(三).仿真

仿真代码:

`timescale 1ns/1ps

module tb_UART_top();

   /* input  */   reg                   clk     ;//system clock 50MHz

   /* input  */   reg                   rst_n   ;//reset, low valid

   /* input  */   reg   [03:00]         key_in  ;//四位按键

   /* input  */   reg                   uart_rx ;//uart数据接收端口

   /* output */   wire                  uart_tx ;//uart数据发送端口

   /* output */   wire  [05:00]         sel     ;//数码管位选

   /* output */   wire  [07:00]         seg     ;//数码管段选

defparam   UART_top_inist.key_bit_state_inist.delay20ms = 20 ,

           UART_top_inist.seg_drive_inist.delay2ms      = 2  ,

           UART_top_inist.uart_tx_inist.SYS_CLK         = 5000 ,

           UART_top_inist.uart_tx_inist.BPS9600         = 100  ,

           UART_top_inist.uart_tx_inist.BPS115200       = 1000  ,

           UART_top_inist.uart_rx_inist.SYS_CLK         = 5000 ,

           UART_top_inist.uart_rx_inist.BPS9600         = 100  ,

           UART_top_inist.uart_rx_inist.BPS115200       = 1000 ;

parameter  clk_peroid = 20 ;

UART_top UART_top_inist(

   /* input                      */ . clk     (clk    ), //system clock 50MHz

   /* input                      */ . rst_n   (rst_n  ), //reset, low valid

   /* input       [03:00]        */ . key_in  (key_in ), //四位按键

   /* input                      */ . uart_rx (uart_rx), //uart数据接收端口

   /* output                     */ . uart_tx (uart_tx), //uart数据发送端口

   /* output      [05:00]        */ . sel     (sel    ), //数码管位选

   /* output      [07:00]        */ . seg     (seg    )  //数码管段选

);

initial clk = 0;

always #10 clk = ~clk ;

initial begin

    key_in = 4'b1111;

    rst_n = 1'b0 ;

    #(3 * clk_peroid + 3);

    rst_n = 1'b1 ;

    #(2000*clk_peroid);

    /* 接收8帧数据 */

    recive();//第一个数据

    #(2000*clk_peroid);

    recive();//第2个数据

    #(2000*clk_peroid);

    recive();//第3个数据

    #(2000*clk_peroid);

    recive();//第4个数据

    #(2000*clk_peroid);

    recive();//第5个数据

    #(2000*clk_peroid);

    recive();//第6个数据

    #(2000*clk_peroid);

    recive();//第7个数据

    #(2000*clk_peroid);

    recive();//第8个数据

    #(1000_0*clk_peroid);//7

    repeat(1)begin

        @(UART_top_inist.uart_tx_inist.cnt_byte == 7 && UART_top_inist.uart_tx_inist.end_cnt_bit )

        #29;

    end

    /* 改变波特率 */

    key_in = 4'b1110 ;

    #(300*clk_peroid);

    key_in = 4'b1111 ;

    /* 接收8帧数据 */

    recive();//第一个数据

    #(2000*clk_peroid);

    recive();//第2个数据

    #(2000*clk_peroid);

    recive();//第3个数据

    #(2000*clk_peroid);

    recive();//第4个数据

    #(2000*clk_peroid);

    recive();//第5个数据

    #(2000*clk_peroid);

    recive();//第6个数据

    #(2000*clk_peroid);

    recive();//第7个数据

    #(2000*clk_peroid);

    recive();//第8个数据

    #(1000_000*clk_peroid);//7

    $stop;

end

task  recive();

    begin

        uart_rx = 1'b1 ;

        #( (UART_top_inist.uart_rx_inist.SYS_CLK /UART_top_inist.uart_rx_inist.bps_param)*clk_peroid + 3);

        uart_rx = 1'b0 ;  

        repeat(7)begin

           #( (UART_top_inist.uart_rx_inist.SYS_CLK /UART_top_inist.uart_rx_inist.bps_param)*clk_peroid + 3);

                uart_rx = {$random}%2 ;

        end

         #( (UART_top_inist.uart_rx_inist.SYS_CLK /UART_top_inist.uart_rx_inist.bps_param)*clk_peroid + 3);

        uart_rx = 1'b0;  

         #( (UART_top_inist.uart_rx_inist.SYS_CLK /UART_top_inist.uart_rx_inist.bps_param)*clk_peroid + 3);

        uart_rx = 1'b1;  

        #(1000*clk_peroid);

    end

endtask //

endmodule

                                                        串口接收模块仿真

                                                        串口接收模块时序

                                                        fifo里暂存的数据

                                                        发送模块仿真

三.上板验证

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值