赞
踩
本文涉及的所有代码仅用于学习交流,不得用于其他用途
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<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 //<uart source clock frequency> //AXI_Uart_Lite base addresses #define AXI_UART_Lite_0 (unsigned int)(0x43C30000) //<the base address of IIC Master0> //AXI_Uart_Lite registers offset addresses #define AXI_Uart_CTL0 (unsigned int)(0x00000000) //<the offset of Uart control Register> #define AXI_Uart_TXBUF (unsigned int)(0x00000004) //<the offset of Uart Tx Buffer Register> #define AXI_Uart_RXBUF (unsigned int)(0x00000008) //<the offset of Uart Rx Buffer Register> #define AXI_Uart_IFG0 (unsigned int)(0x0000000C) //<the offset of Uart Status Buffer Register> #define AXI_Uart_BAUNDS_DIV (unsigned int)(0x00000010) //<the offset of Uart baunds rate division Register> //AXI_Uart_Lite bit function definitions of CTL0 register #define AXI_UART_CTL0_RST (unsigned int)(0x00000000) //<When this bit set as low, device reset> #define AXI_UART_CTL0_EN (unsigned int)(0x00000001) //<When this bit set as high, device enable> #define AXI_UART_CTL0_DVALID (unsigned int)(0x00000002) //<When this bit set as high, a tx data of tx buffer will be written to tx fifo> #define AXI_UART_CTL0_RD (unsigned int)(0x00000004) //<When this bit set as high, a rx data will be read from rx fifo into rx buffer> #define AXI_UART_CTL0_TXINTEN (unsigned int)(0x00000008) //<When this bit set as high, a rx data will be read from rx fifo into rx buffer> #define AXI_UART_CTL0_RXINTEN (unsigned int)(0x00000010) //<When this bit set as high, a rx data will be read from rx fifo into rx buffer> //AXI_Uart_Lite bit function definitions of IFG0 register #define AXI_UART_IFG0_TXFIFO_FULL (unsigned int)(0x00000001) //<When this bit is high, tx fifo is full> #define AXI_UART_IFG0_TXFIFO_EMPTY (unsigned int)(0x00000002) //<When this bit is high, tx fifo is empty> #define AXI_UART_IFG0_RXFIFO_FULL (unsigned int)(0x00000004) //<When this bit is high, rx fifo is full> #define AXI_UART_IFG0_RXFIFO_EMPTY (unsigned int)(0x00000008) //<When this bit is high, rx fifo is empty> #define AXI_UART_IFG0_TX_FINISH (unsigned int)(0x00000010) //<When this bit is high, Uart has finished transmit a data> #define AXI_UART_IFG0_TX_BUSY (unsigned int)(0x00000020) //<When this bit is high, Uart is transmitting a data> #define AXI_UART_IFG0_RX_FINISH (unsigned int)(0x00000040) //<When this bit is high, Uart has received a data> #define AXI_UART_IFG0_RX_BUSY (unsigned int)(0x00000080) //<When this bit is high, Uart is receiving a data> #define AXI_UART_IFG0_RX_DVALID (unsigned int)(0x00000100) //<When this bit is high, the received data is valid> #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_ */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。