存储系统作为不可或缺的环节,访问速度和存储大小决定了系统所能胜任的应用场景。
FPGA虽然配备了数量丰富的片上单周期RAM,但是容量受限于芯片大小和成本。
当面对图像视频应用,或者大吞吐率的数据采集时,就需要外扩DDR SRAM二级存储来满足需求。
本期的主角盘古PGL50H FPGA就贴心的在核心板上,为我们配备了两片DDR3的芯片,来完成二级存储的需求。
两片DDR3组成32bit的总线数据接口,最高时钟频率为400MHz,因此理论带宽高达400M*2*32 = 25600 Mbps。足以满足4k@60的视频应用(4096*2160*32*60 = 16200 Mbps)。
希望通过本贴你可以学会:
- 如何生成IP DDR3控制器
- 完成一个简单Simplified AXI控制模块的编写
- 如何使用PDS 在线Debug工具完成波形抓取
生成DDR3控制器
新建一个FPGA工程,然后在菜单栏的Tools打开IP Compiler,选中DDR3 Interface,输入一个自己喜欢的Instance Name后,点击Customize进行具体参数设置。
下面是IP的接口示意图,作为一般应用主要关注以下几组信号即可:
- 时钟信号
- axi总线信号
- mem信号
apb信号和debug信号可以不接,不影响正常使用
生成IP后,还需要编写DDR IP的约束文件,对其时钟频率,PLL位置和管脚进行约束。
下面是对DDR3 IP的例化,可以看到IP提供的AXI接口地址位宽是28bit,数据位宽是256bit。
这里有一个需要注意的地方就是AXI的地址最小访问DDR3存储空间是4个Byte,即32bit DDR3位宽, 而不是AXI的32Byte(256bit)。
parameter MEM_ROW_ADDR_WIDTH = 15;
parameter MEM_COL_ADDR_WIDTH = 10;
parameter MEM_BADDR_WIDTH = 3;
parameter MEM_DQ_WIDTH = 32;
parameter MEM_DM_WIDTH = MEM_DQ_WIDTH/8;
parameter MEM_DQS_WIDTH = MEM_DQ_WIDTH/8;
parameter CTRL_ADDR_WIDTH = MEM_ROW_ADDR_WIDTH + MEM_BADDR_WIDTH + MEM_COL_ADDR_WIDTH;
ddr_unit #
(
//***************************************************************************
// The following parameters are Memory Feature
//***************************************************************************
.MEM_ROW_WIDTH (MEM_ROW_ADDR_WIDTH),
.MEM_COLUMN_WIDTH (MEM_COL_ADDR_WIDTH),
.MEM_BANK_WIDTH (MEM_BADDR_WIDTH ),
.MEM_DQ_WIDTH (MEM_DQ_WIDTH ),
.MEM_DM_WIDTH (MEM_DM_WIDTH ),
.MEM_DQS_WIDTH (MEM_DQS_WIDTH ),
.CTRL_ADDR_WIDTH (CTRL_ADDR_WIDTH )
)ddr_top(
.ref_clk (ref_clk ),
.resetn (rst_board ),
.ddr_init_done (ddr_init_done ),
.ddrphy_clkin (core_clk ),
.pll_lock (pll_lock ),
.axi_awaddr (axi_awaddr ),
.axi_awuser_ap (axi_awuser ),
.axi_awuser_id (axi_awuser_id ),
.axi_awlen (axi_awlen ),
.axi_awready (axi_awready ),
.axi_awvalid (axi_awvalid ),
.axi_wdata (axi_wdata ),
.axi_wstrb (axi_wstrb ),
.axi_wready (axi_wready ),
.axi_wusero_id (axi_wid ),
.axi_wusero_last (axi_wlast ),
.axi_araddr (axi_araddr ),
.axi_aruser_ap (axi_aruser ),
.axi_aruser_id (axi_aruser_id ),
.axi_arlen (axi_arlen ),
.axi_arready (axi_arready ),
.axi_arvalid (axi_arvalid ),
.axi_rdata (axi_rdata ),
.axi_rid (axi_rid ),
.axi_rlast (axi_rlast ),
.axi_rvalid (axi_rvalid ),
.apb_clk (1'b0 ),
.apb_rst_n (1'b0 ),
.apb_sel (1'b0 ),
.apb_enable (1'b0 ),
.apb_addr (8'd0 ),
.apb_write (1'b0 ),
.apb_ready ( ),
.apb_wdata (16'd0 ),
.apb_rdata ( ),
.apb_int ( ),
.debug_data ( ),
.debug_slice_state ( ),
.debug_calib_ctrl ( ),
.ck_dly_set_bin ( ),
.force_ck_dly_en (0 ),
.force_ck_dly_set_bin (0 ),
.dll_step ( ),
.dll_lock ( ),
.init_read_clk_ctrl (0 ),
.init_slip_step (0 ),
.force_read_clk_ctrl (0 ),
.ddrphy_gate_update_en (0 ),
.update_com_val_err_flag ( ),
.rd_fake_stop (0 ),
.mem_rst_n (mem_rst_n ),
.mem_ck (mem_ck ),
.mem_ck_n (mem_ck_n ),
.mem_cke (mem_cke ),
.mem_cs_n (mem_cs_n ),
.mem_ras_n (mem_ras_n ),
.mem_cas_n (mem_cas_n ),
.mem_we_n (mem_we_n ),
.mem_odt (mem_odt ),
.mem_a (mem_a ),
.mem_ba (mem_ba ),
.mem_dqs (mem_dqs ),
.mem_dqs_n (mem_dqs_n ),
.mem_dq (mem_dq ),
.mem_dm (mem_dm )
);
);
编写AXI Lite控制器
完成DDR3 IP的定制后,需要编写Simplified AXI控制模块。
由于是被裁剪过的AXI,时序逻辑会相对简单,同时只有4组通道,每组通道的接口也有所减少。
这里编写了四个模块,完整源代码会贴在附件,分别对应了:
- 写地址控制模块 aw_master.v
- 读地址控制模块 ar_master.v
- 写数据控制模块 w_master.v
- 读数据控制模块 r_master.v
写地址控制模块
output axi_awvalid_o,
input axi_awready_i,
output [3:0] axi_awid_o,
output [3:0] axi_awlen_o,
output axi_awuser_o,
output [27:0] axi_awaddr_o,
output [27:0] axi_awaddr_o,
读地址控制模块
output axi_arvalid_o,
input axi_arready_i,
output [3:0] axi_arid_o,
output [3:0] axi_arlen_o,
output axi_aruser_o,
output [27:0] axi_araddr_o,
output [27:0] axi_araddr_o,
写数据控制模块
input axi_wready_i,
input [3:0] axi_wid_i,
input axi_wlast_i,
output reg [31:0] axi_wstrb_o,
output reg [255:0] axi_wdata_o,
output reg [255:0] axi_wdata_o,
读数据控制模块
input axi_rvalid_i,
input [3:0] axi_rid_i,
input axi_rlast_i,
input [255:0] axi_rdata_i,
input [255:0] axi_rdata_i,
input [255:0] axi_rdata_i,
使用在线Debug工具完成波形抓取
由于在综合工程中,综合工具会对信号进行优化,导致在debug的时候找不到需要的信号。
为了避免上述尴尬的发生,我们需要在声明信号的时候加上一句约束。
/*synthesis PAP_MARK_DEBUG="1"*/
/*synthesis PAP_MARK_DEBUG="1"*/
加上上述语句之后,可以在PDS的Tools下栏中打开Inserter。
通过这个工具,可以对DebugCore进行设置。
在Trigger Parameter一栏中,可以对采样端口数量(每个端口采集的信号位宽最大为256bits),单个端口采样的深度等进行配置。
在Net Connections一栏中,可以设置采集的具体信号。
完成所有设置后,对Inserter进行保存,会在Constraints一栏中多出一个fic结尾的文件,这个会在下载bit文件的时候一起被配置到FPGA中。
最后一步是打开Tool下拉中的Debugger工具,将bit文件和调试文件一起下载。
可以看到抓出的Simplified Axi的AW和W通道的波形。
这里还要为PDS一个贴心的功能点个赞,就是可以自动统计高低电平所占用的时钟周期数,并显示在波形中。
这里编写了一个简单的测速DDR3访问速度的工程,当按下KEY2,FPGA会将等同于一张4K分辨率大小的数据写入DDR3中,同时开始计时,当完成写入后停止计时,并通过Debugger的波形抓取结束时的时钟周期,就可以完成DDR3的带宽统计。
图中显示共消耗了1296026个时钟周期,就是消耗了12960260ns,总计完成了32MB的数据写入到DDR3中。
得到 32 * 8 / 0.01296026 = 19752Mbps,接近理论最大值25600Mbps,所以能够应付简单的4K图像处理。
*附件:SimplifiedAXI.7z