在前一章中我们已经完成了从机接口模板代码的设计;在本篇中,我们将对设计的从机代码进行板级验证;
验证FPGA选用Xilinx的Zynq 7000,基于Vivado平台进行;
认证过程将采用软硬协同验证,其中内核为MicroBlaze;
测试IP核将基于模板代码修改:
修改后的逻辑文件:
//AXI Slave interface
module axi_lite_logic#(
//axi-lite parameter definition start here
//data width / address width
parameter integer C_AXI_SLV_REG_NUM = 8,
parameter integer C_AXI_DATA_WIDTH = 32,
parameter integer C_AXI_ADDR_WIDTH = $clog2(C_AXI_SLV_REG_NUM*4)+1
)
(
output wire [3:0] led,
input wire [3:0] key,
//system signals definition
input wire s_axi_aclk,
input wire s_axi_aresentn,
//write address signals definition
input wire [C_AXI_ADDR_WIDTH - 1:0] s_axi_awaddr,
input wire s_axi_awvalid,
output wire s_axi_awready,
//write data signals definition
input wire [C_AXI_DATA_WIDTH - 1:0] s_axi_wdata,
input wire s_axi_wvalid,
output wire s_axi_wready,
input wire [(C_AXI_DATA_WIDTH/8) - 1:0] s_axi_wstrb,
//write response signals definition
output wire [1:0] s_axi_bresp,
output wire s_axi_bvalid,
input wire s_axi_bready,
//read address signals definition
input wire [C_AXI_ADDR_WIDTH - 1:0] s_axi_araddr,
input wire s_axi_arvalid,
output wire s_axi_arready,
//read data signals definition
output wire [C_AXI_DATA_WIDTH - 1:0] s_axi_rdata,
output wire s_axi_rvalid,
input wire s_axi_rready,
//read response signals definition
output wire s_axi_rresp,
//protect signals definition
input wire s_axi_arprot
);
localparam integer ADDR_SHIFT = C_AXI_DATA_WIDTH/16;
reg [C_AXI_ADDR_WIDTH - 1:0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1:0] axi_bresp;
reg axi_bvalid;
reg aw_en;
reg [C_AXI_ADDR_WIDTH - 1:0] axi_araddr;
reg axi_arready;
reg axi_rvalid;
reg [C_AXI_DATA_WIDTH - 1:0] axi_rdata;
reg [C_AXI_DATA_WIDTH - 1:0] reg_data_out;
wire register_wr_en;
//register definition
reg [(C_AXI_DATA_WIDTH - 1) : 0] slv_reg[0:(C_AXI_SLV_REG_NUM-1)];
assign led = slv_reg[0][3:0];
//inner logic definition
assign register_wr_en = axi_wready & s_axi_wvalid & axi_awready & s_axi_awvalid;
//inner signal connect
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_rvalid = axi_rvalid;
assign s_axi_rresp = 2'b00;
//---------------------------write address input logic--------------------------------//
//transmit finish whe s_axi_awvalid=1 axi_awready = 1 s_axi_wvalid = 1
always @(posedge s_axi_aclk) begin : address_input_proc_
if(~s_axi_aresentn) begin
axi_awaddr <= 'b0;
axi_awready <= 1'b0;
aw_en <= 1'b1;
end
else begin
if(aw_en && s_axi_awvalid && (~axi_awready) && (s_axi_wvalid))
begin
aw_en <= 1'b0;
axi_awaddr <= s_axi_awaddr;
axi_awready <= 1'b1;
end
else if(axi_bvalid && s_axi_bready)
begin
aw_en <= 1'b1;
end
else
begin
axi_awready <= 1'b0;
end
end
end
//-------------------------write data logic------------------------------------//
always @(posedge s_axi_aclk) begin : write_data_signal_proc_
if(~s_axi_aresentn) begin
axi_wready <= 1'b0;
end
else begin
if(aw_en && s_axi_awvalid && s_axi_wvalid && ~axi_wready)
begin
axi_wready <= 1'b1;
end
else
begin
axi_wready <= 1'b0;
end
end
end
//------------------------write back response logic--------------------------//
always @(posedge s_axi_aclk) begin : wr_back_logic_proc_
if(~s_axi_aresentn) begin
axi_bresp <= 'b0;
axi_bvalid <= 1'b0;
end
else begin
if(s_axi_awvalid & axi_awready & s_axi_wvalid & axi_wready & (~axi_bvalid))
begin
axi_bresp <= 'b0;
axi_bvalid <= 1'b1;
end
else
begin
axi_bresp <= 'b0;
axi_bvalid <= 1'b0;
end
end
end
//-----------------------read address logic---------------------------------//
always @(posedge s_axi_aclk) begin : read_address_proc_
if(~s_axi_aresentn) begin
axi_araddr <= 'b0;
axi_arready <= 1'b0;
end else begin
if(~axi_arready & s_axi_arvalid)begin
axi_araddr <= s_axi_araddr;
axi_arready <= 1'b1;
end
else
begin
axi_arready <= 1'b0;
end
end
end
//------------------------read data logic--------------------------------//
always @(posedge s_axi_aclk) begin : read_data_proc_
if(~s_axi_aresentn) begin
axi_rdata <= 'b0;
axi_rvalid <= 1'b0;
end else begin
if(axi_arready & s_axi_arvalid & ~axi_rvalid)
begin
axi_rvalid <= 1'b1;
axi_rdata <= reg_data_out;
end
else
begin
axi_rvalid <= 1'b0;
end
end
end
//------------------------write register logic-------------------------------//
integer byte_index;
integer reg_index;
always @(posedge s_axi_aclk) begin : register_write_proc_
if(~s_axi_aresentn) begin
for(reg_index = 0;reg_index<C_AXI_SLV_REG_NUM;reg_index=reg_index+1)
begin
slv_reg[reg_index] <= 'b0;
end
end
else begin
if(register_wr_en)
begin
for(reg_index = 0;reg_index<C_AXI_SLV_REG_NUM;reg_index=reg_index+1)
begin
if(reg_index == (axi_awaddr >> ADDR_SHIFT))
begin
for(byte_index = 0;byte_index <= (C_AXI_DATA_WIDTH/8)-1;byte_index = byte_index + 1)
begin
if(s_axi_wstrb[byte_index] == 1'b1)
begin
slv_reg[reg_index][(byte_index*8)+:8] <= s_axi_wdata[(byte_index*8)+:8];
end
end
end
end
end
end
end
//------------------------read register logic-------------------------------//
always @(*) begin : register_read_proc_
if(~s_axi_aresentn) begin
reg_data_out <= 'b0;
end
else begin
case(axi_araddr >> ADDR_SHIFT)
'd0: reg_data_out <= slv_reg[0];
'd1: reg_data_out <= key;
'd2: reg_data_out <= slv_reg[2];
'd3: reg_data_out <= slv_reg[3];
'd4: reg_data_out <= slv_reg[4];
'd5: reg_data_out <= slv_reg[5];
'd6: reg_data_out <= slv_reg[6];
'd7: reg_data_out <= slv_reg[7];
endcase
end
end
endmodule
在逻辑文件中,我们增加了一组输入端口和一组输出端口,分别用来接收按键的电平和驱动LED亮灭;
output wire [3:0] led,
input wire [3:0] key,
并且我们将第0寄存器的低四位赋值给led端口;
assign led = slv_reg[0][3:0];
在读逻辑中,第二个寄存器读出改为key;
always @(*) begin : register_read_proc_
if(~s_axi_aresentn) begin
reg_data_out <= 'b0;
end
else begin
case(axi_araddr >> ADDR_SHIFT)
'd0: reg_data_out <= slv_reg[0];
'd1: reg_data_out <= key;
'd2: reg_data_out <= slv_reg[2];
'd3: reg_data_out <= slv_reg[3];
'd4: reg_data_out <= slv_reg[4];
'd5: reg_data_out <= slv_reg[5];
'd6: reg_data_out <= slv_reg[6];
'd7: reg_data_out <= slv_reg[7];
endcase
end
end
顶层文件:
module axi_lite_logic_top#(
parameter integer C_AXI_SLV_REG_NUM = 8,
parameter integer C_AXI_DATA_WIDTH = 32,
parameter integer C_AXI_ADDR_WIDTH = $clog2(C_AXI_SLV_REG_NUM*4)+1
) (
output wire [3:0] led,
input wire [3:0] key,
//system signals definition
input wire S_AXI_ACLK,
input wire S_AXI_ARESENTN,
//write address signals definition
input wire [C_AXI_ADDR_WIDTH - 1:0] S_AXI_AWADDR,
input wire S_AXI_AWVALID,
output wire S_AXI_AWREADY,
//write data signals definition
input wire [C_AXI_DATA_WIDTH - 1:0] S_AXI_WDATA,
input wire S_AXI_WVALID,
output wire S_AXI_WREADY,
input wire [(C_AXI_DATA_WIDTH/8) - 1:0] S_AXI_WSTRB,
//write response signals definition
output wire [1:0] S_AXI_BRESP,
output wire S_AXI_BVALID,
input wire S_AXI_BREADY,
//read address signals definition
input wire [C_AXI_ADDR_WIDTH - 1:0] S_AXI_ARADDR,
input wire S_AXI_ARVALID,
output wire S_AXI_ARREADY,
//read data signals definition
output wire [C_AXI_DATA_WIDTH - 1:0] S_AXI_RDATA,
output wire S_AXI_RVALID,
input wire S_AXI_RREADY,
//read response signals definition
output wire S_AXI_RRESP,
//protect signals definition
input wire S_AXI_ARPROT
);
axi_lite_logic#(
//axi-lite parameter definition start here
//data width / address width
.C_AXI_SLV_REG_NUM (C_AXI_SLV_REG_NUM),
.C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH)
)axi_lite_logic_inist0
(
.led(led),
.key(key),
//system signals definition
.s_axi_aclk(S_AXI_ACLK),
.s_axi_aresentn(S_AXI_ARESENTN),
//write address signals definition
.s_axi_awaddr(S_AXI_AWADDR),
.s_axi_awvalid(S_AXI_AWVALID),
.s_axi_awready(S_AXI_AWREADY),
//write data signals definition
.s_axi_wdata(S_AXI_WDATA),
.s_axi_wvalid(S_AXI_WVALID),
.s_axi_wready(S_AXI_WREADY),
.s_axi_wstrb(S_AXI_WSTRB),
//write response signals definition
.s_axi_bresp(S_AXI_BRESP),
.s_axi_bvalid(S_AXI_BVALID),
.s_axi_bready(S_AXI_BREADY),
//read address signals definition
.s_axi_araddr(S_AXI_ARADDR),
.s_axi_arvalid(S_AXI_ARVALID),
.s_axi_arready(S_AXI_ARREADY),
//read data signals definition
.s_axi_rdata(S_AXI_RDATA),
.s_axi_rvalid(S_AXI_RVALID),
.s_axi_rready(S_AXI_RREADY),
//read response signals definition
.s_axi_rresp(S_AXI_RRESP),
//protect signals definition
.s_axi_arprot(S_AXI_ARPROT)
);
endmodule
1.我们将修改后的文件添加到工程中来,然后点击Tools->creat and package new IP2.绑定文件
3.最后完成封装
1.新建一个Block Design;点击Setting->IP->Repository->+
我们找到刚刚封装的IP路径,把它添加到工程里面来;
2.添加测试IP,点击Block Design中的+号,找到测试IP,并添加进来
3.添加AXI_Uart_Lite IP核;
4.添加microblaze软核,并点击Run Block Automation
选择Memory大小为128KB
5.再点击Run connection Automation,Vivado会自动完成连线
6.这里需要注意reset复位信号和sys_clock系统时钟信号;复位信号选择的按键若常态低电平,则需要连一个反相器,sys_clock必须是FPGA的时钟引脚(这里因为我有板载文件,所以直接配置好了)
7.整体SOC图8.选中BD文件右击,选择Create HDL Wrapper,并将生成后的文件设置为顶层
#led0
set_property PACKAGE_PIN M14 [get_ports led[0]]
set_property IOSTANDARD LVCMOS33 [get_ports led[0]]
#led1
set_property PACKAGE_PIN M15 [get_ports led[1]]
set_property IOSTANDARD LVCMOS33 [get_ports led[1]]
#led2
set_property PACKAGE_PIN G14 [get_ports led[2]]
set_property IOSTANDARD LVCMOS33 [get_ports led[2]]
#led3
set_property PACKAGE_PIN D18 [get_ports led[3]]
set_property IOSTANDARD LVCMOS33 [get_ports led[3]]
#key0
set_property PACKAGE_PIN G15 [get_ports key[0]]
set_property IOSTANDARD LVCMOS33 [get_ports key[0]]
#key1
set_property PACKAGE_PIN P15 [get_ports key[1]]
set_property IOSTANDARD LVCMOS33 [get_ports key[1]]
#key2
set_property PACKAGE_PIN W13 [get_ports key[2]]
set_property IOSTANDARD LVCMOS33 [get_ports key[2]]
#key3
set_property PACKAGE_PIN T16 [get_ports key[3]]
set_property IOSTANDARD LVCMOS33 [get_ports key[3]]
#reset_rtl BTN3
set_property PACKAGE_PIN Y16 [get_ports reset_rtl]
set_property IOSTANDARD LVCMOS33 [get_ports reset_rtl]
#The definition of uart_rx_din
#JD2_N
set_property PACKAGE_PIN R14 [get_ports uart_rx_din]
set_property IOSTANDARD LVCMOS33 [get_ports uart_rx_din]
#The definition of uart_tx_dout
#JD2_P
set_property PACKAGE_PIN P14 [get_ports uart_tx_dout]
set_property IOSTANDARD LVCMOS33 [get_ports uart_tx_dout]
点击File->Export->Export Hardware,导出硬件文件
然后点击File-> Launch SDK,新建一个空工程,在新工程中创建main.c文件;
我们还需要将user文件夹手动拷贝到目录下然后编写main.c文件内容如下:
/*
* main.c
*
* Created on: 2023年3月19日
* Author: 93793
*/
#include "../user/headfile.h"
#define BASE_ADDR 0x44A00000
int main()
{
AXI_Uart_Lite_reset(AXI_UART_Lite_0);
AXI_Uart_Lite_Set_Baunds(AXI_UART_Lite_0,9600);
AXI_Uart_Lite_enable(AXI_UART_Lite_0);
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"--------AXI TEST START!----------\n");
Xil_Out32(BASE_ADDR|0x00,0x01);
usleep(500000);
Xil_Out32(BASE_ADDR|0x00,0x03);
usleep(500000);
Xil_Out32(BASE_ADDR|0x00,0x07);
usleep(500000);
Xil_Out32(BASE_ADDR|0x00,0x0f);
Xil_Out32(BASE_ADDR|0x08,0xCC);
if(Xil_In32(BASE_ADDR|0x08) == 0xCC)
{
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"PASS!\n");
}
else
{
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"FAIL!\n");
while(1);
}
while(1)
{
unsigned char key = Xil_In32(BASE_ADDR|0x04);
if(key&0x01)
{
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"B0、");
}
if(key&0x02)
{
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"B1、");
}
if(key&0x04)
{
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"B2、");
}
if(key&0x08)
{
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"B3、");
}
if(key&0xff)
{
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"Down\n");
}
if(key == 0x0f)
{
Xil_Out32(BASE_ADDR|0x00,0x00);
break;
}
sleep(1);
}
AXI_Uart_Lite_send_str(AXI_UART_Lite_0,"--------AXI TEST FINISH!----------\n");
return 0;
}
板级验证过程:
测试物理连接如下将USB转TTL连接电脑,配置SDK Terminal
先下载硬件Xilinx -> Program FPGA
右击工程,选择run as -> Launch on hardware下载程序
下载程序等待亮灯
此时依次波动开关
最终在终端打印的数据为
本篇基于AXI_Lite从机接口的模板文件,设计一验证SOC,最终完成了整个AXI_Lite从机接口的测试;
工程源码:https://gitee.com/gewenjie_host/axi_lite_test