本文涉及的所有代码仅用于学习交流,不得用于其他用途
UART即通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),两个UART设备在进行通信时,发送数据的设备将并行的数据转化为一位一位的串行数据,并按照给定的时钟频率将串行数据依次打到TX通信线上,接收方检测RX上的电平,按照给定的时钟频率读取RX线上电平,并将采样到的串行数据还原成并行数据;
·异步:设备之间没有统一的时钟线连接
·串行:数据以固定的速率一位一位进行发送
·全双工:UART设备能够同时进行数据接收和发送
·逻辑电平:RS232(3V~25V为高电平 -3V或-25V为低电平)
TTL(5V或3.3V为高电平 0V为低电平)
首先UART是一个异步通信,即通信设备之间是不需要时钟线的,这不同于IIC与SPI等串行通信,UART设备通信双方均在自己的时钟系统下工作;
全双工也很好理解,这里发送线与接收线是独立的分开的,且在每个UART设备的UART模块中,都会设计串口接收模块与发送模块,两者可以同时工作;
UART通信使用的逻辑电平有很多,在单片机系统中长用TTL电平进行UART传输,除此之外还有RS232,RS485,但这些都不是一种协议,而是一种电平逻辑规范,我们从高电平的识别上来看,TTL允许电压波动的范围较小,因此它也是非常易受到干扰的;相比之下RS232的电压范围相对宽些;RS485使用的是差分传输,因此抗干扰能力更强;
综上,三种逻辑电平规范在传输距离上:TTL
波特率:描述UART设备传输数据快慢的单位,以每秒位数(bps)表示。
*UART对波特率的要求比较严格,因为其是异步通信,要求通信双方的波特率相差不能超过10%。
UART常用波特率:9600、115200、192000、230400等
假如选择的波特率为9600b/s,则其1s内能够传输9600bit,即9600/8 = 1200字节每秒;
波特率是否越大越好?
在平时的设备通信中,往往会觉得串口传输速率较慢,因此会把波特率提高来加快传输速率,但波特率也不是越大越好,而是越适合越好;
举个简单的例子,在单片机中UART模块后面往往会跟一个FIFO来做数据缓冲,因为单片机读取FIFO的速度有限,如果读FIFO速率小于UART向FIFO的写速率,且FIFO深度有限的情况下,则很容易造成FIFO满,满状态的FIFO不允许数据写入,此时就会造成丢包;
此时有以下几种方法解决,1.增加FIFO深度(参考FIFO文章)2.降低波特率使得读速率大于写速率。3.如果是突发传输,则要根据UART的接收频率、单片机的读出速率来合理计算FIFO深度,使得数据不会溢出(参考FIFO文章);
空闲:UART数据线保持高电平。
起始位:传输线TXD由高电平转换为低电平。(必须有)
数据位:5-8位长度数据位,通常从最低位开始发送。(通常为8位)(必须有)
奇偶校验位:判断数据传输过程中是否有数据位发生错误变化。(可以无)
停止位:在数据包发送结束后,发送数据线保持1~2位高电平。(必须有)
UART在空闲时,会保持TX与RX线为高电平;
当一个数据帧进行发送时,首先发送端(TX)会将高电平拉低(发送一个0bit)示意通信的开始;另一接收端在检测到TX线被拉低后,则开始接收数据;
接下来发送端会将8位数据按照从低位到高位的顺序依次将其打到TX线上,接收方则按照对应波特率读取RX线电平;
如果通信双方设置了需要校验,则在8位数据发送结束后,会附加1bit的奇偶校验位;(数据量少的通信中不需要)
在通信需要结束时,发送端会将TX线拉高,接收方检测到这个高电平则结束这次通信;(停止位可以是1,1.5,2个,一般来说是1个)
例:通过UART发送数据0x33(不带校验位,且1个停止位)
对第三节中的字节传输速率做出修正:
如果是不带校验位,且只有一个停止位,则传输一个字节数据需要10bit,据此得到传输字节的真正速率:
9600/10 = 960字节每秒
设计UART模块实际上是设计UART 发送模块与UART 接收模块;
在设计UART模块过程中需重点关注:
波特率如何产生?
串并转换、并串转换如何进行?
起始位、停止位如何检测?
rx信号如何采样?
波特率的产生:
波特率实际上就是一个分频器,其分频系数为fsys_clk/Baunds;
fsys_clk为UART模块所接的时钟源,Baunds则是所要得到的波特率;
例: fsys_clk = 50_000_000 Hz, Baunds = 115200,则分频系数Div0 = fsys_clk/Baunds = 50_000_000 / 115200 = 434
串并转换、并串转换:
并串转换应用于发送模块,其本质是一个移位寄存器,驱动时钟为波特率分频器输出的时钟,输入信号为并行8位数据,输出为串行数据;首先对并行的8位数据进行重建,得到数据帧;
如0x33则重建后的十位数据为:{1‘b1,0x33,1’b0},最低位的1’b0为起始位,最高位的1’b1为停止位;
串并转换应用于接收模块,其本质上也是一个移位寄存器,只不过其输出为8位并行数据,输入为串行数据;
起始位、停止位检测:
起始位的检测可以通过检测高电平到低电平的下降沿来实现;
停止位可以在接收满8位数据后,在第9位数据上进行检测;
rx信号采样:
接收信号首先需要进行同步处理,因为rx信号来自另一个时钟域;
其次同步后的信号需要采样其中间位置,因为此时的电平是最稳定的;
并串转换器在检测到有效的Valid信号后将tx_data包装成一帧数据{1‘b1,tx_data,1’b0}
同时发送状态机检测到valid信号后,进入到发送状态,此时波特率分频器开始计数,控制比特计数器增,并串转换模块则将对应比特位的数据串行发送出去。
源码:
`timescale 1ns / 1ps
//
// Company: Goodwayhouse
// Engineer: Ge Wen Jie
//
// Create Date: 2022/09/11 11:23:52
// Design Name:
// Module Name: uart_dtx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_dtx#(
parameter integer UART_TX_REF_CLK_FRE = 50_000_000,
parameter integer UART_TX_BAUNDS_RATE = 115200
)(
input wire uart_ref_clk,//uart tx module reference clock
input wire uart_tx_nrst,//uart tx module reset signal
input wire [31:0] uart_baunds_div,//baunds rate division
input wire [7:0] uart_tx_data,//uart tx 8bits data input
input wire uart_tx_data_qvld,//send data valid signal
output wire uart_tx_finish,//uart send data finish flag
output wire uart_tx_busy,//uart tx module busy flag
output wire uart_tx_dout //serial data output
);
//uart tx module baunds rater counter max, start data out, end data out definitions
localparam integer UART_BAUNDS_COUNTER_MAX = UART_TX_REF_CLK_FRE/UART_TX_BAUNDS_RATE;
localparam UART_TX_START_DOUT = 1'b0;
localparam UART_TX_END_DOUT = 1'b1;
//uart tx send state definition
localparam UART_IDLE0 = 3'b000;
// localparam UART_IDLE1 = 3'b001;
localparam UART_TXDATE = 3'b010;
// localparam UART_TXFINISH = 3'b100;
//inner signals definitions
reg tx_out;
reg tx_finish;
reg tx_busy;
reg [3:0] tx_bit_cnt;
reg [31:0] tx_baunds_cnt;
reg [2:0] uart_tx_state;
reg [2:0] uart_tx_next_state;
reg [9:0] uart_tx_buffer;
reg uart_wr_en;
//inner signal connect
assign uart_tx_finish = tx_finish;
assign uart_tx_busy = tx_busy;
assign uart_tx_dout = tx_out;
always@(posedge uart_ref_clk)
begin
if(~uart_tx_nrst)
begin
uart_tx_state <= UART_IDLE0;
end
else begin
uart_tx_state <= uart_tx_next_state;
end
end
always@(*)
begin
case(uart_tx_state)
UART_IDLE0://waiting a valid data input
begin
if(uart_tx_data_qvld)
begin
uart_tx_next_state <= UART_TXDATE;
end
else
begin
uart_tx_next_state <= UART_IDLE0;
end
end
UART_TXDATE://waiting tx module send data finish
begin
if((tx_finish == 1'b1) && (tx_busy == 1'b0))
begin
uart_tx_next_state <= UART_IDLE0;
end
else begin
uart_tx_next_state <= UART_TXDATE;
end
end
default:uart_tx_next_state <= UART_IDLE0;
endcase
end
always@(posedge uart_ref_clk)
begin
if(~uart_tx_nrst)
begin
tx_busy <= 'b0;
tx_finish <= 'b1;
tx_out <= 1'b1;
end
else begin
if((tx_finish == 1'b1)&&(tx_busy == 1'b0)&&(uart_tx_data_qvld))//sample date when tx finish and tx is not busy
begin
uart_tx_buffer <= {UART_TX_END_DOUT,uart_tx_data,UART_TX_START_DOUT};
tx_finish <= 1'b0;
tx_busy <= 1'b1;
tx_out <= 1'b1;
end
else if((tx_finish == 1'b0)&&(tx_busy == 1'b1)&&(uart_tx_state == UART_TXDATE))
begin
if(uart_wr_en==1'b1)
begin
if(tx_bit_cnt != 4'd10)begin tx_out <= uart_tx_buffer[0];uart_tx_buffer <= uart_tx_buffer >> 1; end
else begin tx_out <= UART_TX_END_DOUT;tx_finish <= 1'b1;tx_busy <= 1'b0; end
end
else begin tx_busy <= tx_busy;tx_finish <= tx_finish;tx_out <= tx_out; end
end
end
end
always@(posedge uart_ref_clk)
begin
if(~uart_tx_nrst)
begin
tx_baunds_cnt <= 'b0;
tx_bit_cnt <= 'b0;
uart_wr_en <= 1'b0;
end
else begin
if(uart_tx_state == UART_IDLE0)
begin
tx_baunds_cnt <= 'b0;
tx_bit_cnt <= 'b0;
uart_wr_en <= 1'b0;
end
else if(uart_tx_state == UART_TXDATE)
begin
if(tx_baunds_cnt != uart_baunds_div - 1)
begin
uart_wr_en <= 1'b0;
tx_bit_cnt <= tx_bit_cnt;
tx_baunds_cnt <= tx_baunds_cnt + 1;
end
else begin
uart_wr_en <= 1'b1;
tx_baunds_cnt <= 'b0;
tx_bit_cnt <= tx_bit_cnt + 1;
end
end
end
end
endmodule
串行模块的功能可划分为下图所示;
首先边沿检测电路对rx进行检测,如果检测到下降沿,则开始一次Uart接收,接收状态机检测到边沿检测电路发出的有效信号,则进入接收状态;
在接收状态下,波特率分频器开始工作,这里需要注意,为了能够在rx每比特的中间时刻采用,在第0bit时进行一次f(clk) / Baunds Rate / 2分频,并忽略第一次接收的数据;
比特计数器在检测到接收满8bit后,控制并串模块进行一次高电平停止位的检测,若成功则将并行数据输出,接收状态机回到空闲状态;
源码:
`timescale 1ns / 1ps
//
// Company: Goodwayhouse
// Engineer: Ge Wen Jie
//
// Create Date: 2022/09/15 16:52:19
// Design Name:
// Module Name: uart_drx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_drx#(
parameter integer UART_RX_REF_CLK_FRE = 50_000_000,
parameter integer UART_RX_BAUNDS_RATE = 115200
)(
input wire uart_ref_clk,//uart rx module reference clock
input wire uart_rx_nrst,//uart rx module reset signal
input wire [31:0] uart_baunds_div,//baunds rate division
input wire uart_rx_din, //serial data input
output wire uart_rx_data_qvld,//receive data valid signal
output wire uart_rx_finish,//uart send data finish flag
output wire uart_rx_busy,//uart rx module busy flag
output wire [7:0] uart_rx_data //uart rx 8bits data input
);
localparam IDLE0 = 2'b00;
localparam UART_RECV = 2'b01;
localparam integer UART_BAUNDS_COUNTER_MAX = UART_RX_REF_CLK_FRE/UART_RX_BAUNDS_RATE;
localparam UART_TX_START_DOUT = 1'b0;
localparam UART_TX_END_DOUT = 1'b1;
//inner signals definitions
reg rx_finish;
reg rx_busy;
reg [7:0] rx_data;
reg rx_data_qvld;
reg [7:0] rx_data_buffer;//receive data buffer
reg [3:0] bit_cnt;//receive bit counter
reg [1:0] uart_recv_state;
reg [1:0] uart_recv_next_state;
reg [31:0] rx_baunds_cnt;
reg uart_rd_en;
//inner signals connect
assign uart_rx_busy = rx_busy;
assign uart_rx_data = rx_data;
assign uart_rx_finish = rx_finish;
assign uart_rx_data_qvld = rx_data_qvld;
//uart send start check
wire uart_send_start;
reg uart_rx_din_dff;
assign uart_send_start = uart_rx_din_dff & ~uart_rx_din;
always @(posedge uart_ref_clk) begin
if (~uart_rx_nrst) begin
// reset
uart_rx_din_dff <= 1'b0;
end
else
begin
uart_rx_din_dff <= uart_rx_din;
end
end
always@(posedge uart_ref_clk)
begin
if (~uart_rx_nrst) begin
// reset
uart_recv_state <= IDLE0;
end
else
begin
uart_recv_state <= uart_recv_next_state;
end
end
always@(*)
begin
case(uart_recv_state)
IDLE0:begin//check if UART send start
if(uart_send_start == 1'b1) begin
uart_recv_next_state <= UART_RECV;
end
else begin
uart_recv_next_state <= IDLE0;
end
end
UART_RECV:begin //receiver data from master
if((rx_busy == 1'b0) && (rx_finish == 1'b1))begin
uart_recv_next_state <= IDLE0;
end
else begin
uart_recv_next_state <= UART_RECV;
end
end
default:uart_recv_next_state <= IDLE0;
endcase
end
always@(posedge uart_ref_clk)
begin
if(~uart_rx_nrst)begin
rx_data <= 'b0;
rx_data_buffer <= 'b0;
rx_busy <= 1'b0;
rx_finish <= 1'b1;
rx_data_qvld <= 1'b0;
end
else begin
if((rx_busy == 1'b0) && (rx_finish == 1'b1))
begin
rx_data_qvld <= 1'b0;
if(uart_send_start == 1'b1)
begin
rx_data_buffer <= 'b0;rx_busy <= 1'b1; rx_finish <= 1'b0;
end
end
else if((rx_busy == 1'b1) && (rx_finish == 1'b0) && (uart_recv_state == UART_RECV) && (uart_rd_en == 1'b1))
begin
if((bit_cnt != 4'd0)&&(bit_cnt != 4'd10))
begin
rx_data_buffer <= {uart_rx_din,rx_data_buffer[7:1]};
end
// rx_data_buffer <= {uart_rx_din,rx_data_buffer[7:1]};
else if ((bit_cnt == 4'd10)&&(uart_rx_din == 1'b1)) begin
rx_busy <= 1'b0;
rx_finish <= 1'b1;
rx_data_qvld <= 1'b1;
rx_data <= rx_data_buffer;
rx_data_buffer <= 'b0;
end
end
end
end
always@(posedge uart_ref_clk)
begin
if(~uart_rx_nrst)
begin
rx_baunds_cnt <= 'b0;
bit_cnt <= 'b0;
uart_rd_en <= 1'b0;
end
else begin
if(uart_recv_state == IDLE0)
begin
rx_baunds_cnt <= 'b0;
bit_cnt <= 'b0;
uart_rd_en <= 1'b0;
end
else if(uart_recv_state == UART_RECV)
begin
if(bit_cnt == 1'b0)
begin
if(rx_baunds_cnt != (uart_baunds_div>>1) - 1)
begin
uart_rd_en <= 1'b0;
bit_cnt <= bit_cnt;
rx_baunds_cnt <= rx_baunds_cnt + 1;
end
else
begin
uart_rd_en <= 1'b0;
bit_cnt <= bit_cnt + 1;
rx_baunds_cnt <= 'b0;
end
end
else
begin
if(rx_baunds_cnt != uart_baunds_div - 1)
begin
uart_rd_en <= 1'b0;
bit_cnt <= bit_cnt;
rx_baunds_cnt <= rx_baunds_cnt + 1;
end
else begin
uart_rd_en <= 1'b1;
rx_baunds_cnt <= 'b0;
bit_cnt <= bit_cnt + 1;
end
end
end
end
end
endmodule
在顶层的设计中,我们需要做:
Tx模块并行输入端的缓冲(FIFO)
Rx模块并行输出端的缓冲(FIFO)
rx信号的跨时钟域处理;
不同于我们之前说的FIFO,本次设计中的FIFO主要是为了解决高速到低速通信的匹配问题,因此不需要使用到异步FIFO;
对于rx信号的跨时钟域处理,使用典型的2级触发器;
注:这里的parameter实际上只用到了FIFO_DATA_DEPTH和FIFO_DATA_WIDTH,是第一版设计中保留的;这版中波特率的设置通过uart_baunds_div寄存器直接进行分频
顶层源码:
`timescale 1ns / 1ps
//
// Company: Goodwayhouse
// Engineer: Ge Wen Jie
//
// Create Date: 2022/09/11 15:36:29
// Design Name:
// Module Name: uart_dtxrx_top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_dtxrx_top#(
parameter integer UART_REF_CLK_FRE = 50_000_000,
parameter integer UART_BAUNDS_RATE = 115200,
parameter integer FIFO_DATA_DEPTH = 8,
parameter integer FIFO_DATA_WIDTH = 8
)(
input wire uart_ref_clk,//uart tx module reference clock
input wire uart_nrst,//uart tx module reset signal
input wire [7:0] uart_tx_data,//uart tx 8bits data input
input wire uart_tx_data_qvld,//send data valid signal
input wire uart_rx_data_rd,//send data valid signal
//uart inner signals
input wire [31:0] uart_baunds_div,//baunds rate division
output wire uart_tx_finish,//uart send data finish flag
output wire uart_tx_busy,//uart tx module busy flag
output wire uart_rx_data_qvld,//receive data valid signal
output wire uart_rx_finish,//uart send data finish flag
output wire uart_rx_busy,//uart rx module busy flag
output wire [7:0] uart_rx_data,//uart rx 8bits data input
//FIFO status signals
output wire tx_fifo_full,//tx data fifo full signal
output wire tx_fifo_empty,//tx data fifo empty signal
output wire rx_fifo_full,//rx data fifo full signal
output wire rx_fifo_empty,//rx data fifo empty signal
//uart tx/rx port
output wire uart_tx_dout,//serial data output
input wire uart_rx_din,//serial data output
//interrupt signals output
input wire uart_tx_interrupt_en,//uart tx event finish interrupt enable
input wire uart_rx_interrupt_en,//uart rx event finish interrupt enable
output wire uart_tx_interrupt,//uart tx event finish interrupt
output wire uart_rx_interrupt //uart rx event finish interrupt
);
//*************************************FIFO ADDRESS WIDTH definition start**********************************//
localparam FIFO_ADDR_WIDTH = $clog2(FIFO_DATA_DEPTH);
//*************************************FIFO ADDRESS WIDTH definition start**********************************//
wire rx_dff0,rx_dff1;
dff#(
.DFF_LEVEL(1),
.DATA_WIDTH(1)
)rx_din_dff0(
.clk(uart_ref_clk),
.din(uart_rx_din),
.dout(rx_dff0),
.nrst(uart_nrst)
);
dff#(
.DFF_LEVEL(1),
.DATA_WIDTH(1)
)rx_din_dff1(
.clk(uart_ref_clk),
.din(rx_dff0),
.dout(rx_dff1),
.nrst(uart_nrst)
);
reg clk_tx_data_rd;
wire [7:0] tx_data_fifo2uart;
wire [7:0] rx_data_uart2fifo;
reg tx_data_fifo2uart_qvld;
reg [1:0] uart_tx_state;
wire rx_fifo_wr_clk;
wire rx_fifo_wr_clk_dff;
assign rx_fifo_wr_clk = uart_rx_data_qvld&(~rx_fifo_full);
assign uart_tx_interrupt = uart_tx_finish & uart_tx_interrupt_en;
assign uart_rx_interrupt = uart_rx_data_qvld & uart_rx_interrupt_en;
dff#(
.DFF_LEVEL(1),
.DATA_WIDTH(1)
)rx_wr_clk_dff2(
.clk(uart_ref_clk),
.din(rx_fifo_wr_clk),
.dout(rx_fifo_wr_clk_dff),
.nrst(uart_nrst)
);
always@(posedge uart_ref_clk, negedge uart_nrst)
begin
if(~uart_nrst)
begin
clk_tx_data_rd <= 1'b0;
tx_data_fifo2uart_qvld <= 1'b0;
uart_tx_state <= 4'd0;
end
else
begin
case(uart_tx_state)
4'd0:
begin
tx_data_fifo2uart_qvld <= 1'b0;
if(tx_fifo_empty)
begin
uart_tx_state <= 4'd0;
end
else
begin
clk_tx_data_rd <= 1'b1;
uart_tx_state <= 4'd1;
end
end
4'd1:
begin
clk_tx_data_rd <= 1'b0;
tx_data_fifo2uart_qvld <= 1'b1;
if(~uart_tx_busy)
uart_tx_state <= uart_tx_state;
else
uart_tx_state <= 4'd2;
end
4'd2:
begin
tx_data_fifo2uart_qvld <= 1'b0;
if(uart_tx_busy)
uart_tx_state <= uart_tx_state;
else
uart_tx_state <= 4'd0;
end
endcase
end
end
//tx data from FIFO
FIFO#(
.DATA_WIDTH (FIFO_DATA_WIDTH),
.DATA_DEPTH (FIFO_DATA_DEPTH),
.ADDR_WIDTH (FIFO_ADDR_WIDTH)
)FIFO_tx_inist0
(
. clk_wr(uart_tx_data_qvld),
. clk_rd(clk_tx_data_rd),
. wr_en(1),
. rd_en(1),
. fifo_en(1),
. fifo_rst(~uart_nrst),
. wr_data(uart_tx_data),
. rd_data(tx_data_fifo2uart),
. full(tx_fifo_full),
. empty(tx_fifo_empty)
);
//rx data from FIFO
FIFO#(
.DATA_WIDTH (FIFO_DATA_WIDTH),
.DATA_DEPTH (FIFO_DATA_DEPTH),
.ADDR_WIDTH (FIFO_ADDR_WIDTH)
)FIFO_rx_inist0
(
. clk_wr(rx_fifo_wr_clk_dff),
. clk_rd(uart_rx_data_rd),
. wr_en(1),
. rd_en(1),
. fifo_en(1),
. fifo_rst(~uart_nrst),
. wr_data(rx_data_uart2fifo),
. rd_data(uart_rx_data),
. full(rx_fifo_full),
. empty(rx_fifo_empty)
);
uart_dtx#(
.UART_TX_REF_CLK_FRE(UART_REF_CLK_FRE),
.UART_TX_BAUNDS_RATE(UART_BAUNDS_RATE)
)uart_dtx_inist0(
. uart_ref_clk(uart_ref_clk),//uart tx module reference clock
. uart_tx_nrst(uart_nrst),//uart tx module reset signal
. uart_baunds_div(uart_baunds_div),
. uart_tx_data(tx_data_fifo2uart),//uart tx 8bits data input
. uart_tx_data_qvld(tx_data_fifo2uart_qvld),//send data valid signal
. uart_tx_finish(uart_tx_finish),//uart send data finish flag
. uart_tx_busy(uart_tx_busy),//uart tx module busy flag
. uart_tx_dout(uart_tx_dout) //serial data output
);
uart_drx#(
. UART_RX_REF_CLK_FRE(UART_REF_CLK_FRE),
. UART_RX_BAUNDS_RATE(UART_BAUNDS_RATE)
)uart_drx_inist0(
. uart_ref_clk(uart_ref_clk),//uart rx module reference clock
. uart_rx_nrst(uart_nrst),//uart rx module reset signal
. uart_baunds_div(uart_baunds_div),
. uart_rx_din(rx_dff1), //serial data input
. uart_rx_data_qvld(uart_rx_data_qvld),//receive data valid signal
. uart_rx_finish(uart_rx_finish),//uart send data finish flag
. uart_rx_busy(uart_rx_busy),//uart rx module busy flag
. uart_rx_data(rx_data_uart2fifo) //uart rx 8bits data input
);
endmodule
简单同步FIFO源码
module FIFO#(
parameter integer DATA_WIDTH = 32,
parameter integer DATA_DEPTH = 64,
parameter integer ADDR_WIDTH = 5
)
(
input clk_wr,
input clk_rd,
input wr_en,
input rd_en,
input fifo_en,
input fifo_rst,
input [DATA_WIDTH-1:0] wr_data,//写数据输入
output [DATA_WIDTH-1:0] rd_data,//读数据输出
output full,//fifo满信号
output empty, //fifo 空信号
output fifo_wr_en,
output fifo_rd_en,
output fifo_busy,
output [ADDR_WIDTH-1:0] wr_add,
output [ADDR_WIDTH-1:0] rd_add
);
wire [ADDR_WIDTH-1:0] wr_addr,rd_addr;//送入RAM的读写地址
reg [ADDR_WIDTH:0] wr_addr_a,rd_addr_a;//扩位后的写地址与读地址
wire fifo_wr,fifo_rd;//fifo读写控制
reg [7:0] fifo_data_in;
reg [3:0] rd_add0;
wire [ADDR_WIDTH:0] rd_addr_a1;
assign fifo_busy = clk_rd;
assign wr_add = wr_addr;
assign rd_add = rd_addr;
assign wr_addr = wr_addr_a[ADDR_WIDTH-1:0];
assign rd_addr = rd_addr_a[ADDR_WIDTH-1:0];
assign fifo_wr = (wr_en)&(!full);//写使能且非满
assign fifo_rd = (rd_en)&(!empty);//读使能且非空
assign fifo_wr_en = fifo_wr;//写使能且非满
assign fifo_rd_en = fifo_rd;//读使能且非空
//空、满信号判断
assign empty = ((rd_addr_a == wr_addr_a)||(wr_addr_a==0)&(rd_addr_a==0)) ? 1:0;
assign full = ((rd_addr_a[ADDR_WIDTH] != wr_addr_a[ADDR_WIDTH])&&(rd_addr_a[ADDR_WIDTH-1:0] == wr_addr_a[ADDR_WIDTH-1:0])) ? 1:0;
always @ (posedge clk_wr , posedge fifo_rst)//fifo写
begin
if(fifo_rst)
begin
wr_addr_a <= 5'd0;//第一个不写
end
else
begin
if(fifo_en & wr_en & !full)
wr_addr_a <= wr_addr_a + 5'd1;
else
wr_addr_a <= wr_addr_a;
end
end
always @ (posedge clk_rd , posedge fifo_rst)//fifo读
begin
if(fifo_rst)
begin
rd_addr_a <= 5'd0;
end
else
begin
if(fifo_en & rd_en & !empty )
rd_addr_a <= rd_addr_a + 5'd1;
else
rd_addr_a <= rd_addr_a;
end
end
RAM_LUT#
(
.DATA_WIDTH(DATA_WIDTH),
.DATA_DEPTH(DATA_DEPTH),
.ADDR_WIDTH(ADDR_WIDTH)
)
RAM_LUT_inist0
(
.fifo_rst(fifo_rst),
. data_in(wr_data),
. addr_a(wr_addr),
. clk_a(clk_wr),
. ena(fifo_wr),
. data_out(rd_data),
. addr_b(rd_addr),
. clk_b(clk_rd),
. enb(fifo_rd)
);
endmodule
module RAM_LUT#
(
parameter integer DATA_WIDTH = 32,
parameter integer ADDR_WIDTH = 4,
parameter integer DATA_DEPTH = 16
)
(
input fifo_rst,
input [DATA_WIDTH - 1:0] data_in,
input [ADDR_WIDTH - 1:0] addr_a,
input clk_a,
input ena,
output reg [DATA_WIDTH - 1:0] data_out,
input [ADDR_WIDTH - 1:0] addr_b,
input clk_b,
input enb
);
reg [31:0] ram[DATA_DEPTH:0];
integer i;
always@(posedge clk_a,posedge fifo_rst)
begin
if(fifo_rst)
for(i=0;i<DATA_DEPTH;i=i+1) ram[i] <= 'd0;
else if(ena)
ram[addr_a] <= data_in;
end
always@(posedge clk_b,posedge fifo_rst)
begin
if(fifo_rst)
data_out <= 'd0;
else if(enb)
begin
data_out <= ram[addr_b];
end
end
endmodule
多级同步器源码:
`timescale 1ns / 1ps
//
// Company: Goodwayhouse
// Engineer: Ge Wen Jie
//
// Create Date: 2022/08/08 21:33:25
// Design Name:
// Module Name: dff
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module dff#(
parameter integer DFF_LEVEL = 1,
parameter integer DATA_WIDTH = 8
)(
input wire clk,
input wire [DATA_WIDTH - 1:0] din,
input wire [DATA_WIDTH - 1:0] dout,
input wire nrst
);
reg [DATA_WIDTH - 1:0] din_buff [DFF_LEVEL-1:0];
assign dout = din_buff[DFF_LEVEL-1];
integer i;
always @(posedge clk or negedge nrst) begin
if (~nrst) begin
// reset
for(i=0;i<DFF_LEVEL;i=i+1)
begin
din_buff[i] <= 'b0;
end
end
else begin
for(i=1;i<DFF_LEVEL;i=i+1)
begin
din_buff[i] <= din_buff[i-1];
end
din_buff[0] <= din;
end
end
endmodule
仿真源码:
module uart_tb(
);
parameter integer UART_REF_CLK_FRE = 50_000_000;
parameter integer UART_BAUNDS_RATE = 50_000_000/4;
//FIFO true depth
parameter integer FIFO_DATA_DEPTH = 8;
//FIFO degth's margin
parameter integer FIFO_DATA_WIDTH = 8;
reg uart_ref_clk;//uart tx module reference clock
reg uart_nrst;//uart tx module reset signal
reg [7:0] uart_tx_data;//uart tx 8bits data input
reg uart_tx_data_qvld;//send data valid signal
//uart inner signals
wire uart_tx_finish;//uart send data finish flag
wire uart_tx_busy;//uart tx module busy flag
wire uart_rx_data_qvld;//receive data valid signal
wire uart_rx_finish;//uart send data finish flag
wire uart_rx_busy;//uart rx module busy flag
wire [7:0] uart_rx_data;//uart rx 8bits data input
//FIFO status signals
wire tx_fifo_full;
wire tx_fifo_empty;
wire rx_fifo_full;
wire rx_fifo_empty;
//uart tx/rx port
wire uart_tx_dout;//serial data output
reg uart_rx_din ;//serial data output
reg uart_rx_data_rd;
initial begin
uart_ref_clk = 0;
forever begin
#1 uart_ref_clk = ~uart_ref_clk;
end
end
initial begin
uart_nrst = 0;
uart_tx_data = 0;
uart_tx_data_qvld= 0;
uart_rx_din = 1;
#2 uart_nrst = 1;
forever begin
if(~tx_fifo_full)
begin
uart_tx_data = uart_tx_data+1;
#2 uart_tx_data_qvld = 1;
#2 uart_tx_data_qvld = 0;
end
else #2;
end
end
always @(posedge uart_ref_clk) begin
if(~uart_nrst) begin
uart_rx_data_rd <= 1'b0;
end
else
begin
if(~rx_fifo_empty & uart_rx_data_rd == 1'b0)
begin
uart_rx_data_rd <= 1'b1;
end
else
begin
uart_rx_data_rd <= 1'b0;
end
end
end
uart_dtxrx_top#(
.UART_REF_CLK_FRE(UART_REF_CLK_FRE),
.UART_BAUNDS_RATE(UART_BAUNDS_RATE),
.FIFO_DATA_DEPTH (FIFO_DATA_DEPTH),
.FIFO_DATA_WIDTH (FIFO_DATA_WIDTH)
)uart_dtxrx_top_inist(
. uart_ref_clk(uart_ref_clk),//uart tx module reference clock
. uart_nrst(uart_nrst),//uart tx module reset signal
. uart_tx_data(uart_tx_data),//uart tx 8bits data input
. uart_tx_data_qvld(uart_tx_data_qvld),//send data valid signal
. uart_rx_data_rd(uart_rx_data_rd),
. uart_baunds_div(4),
//uart inner signals
. uart_tx_finish(uart_tx_finish),//uart send data finish flag
. uart_tx_busy(uart_tx_busy),//uart tx module busy flag
. uart_rx_data_qvld(uart_rx_data_qvld),//receive data valid signal
. uart_rx_finish(uart_rx_finish),//uart send data finish flag
. uart_rx_busy(uart_rx_busy),//uart rx module busy flag
. uart_rx_data(uart_rx_data),//uart rx 8bits data input
//FIFO status signals
. tx_fifo_full(tx_fifo_full),
. tx_fifo_empty(tx_fifo_empty),
. rx_fifo_full ( rx_fifo_full),
. rx_fifo_empty(rx_fifo_empty),
//uart tx/rx port
. uart_tx_dout(uart_tx_dout), //serial data output
. uart_rx_din(uart_tx_dout) //serial data output
);
endmodule
如果是纯FPGA设计,这步就可以省略了,上面的模块已经能够使用了;
AXI主要是为了能够让其与CPU交互,进行软硬协同设计;
`timescale 1 ns / 1 ps
module AXI_Uart_Lite_v1_0_S0_AXI #
(
// Users to add parameters here
parameter integer UART_REF_CLK_FRE = 50_000_000,
parameter integer UART_BAUNDS_RATE = 115200,
parameter integer FIFO_DATA_DEPTH = 8,
parameter integer FIFO_DATA_WIDTH = 8,
// User parameters ends
// Do not modify the parameters beyond this line
// Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH= 32,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH= 5
)
(
// Users to add ports here
input wire uart_ref_clk,//uart tx module reference clock
output wire uart_tx_interrupt,//uart tx event finish interrupt
output wire uart_rx_interrupt, //uart rx event finish interrupt
output wire uart_tx_dout,//serial data output
input wire uart_rx_din,//serial data output
// User ports ends
// Do not modify the ports beyond this line
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Write channel Protection type. This signal indicates the
// privilege and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Write address valid. This signal indicates that the master signaling
// valid write address and control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that the slave is ready
// to accept an address and associated control signals.
output wire S_AXI_AWREADY,
// Write data (issued by master, acceped by Slave)
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte lanes hold
// valid data. There is one write strobe bit for each eight
// bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Write response valid. This signal indicates that the channel
// is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether the
// transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Read address valid. This signal indicates that the channel
// is signaling valid read address and control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that the slave is
// ready to accept an address and associated control signals.
output wire S_AXI_ARREADY,
// Read data (issued by slave)
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of the
// read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read valid. This signal indicates that the channel is
// signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
);
// UART signals
wire uart_tx_finish;//uart send data finish flag
wire uart_tx_busy;//uart tx module busy flag
wire uart_rx_data_qvld;//receive data valid signal
wire uart_rx_finish;//uart send data finish flag
wire uart_rx_busy;//uart rx module busy flag
wire [7:0] uart_rx_data;//uart rx 8bits data input
//FIFO status signals
wire tx_fifo_full;//tx data fifo full signal
wire tx_fifo_empty;//tx data fifo empty signal
wire rx_fifo_full;//rx data fifo full signal
wire rx_fifo_empty;//rx data fifo empty signal
// AXI4LITE signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1 : 0] axi_bresp;
reg axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
reg [1 : 0] axi_rresp;
reg axi_rvalid;
// Example-specific design signals
// local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
// ADDR_LSB is used for addressing 32/64 bit registers/memories
// ADDR_LSB = 2 for 32 bits (n downto 2)
// ADDR_LSB = 3 for 64 bits (n downto 3)
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer OPT_MEM_ADDR_BITS = 2;
//----------------------------------------------
//-- Signals for user logic register space example
//------------------------------------------------
//-- Number of Slave Registers 8
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg0;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg1;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg2;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg3;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg4;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg5;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg6;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg7;
wire slv_reg_rden;
wire slv_reg_wren;
reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
integer byte_index;
reg aw_en;
// I/O Connections assignments
assign S_AXI_AWREADY= axi_awready;
assign S_AXI_WREADY= axi_wready;
assign S_AXI_BRESP= axi_bresp;
assign S_AXI_BVALID= axi_bvalid;
assign S_AXI_ARREADY= axi_arready;
assign S_AXI_RDATA= axi_rdata;
assign S_AXI_RRESP= axi_rresp;
assign S_AXI_RVALID= axi_rvalid;
// Implement axi_awready generation
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awready <= 1'b0;
aw_en <= 1'b1;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// slave is ready to accept write address when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_awready <= 1'b1;
aw_en <= 1'b0;
end
else if (S_AXI_BREADY && axi_bvalid)
begin
aw_en <= 1'b1;
axi_awready <= 1'b0;
end
else
begin
axi_awready <= 1'b0;
end
end
end
// Implement axi_awaddr latching
// This process is used to latch the address when both
// S_AXI_AWVALID and S_AXI_WVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// Write Address latching
axi_awaddr <= S_AXI_AWADDR;
end
end
end
// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_wready <= 1'b0;
end
else
begin
if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
begin
// slave is ready to accept write data when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_wready <= 1'b1;
end
else
begin
axi_wready <= 1'b0;
end
end
end
// Implement memory mapped register select and write logic generation
// The write data is accepted and written to memory mapped registers when
// axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
// select byte enables of slave registers while writing.
// These registers are cleared when reset (active low) is applied.
// Slave register write enable is asserted when valid address and data are available
// and the slave is ready to accept the write address and write data.
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
slv_reg2 <= 0;
slv_reg3 <= 0;
slv_reg4 <= 0;
slv_reg5 <= 0;
slv_reg6 <= 0;
slv_reg7 <= 0;
end
else begin
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
3'h0:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h1:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 1
slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h2:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 2
slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h3:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 3
slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h4:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 4
slv_reg4[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h5:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 5
slv_reg5[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h6:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 6
slv_reg6[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h7:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 7
slv_reg7[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
default : begin
slv_reg0 <= slv_reg0;
slv_reg1 <= slv_reg1;
slv_reg2 <= slv_reg2;
slv_reg3 <= slv_reg3;
slv_reg4 <= slv_reg4;
slv_reg5 <= slv_reg5;
slv_reg6 <= slv_reg6;
slv_reg7 <= slv_reg7;
end
endcase
end
end
end
// Implement write response logic generation
// The write response and response valid signals are asserted by the slave
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
// This marks the acceptance of address and indicates the status of
// write transaction.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
end
else
begin
if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
begin
// indicates a valid write response is available
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0; // 'OKAY' response
end // work error responses in future
else
begin
if (S_AXI_BREADY && axi_bvalid)
//check if bready is asserted while bvalid is high)
//(there is a possibility that bready is always asserted high)
begin
axi_bvalid <= 1'b0;
end
end
end
end
// Implement axi_arready generation
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is
// de-asserted when reset (active low) is asserted.
// The read address is also latched when S_AXI_ARVALID is
// asserted. axi_araddr is reset to zero on reset assertion.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
axi_araddr <= 32'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID)
begin
// indicates that the slave has acceped the valid read address
axi_arready <= 1'b1;
// Read address latching
axi_araddr <= S_AXI_ARADDR;
end
else
begin
axi_arready <= 1'b0;
end
end
end
// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_ARVALID and axi_arready are asserted. The slave registers
// data are available on the axi_rdata bus at this instance. The
// assertion of axi_rvalid marks the validity of read data on the
// bus and axi_rresp indicates the status of read transaction.axi_rvalid
// is deasserted on reset (active low). axi_rresp and axi_rdata are
// cleared to zero on reset (active low).
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else
begin
if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
begin
// Valid read data is available at the read data bus
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0; // 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
// Read data is accepted by the master
axi_rvalid <= 1'b0;
end
end
end
// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
3'h0 : reg_data_out <= slv_reg0;
3'h1 : reg_data_out <= slv_reg1;
3'h2 : reg_data_out <= uart_rx_data;
3'h3 : reg_data_out <= {uart_rx_data_qvld,uart_rx_busy,uart_rx_finish,uart_tx_busy,uart_tx_finish,
rx_fifo_empty,rx_fifo_full,tx_fifo_empty,tx_fifo_full};
3'h4 : reg_data_out <= slv_reg4;
3'h5 : reg_data_out <= slv_reg5;
3'h6 : reg_data_out <= slv_reg6;
3'h7 : reg_data_out <= slv_reg7;
default : reg_data_out <= 0;
endcase
end
// Output register or memory read data
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rdata <= 0;
end
else
begin
// When there is a valid read address (S_AXI_ARVALID) with
// acceptance of read address by the slave (axi_arready),
// output the read dada
if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
end
end
end
// Add user logic here
uart_dtxrx_top#(
.UART_REF_CLK_FRE(UART_REF_CLK_FRE),
.UART_BAUNDS_RATE(UART_BAUNDS_RATE),
.FIFO_DATA_DEPTH (FIFO_DATA_DEPTH),
.FIFO_DATA_WIDTH (FIFO_DATA_WIDTH)
)uart_dtxrx_top_inist(
.uart_ref_clk(uart_ref_clk),//uart tx module reference clock
.uart_nrst(slv_reg0[0]),//uart tx module reset signal
.uart_baunds_div(slv_reg4),
.uart_tx_interrupt_en(slv_reg0[3]),
.uart_rx_interrupt_en(slv_reg0[4]),
.uart_tx_data(slv_reg1[7:0]),//uart tx 8bits data input
.uart_tx_data_qvld(slv_reg0[1]),//send data valid signal
.uart_rx_data_rd(slv_reg0[2]),//send data valid signal
//uart inner signals
.uart_tx_finish(uart_tx_finish),//uart send data finish flag
.uart_tx_busy(uart_tx_busy),//uart tx module busy flag
.uart_rx_data_qvld(uart_rx_data_qvld),//receive data valid signal
.uart_rx_finish(uart_rx_finish),//uart send data finish flag
.uart_rx_busy(uart_rx_busy),//uart rx module busy flag
.uart_rx_data(uart_rx_data),//uart rx 8bits data input
//FIFO status signals
.tx_fifo_full(tx_fifo_full),//tx data fifo full signal
.tx_fifo_empty(tx_fifo_empty),//tx data fifo empty signal
.rx_fifo_full(rx_fifo_full),//rx data fifo full signal
.rx_fifo_empty(rx_fifo_empty),//rx data fifo empty signal
//uart tx/rx port
.uart_tx_dout(uart_tx_dout),//serial data output
.uart_rx_din(uart_rx_din),//serial data output
//interrupt signals output
.uart_tx_interrupt(uart_tx_interrupt),//uart tx event finish interrupt
.uart_rx_interrupt(uart_rx_interrupt) //uart rx event finish interrupt
);
// User logic ends
endmodule
在完成了AXI封装后,我们只需通过C语言控制其寄存器完成相关逻辑即可。
由于篇幅原因,这里只给出头文件,想要C的可以私聊博主;
/*******************************************************************************
* IIC_Master_user.h
*
* Platform: Vivado.2018.3
* Created on: 2022年9月18日
* Author: Ge Wen Jie
* Copyright owner:Ge Wen Jie
//--------------------------------Describe-------------------------------------
* This file contains definitions of the offset address of AXI_Uart_Lite registers
* and the functional definitions of each bit of a register.
* If users want to add or alter the basic addresses of AXI_Uart_Lite devices, you
* can alter the AXI_UART_Lite_0 definition.
* *Other: to ensure a right Baunds rate can be set
*User must alter the AXI_UART_LITE_SOURCE_CLK definition in AXI_Uart_Lite_user.h as the real clock frequency of AXI_Uart_Lite device
*in SOC block design project.
//-----------------------------------------------------------------------------
*You can see http://hihii11.github.io/GWJ_BLOG.html for more information
*about IPs.
*******************************************************************************/
#ifndef USER_AXI_UART_LITE_USER_H_
#define USER_AXI_UART_LITE_USER_H_
#define AXI_UART_LITE_SOURCE_CLK 50000000 //
//AXI_Uart_Lite base addresses
#define AXI_UART_Lite_0 (unsigned int)(0x43C30000) //
//AXI_Uart_Lite registers offset addresses
#define AXI_Uart_CTL0 (unsigned int)(0x00000000) //
#define AXI_Uart_TXBUF (unsigned int)(0x00000004) //
#define AXI_Uart_RXBUF (unsigned int)(0x00000008) //
#define AXI_Uart_IFG0 (unsigned int)(0x0000000C) //
#define AXI_Uart_BAUNDS_DIV (unsigned int)(0x00000010) //
//AXI_Uart_Lite bit function definitions of CTL0 register
#define AXI_UART_CTL0_RST (unsigned int)(0x00000000) //
#define AXI_UART_CTL0_EN (unsigned int)(0x00000001) //
#define AXI_UART_CTL0_DVALID (unsigned int)(0x00000002) //
#define AXI_UART_CTL0_RD (unsigned int)(0x00000004) //
#define AXI_UART_CTL0_TXINTEN (unsigned int)(0x00000008) //
#define AXI_UART_CTL0_RXINTEN (unsigned int)(0x00000010) //
//AXI_Uart_Lite bit function definitions of IFG0 register
#define AXI_UART_IFG0_TXFIFO_FULL (unsigned int)(0x00000001) //
#define AXI_UART_IFG0_TXFIFO_EMPTY (unsigned int)(0x00000002) //
#define AXI_UART_IFG0_RXFIFO_FULL (unsigned int)(0x00000004) //
#define AXI_UART_IFG0_RXFIFO_EMPTY (unsigned int)(0x00000008) //
#define AXI_UART_IFG0_TX_FINISH (unsigned int)(0x00000010) //
#define AXI_UART_IFG0_TX_BUSY (unsigned int)(0x00000020) //
#define AXI_UART_IFG0_RX_FINISH (unsigned int)(0x00000040) //
#define AXI_UART_IFG0_RX_BUSY (unsigned int)(0x00000080) //
#define AXI_UART_IFG0_RX_DVALID (unsigned int)(0x00000100) //
#define Uart_Device_Delay usleep(100)
extern void AXI_Uart_Lite_reset(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_enable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_disable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_Set_Baunds_Rate_div(AXI_BaseAddress_Data_Type Base_addr,AXI_Register_Data_Type Baunds_Div);
extern void AXI_Uart_Lite_Set_Baunds(AXI_BaseAddress_Data_Type Base_addr,uint32 Baunds);
extern void AXI_Uart_Lite_Tx_interrupt_enable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_Tx_interrupt_disable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_Rx_interrupt_enable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_Rx_interrupt_disable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_send_byte(AXI_BaseAddress_Data_Type Base_addr, uint8 data);
extern uint8 AXI_Uart_Lite_read_byte(AXI_BaseAddress_Data_Type Base_addr, uint8 * data);
extern uint16 AXI_Uart_Lite_read_IFG(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_send_len(AXI_BaseAddress_Data_Type Base_addr,uint8 * buffer,uint16 len);
extern void AXI_Uart_Lite_send_str(AXI_BaseAddress_Data_Type Base_addr,uint8 * str);
#endif /* USER_AXI_UART_LITE_USER_H_ */