AXI总线之AXI DDR3设计

开发平台:Vivado 2017.4

FPGA 芯片:XC7K325T-2FFG676

DDR3芯片:MT41J256M16JT-125

 

目录

一、设计背景

二、设计过程

2.1、Block Design设计

2.2、数据采集和AXI主设备(数据写入模块)设计

2.3、调试与验证

2.3.1、仿真调试

三、常见问题和解决方案

四、总结


一、设计背景

        该设计是基于并行数据采集项目上进行的,主要是将采集的数据通过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读取的全流程实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值