Xilinx FPGA:vivado实现单端RAM存储 串口通信

一、实验要求

要求使用串口工具发送出去的数据存储到单端RAM中再读取数据,完成回环测试。

二、信号流向分析

但连线图其实更复杂一些

三、程序设计

rx模块:

`timescale 1ns / 1ps
/串口接收端    串行转并行
module uart_rx(
    input                  sys_clk ,
    input                  rst_n    ,
    input                  rx_data   ,  //输入串行数据
    output    reg[7:0]     uart_data , // 输出并行数据
    output    reg          rx_done     //数据传输完成结束信号  
         
    );
    
    parameter         SYSCLK =   50_000_000  ;
    parameter         Baud   =   115200      ;
    parameter         COUNT  =   SYSCLK/Baud ;//434   传输1比特所需要的时钟周期
    parameter         MID    =   COUNT/2     ;
    
    parameter         MODE_CHECK = 0 ;
    
    ///产生开始信号(检测下降沿-----二级寄存)
    reg           rx_reg1     ;
    reg           rx_reg2     ;
    
    wire          start_flag    ;  开始信号
    reg           rx_flag       ;
    reg    [4:0]   cnt_bit      ;///0~10
    reg    [9:0]   cnt          ;///434
    reg    [8:0]   data_reg     ;
    reg            check        ;

    
    always@(posedge sys_clk)
          if(!rst_n)begin
          rx_reg1 <= 1 ;  //处于空闲位  数据线上无数据
          rx_reg2 <= 1 ;  //处于空闲位  数据线上无数据
          end
          else
              begin
                  rx_reg1 <= rx_data  ;
                  rx_reg2 <= rx_reg1  ;
              end
     assign start_flag = ~rx_reg1 & rx_reg2  ;  
     //rx_flag
     always@(posedge sys_clk )
           if(!rst_n)
              rx_flag <= 0 ;
           else if (start_flag)
              rx_flag <= 1 ;
           else if ( cnt_bit == 10 && cnt == MID -1 )
              rx_flag <= 0 ;
           else
              rx_flag <= rx_flag ;
   cnt 434  
     always@(posedge sys_clk )
           if(!rst_n)
              cnt <= 0;
           else if ( rx_flag == 1 )begin
                if ( cnt == COUNT -1) ///一定要减一,如果不减一,实际会计到435次,反算回去波特率就不是115200了
                    cnt <= 0;
                else
                    cnt <= cnt +1 ;
           end
           else
               cnt <= 0 ;
   
   /计数器
      always@(posedge sys_clk )
           if(!rst_n)
              cnt_bit <= 0 ;
           else if ( rx_flag )begin
                if ( cnt == COUNT -1)begin
                    if(cnt_bit == 10)
                    cnt_bit <= 0 ;
                    else
                    cnt_bit <= cnt_bit +1 ;
                    end
                else
                cnt_bit <= cnt_bit     ;
                end
           else
           cnt_bit <= 0 ;
   
  
           
    [7:0]uart_data
    /*
    cnt_bit = 0 起始位
    cnt_bit = 1-8 数据位
    cnt_bit = 9 停止位
    但是  [7:0]uart_data   没有uart[8]
    所以
    if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9 )
         uart_data[cnt_bit -1] <= rx_data ;
    */
//    always@(posedge sys_clk )
//          if(!rst_n)
//          uart_data <= 0 ;
//          else if ( rx_flag )begin
//                if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9 )
//                uart_data[cnt_bit -1] <= rx_data ; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
//                else
//                uart_data <= uart_data ;
//          end
//          else
//          uart_data <= 0 ;
    因为这里要让uart_data只在rx_done的时候有值,所以定义一个中间寄存器data_reg
     always@(posedge sys_clk )
          if(!rst_n)
          data_reg <= 0 ;
          else if ( rx_flag )begin
                if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 10 )
                data_reg[cnt_bit -1] <= rx_data ; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
                else
                data_reg <= data_reg ;
          end
          else
          data_reg <= 0 ;
     
//  其他赋值方法1
//      always@(posedge sys_clk )
//          if(!rst_n)
//             uart_data <= 0;
//          else if (cnt == MID -1)begin
//             case(cnt_bit)
//             0:  uart_data  <= 0  ;
//             1:  uart_data [0] <= rx_data  ;
//             2:  uart_data [1] <= rx_data  ;
//             3:  uart_data [2] <= rx_data  ;
//             4:  uart_data [3] <= rx_data  ;
//             5:  uart_data [4] <= rx_data  ;
//             6:  uart_data [5] <= rx_data  ;
//             7:  uart_data [6] <= rx_data  ;
//             8:  uart_data [7] <= rx_data  ;
//             9:  uart_data  <= uart_data  ;  ///停止位的时候数据传完,保持
//             endcase
//             end
//           else
//             uart_data  <= uart_data  ;
   
//   其他赋值方法2
//    always@(posedge sys_clk )
//          if(!rst_n)
//          uart_data <= 0 ;
//          else if (cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9)
//               uart_data <= {rx_data,uart_data[7:1]} ;
//          else
//               uart_data <= uart_data ;

    check
    always@(posedge sys_clk)
          if(!rst_n)
              check <= 0 ;
          else if (rx_flag )begin
               if ( cnt_bit == 10 )
                   check <= ^data_reg  ;
               else
                   check <= check ;
          end
          else
               check <= 0 ;

          
   给uart_data赋值
     always@(posedge sys_clk )
          if(!rst_n)     
             uart_data <= 0 ;   
          else if (rx_flag)begin
//               if (cnt_bit == 10 &&  check == MODE_CHECK)
               if (cnt_bit == 10 && cnt == MID/2 -1 && check == MODE_CHECK)
               uart_data <= data_reg[7:0] ;
               else
               uart_data <= uart_data ;
          end
          else
          uart_data <= uart_data ; 可以保持到下一个数据到来
//              uart_data <= 0 ;  只保持在rx_done处于高电平的时候
  /rx_done
     always@(posedge sys_clk )
          if(!rst_n)           
            rx_done <= 0 ;
          else if (rx_flag)begin
               if ( cnt_bit == 10 && cnt == MID/2 -1)
                   rx_done <= 1 ;
               else
                   rx_done <= 0 ;       
          end
          else
            rx_done <= 0 ;  



      
endmodule

tx模块:

`timescale 1ns / 1ps
module uart_tx(
     input               sys_clk   ,
     input               rst_n     ,
     input    [7:0]      uart_data ,
     input               rx_done   ,
     output   reg        tx_data   , 
     output   reg        tx_done
    );
    parameter         SYSCLK =   50_000_000  ;
    parameter         Baud   =   115200      ;
    parameter         COUNT  =   SYSCLK/Baud ;//434   传输1比特所需要的时钟周期
    parameter         MID    =   COUNT/2     ;

    wire                 start_flag ;
    reg                  tx_flag   ;
 
    reg                  tx_reg1   ;
    reg                  tx_reg2   ;
    
    reg[4:0]             cnt_bit   ;
    reg[10:0]            cnt       ;



