昨天主要梳理了一下AXI各个信号的功能,今天使用Vivado软件来实现一个自定义的AXI_Lite接口,并对源码进行解析.
`timescale 1 ns / 1 ps
这一行设置了 Verilog 的时间单位和精度。这里的时间单位是纳秒(ns),精度是皮秒(ps)。这意味着所有的仿真时间都将以皮秒为单位进行量化。
module breath_led_ip_v1_0 #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Parameters of Axi Slave Bus Interface S00_AXI
parameter integer C_S00_AXI_DATA_WIDTH = 32,
parameter integer C_S00_AXI_ADDR_WIDTH = 4
)
这是模块定义的开始部分。`breath_led_ip_v1_0` 是模块名,紧跟其后的 `#` 表示模块支持参数化。接下来的两个 `parameter` 定义了与 AXI4-Lite 接口相关的两个参数:
- `C_S00_AXI_DATA_WIDTH`: 数据总线宽度,这里是32位。
- `C_S00_AXI_ADDR_WIDTH`: 地址总线宽度,这里是4位,意味着最多能够寻址到 \(2^4 = 16\) 个地址。
(
// Users to add ports here
// User ports ends
// Do not modify the ports beyond this line
// Ports of Axi Slave Bus Interface S00_AXI
input wire s00_axi_aclk,
input wire s00_axi_aresetn,
input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
input wire [2 : 0] s00_axi_awprot,
input wire s00_axi_awvalid,
output wire s00_axi_awready,
input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
input wire s00_axi_wvalid,
output wire s00_axi_wready,
output wire [1 : 0] s00_axi_bresp,
output wire s00_axi_bvalid,
input wire s00_axi_bready,
input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
input wire [2 : 0] s00_axi_arprot,
input wire s00_axi_arvalid,
output wire s00_axi_arready,
output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
output wire [1 : 0] s00_axi_rresp,
output wire s00_axi_rvalid,
input wire s00_axi_rready
);
这部分列出了模块的所有端口,这些端口属于 AXI4-Lite Slave 接口 `S00_AXI`:
- `s00_axi_aclk`: AXI 时钟信号。
- `s00_axi_aresetn`: 异步复位信号,低电平有效。
- `s00_axi_awaddr`: 写地址通道地址。
- `s00_axi_awprot`: 写地址通道保护类型。
- `s00_axi_awvalid`: 写地址通道有效信号。
- `s00_axi_awready`: 写地址通道准备信号。
- `s00_axi_wdata`: 写数据通道数据。
- `s00_axi_wstrb`: 写数据通道字节使能。
- `s00_axi_wvalid`: 写数据通道有效信号。
- `s00_axi_wready`: 写数据通道准备信号。
- `s00_axi_bresp`: 写响应通道响应。
- `s00_axi_bvalid`: 写响应通道有效信号。
- `s00_axi_bready`: 写响应通道准备信号。
- `s00_axi_araddr`: 读地址通道地址。
- `s00_axi_arprot`: 读地址通道保护类型。
- `s00_axi_arvalid`: 读地址通道有效信号。
- `s00_axi_arready`: 读地址通道准备信号。
- `s00_axi_rdata`: 读数据通道数据。
- `s00_axi_rresp`: 读数据通道响应。
- `s00_axi_rvalid`: 读数据通道有效信号。
- `s00_axi_rready`: 读数据通道准备信号。
// Instantiation of Axi Bus Interface S00_AXI
breath_led_ip_v1_0_S00_AXI # (
.C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH),
.C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH)
) breath_led_ip_v1_0_S00_AXI_inst (
.S_AXI_ACLK(s00_axi_aclk),
.S_AXI_ARESETN(s00_axi_aresetn),
.S_AXI_AWADDR(s00_axi_awaddr),
.S_AXI_AWPROT(s00_axi_awprot),
.S_AXI_AWVALID(s00_axi_awvalid),
.S_AXI_AWREADY(s00_axi_awready),
.S_AXI_WDATA(s00_axi_wdata),
.S_AXI_WSTRB(s00_axi_wstrb),
.S_AXI_WVALID(s00_axi_wvalid),
.S_AXI_WREADY(s00_axi_wready),
.S_AXI_BRESP(s00_axi_bresp),
.S_AXI_BVALID(s00_axi_bvalid),
.S_AXI_BREADY(s00_axi_bready),
.S_AXI_ARADDR(s00_axi_araddr),
.S_AXI_ARPROT(s00_axi_arprot),
.S_AXI_ARVALID(s00_axi_arvalid),
.S_AXI_ARREADY(s00_axi_arready),
.S_AXI_RDATA(s00_axi_rdata),
.S_AXI_RRESP(s00_axi_rresp),
.S_AXI_RVALID(s00_axi_rvalid),
.S_AXI_RREADY(s00_axi_rready)
);
```
这行代码实例化了一个名为 `breath_led_ip_v1_0_S00_AXI` 的子模块,并且将前面定义的端口参数传递给这个子模块。`.S_AXI_ACLK(s00_axi_aclk)` 这样的形式将外部接口信号连接到子模块的相应端口上。
// Add user logic here
// User logic ends
这两行是用来提醒用户在此处添加自定义逻辑的地方。实际的模块功能应该在这里实现。
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;
声明了四个寄存器 slv_reg0
至 slv_reg3,这四个寄存器是在前面配置寄存器数量时确定的,我们主要就是通过AXI协议来读写这四个寄存器.具体读写实现此处不再赘述.
继续完成用户自定义逻辑之后,打开SDK,可以找到vivado自动生成的操作函数
之后操作上面定义的四个寄存器,在xparameters.h中找到定义的
-
XPAR_BREATH_LED_IP_0_DEVICE_ID
: 给出了第一个BREATH_LED_IP
实例的设备ID。这里的值为0,这通常是第一个实例的标准ID。 -
XPAR_BREATH_LED_IP_0_S00_AXI_BASEADDR
: 这个宏定义了BREATH_LED_IP_0
实例通过AXI4接口与外部总线交互的基地址。在这个例子中,基地址设置为0x43C00000
。这意味着任何想要访问这个IP核的外设都需要通过这个基地址来发送请求。 -
XPAR_BREATH_LED_IP_0_S00_AXI_HIGHADDR
: 定义了BREATH_LED_IP_0
可寻址空间的最高地址。在这个例子中,最高地址为0x43C0FFFF
,这意味着该IP核的地址空间大小为0x10000
字节(即64KB)。