fpga项目:uart_tx_fifo(初稿) - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki

代码结构

image

L1代码(syn_uart_tx_fifo_wrapper2.v)

  • 这一层的代码主要是产生实现命令模块
  • 使用了mini状态机来控制收发片段
`timescale 1ns / 1ps

module syn_uart_tx_fifo_wrapper2(
    input clk,
    input rst_n,
    
    input  sw,
    output empty,
    output full,
    
    output tx
    );
    
    //create one time counter to trigger fsm
    parameter DIV_MAX=1000;    
    reg [10:0] divcnt;
    always @(posedge clk or negedge rst_n)
    if(rst_n==0)
        divcnt<=0;
    else if(add_divcnt) begin
        if(end_divcnt)
            divcnt<=DIV_MAX-1;   //one timer counter
        else
            divcnt<=divcnt+1;
    end  
    
    assign add_divcnt=sw;    
    assign end_divcnt=add_divcnt && (divcnt==DIV_MAX-1); 
    assign end_pulse=add_divcnt && (divcnt==DIV_MAX-2);  //how to use counter  
    
    //mini-fsm to control xfer  
    parameter IDLE=1'b0;
    parameter ACT=1'b1;
    reg state;
    always @(posedge clk or negedge rst_n)
    if(rst_n==0)
        state<=IDLE;
    else case(state)
            IDLE: if(end_pulse) state<=ACT;
            ACT: if(full) state<=IDLE;
            default: state<=IDLE;
        endcase    
    
    reg [7 : 0] din;
    reg wr_en;
    
    always @(posedge clk or negedge rst_n)
    if(rst_n==0) begin
        din<="A";
        wr_en<=0;
    end
    else case(state)
        IDLE: begin
            din<="A";
            wr_en<=0;        
        end
        ACT: begin
             wr_en<=1;            
            if(din=="z") 
                din<="A";
            else
                din<=din+1;       
        end
    endcase
    
    uart_tx_wrapper uart_tx_wrapper_inst
(
    .clk(clk),
    .rst_n(rst_n),
    
    .din(din),
    .wr_en(wr_en),
    .full(full),
    .empty(empty),
    .tx(tx)
);  

endmodule

L2代码(uart_tx_wrapper.v)

  • 这一层的代码主要是实现uart_tx+fifo的适配
  • 注意从xfer_end到xfer_start,要控制速率,要加延时。太快了,上位机识别不了。
`timescale 1ns / 1ps

module uart_tx_wrapper(
    input clk,
    input rst_n,
    
    input [7:0] din,
    input wr_en,
    
    output full,
    output empty,
    output tx
    );
    
    wire empty;
    wire xfer_start;
    reg rd_en;
    wire [7 : 0] dout;
    wire [8 : 0] data_count;
    
    reg empty_s;
    always @(posedge clk or negedge rst_n)
    if(rst_n==0)
        empty_s<=0;
    else
        empty_s<=empty;
    
    assign first_rd_en=(~empty) & empty_s;            //第一次读,由empty信号下降沿自动启动   

    always @(posedge clk or negedge rst_n)
    if(rst_n==0)
        rd_en <= 0;
    else    
        rd_en <= (first_rd_en|xfer_end) & (~empty);  //fifo不为空的时候才可以读
 
     /*********************************************************************************/    
    //rd_en-->xfer_start, should wait some time, otherwise pc cannot recognize
    //mini-fsm to control counter 
    parameter CNT_MAX=12'h2ff;   
    reg[11:0] div_cnt;
    reg cnt_valid;
    always @(posedge clk or negedge rst_n)
    if(rst_n==0)
        div_cnt<=0;
    else if(add_div_cnt) begin
        if(end_div_cnt)
            div_cnt<=0;
        else
            div_cnt<=div_cnt+1; 
    end 
    
    assign add_div_cnt=cnt_valid;
    assign end_div_cnt=add_div_cnt && (div_cnt==CNT_MAX-1);

    always @(posedge clk or negedge rst_n)
    if(rst_n==0)
        cnt_valid<=0;
    else case(cnt_valid)
        0: if(rd_en) cnt_valid<=1;
        1: if(end_div_cnt) cnt_valid<=0;
    endcase     

    assign xfer_start= end_div_cnt;
    /*********************************************************************************/        
    
    uart_tx uart_tx_inst
    (
        .sys_clk(clk),
        .sys_rst_n(rst_n),
        .pi_data(dout),
        .pi_flag(xfer_start),
        .po_ack(xfer_end),
        .tx(tx)
    );      
    
    fifo_generator_0 your_instance_name (
      .clk(clk),                // input wire clk
      .din(din),                // input wire [7 : 0] din
      .wr_en(wr_en),            // input wire wr_en
      .rd_en(rd_en),            // input wire rd_en
      .dout(dout),              // output wire [7 : 0] dout
      .full(full),              // output wire full
      .empty(empty),            // output wire empty
      .data_count(data_count)  // output wire [8 : 0] data_count
    ); 
endmodule

L3代码(uart_tx.v)

  • 这一层代码是底层接口模块,实现uart的发送功能
  • 是在野火的代码的基础上,增加了po_ack功能,实现流控。因为uart是慢速模块,必须要使用从机的rdy机制实现流控。
`timescale  1ns/1ns

module  uart_tx
#(
    parameter   UART_BPS    =   'd115200,         //涓插彛娉㈢壒鐜?
    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              po_ack,
     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璁℃暟鍒癇AUD_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:褰揵aud_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:杈撳嚭鏁版嵁鍦ㄦ弧瓒硆s232鍗忚锛堣捣濮嬩綅涓?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


always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            po_ack<=0;
         else if(bit_flag && bit_cnt==9)
            po_ack<=1;
         else
            po_ack<=0;
        
        
endmodule

引脚约束(z7020+eda_v3)

create_clock -period 20.000 -name clk [get_ports clk]
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports clk]
set_property -dict {PACKAGE_PIN D18 IOSTANDARD LVCMOS33} [get_ports rst_n]

set_property -dict {PACKAGE_PIN G17 IOSTANDARD LVCMOS33} [get_ports sw]
set_property -dict {PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [get_ports full]
set_property -dict {PACKAGE_PIN T16 IOSTANDARD LVCMOS33} [get_ports empty]
set_property -dict {PACKAGE_PIN W14 IOSTANDARD LVCMOS33} [get_ports tx]
set_property -dict {PACKAGE_PIN Y14 IOSTANDARD LVCMOS33} [get_ports rx]

仿真波形

image image image

image 存在的问题:因此我们用full信号来作为状态切换和产生wr_en信号的条件,这导致了wr_en信号存在2拍延时。也就是full后,wr_en会继续写2个,会导致溢出。 解决方案:state的转换用almost_full信号(提前一拍),wr_en用组合逻辑

上板验证

image

由于fifo深度是512,按一次会发送512个字符。