开发平台:Vivado 2017.4
FPGA 芯片:XC7K325T-2FFG676
DDR3芯片:MT41J256M16JT-125
目录
一、设计背景
该设计是基于并行数据采集项目上进行的,主要是将采集的数据通过AXI 总线设计放入DDR当中,然后上位机通过DMA读取DDR3中的数据。关于AXI总线的介绍可以看我前面的几篇文章,都做了详细的解释,本篇文章主要是对该设计过程的详细介绍。
二、设计过程
首先本次设计步骤如下:
1. 确认K7325T FPGA的DDR3控制器和AXI接口的规格。
2. 使用厂商工具生成DDR3控制器IP,并配置AXI接口参数。
3. 设计或配置AXI主设备(数据写入模块)来生成写DDR3的请求。
4. 设计或配置DMA控制器作为AXI主设备,生成读请求从DDR3读取数据。
5. 使用AXI Interconnect IP连接主设备和DDR3控制器,配置地址映射和仲裁。
6. 编写或集成控制逻辑,协调数据写入和DMA传输的启动和停止。
7. 进行功能仿真,验证数据写入和读出的正确性。
8. 综合、布局布线,生成比特流,下载到FPGA进行实测。
9. 调试和优化时序,处理实际运行中的问题。
2.1、Block Design设计
在确认好FPGA型号创建工程后进行Block Design设计,根据需求使用的是XDMA,选用的速度是2.5GT/s,AXI Data Width选用的64 bit,后面配置一下内存大小即可;
然后主要的就是DDR3 mig的相关配置,主要是根据自己板卡上面的DDR3进行选型和参数配置;比起常规的MIG就多了个AXI Parameter设置,Memory Part根据自己板卡的型号和大小选择就好;我这里选用的是MT41J256M16JT-125,因为用了两片,所以选用的位宽是32 bit的;
下面就是本次设计的整个BD设计框架,通过S01_AXI_0从外部的AXI总线设计中将数据写入DDR3当中,软件通过使用DMA通过PCIE总线将DDR3的数据读出来进行处理,其中BRAM仅用于寄存器控制;
在Address Editor中进行地址范围分配,避免在读写时冲突;
2.2、数据采集和AXI主设备(数据写入模块)设计
关于数据采集我使用的是AD7606,这款芯片的采集代码网上挺多的,挺好找的,如果想自己写也可以通过芯片手册手撕,挺简单,注意转换时间和数据读取就行,这里就不做介绍了,主要说一下对AXI逻辑的设计。
进行AXI读写控制设计需要遵守AXI4写事务协议(突发长度、地址对齐),可以通过状态机来进行设计;每次写入128个64bit数据(1024 byte),每次开始写入地址为0,根据突发长度和起始地址,来将写入地址累加,也可以根据自己的需求将地址分为几段,每段写入的数据可以自己安排;下面是AXI总线的状态机设计,数据是从fifo中读出来的;
module aq_axi_master(
// Reset, Clock
input wire ARESETN,
input wire ACLK,
// Master Write Address
output wire[ 0:0] M_AXI_AWID,
output wire[31:0] M_AXI_AWADDR,
output wire[ 7:0] M_AXI_AWLEN, // Burst Length: 0-255
output wire[ 2:0] M_AXI_AWSIZE, // Burst Size: Fixed 2'b011
output wire[ 1:0] M_AXI_AWBURST, // Burst Type: Fixed 2'b01(Incremental Burst)
output wire M_AXI_AWLOCK, // Lock: Fixed 2'b00
output wire[ 3:0] M_AXI_AWCACHE, // Cache: Fiex 2'b0011
output wire[ 2:0] M_AXI_AWPROT, // Protect: Fixed 2'b000
output wire[ 3:0] M_AXI_AWQOS, // QoS: Fixed 2'b0000
output wire[ 0:0] M_AXI_AWUSER, // User: Fixed 32'd0
output wire M_AXI_AWVALID,
input wire M_AXI_AWREADY,
// Master Write Data
output wire[63:0] M_AXI_WDATA,
output wire[ 7:0] M_AXI_WSTRB,
output wire M_AXI_WLAST,
output wire[ 0:0] M_AXI_WUSER,
output wire M_AXI_WVALID,
input wire M_AXI_WREADY,
// Master Write Response
input wire[ 0:0] M_AXI_BID,
input wire[ 1:0] M_AXI_BRESP,
input wire[ 0:0] M_AXI_BUSER,
input wire M_AXI_BVALID,
output wire M_AXI_BREADY,
// Local Bus
input wire MASTER_RST,
input wire WR_START,
input wire[31:0] WR_ADRS,
input wire[31:0] WR_LEN,
output wire WR_READY,
output wire WR_FIFO_RE,
input wire WR_FIFO_EMPTY,
input wire WR_FIFO_AEMPTY,
input wire[63:0] WR_FIFO_DATA,
output wire WR_DONE,
input wire RD_START,
input wire[31:0] RD_ADRS,
input wire[31:0] RD_LEN,
output wire RD_READY,
output wire RD_FIFO_WE,
input wire RD_FIFO_FULL,
input wire RD_FIFO_AFULL,
output wire[63:0] RD_FIFO_DATA,
output wire RD_DONE,
output wire[31:0] DEBUG
);
//--------------------------------------
// localparam
//--------------------------------------
localparam S_WR_IDLE = 3'd0;
localparam S_WA_WAIT = 3'd1;
localparam S_WA_START = 3'd2;
localparam S_WD_WAIT = 3'd3;
localparam S_WD_PROC = 3'd4;
localparam S_WR_WAIT = 3'd5;
localparam S_WR_DONE = 3'd6;
//--------------------------------------
// register
//--------------------------------------
reg [ 2:0] wr_state;
reg [31:0] reg_wr_adrs;
reg [31:0] reg_wr_len;
reg reg_awvalid, reg_wvalid, reg_w_last;
reg [ 7:0] reg_w_len;
reg [ 7:0] reg_w_stb;
reg [ 1:0] reg_wr_status;
reg [ 3:0] reg_w_count, reg_r_count;
reg [ 7:0] rd_chkdata, wr_chkdata;
reg [ 1:0] resp;
reg rd_first_data;
reg rd_fifo_enable;
reg [31:0] rd_fifo_cnt;
reg reg_wvalid_dly;
//--------------------------------------
// wire
//--------------------------------------
//--------------------------------------
// assign
//--------------------------------------
assign WR_DONE = (wr_state == S_WR_DONE);
assign WR_FIFO_RE = rd_first_data | (reg_wvalid & ~WR_FIFO_EMPTY & M_AXI_WREADY & rd_fifo_enable);
assign M_AXI_AWID = 1'b0;
assign M_AXI_AWADDR[31:0] = reg_wr_adrs[31:0];
assign M_AXI_AWLEN[7:0] = reg_w_len[7:0];
assign M_AXI_AWSIZE[2:0] = 2'b011;
assign M_AXI_AWBURST[1:0] = 2'b01;
assign M_AXI_AWLOCK = 1'b0;//
assign M_AXI_AWCACHE[3:0] = 4'b0011;
assign M_AXI_AWPROT[2:0] = 3'b000;
assign M_AXI_AWQOS[3:0] = 4'b0000;
assign M_AXI_AWUSER[0] = 1'b1;
assign M_AXI_AWVALID = reg_awvalid;
assign M_AXI_WDATA[63:0] = WR_FIFO_DATA[63:0];
assign M_AXI_WSTRB[7:0] = (reg_wvalid & ~WR_FIFO_EMPTY)?8'hFF:8'h00;
assign M_AXI_WLAST = (reg_w_len[7:0] == 8'd0)?1'b1:1'b0;
assign M_AXI_WUSER = 1;
assign M_AXI_WVALID = reg_wvalid & ~WR_FIFO_EMPTY;
//assign M_AXI_WVALID = reg_wvalid_dly & ~WR_FIFO_EMPTY;
assign M_AXI_BREADY = M_AXI_BVALID;
assign WR_READY = (wr_state == S_WR_IDLE)?1'b1:1'b0;
//------------------------------------------------------------
//------------------------------------------------------------
always @(posedge ACLK or negedge ARESETN)//fifo control
begin
if(!ARESETN)
rd_fifo_cnt <= 32'd0;
else if(WR_FIFO_RE)
rd_fifo_cnt <= rd_fifo_cnt + 32'd1;
else if(wr_state == S_WR_IDLE)
rd_fifo_cnt <= 32'd0;
end
//------------------------------------------------------------
//------------------------------------------------------------
always @(posedge ACLK or negedge ARESETN)
begin
if(!ARESETN)
rd_fifo_enable <= 1'b0;
else if(wr_state == S_WR_IDLE && WR_START)
rd_fifo_enable <= 1'b1;
else if(WR_FIFO_RE && (rd_fifo_cnt == RD_LEN[31:3] - 32'd1) )
rd_fifo_enable <= 1'b0;
end
//------------------------------------------------------------
//------------------------------------------------------------
// Write State
always @(posedge ACLK or negedge ARESETN)
begin
if(!ARESETN)
begin
wr_state <= S_WR_IDLE;
reg_wr_adrs[31:0] <= 32'd0;
reg_wr_len[31:0] <= 32'd0;
reg_awvalid <= 1'b0;
reg_wvalid <= 1'b0;
reg_w_last <= 1'b0;
reg_w_len[7:0] <= 8'd0;
reg_w_stb[7:0] <= 8'd0;
reg_wr_status[1:0] <= 2'd0;
reg_w_count[3:0] <= 4'd0;
reg_r_count[3:0] <= 4'd0;
wr_chkdata <= 8'd0;
rd_chkdata <= 8'd0;
resp <= 2'd0;
rd_first_data <= 1'b0;
end
else
begin
if(MASTER_RST)
begin
wr_state <= S_WR_IDLE;
end
else
begin
case(wr_state)
S_WR_IDLE:
begin
if(WR_START)
begin//burst req
wr_state <= S_WA_WAIT;
reg_wr_adrs[31:0] <= WR_ADRS[31:0];
reg_wr_len[31:0] <= WR_LEN[31:0] -32'd1;
//rd_first_data <= 1'b1;
rd_first_data <= 1'b0;
end
reg_awvalid <= 1'b0;
reg_wvalid <= 1'b0;
reg_w_last <= 1'b0;
reg_w_len[7:0] <= 8'd0;
reg_w_stb[7:0] <= 8'd0;
reg_wr_status[1:0] <= 2'd0;
end
S_WA_WAIT:
begin//地址设置阶段
if(!WR_FIFO_AEMPTY | (reg_wr_len[31:11] == 21'd0))
begin
wr_state <= S_WA_START;
end
rd_first_data <= 1'b0;
end
S_WA_START:
begin
wr_state <= S_WD_WAIT;
reg_awvalid <= 1'b1;
reg_wr_len[31:11] <= reg_wr_len[31:11] - 21'd1;
if(reg_wr_len[31:11] != 21'd0)
begin
reg_w_len[7:0] <= 8'hFF;//len 256
reg_w_last <= 1'b0;
reg_w_stb[7:0] <= 8'hFF;
end
else
begin
reg_w_len[7:0] <= reg_wr_len[10:3];
reg_w_last <= 1'b1;
reg_w_stb[7:0] <= 8'hFF;
end
end
S_WD_WAIT:
begin
if(M_AXI_AWREADY)
begin//
wr_state <= S_WD_PROC;
reg_awvalid <= 1'b0;
reg_wvalid <= 1'b1;
end
end
S_WD_PROC:
begin
if(M_AXI_WREADY & ~WR_FIFO_EMPTY)
begin//
if(reg_w_len[7:0] == 8'd0)
begin
wr_state <= S_WR_WAIT;
reg_wvalid <= 1'b0;
reg_w_stb[7:0] <= 8'h00;
end
else
begin
reg_w_len[7:0] <= reg_w_len[7:0] -8'd1;
end
end
end
S_WR_WAIT:
begin
if(M_AXI_BVALID)
begin
reg_wr_status[1:0] <= reg_wr_status[1:0] | M_AXI_BRESP[1:0];
if(reg_w_last)
begin
wr_state <= S_WR_DONE;
end
else
begin
wr_state <= S_WA_WAIT;
reg_wr_adrs[31:0] <= reg_wr_adrs[31:0] + 32'd2048;
end
end
end
S_WR_DONE:
begin
wr_state <= S_WR_IDLE;
end
default:
begin
wr_state <= S_WR_IDLE;
end
endcase
end
end
end
//------------------------------------------------------------
//------------------------------------------------------------
endmodule
2.3、调试与验证
2.3.1、仿真调试
创建Testbench文件来进行仿真调试,模拟DDR3控制器响应,检查AXI事务是否符合协议;关键检查点:突发传输的地址递增是否正确、WLAST和RLAST信号是否在最后一拍置位。
下面分别是一次和多次突发传输的仿真过程,经过调试后达到了上面的需求;
在仿真调试完成后需要生成bit文件进行板上调试,这时候可以加上ila对相关信号进行监控,主要监控AWVALID/AWREADY、WVALID/WREADY等握手信号,在调试过程中可以使用XDMA官方驱动进行调试,可以查看对应地址读出来的数据和写入的数据是否一致;
三、常见问题和解决方案
1.DDR3初始化失败:
检查DDR3控制器的物理层配置(时钟、电压、时序参数)。
使用厂商提供的校准工具(如MIG的校准状态信号)。
2.AXI握手信号死锁:
确保主设备在VALID置位后等待READY,避免无限期阻塞。
添加超时计数器强制释放信号。
3.DMA传输数据错位:
确认源地址与目标地址的对齐方式(如64位对齐)。
检查DMA传输长度是否按数据宽度对齐。
4.带宽不足:
提升AXI总线时钟频率。
使用多端口DDR3控制器或增加AXI总线位宽(如128位→256位)。
四、总结
在K7325T FPGA上实现AXI总线与DDR3的交互及DMA传输,需重点注意:
1、协议合规性:严格遵循AXI4时序,避免握手死锁。
2、DDR3控制器配置:物理层参数与AXI接口匹配。
3、资源优化:合理使用AXI Interconnect减少逻辑开销。
4、调试手段:结合仿真与ILA抓取信号快速定位问题。
通过模块化设计与分阶段验证,可高效完成从数据写入到DMA读取的全流程实现。