tx_start
    always@(posedge sys_clk)
          if(!rst_n)begin
             tx_reg1 <= 0 ;
             tx_reg2 <= 0 ;
           end
           else begin
              tx_reg1 <= rx_done  ;
              tx_reg2 <= tx_reg1  ;
           end
 
   assign  start_flag = tx_reg1 & ~tx_reg2 ;
 
 ///tx_flag
   always@(posedge sys_clk)
         if(!rst_n)
            tx_flag <= 0 ;
         else if ( start_flag )
            tx_flag <= 1 ;
         else if ( cnt == COUNT -1 && cnt_bit == 10)
//         else if ( cnt == MID -1 && cnt_bit == 10)
            tx_flag <= 0 ;
         else
            tx_flag <= tx_flag ;
 ///计时器
      cnt 434  
     always@(posedge sys_clk )
           if(!rst_n)
              cnt <= 0;
           else if ( tx_flag == 1 )begin
                if ( cnt == COUNT -1) ///一定要减一,如果不减一,实际会计到435次,反算回去波特率就不是115200了
                    cnt <= 0;
                else
                    cnt <= cnt +1 ;
           end
           else
               cnt <= 0 ;
  
     /计数器
      always@(posedge sys_clk )
           if(!rst_n)
              cnt_bit <= 0 ;
           else if ( tx_flag )begin
                if ( cnt == COUNT -1)begin
                    if(cnt_bit == 10)///0123456789 10
                    cnt_bit <= 0 ;
                    else
                    cnt_bit <= cnt_bit +1 ;
                    end
                else
                cnt_bit <= cnt_bit     ;
                end
           else
           cnt_bit <= 0 ;
           
  parameter             MODE_CHECK = 0 ;
 
