fpga项目:uart_tx_fifo(改进) - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki
说明
初稿存在full->wr_en有2个cycle延迟的问题,导致full后会继续写2个数据,产生溢出。这样不好。 本设计进行了改进,改进的方法是:1)使用了fifo的almost_full信号,提前一拍;2)用组合逻辑产生wr_en,提前一拍。
代码结构
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(almost_full) state<=IDLE;
default: state<=IDLE;
endcase
reg [7 : 0] din;
always @(posedge clk or negedge rst_n)
if(rst_n==0) begin
din<="A";
end
else case(state)
IDLE: begin
din<="A";
end
ACT: begin
if(din=="z")
din<="A";
else
din<=din+1;
end
endcase
assign wr_en=(state==ACT);
uart_tx_wrapper uart_tx_wrapper_inst
(
.clk(clk),
.rst_n(rst_n),
.din(din),
.wr_en(wr_en),
.full(full),
.almost_full(almost_full),
.empty(empty),
.almost_empty(almost_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 almost_full,
output empty,
output almost_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
.almost_full(almost_full), // output wire almost_full
.empty(empty), // output wire empty
.almost_empty(almost_empty), // output wire almost_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]
仿真波形
可以看到,处理后full=1信号与wr_en=0同步产生。不会继续写了,因此不会溢出。
最后一个写入的数据是应该是0x71。
我们看到最后一个读出的数据确实是0x71。
上板验证
由于fifo深度是512,按一次会发送512个字符。