Hello大家好,我是小黄。
最近工作上师傅要我自己写一个uart转axi_master的模块。一开始想xilinx应该有现成的ip核可以调用,没想到查了一下只有axi_uart做从机的ip。无奈之下,只能自己写一个。现在该模块已经写好了,接下来我将给大家展示一下我的工程。在这篇博客最后面我会将本工程的源码分享给大家。
首先给大家看下我对这个模块的划分。
模块的端口有串口输入输出,还有一个axi_master接口。相关rtl代码如下:
/***************************************************
* Module Name : uart2axi_master
* Engineer : Huangruigui
* Target Device :
* Tool versions :
* Create Date :
* Revision : v1.0
* Description :
**************************************************/
module uart2axi_master #(
parameter M_AXI_ADDR_WIDTH = 8'd32,
parameter M_AXI_DATA_WIDTH = 8'd32,
parameter BAUD_SET = 16'd10416, // 波特率设置,默认9600
parameter RX_TOTAL_BYTE = 8'd9, // 串口接收到9byte数据开始工作
parameter TX_TOTAL_BYTE = 8'd4, // 串口发送4byte数据给上位机
parameter WRITE_BIT = 5'd1, // 设置最高位1为写操作
parameter READ_BIT = 5'd2 // 设置最高位2为读操作
)(
// Global Clock Signal
input wire m_axi_aclk, // 模块基准时钟,100MHz
input wire m_axi_aresetn, // 模块复位信号,低电平有效
// Master Write Addr Interface
output wire m_axi_awvalid,
input wire m_axi_awready,
output wire [M_AXI_ADDR_WIDTH-1:0] m_axi_awaddr,
// Master Write Date Interface
output wire m_axi_wvalid,
input wire m_axi_wready,
output wire [M_AXI_DATA_WIDTH-1:0] m_axi_wdata,
output wire [M_AXI_DATA_WIDTH/8-1:0] m_axi_wstrb,
// Master Read Addr Interface
output wire m_axi_arvalid,
input wire m_axi_arready,
output wire [M_AXI_ADDR_WIDTH-1:0] m_axi_araddr,
// Master Read Date Interface
output wire m_axi_rready,
input wire m_axi_rvalid,
input wire [M_AXI_DATA_WIDTH-1:0] m_axi_rdata,
input wire [1:0] m_axi_rresp,
// Master Response Interface
output wire m_axi_bready,
input wire m_axi_bvalid,
input wire [1:0] m_axi_bresp,
// UART Interface
input wire UART_RX,
output wire UART_TX
);
// wire define
wire uart_write;
wire [M_AXI_ADDR_WIDTH-1:0] uart_write_addr;
wire [M_AXI_DATA_WIDTH-1:0] uart_write_data;
wire uart_read;
wire [M_AXI_ADDR_WIDTH-1:0] uart_read_addr;
wire uart_tx_en;
wire [M_AXI_DATA_WIDTH-1:0] uart_tx_data;
uart2axi_master_intf #(
.M_AXI_ADDR_WIDTH ( M_AXI_ADDR_WIDTH),
.M_AXI_DATA_WIDTH ( M_AXI_DATA_WIDTH),
.RX_TOTAL_BYTE ( RX_TOTAL_BYTE ),
.TX_TOTAL_BYTE ( TX_TOTAL_BYTE )
)uart2axi_master_intf(
.m_axi_aclk ( m_axi_aclk ),
.m_axi_aresetn ( m_axi_aresetn ),
.m_axi_awvalid ( m_axi_awvalid ),
.m_axi_awready ( m_axi_awready ),
.m_axi_awaddr ( m_axi_awaddr ),
.m_axi_wvalid ( m_axi_wvalid ),
.m_axi_wready ( m_axi_wready ),
.m_axi_wdata ( m_axi_wdata ),
.m_axi_wstrb ( m_axi_wstrb ),
.m_axi_arvalid ( m_axi_arvalid ),
.m_axi_arready ( m_axi_arready ),
.m_axi_araddr ( m_axi_araddr ),
.m_axi_rready ( m_axi_rready ),
.m_axi_rvalid ( m_axi_rvalid ),
.m_axi_rdata ( m_axi_rdata ),
.m_axi_rresp ( m_axi_rresp ),
.m_axi_bready ( m_axi_bready ),
.m_axi_bvalid ( m_axi_bvalid ),
.m_axi_bresp ( m_axi_bresp ),
.uart_write ( uart_write ),
.uart_write_addr ( uart_write_addr ),
.uart_write_data ( uart_write_data ),
.uart_read ( uart_read ),
.uart_read_addr ( uart_read_addr ),
.uart_tx_en ( uart_tx_en ),
.uart_tx_data ( uart_tx_data )
);
uart2axi_master_logic #(
.M_AXI_ADDR_WIDTH ( M_AXI_ADDR_WIDTH),
.M_AXI_DATA_WIDTH ( M_AXI_DATA_WIDTH),
.BAUD_SET ( BAUD_SET ),
.RX_TOTAL_BYTE ( RX_TOTAL_BYTE ),
.TX_TOTAL_BYTE ( TX_TOTAL_BYTE ),
.WRITE_BIT ( WRITE_BIT ),
.READ_BIT ( READ_BIT )
)uart2axi_master_logic(
.sys_clk ( m_axi_aclk ),
.sys_rst_n ( m_axi_aresetn ),
.uart_write ( uart_write ),
.uart_write_addr ( uart_write_addr ),
.uart_write_data ( uart_write_data ),
.uart_read ( uart_read ),
.uart_read_addr ( uart_read_addr ),
.uart_tx_en ( uart_tx_en ),
.uart_tx_data ( uart_tx_data ),
.uart_rx ( UART_RX ),
.uart_tx ( UART_TX )
);
endmodule
//*****************文件结束***************************
接下来首先设计的是串口发送接收模块,这里我用1位起始位,8位数据位,1位停止位,波特率9600。模块基准时钟100MHz,用户使用时,可以根据模块的BAUD_SET参数修改波特率,计算时用100_000_000/波特率,将得到的数值取整赋值给BAUD_SET就可以了
module uart_rx_logic #(
parameter BAUD_SET = 16'd10416
)(
input wire sys_clk,
input wire sys_rst_n,
output reg [7:0] rx_data,
output reg rx_done,
output reg uart_rx
);
这个是串口接收模块的端口,用来接收上位机发送的数据。为了提高该模块的抗干扰能力,首先将uart_rx信号进行消除亚稳态,在进行寄存得到下降沿,以此作为模块开始工作标志信号。接着将每一位数据分成16次采样,采中间6位做平均计算,若第二位数据是否为1,即累加的数大于等于4, 就认为接收到的是1,否则是0。
case(bps_clk_cnt)
0:begin
START_BIT <= 3'd0;
r_rx_data[0] <= 3'd0;
r_rx_data[1] <= 3'd0;
r_rx_data[2] <= 3'd0;
r_rx_data[3] <= 3'd0;
r_rx_data[4] <= 3'd0;
r_rx_data[5] <= 3'd0;
r_rx_data[6] <= 3'd0;
r_rx_data[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
6,7,8,9,10,11: START_BIT <= START_BIT + uart_rx_r[1];
22,23,24,25,26,27: r_rx_data[0] <= r_rx_data[0] + uart_rx_r[1];
38,39,40,41,42,43: r_rx_data[1] <= r_rx_data[1] + uart_rx_r[1];
54,55,56,57,58,59: r_rx_data[2] <= r_rx_data[2] + uart_rx_r[1];
70,71,72,73,74,75: r_rx_data[3] <= r_rx_data[3] + uart_rx_r[1];
86,87,88,89,90,91: r_rx_data[4] <= r_rx_data[4] + uart_rx_r[1];
102,103,104,105,106,107:r_rx_data[5] <= r_rx_data[5] + uart_rx_r[1];
118,119,120,121,122,123:r_rx_data[6] <= r_rx_data[6] + uart_rx_r[1];
134,135,136,137,138,139:r_rx_data[7] <= r_rx_data[7] + uart_rx_r[1];
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + uart_rx_r[1];
default:
begin
START_BIT <= START_BIT;
r_rx_data[0] <= r_rx_data[0];
r_rx_data[1] <= r_rx_data[1];
r_rx_data[2] <= r_rx_data[2];
r_rx_data[3] <= r_rx_data[3];
r_rx_data[4] <= r_rx_data[4];
r_rx_data[5] <= r_rx_data[5];
r_rx_data[6] <= r_rx_data[6];
r_rx_data[7] <= r_rx_data[7];
STOP_BIT <= STOP_BIT;
end
endcase
//串口接收程序结束
接着是串口发送程序。
串口发送相对串口接收简单,这里我直接贴出相关代码。
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
uart_tx <= 1;
else
begin
case(bps_clk_cnt)
0: uart_tx <= 1'b1;
1: uart_tx <= START_BIT;
2: uart_tx <= tx_data_t[0];
3: uart_tx <= tx_data_t[1];
4: uart_tx <= tx_data_t[2];
5: uart_tx <= tx_data_t[3];
6: uart_tx <= tx_data_t[4];
7: uart_tx <= tx_data_t[5];
8: uart_tx <= tx_data_t[6];
9: uart_tx <= tx_data_t[7];
10:uart_tx <= STOP_BIT;
default:uart_tx <= 1'b1;
endcase
end
end
//未完待续
串口发送接收程序源码
完成了串口发送接收程序的测试,接下来对接收到的数据处理模块的编写。
因为是串口转axi_master,所以串口需要发送读写位,地址位,数据位,其中地址和数据是32位的,因此串口总共需要发送9byte数据才可以解析。这里为了方便,我将写入的数据返回给上位机作为应答,没有用CRC校验(因为我不会。。。)
因为这个模块比较复杂一点,我用另一篇博客写好了代码给大家使用
串口发送接收模块顶层
到这里就很快啦,明天把axi_master接口搞定。
好了,忙前忙后,把整个模块搞定了。
先给大家看下波形图。
从图中可以看出,我首先是发起总线写命令,当传输完一帧数据后,地址、数据总线出现了目标数据,符合预期。接着发起读命令,发送读命令和读地址,发送完成后,可以看出读地址总线出现了预期的数据,接着我模拟从机,产生一个读有效脉冲和读数据,主机接收到信号后,产生串口发送信号,串口发送四次后,仿真停止。这一切看起来都很正常,实际……应该也是正常的。
老规矩,我在另一篇博客将源码分享给大家,希望大家使用后有什么不懂的地方或者模块有bug的地方能够告知一下我哈。
串口转axi主机总线接口程序源码
整个工程到这里就差不多结束啦。