赋值
     always@(posedge sys_clk )
          if(!rst_n)
          tx_data <= 1 ;   //表示没有数据
          else if ( tx_flag )begin
                if (   cnt_bit > 0 && cnt_bit < 9 )
                ///cnt_bit 0 12345678 9 
                ///tx_data 0123456789
                ///uart_data 01234567
                tx_data <= uart_data [cnt_bit-1]; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
                else if(cnt_bit == 0)
                tx_data <= 0 ;
                else if(cnt_bit == 9)
                tx_data <= (MODE_CHECK == 0)? ^uart_data: ~^uart_data;
              /*
              MODE_CHECK == 0是偶校验,假如uart_data是1110_0000,其异或的结果
              是1,将异或的结果作为校验位,让数据位和校验位异或的结果为0,满足偶校验。
              假如uart_data是1110_1000,其异或的结果是0,将异或的结果作为校验位,
              让数据位和校验位异或的结果为0,满足偶校验。奇校验则相反。
              */
                else if (cnt_bit == 10)///停止位
                tx_data <= 1 ;
                else
                tx_data <= tx_data ;
          end
          else
          tx_data <= 1 ;
          
          
       always@(posedge sys_clk )
          if(!rst_n)           
            tx_done <= 0 ;
          else if (tx_flag)begin
               if ( cnt_bit == 10 && cnt == COUNT -1)
//               if ( cnt_bit == 10 && cnt == MID/2 -1)
                   tx_done <= 1 ;
               else
                   tx_done <= 0 ;       
          end
          else
            tx_done <= 0 ;  
endmodule

顶层RAM模块

