1、IP打包
由于所需打包系统中使用了AXI接口进行DDR访存,所以需要将IP接口打包为Vivado可以识别的AXI接口,以保证后续系统集成的顺利进行,因此需要使用下图所示的Vivado IP打包工具。
选择下图所示的生成AXI4接口例程,将我们的RTL设计放入其中,就可以将该RTL设计包裹在这一例程中,并且实现对外的AXI接口。
如下图所示为继续配置该AXI接口例程,可以设置该AXI接口的类型和模式,但是位宽在这里只能设置为32bits,与我所需的256bits位宽不一致,不过这里暂时不用担心,直接设置为32bits然后完成IP。
IP完成设置后会在定义的文件夹中生成相应的代码和配置文件,进入到对应文件夹找到该IP RTL代码文件,如下图所示,名字中前半部分都是自己定义的。其中第一个代码为AXI例程的wrapper,不用管。第二份代码为具体的AXI接口逻辑实现,例程中还包含了简单的访存测试以及对应的接口,具体如何改动和测试参考【DNN Weaver FPGA实现】Vivado SDK实现BRAM、DDR访存。
这里由于是借用这个IP的外壳打包我们的RTL代码设计,所以直接把第二份代码中的逻辑删干净,仅保留接口定义和必要的信号连接,然后例化我们的模块,将AXI接口线直接接出即可。当然如果还有别的IO信号也可以在接口上添加。具体代码例子放在最后。搞好这部分以后回到Vivado中,创建Block Design,直接ADD IP中查找这个AXI IP的名字然后例化,如下图所示。
例化完成IP后,右键点击IP进入Edit IP in Packager。
在这里可以直接对IP的RTL代码进行改动,像刚刚没有改好的RTL代码,可以在Packager中进行后续修改。例如上面提到的接口位宽需要256bits,而生成的代码中位宽只有32bit,就需要我们将wrapper中定义接口位宽的C_M_AXI_DATA_WIDTH参数修改为256,修改之后发现package IP后接口位宽并没有发生变化,说明直接修改有问题,因此找到下图所示的参数配置界面,打开C_M_AXI_DATA_WIDTH参数配置,发现默认参数值只有32bits,点击加号按钮添加默认值256,就可以将下面的Default Value值设定为256了,这样将IP打包得到的接口大小即为256bits。
如果想要在IP上加入新的端口,就直接在RTL代码中写好保存,再进入下图所示Package IP界面中的Ports and Interfaces选项中点击上面的红框,即可自动完成新接口的打包。
然后重新打包IP,回到Block Design,更新IP状态,即可看到新IP接口。
至此,IP打包的内容基本完成,后续对IP的任何修改都需要按照上述方法进入IP Packager来实现。
2、ZYNQ系统集成
进入Block Design,添加Zynq Processing System,并双击Block配置系统资源,配置一个AXI HP Slave接口用于我们IP的DDR访存。这里注意到HP接口位宽只能设置为64bits或者32bits,而我们的IP需要256bits的数据位宽,这一问题先不管。
配置好Zynq系统资源后,点击Run Connection Automation,即可将上述打包好的IP与Zynq系统自动连接完整,如下图所示。注意到左边的红框为我们打包的IP,右边的红框就是Zynq PS端系统。可以看到Vivado自动帮我们例化了两个模块,一个是生成总线reset信号的模块,而另一个axi_smc则是AXI Interconnect模块,一般来说是用于多组AXI主从设备之间互联的。本系统只有一组主从设备,之所以需要这一模块,就是因为刚才所说的两者AXI数据接口位宽不匹配,需要进行不同位宽AXI之间进行数据的串并转换,而axi_smc模块中就包含了这一功能,或者说包含了AXI Dwidth Converter模块。所以说Vivado的功能非常强大,帮助用户减轻了系统集成负担。
点击工具栏中的Validate Design,发现Block Design正确,点开axi_smc的主从接口,确认两端AXI接口位宽都是正确的。
最后,右键Source->design_1(Block Design名称),点击Generate Output Hardware,完成后再次右键点击Create HDL Wrapper,即可完成Block Design全部设计流程。
除此之外,需要提及的一点是,如果需要在用户RTL代码中例化使用Vivado中的IP模块而不是在Block Design中添加IP,需要在IP Catelog中自定义好所需的IP配置,然后直接在RTL代码中使用该IP配置的名称例化。但是需要注意的是,如果这些Vivado IP用在打包好的用户IP中,需要在打包IP的IP Packager中的IP Catelog进行自定义配置,而不能在总工程中配置,这样会导致用户IP找不到配置的Vivado IP。(这段说的有点乱……)
3、IP打包代码示例
// IP打包代码示例
`timescale 1 ns / 1 ps
module AXI_IP_demo_v1_0_M00_AXI #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Base address of targeted slave
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000,
// Burst Length. Supports 1, 2, 4, 8, 16, 32, 64, 128, 256 burst lengths
parameter integer C_M_AXI_BURST_LEN = 16,
// Thread ID Width
parameter integer C_M_AXI_ID_WIDTH = 1,
// Width of Address Bus
parameter integer C_M_AXI_ADDR_WIDTH = 32,
// Width of Data Bus
parameter integer C_M_AXI_DATA_WIDTH = 32,
// Width of User Write Address Bus
parameter integer C_M_AXI_AWUSER_WIDTH = 0,
// Width of User Read Address Bus
parameter integer C_M_AXI_ARUSER_WIDTH = 0,
// Width of User Write Data Bus
parameter integer C_M_AXI_WUSER_WIDTH = 0,
// Width of User Read Data Bus
parameter integer C_M_AXI_RUSER_WIDTH = 0,
// Width of User Response Bus
parameter integer C_M_AXI_BUSER_WIDTH = 0
)
(
// Users to add ports here
output wire dnnweaver2_done,
input wire decoder_start,
output wire [4-1:0]ld_state_q,
// User ports ends
// Do not modify the ports beyond this line
// Initiate AXI transactions
// Global Clock Signal.
input wire M_AXI_ACLK,
// Global Reset Singal. This Signal is Active Low
input wire M_AXI_ARESETN,
// Master Interface Write Address ID
output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID,//**
// Master Interface Write Address
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR,
// Burst length. The burst length gives the exact number of transfers in a burst
output wire [7 : 0] M_AXI_AWLEN,
// Burst size. This signal indicates the size of each transfer in the burst
output wire [2 : 0] M_AXI_AWSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst is calculated.
output wire [1 : 0] M_AXI_AWBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
output wire M_AXI_AWLOCK,//**
// Memory type. This signal indicates how transactions
// are required to progress through a system.
output wire [3 : 0] M_AXI_AWCACHE,//**
// 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.
output wire [2 : 0] M_AXI_AWPROT,//**
// Quality of Service, QoS identifier sent for each write transaction.
output wire [3 : 0] M_AXI_AWQOS,//**
// Optional User-defined signal in the write address channel.
output wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER,//**
// Write address valid. This signal indicates that
// the channel is signaling valid write address and control information.
output wire M_AXI_AWVALID,
// Write address ready. This signal indicates that
// the slave is ready to accept an address and associated control signals
input wire M_AXI_AWREADY,
// Master Interface Write Data.
output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_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.
output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB,
// Write last. This signal indicates the last transfer in a write burst.
output wire M_AXI_WLAST,
// Optional User-defined signal in the write data channel.
output wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER,//**
// Write valid. This signal indicates that valid write
// data and strobes are available
output wire M_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
input wire M_AXI_WREADY,
// Master Interface Write Response.
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID,//**
// Write response. This signal indicates the status of the write transaction.
input wire [1 : 0] M_AXI_BRESP,
// Optional User-defined signal in the write response channel
input wire [C_M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER,//**
// Write response valid. This signal indicates that the
// channel is signaling a valid write response.
input wire M_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
output wire M_AXI_BREADY,
// Master Interface Read Address.
output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID,
// Read address. This signal indicates the initial
// address of a read burst transaction.
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR,
// Burst length. The burst length gives the exact number of transfers in a burst
output wire [7 : 0] M_AXI_ARLEN,
// Burst size. This signal indicates the size of each transfer in the burst
output wire [2 : 0] M_AXI_ARSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst is calculated.
output wire [1 : 0] M_AXI_ARBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
output wire M_AXI_ARLOCK,//**
// Memory type. This signal indicates how transactions
// are required to progress through a system.
output wire [3 : 0] M_AXI_ARCACHE,//**
// 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.
output wire [2 : 0] M_AXI_ARPROT,//**
// Quality of Service, QoS identifier sent for each read transaction
output wire [3 : 0] M_AXI_ARQOS,//**
// Optional User-defined signal in the read address channel.
output wire [C_M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER,//**
// Write address valid. This signal indicates that
// the channel is signaling valid read address and control information
output wire M_AXI_ARVALID,
// Read address ready. This signal indicates that
// the slave is ready to accept an address and associated control signals
input wire M_AXI_ARREADY,
// Read ID tag. This signal is the identification tag
// for the read data group of signals generated by the slave.
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID,
// Master Read Data
input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA,
// Read response. This signal indicates the status of the read transfer
input wire [1 : 0] M_AXI_RRESP,
// Read last. This signal indicates the last transfer in a read burst
input wire M_AXI_RLAST,
// Optional User-defined signal in the read address channel.
input wire [C_M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER,//**
// Read valid. This signal indicates that the channel
// is signaling the required read data.
input wire M_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
output wire M_AXI_RREADY
);
// function called clogb2 that returns an integer which has the
//value of the ceiling of the log base 2
// function called clogb2 that returns an integer which has the
// value of the ceiling of the log base 2.
function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth >> 1;
end
endfunction
// C_TRANSACTIONS_NUM is the width of the index counter for
// number of write or read transaction.
localparam integer C_TRANSACTIONS_NUM = clogb2(C_M_AXI_BURST_LEN-1);
// Burst length for transactions, in C_M_AXI_DATA_WIDTHs.
// Non-2^n lengths will eventually cause bursts across 4K address boundaries.
localparam integer C_MASTER_LENGTH = 12;
// total number of burst transfers is master length divided by burst length and burst size
localparam integer C_NO_BURSTS_REQ = C_MASTER_LENGTH-clogb2((C_M_AXI_BURST_LEN*C_M_AXI_DATA_WIDTH/8)-1);
// Example State machine to initialize counter, initialize write transactions,
// initialize read transactions and comparison of read data with the
// written data words.
parameter [1:0] IDLE = 2'b00, // This state initiates AXI4Lite transaction
// after the state machine changes state to INIT_WRITE
// when there is 0 to 1 transition on INIT_AXI_TXN
INIT_WRITE = 2'b01, // This state initializes write transaction,
// once writes are done, the state machine
// changes state to INIT_READ
INIT_READ = 2'b10, // This state initializes read transaction
// once reads are done, the state machine
// changes state to INIT_COMPARE
INIT_COMPARE = 2'b11; // This state issues the status of comparison
// of the written data with the read data
assign M_AXI_AWID = 'b0;
assign M_AXI_AWLOCK = 1'b0;
assign M_AXI_AWCACHE = 4'b0010;
assign M_AXI_AWPROT = 3'h0;
assign M_AXI_AWQOS = 4'h0;
assign M_AXI_AWUSER = 'b1;
assign M_AXI_WUSER = 'b0;
assign M_AXI_ARLOCK = 1'b0;
assign M_AXI_ARCACHE = 4'b0010;
assign M_AXI_ARPROT = 3'h0;
assign M_AXI_ARQOS = 4'h0;
assign M_AXI_ARUSER = 'b1;
// My RTL Block
wrapper wrapper_axi_u(
.clk(M_AXI_ACLK),
.reset(M_AXI_ARESETN),
.dnnweaver2_done(dnnweaver2_done),
.decoder_start(decoder_start),
// CL_wrapper -> DDR0 AXI4 interface
.s00_axi_awaddr_0(M_AXI_AWADDR),
.s00_axi_awlen_0(M_AXI_AWLEN),
.s00_axi_awsize_0(M_AXI_AWSIZE),
.s00_axi_awburst_0(M_AXI_AWBURST),
.s00_axi_awvalid_0(M_AXI_AWVALID),
.s00_axi_awready(M_AXI_AWREADY),
.s00_axi_wdata_0(M_AXI_WDATA),
.s00_axi_wstrb_0(M_AXI_WSTRB),
.s00_axi_wlast_0(M_AXI_WLAST),
.s00_axi_wvalid_0(M_AXI_WVALID),
.s00_axi_wready(M_AXI_WREADY),
.s00_axi_bresp(M_AXI_BRESP),
.s00_axi_bvalid(M_AXI_BVALID),
.s00_axi_bready_0(M_AXI_BREADY),
.s00_axi_arid_0(M_AXI_ARID),
.s00_axi_araddr_0(M_AXI_ARADDR),
.s00_axi_arlen_0(M_AXI_ARLEN),
.s00_axi_arsize_0(M_AXI_ARSIZE),
.s00_axi_arburst_0(M_AXI_ARBURST),
.s00_axi_arvalid_0(M_AXI_ARVALID),
.s00_axi_arready(M_AXI_ARREADY),
.s00_axi_rid(M_AXI_RID),
.s00_axi_rdata(M_AXI_RDATA),
.s00_axi_rresp(M_AXI_RRESP),
.s00_axi_rlast(M_AXI_RLAST),
.s00_axi_rvalid(M_AXI_RVALID),
.s00_axi_rready_0(M_AXI_RREADY),
.ld_state_q(ld_state_q)
);
endmodule