`timescale 1ns / 1ps
/*
单端RAM回环测试
写入16个数据,读出16个数据
*/
module single_ram_test(
    input                sys_clk      ,
    input                rst_n       ,
    input                rx_data     ,
    output               tx_data 
    );
wire    [7:0]   uart_data ;
wire    [7:0]      douta  ;
wire            rx_done   ;
wire            tx_done   ;
reg             tx_start   ;

uart_rx uart_rx_1(
.    sys_clk       ( sys_clk     )  ,
.    rst_n         ( rst_n      )  ,
.    rx_data       ( rx_data    )  ,
.   uart_data      (uart_data   )  ,
.   rx_done        (rx_done     )
    );
uart_tx uart_tx_1(
    . sys_clk      ( sys_clk    )    ,
    . rst_n       ( rst_n     )    ,
    . rx_done     ( tx_start   )    ,
    . uart_data   ( douta )    ,
    . tx_data     ( tx_data   )    ,
    . tx_done     ( tx_done   )     
    );
//ram_ctrl
wire            ena         ;
assign          ena = 1     ;
wire             wea         ;
reg  [6:0]      addra       ;
wire  [7:0]      dina        ;


//addra
always@(posedge sys_clk)
    if(!rst_n)
        addra <= 15;
    else if(rx_done)begin
        if(addra == 4'd15)
            addra <= 0;
        else
            addra <= addra + 1;
    end
    else
        addra <= addra;            
//wea
assign  wea = 1;
//dina
assign  dina = uart_data;
//tx_start
reg [2:0]    cnt  ;
always@(posedge sys_clk)
    if(!rst_n) 
       cnt <= 0 ;
    else if ( addra == 0 )begin 
         if ( cnt == 7 )
              cnt <= cnt  ;
         else
              cnt <= cnt +1 ;
    end
    else
        cnt <= 0 ;





always@(posedge sys_clk)
    if(!rst_n) 
        tx_start <= 0;
    else if(addra == 0  && cnt == 6 )      
//    else if(addra == 0   )  
 /*addra[0]中存放着dina[0],我们想将发送数据的截至信号tx_done作为下一个
数据发送的条件,但是第一个数据是接收不到它上一个数据的tx_done的,所以只能
人为的将它的tx_start拉高,tx_start在tx模块中取代着rx_done的功能,意味着当
tx_start = 1的时候,tx模块的rx_done=1 ,则tx_flag将处于高电平状态,数据开始
在tx模块进行接收,数据接收完成后产生tx_done信号,作为下一个数据传输开始的条件。

 */
        tx_start <= 1;   得到tx_start是一个尖峰脉冲,发送第一个信号
    else if(addra == 15 && tx_done)//保证最后一个数据发完不会再发
        tx_start <= 0;
    else if ( tx_done)
        tx_start <= 1 ;
    else  
        tx_start <= 0;


    


//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
single_ram your_instance_name (
  .clka(sys_clk),    // input wire clka
  .wea(wea),      // input wire [0 : 0] wea
  .addra(addra),  // input wire [6 : 0] addra
  .dina(dina),    // input wire [7 : 0] dina
  .douta(douta)  // output wire [7 : 0] douta
);
// INST_TAG_END ------ End INSTANTIATION Template ---------

endmodule

四、几个非常需要注意的点

(1)

(2)


always@(posedge sys_clk)
    if(!rst_n) 
        tx_start <= 0;
    else if(addra == 0  && cnt == 6 )      
//    else if(addra == 0   )  
 /*addra[0]中存放着dina[0],我们想将发送数据的截至信号tx_done作为下一个
数据发送的条件,但是第一个数据是接收不到它上一个数据的tx_done的,所以只能
人为的将它的tx_start拉高,tx_start在tx模块中取代着rx_done的功能,意味着当
tx_start = 1的时候,tx模块的rx_done=1 ,则tx_flag将处于高电平状态,数据开始
在tx模块进行接收,数据接收完成后产生tx_done信号,作为下一个数据传输开始的条件。

 */
        tx_start <= 1;   得到tx_start是一个尖峰脉冲,发送第一个信号
    else if(addra == 15 && tx_done)//保证最后一个数据发完不会再发
        tx_start <= 0;
    else if ( tx_done)
        tx_start <= 1 ;
    else  
        tx_start <= 0;

(3)

承接(2),如果非要让第一个tx_start不是一个尖峰脉冲信号,可以这样修改:

但是我们还是尽量让tx_start是一个尖峰脉冲,因为此时tx_start不仅仅代替了rx_done的功能,也因为将第一个数据写入地址0是比较规范的写法。

五、仿真程序

`timescale 1ns / 1ps
module test_bench(  );

    reg                sys_clk     ;
    reg                rst_n       ;
    reg       [8:0]    rx_data     ;
    wire               tx_data     ;
    
    
  parameter         SYSCLK =   50_000_000  ;
  parameter         Baud   =   115200      ;
  parameter         COUNT  =   SYSCLK/Baud ;//434   传输1比特所需要的时钟周期
  parameter         MID    =   COUNT/2     ;
   
  initial 
        begin
             sys_clk = 0 ;
             rst_n   = 0 ;
             #10
             rst_n   = 1 ;
        end 
   
   always #1 sys_clk = ~sys_clk  ;
   
     initial
        begin
            uart_out (8'h33);  //0011_0011
            uart_out (8'hff);  //1111_1111
            uart_out (8'h18);  //0001_1000
            uart_out (8'h78); //0111_1000
            uart_out (8'h66);  //0110_0110
            uart_out (8'h1E);  //0001_1110
            uart_out (8'hCC);  //1100_1100
            uart_out (8'h9F);  //1001_1111
            uart_out (8'h9F);  //1001_1111
            uart_out (8'h66);
            uart_out (8'h33);
            uart_out (8'h1E);
            uart_out (8'hCC);
            uart_out (8'h9F);
            uart_out (8'h66);
            uart_out (8'h66);
        end
  
     
  //任务函数
  task     uart_out   ;
     input      [8:0]    DATA   ;
         begin
              rx_data = 1 ;///空闲位初始
              #20 
              rx_data = 0 ;///起始位
               ///传输1bit的计时次数*1周期时间=总时间
               #(COUNT*2)  rx_data = DATA[0] ;///数据位第一位
               #(COUNT*2)  rx_data = DATA[1] ;///数据位第二位
               #(COUNT*2)  rx_data = DATA[2] ;
               #(COUNT*2)  rx_data = DATA[3] ;
               #(COUNT*2)  rx_data = DATA[4] ;
               #(COUNT*2)  rx_data = DATA[5] ;
               #(COUNT*2)  rx_data = DATA[6] ;
               #(COUNT*2)  rx_data = DATA[7] ;
               #(COUNT*2)  rx_data = 0       ;
/*这一步相当于代替了串口调试工具,串口调试工具根据选择的"偶校验"
自动在数据位后面生成"0"或者"1",我们这边只能赋值一个数"0"或者"1"。
我选择赋值为0,所以注定了"8'h31""8'hef""8'h10"是通不过的。
*/
               #(COUNT*2)  rx_data = 1       ;
               #(COUNT*2)                    ;//停止位也需要时间
//               # 80000                       ;
         end
  
  
  
  endtask
   
   
   
   
   
   
   

single_ram_test single_ram_test_u1(
                                 .  sys_clk   (sys_clk )   ,
                                 .  rst_n     (rst_n   )  ,
                                 .  rx_data   (rx_data )  ,
                                 .  tx_data   (tx_data )
    );
endmodule

仿真结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值