小弟最近在调试紫光的ddr3IP,有些心得经验,坐下记录,仅供各位参考
DDR3存储构架设计:
DDR3型号 :MT41K12816MXX;
DDR3存储空间:16M x 16bits x 8Banks = 8Banks x 16384 x 128 x 8 x 16bits;也就是单片共8个Bank、每Bank共16384个Row地址、每Row共128个Col地址、每Col为128bits = 8x16bits。
由上述描述
本次DDR3读写的存储空间设计存储空间划分如下:在一个bank内通过发送5000多个16bit的自加数验证读写,验证ddr3读写,这样简单的读写,轮询机制只需要对两个通道进行仲裁,数据读写各占一个通道,
对于ddr3写操作,由于选用连续的读写方式,这样每次写完一行,在去写下一行
ddr3_ipcore
这样总体在顶层需要一个AXI接口模块ddr3_ctrl方便我们控制ddr3的读写,控制状态机的跳转用来实现数据的写入和读出
由ddr_ctrl模块按照紫光简化的AXI协议(如下图)控制ddr3_IP读地址通道,写地址通道,读数据通道,写数据通道,进行读写操作,包括一些握手信号,突发数据起始地址,突发长度(下图为上板抓取的紫光ddr3 demo时序列):
ddr3行列以及bank地址划分如下:
这样一旦ddr_ctrl模块发起读写请求,按照axi协议要求和ddr3握手成功,送出突发长度和起始地址,完成一次突发读写,对于连续的地址只需要按照数据总量和突发长度,需要的突发次数,把一个row的所有col写满后,跳转到下一个row。
本次读写设计中程序架构如下图:
在设计中顶层模块主要包含ddr3_ctrl用来控制和AXI接口的通信,和ddr3_fifo_ctrl模块的用来控制fifo给AXi接口提供AXI_wdata和AXI_rdata的读写数据,ddr_test用来产生测试的自己数数据
DDR3 IP 核的接口信号很多,下图为ddr3的IP核配置:
PLL时钟模块配置:
rd_fifo IP 核参数配置如下图所示:
wr_fifo IP 核参数配置如下图所示:
在代码中定义了四个参数,分别为 ddr3 读写起始地址(APP_ADDR_MIN)、 ddr3 读写结束地址(APP_ADDR_MAX)、 ddr3 读写突发长度(BURST_LENGTH) 以及读写 ddr3 的最大数据量(DATA_MAX)。其中 APP_ADDR_MIN、 APP_ADDR_MAX 和 DATA_MAX 是以一个 16bit 的数据为一个单位, BURST_LENGTH 是以一个 128bit 的数据为一个单位。
APP_ADDR_MAX = APP_ADDR_MIN + BURST_LENGTH * 8 * n(n 表示突发次数)
DATA_MAX = APP_ADDR_MAX - APP_ADDR_MIN
对于突发长度(BURST_LENGTH) 的设置, 根据配置,列地址是 10 位,列地址边界就是 1023,突发结束地址不能超过 1023(即 1024 个字节),超过就需要分两次,分两次的话实测是有可能会发生列地址回滚现象的,即列地址回滚到 0(起始地址) ,覆盖了一部分以 0 为起始地址的数据, 由于 DDR 是硬核 IP,无法规避,只能从应用层控制,所以尽量设置为 2 的整数次幂且不要超过 64(例如 2、 4、 8、 16、 32、 64),以此来规避可能出现的因为回滚造成数据覆盖而导致读写错误问题。
由于 DDR3 控制器被封装成 FIFO 接口,在使用时只需要像 读/写 FIFO 那样给出读/写使能即可,如代码 51 ~ 62 行所示。同时定义了最大和最小读写地址,在调用时数据在该地址空间中连续读写。
指定 DDR3 控制器的数据突发长度,由于 DDR3 IP 核的突发长度位宽为 4 位,因此控制器的突发长度不能大于 16。
ddr3 控制器顶层模块主要完成 ddr3 读写控制器模块、 FIFO 控制模块和 ddr3 IP 核的例化。ddr3读写控制器模块负责与 ddr3 IP 核模块的命令和地址的交互,根据 FIFO 控制模块中 fifo 的剩余数据量来切换 DDR3 的读写命令和地址。 ddr3 IP 核模块一边与用户端进行交互,另一边对芯片进行操作,以实现数据的存储。 FIFO 控制模块负责对输入和输出的数据进行时钟域的切换和位宽的转换。
代码中计算了最大突发次数,由于是从 0 地址开始写入,所以 lenth_cnt_max = app_addr_wr_max / (wr_bust_len * 8) ;若不是从 0 地址开始写入,则 lenth_cnt_max = (app_addr_wr_max - app_addr_wr_min) / (wr_bust_len * 8) 。
lenth_cnt_max(最大突发次数计数器) 的位宽为 10, 10 位宽的计数器最大可以计数到 1023,而根据上面的公式可以计算出本次读写例程只需要进行 10 次突发, 所以 10 位宽的计数器对于本次实验来说是绰绰有余的,但是当存储更大的数据量时,随着所需突发次数的增加,lenth_cnt_max 的位宽也需要做出相应的增大,否则就会出现读写错误的现象。
为了稳定 ddr3 初始化完成信号,因为 ddr3 IP 核对初始化完成信号存在信号校准,所以初始化完成后该信号并非一直保持为高,会有跳动,因此在这里做当检测到一次 ddr3 初始化完成信号后,就将该信号一直拉高,使后续模块运行时, 时序不受影响。
执行写地址操作, ddr3 初始化完成后,若写地址计数小于最后一次写地址起始位时,如果当前状态机处于写地址状态且写地址准备信号有效,拉高写地址有效信号; 写地址有效信号和写地址准备信号同时为高时,写地址计数器 (axi_awaddr_n) 增加一个突发长度所需的地址并将写地址有效信号拉低,即写地址有效信号只拉高了一个时钟周期。 若写地址计数小于最后一次写地址起始位时,当写地址有效信号和写地址准备信号同时为高时,将写地址计数器清零(即回到写起始地址),其他信号变化相同。
代码执行写数据操作,ddr3 初始化完成后,若突发写次数计数器小于最大突发次数时,如果当前状态机处于写数据状态且写数据准备信号有效时,拉高写数据有效信号直至完成一次突发写操作后再将其拉低。 因为写 DDR 时已经提前让 FIFO 准备好第一个数据,所以使能在写结尾要减少一个使能周期,因此在写数据有效信号和写数据准备信号同时为高时,若突发长度计数器 (init_addr) 小于突发长度 -2 时,突发长度计数器加 1; 若突发长度计数器 (init_addr) 等于突发长度 -2 (即写倒数第二个数) 时,将 wrfifo_en_ctrl 信号拉高(即在写结尾减少一个使能周期);若突发长度计数器 (init_addr) 等于突发长度 -1 (即写最后一个数) 时,将 wrfifo_en_ctrl 信号拉低,即 wrfifo_en_ctrl 信号只拉高一个时钟周期,为下一次写数据操作做准备。
读地址操作的信号跳转与写地址操作时类似,这里不再赘述。
上图为ddr控制模块的状态转移图,在复位结束后,如果 DDR3 没有初始化完成,那么状态一直在空闲状态 (IDLE) ,否则跳到 DDR3 空闲状态 (DDR3_DONE) 。
状态机DDR3_done处理 DDR3 写请求,以免写 FIFO 溢出,造成写入 DDR3 的数据丢失。当写 FIFO 中的数据量大于一次突发写长度时,执行 DDR3 写地址操作 (WRITE_ADDR) 。
状态机DDR3_done处理 DDR3 读请求,以免读 FIFO 读空,造成空读现象。当读 FIFO 中的数据量小于一次读突发长度时,执行 DDR3 读地址操作 (READ_ADDR) 。
处理 DDR3 写地址跳转到写数据状态的过程,当写地址有效信号和写地址准备信号同时为高时,状态机由写地址状态 (WRITE_ADDR) 跳转到写数据状态 (WRITE_DATA) ;当执行完一次突发写长度后,状态机由写数据状态跳转到 DDR3 空闲状态 (DDR3_DONE) 。
处理 DDR3 读地址跳转到读数据状态的过程,跳转机制与写状态类似,有别处在于读数据状态 (READ_DATA) 跳转到 DDR3 空闲状态 (DDR3_DONE) 的条件是最后一次读信号 (axi_rlast) 为 1 时。
该模块例化了两个 FIFO IP 核,分别为 128 位进 16 位出的读 FIFO 和 16 位进 128 位出的写 FIFO。读 FIFO 是将 DDR3 输出的 128 位宽的数据转为 16 位宽的数据后输出给用户;写 FIFO 是将用户输入的 16 位宽的数据转为 128 位宽的数据后输出给 DDR3。
上图为上板debug抓取的读写数据时的axi接口时序,以及最后AXI_rdata输出的自加数
ddr3_rw_top代码
module ddr3_rw_top#(
parameter MEM_ROW_WIDTH = 13 ,
parameter MEM_COLUMN_WIDTH = 10 ,
parameter MEM_BANK_WIDTH = 3 ,
parameter MEM_DQ_WIDTH = 16 ,
parameter MEM_DQS_WIDTH = 2 ,
parameter DATA_BITS = MEM_DQ_WIDTH*8
)
(
input sys_clk , //系统时钟50M
input sys_rst_n , //系统复位
output led_error , //读写错误led灯
output led_ddr_init_done, //ddr3初始化完成led灯
//DDR3接口
output mem_cs_n,
output mem_rst_n,
output mem_ck,
output mem_ck_n,
output mem_cke,
output mem_ras_n,
output mem_cas_n,
output mem_we_n,
output mem_odt,
output [MEM_ROW_WIDTH-1:0] mem_a,
output [MEM_BANK_WIDTH-1:0] mem_ba,
inout [MEM_DQ_WIDTH/8-1:0] mem_dqs,
inout [MEM_DQ_WIDTH/8-1:0] mem_dqs_n,
inout [MEM_DQ_WIDTH-1:0] mem_dq,
output [MEM_DQ_WIDTH/8-1:0] mem_dm
);
//parameter define
parameter APP_ADDR_MIN = 28'd0 ; //ddr3读写起始地址,以一个16bit的数据为一个单位
//APP_ADDR_MAX = APP_ADDR_MIN + BURST_LENGTH * 8 * n(n表示突发次数)
parameter APP_ADDR_MAX = 28'd5120 ; //ddr3读写结束地址,以一个16bit的数据为一个单位
parameter BURST_LENGTH = 5'd16 ; //ddr3读写突发长度,64个128bit的数据
parameter DATA_MAX = APP_ADDR_MAX - APP_ADDR_MIN; //读写ddr3的最大数据量
//wire define
wire [15:0] wr_data ; //DDR3控制器模块写数据
wire [15:0] rd_data ; //DDR3控制器模块读数据
wire wr_en ; //DDR3控制器模块写使能
wire rd_en ; //DDR3控制器模块读使能
wire ddr_init_done ; //ddr3初始化完成信号
wire error_flag ; //ddr3读写错误标志
wire reset_o ;
wire clkout3 ;
//ddr3控制器顶层模块
ddr3_top #(
.MEM_ROW_WIDTH (MEM_ROW_WIDTH ),
.MEM_COLUMN_WIDTH (MEM_COLUMN_WIDTH ),
.MEM_BANK_WIDTH (MEM_BANK_WIDTH ),
.MEM_DQ_WIDTH (MEM_DQ_WIDTH ),
.MEM_DQS_WIDTH (MEM_DQS_WIDTH ),
.DATA_BITS (DATA_BITS )
)u_ddr3_top(
.refclk_in (sys_clk ),
.rst_n (sys_rst_n ),
.app_addr_rd_min (APP_ADDR_MIN ),
.app_addr_rd_max (APP_ADDR_MAX ),
.rd_bust_len (BURST_LENGTH ),
.app_addr_wr_min (APP_ADDR_MIN ),
.app_addr_wr_max (APP_ADDR_MAX ),
.wr_bust_len (BURST_LENGTH ),
.wr_clk (sys_clk ),
.rd_clk (sys_clk ),
.datain_valid (wr_en ),
.datain (wr_data ),
.rdata_req (rd_en ),
.reset_o (reset_o ),
.dataout (rd_data ),
.init_done (ddr_init_done ),
.clkout3 (clkout3) ,
//DDR3接口
.mem_cs_n (mem_cs_n ),
.mem_rst_n (mem_rst_n ),
.mem_ck (mem_ck ),
.mem_ck_n (mem_ck_n ),
.mem_cke (mem_cke ),
.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 )
);
//ddr3测试数据模块
ddr_test u_ddr_test(
.clk_50m (clkout3 ), //时钟
.rst_n (reset_o ), //复位,低有效
.wr_en (wr_en ), //写使能
.wr_data (wr_data ), //写数据
.rd_en (rd_en ), //读使能
.rd_data (rd_data ), //读数据
.data_max (DATA_MAX ), //读写ddr的最大数据量
.ddr3_init_done(ddr_init_done ), //ddr3初始化完成信号
.error_flag (error_flag ) //ddr3读写错误
);
//利用LED灯指示ddr3读写测试的结果及ddr3是否初始化完成
led_disp u_led_disp(
.clk_50m (clkout3 ),
.rst_n (reset_o ),
.ddr3_init_done (ddr_init_done ),
.error_flag (error_flag ),
.led_error (led_error ),
.led_ddr_init_done (led_ddr_init_done)
);
endmodule
下面展示一些 内联代码片
。
ddr3_top代码
module ddr3_top#(
parameter MEM_ROW_WIDTH = 13 ,
parameter MEM_COLUMN_WIDTH = 10 ,
parameter MEM_BANK_WIDTH = 3 ,
parameter MEM_DQ_WIDTH = 16 ,
parameter MEM_DQS_WIDTH = 2 ,
parameter DATA_BITS = MEM_DQ_WIDTH*8
)
(
input refclk_in ,//外部参考时钟输入
input rst_n ,//外部复位输入
input [27:0] app_addr_rd_min ,//读ddr3的起始地址
input [27:0] app_addr_rd_max ,//读ddr3的结束地址
input [4:0] rd_bust_len ,//从ddr3中读数据时的突发长度
input [27:0] app_addr_wr_min ,//读ddr3的起始地址
input [27:0] app_addr_wr_max ,//读ddr3的结束地址
input [4:0] wr_bust_len ,//从ddr3中读数据时的突发长度
//用户
input wr_clk ,//wfifo写时钟
input rd_clk ,//rfifo读时钟
input datain_valid ,//数据有效使能信号
input [15:0] datain ,//有效数据
input rdata_req ,//请求数据输入
output reset_o ,
output [15:0] dataout ,//rfifo输出数据
output clkout3 /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ,
output init_done ,//DDR初始化完成
output mem_cs_n,
output mem_rst_n,
output mem_ck,
output mem_ck_n,
output mem_cke,
output mem_ras_n,
output mem_cas_n,
output mem_we_n,
output mem_odt,
output [MEM_ROW_WIDTH-1:0] mem_a,
output [MEM_BANK_WIDTH-1:0] mem_ba,
inout [MEM_DQ_WIDTH/8-1:0] mem_dqs,
inout [MEM_DQ_WIDTH/8-1:0] mem_dqs_n,
inout [MEM_DQ_WIDTH-1:0] mem_dq,
output [MEM_DQ_WIDTH/8-1:0] mem_dm
);
parameter CTRL_ADDR_WIDTH = MEM_ROW_WIDTH + MEM_BANK_WIDTH + MEM_COLUMN_WIDTH;
wire core_clk /* synthesis syn_keep = 1 */ ;
wire pll_lock /* synthesis syn_keep = 1 */ ;
wire phy_pll_lock /* synthesis syn_keep = 1 */ ;
wire gpll_lock /* synthesis syn_keep = 1 */ ;
wire rst_gpll_lock /* synthesis syn_keep = 1 */ ;
wire ddrphy_cpd_lock /* synthesis syn_keep = 1 */ ;
wire ddr_init_done /* synthesis syn_keep = 1 */ ;
wire sys_rst;
wire rst;
wire rstn;
wire core_rst;
wire core_rstn;
//wire define
wire [25:0] axi_awaddr /* synthesis syn_keep = 1 */ ;
wire [3:0] axi_awlen /* synthesis syn_keep = 1 */ ;
wire axi_awready /* synthesis syn_keep = 1 */;
wire axi_awvalid /* synthesis syn_keep = 1 */ ;
wire [127:0] axi_wdata ;
wire [15:0] axi_wstrb /* synthesis syn_keep = 1 */ ;
wire axi_wvalid /* synthesis syn_keep = 1 */ ;
wire axi_wready/* synthesis syn_keep = 1 */;
wire [25:0] axi_araddr /* synthesis syn_keep = 1 */ ;
wire [3:0] axi_arlen/* synthesis syn_keep = 1 */;
wire axi_arready/* synthesis syn_keep = 1 */;
wire axi_arvalid/* synthesis syn_keep = 1 */;
wire [127:0] axi_rdata ;
wire axi_rlast /* synthesis syn_keep = 1 */ ;
wire axi_rvalid /* synthesis syn_keep = 1 */ ;
reg s_rst_n = 1'b0;
reg [7:0] s_cnt = 8'd0;
wire resetn/* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/;
wire axi_awuser_ap /* synthesis syn_keep = 1 */ ;
wire [3:0] axi_awuser_id /* synthesis syn_keep = 1 */ ;
wire [3:0] axi_wusero_id /* synthesis syn_keep = 1 */ ;
wire axi_wusero_last /* synthesis syn_keep = 1 */ ;
wire axi_aruser_ap /* synthesis syn_keep = 1 */ ;
wire [3:0] axi_aruser_id /* synthesis syn_keep = 1 */ ;
wire [3:0] axi_rid /* synthesis syn_keep = 1 */ ;
wire [10:0] wfifo_rcount ;//rfifo剩余数据计数
wire [10:0] rfifo_wcount ;//wfifo写进数据计数
wire wrfifo_en_ctrl ;//写FIFO数据读使能控制位
wire wfifo_rden ;//写FIFO数据读使能
wire pre_wfifo_rden ;//写FIFO数据预读使能
wire [29:0] debug_calib_ctrl ;
wire [1:0] dbg_dll_upd_state ;
wire [17*MEM_DQS_WIDTH -1:0] dbg_slice_status ;
wire [22*MEM_DQS_WIDTH -1:0] dbg_slice_state ;
wire [66*MEM_DQS_WIDTH -1:0] debug_data ;
wire [8:0] debug_gpll_dps_phase ;
wire [2:0] dbg_rst_dps_state ;
wire [5:0] dbg_tran_err_rst_cnt ;
wire dbg_ddrphy_init_fail ;
wire [9:0] debug_dps_cnt_dir0 ;
wire [9:0] debug_dps_cnt_dir1 ;
wire [7:0] ck_dly_set_bin ;
wire align_error ;
wire [3:0] debug_rst_state ;
wire [3:0] debug_cpd_state ;
wire clk_inbuf;
wire pll_sys_lock;
wire pll_sys_rst;
wire pll_led_lock;
wire pll_led_rst;
wire sys_clk /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/;
wire phy0_tx_clk;
wire phy0_tx_clk;
wire ref_clk /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ;
//*****************************************************
//** main code
//*****************************************************
//因为预读了一个数据所以读使能wfifo_rden要少一个周期通过wrfifo_en_ctrl控制
assign ref_clk = refclk_in;
assign wfifo_rden = axi_wvalid && axi_wready && (~wrfifo_en_ctrl) ;
assign pre_wfifo_rden = axi_awvalid && axi_awready ;
assign init_done = ddr_init_done;
assign resetn = s_rst_n;
GTP_CLKBUFG U_CLKBUFG (
.CLKOUT (clk_inbuf),
.CLKIN (refclk_in)
);
pll_sys pll_sys_inst (
.clkin1 (clk_inbuf), // input
.rst (pll_sys_rst), // input
.lock (pll_sys_lock), // output
.clkout0 (sys_clk), // output
.clkout1 (phy0_tx_clk), // output
.clkout2 (phy1_tx_clk), // output
.clkout3 (clkout3)
);
always @(posedge clk_inbuf) begin
if(s_cnt == 8'hff) begin
s_rst_n = 1'b1;
s_cnt <= s_cnt;
end
else
s_cnt <= s_cnt + 8'd1;
end
sys_rst u_sys_rst(
.clk (clk_inbuf),
.pll_sys_lock (pll_sys_lock),
.pll_sys_rst (pll_sys_rst),
.pll_led_lock (pll_led_lock),
.pll_led_rst (pll_led_rst),
.pll_lock (pll_lock),
.phy_pll_lock (phy_pll_lock),
.gpll_lock (gpll_lock),
.rst_gpll_lock (rst_gpll_lock),
.ddrphy_cpd_lock (ddrphy_cpd_lock),
.ddr_init_done (ddr_init_done),
.ddr_core_rstn (ddr_core_rstn),
.sys_rst (sys_rst)
);
ddr3_rst_ctrl u_ddr3_rst_ctrl
(
.clk (sys_clk),
.core_clk (core_clk),
.rst_in (sys_rst),
.rst_o (rst),
.rstn_o (rstn),
.core_rst_o (core_rst),
.reset_o (reset_o),
.core_rstn_o (core_rstn)
);
//ddr3读写控制器模块
rw_ctrl_128bit u_rw_ctrl_128bit
(
.clk (core_clk ),
.rst_n (core_rstn ),
.ddr_init_done (ddr_init_done ),
.axi_awaddr (axi_awaddr ),
.axi_awlen (axi_awlen ),
.axi_awready (axi_awready ),
.axi_awvalid (axi_awvalid ),
.axi_wstrb (axi_wstrb ),
.axi_wvalid (axi_wvalid ),
.axi_wready (axi_wready ),
//.axi_wlast (axi_wlast ),
.wrfifo_en_ctrl (wrfifo_en_ctrl ),
.axi_araddr (axi_araddr ),
.axi_arlen (axi_arlen ),
.axi_arready (axi_arready ),
.axi_arvalid (axi_arvalid ),
.axi_rlast (axi_rlast ),
.axi_rvalid (axi_rvalid ),
.wfifo_rcount (wfifo_rcount ),
.rfifo_wcount (rfifo_wcount ),
.app_addr_rd_min (app_addr_rd_min ),
.app_addr_rd_max (app_addr_rd_max ),
.rd_bust_len (rd_bust_len ),
.app_addr_wr_min (app_addr_wr_min ),
.app_addr_wr_max (app_addr_wr_max ),
.wr_bust_len (wr_bust_len )
);
ddr3_ipcore #(
.MEM_ROW_WIDTH (MEM_ROW_WIDTH ),
.MEM_COLUMN_WIDTH (MEM_COLUMN_WIDTH ),
.MEM_BANK_WIDTH (MEM_BANK_WIDTH ),
.MEM_DQ_WIDTH (MEM_DQ_WIDTH ),
.MEM_DM_WIDTH (MEM_DQS_WIDTH ),
.MEM_DQS_WIDTH (MEM_DQS_WIDTH ),
.CTRL_ADDR_WIDTH (CTRL_ADDR_WIDTH )
)u_ddr3_ipcore(
.ref_clk (ref_clk ),
.resetn (resetn ),
//.resetn (ddr_core_rstn),
.core_clk (core_clk ),
.pll_lock (pll_lock ),
.phy_pll_lock (phy_pll_lock ),
.gpll_lock (gpll_lock ),
.rst_gpll_lock (rst_gpll_lock ),
.ddrphy_cpd_lock (ddrphy_cpd_lock ),
.ddr_init_done (ddr_init_done ),
.axi_awaddr (axi_awaddr ),
.axi_awuser_ap (1'b0 ),
.axi_awuser_id (4'd0 ),
.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_wusero_id ),
.axi_wusero_last (axi_wusero_last ),
.axi_araddr (axi_araddr ),
.axi_aruser_ap (1'b0 ),
.axi_aruser_id (4'd0 ),
.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 ( ),
.mem_cs_n (mem_cs_n ),
.mem_rst_n (mem_rst_n ),
.mem_ck (mem_ck ),
.mem_ck_n (mem_ck_n ),
.mem_cke (mem_cke ),
.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 ),
.dbg_gate_start (1'b0 ),
.dbg_cpd_start (1'b0 ),
.dbg_ddrphy_rst_n (1'b1 ),
.dbg_gpll_scan_rst (1'b0 ),
.samp_position_dyn_adj (1'b0 ),
.init_samp_position_even (32'b0 ),
.init_samp_position_odd (32'b0 ),
.wrcal_position_dyn_adj (1'b0 ),
.init_wrcal_position (32'b0 ),
.force_read_clk_ctrl (1'b0 ),
.init_slip_step (16'b0 ),
.init_read_clk_ctrl (12'b0 ),
.debug_calib_ctrl (debug_calib_ctrl ),
.dbg_dll_upd_state (dbg_dll_upd_state ),
.dbg_slice_status (dbg_slice_status ),
.dbg_slice_state (dbg_slice_state ),
.debug_data (debug_data ),
.debug_gpll_dps_phase (debug_gpll_dps_phase ),
.dbg_rst_dps_state (dbg_rst_dps_state ),
.dbg_tran_err_rst_cnt (dbg_tran_err_rst_cnt ),
.dbg_ddrphy_init_fail (dbg_ddrphy_init_fail ),
.debug_cpd_offset_adj (1'b0 ),
.debug_cpd_offset_dir (1'b0 ),
.debug_cpd_offset (10'b0 ),
.debug_dps_cnt_dir0 (debug_dps_cnt_dir0 ),
.debug_dps_cnt_dir1 (debug_dps_cnt_dir1 ),
//.ck_dly_set_bin (8'h32 ),//延迟设置 步进5ps 目前为250ps
.ck_dly_en (1'b1 ),
.init_ck_dly_step (8'h00 ),
.ck_dly_set_bin (ck_dly_set_bin ),
.align_error (align_error ),
.debug_rst_state (debug_rst_state ),
.debug_cpd_state (debug_cpd_state )
);
//ddr3控制器fifo控制模块
ddr3_fifo_ctrl u_ddr3_fifo_ctrl (
.rst_n (sys_rst && ddr_init_done ) , //复位
//输入源接口
.wr_clk (sys_clk ) , //写时钟
.rd_clk (sys_clk ) , //读时钟
.clk_100 (core_clk ) , //用户时钟
.datain_valid (datain_valid ) , //数据有效使能信号
.datain (datain ) , //有效数据
.rfifo_din (axi_rdata ) , //用户读数据
.rdata_req (rdata_req ) , //请求像素点颜色数据输入
.rfifo_wren (axi_rvalid ) , //ddr3读出数据的有效使能
.wfifo_rden (wfifo_rden ) , //ddr3 写使能
//用户接口
.wfifo_rcount (wfifo_rcount ) , //rfifo剩余数据计数
.rfifo_wcount (rfifo_wcount ) , //wfifo写进数据计数
.wfifo_dout (axi_wdata ) , //用户写数据
.pic_data (dataout ) //rfifo输出数据
);
endmodule
下面展示一些 内联代码片
。
系统 复位代码
`timescale 1ns / 1ps
module sys_rst (
input wire clk,
input wire pll_sys_lock,
output wire pll_sys_rst,
input wire pll_led_lock,
output wire pll_led_rst,
input wire pll_lock,
input wire phy_pll_lock,
input wire gpll_lock,
input wire rst_gpll_lock,
input wire ddrphy_cpd_lock,
input wire ddr_init_done,
output wire ddr_core_rstn,
output wire sys_rst
);
//----------------------------------- parameter ------------------------------------
//==================================================================================
//---------------------------------- reg / wire ------------------------------------
reg [15:0] pll_sys_lockcnt = 16'h0000;
reg pll_sys_restart ;
reg pll_sys_rst_temp ;
reg [15:0] pll_led_lockcnt = 16'h0000;
reg pll_led_restart ;
reg pll_led_rst_temp ;
reg ddr_all_lock;
reg [19:0] ddr_lockcnt = 20'h00000;
reg ddr_restart ;
reg ddr_rst_temp ;
reg [15:0] sys_rstcnt = 16'h0000;
reg sys_rst_temp ;
//==================================================================================
//------------------------------- global code design -------------------------------
//==================================================================================
//----------------------------------- code design ----------------------------------
//pll_sys
always@(posedge clk)
begin
if(pll_sys_restart)
pll_sys_lockcnt <= 16'd0 ;
else if(pll_sys_lockcnt < 60000)
pll_sys_lockcnt <= pll_sys_lockcnt + 1 ;
end
always@(posedge clk)
begin
if(pll_sys_lockcnt < 50000) //1ms
pll_sys_restart <= 0 ;
else if(~pll_sys_lock)
pll_sys_restart <= 1 ;
else
pll_sys_restart <= 0 ;
end
always@(posedge clk)
begin
if(pll_sys_lockcnt < 8) //160ns > min10ns
pll_sys_rst_temp <= 1 ;
else
pll_sys_rst_temp <= 0 ;
end
assign pll_sys_rst = pll_sys_rst_temp ;
//pll_led
always@(posedge clk)
begin
if(pll_led_restart)
pll_led_lockcnt <= 16'd0 ;
else if(pll_led_lockcnt < 60000)
pll_led_lockcnt <= pll_led_lockcnt + 1 ;
end
always@(posedge clk)
begin
if(pll_led_lockcnt < 50000) //1ms
pll_led_restart <= 0 ;
else if(~pll_led_lock)
pll_led_restart <= 1 ;
else
pll_led_restart <= 0 ;
end
always@(posedge clk)
begin
if(pll_led_lockcnt < 8) //160ns > min10ns
pll_led_rst_temp <= 1 ;
else
pll_led_rst_temp <= 0 ;
end
assign pll_led_rst = pll_led_rst_temp ;
//ddr
always@(posedge clk)
begin
ddr_all_lock <= ddr_init_done & pll_lock & phy_pll_lock & gpll_lock & rst_gpll_lock & ddrphy_cpd_lock ;
end
always@(posedge clk)
begin
if(ddr_restart)
ddr_lockcnt <= 16'd0 ;
else if(ddr_lockcnt < 600000)
ddr_lockcnt <= ddr_lockcnt + 1 ;
end
always@(posedge clk)
begin
if(ddr_lockcnt < 500000) //10ms
ddr_restart <= 0 ;
else if(~ddr_all_lock)
ddr_restart <= 1 ;
else
ddr_restart <= 0 ;
end
always@(posedge clk)
begin
if(ddr_lockcnt < 25000) //500us
ddr_rst_temp <= 0 ;
else
ddr_rst_temp <= 1 ;
end
assign ddr_core_rstn = ddr_rst_temp ;
//sys_rst
always@(posedge clk)
begin
if((~ddr_all_lock)||(~pll_sys_lock)||(~pll_led_lock))
sys_rstcnt <= 16'd0 ;
else if(sys_rstcnt < 65500)
sys_rstcnt <= sys_rstcnt + 1 ;
end
always@(posedge clk)
begin
if(sys_rstcnt < 100)
sys_rst_temp <= 1'b0 ;
else if(sys_rstcnt < 200)
sys_rst_temp <= 1'b1 ;
else
sys_rst_temp <= 1'b0 ;
end
assign sys_rst = sys_rst_temp ;
//==================================================================================
endmodule
下面展示一些 内联代码片
。
ddr3 复位代码
`timescale 1ns / 1ps
module ddr3_rst_ctrl
(
input wire clk ,
input wire core_clk ,
input wire rst_in ,
output wire rst_o ,
output wire rstn_o ,
output wire core_rst_o ,
output wire reset_o ,
output wire core_rstn_o
);
//----------------------------------- parameter ------------------------------------
//==================================================================================
//---------------------------------- reg / wire ------------------------------------
wire rstn_in;
assign rstn_in = ~rst_in;
reg [7:0] rst_cnt;
reg rst;
reg [7:0] core_rst_cnt;
reg core_rst;
//==================================================================================
//------------------------------- global code design -------------------------------
//==================================================================================
//----------------------------------- code design ----------------------------------
always@(posedge clk or negedge rstn_in)
begin
if(~rstn_in)
rst_cnt <= 8'd0 ;
else if(rst_cnt<255)
rst_cnt <= rst_cnt + 1 ;
end
always@(posedge clk)
begin
if(rst_cnt<200)
rst <= 1'b1 ;
else
rst <= 1'b0 ;
end
assign rst_o = rst;
assign rstn_o = ~rst;
//always@(posedge core_clk or negedge rstn_in)
//begin
// if(~rstn_in)
// core_rst_cnt <= 8'd0 ;
// else if(core_rst_cnt<255)
// core_rst_cnt <= core_rst_cnt + 1 ;
//end
//
//always@(posedge core_clk)
//begin
// if(core_rst_cnt<200)
// core_rst <= 1'b1 ;
// else
// core_rst <= 1'b0 ;
//end
assign core_rst_o = rst;
assign core_rstn_o = ~rst;
assign reset_o = ~rst;
//==================================================================================
endmodule
下面展示一些 内联代码片
。
ddr3控制模块代码
module rw_ctrl_128bit
(
input wire clk , //时钟
input wire rst_n , //复位
input wire ddr_init_done , //DDR初始化完成
output wire [25:0] axi_awaddr , //写地址
output wire [3:0] axi_awlen , //写突发长度
input wire axi_awready , //写地址准备信号
output wire axi_awvalid , //写地址有效信号
output wire [15:0] axi_wstrb , //写选通
output wire axi_wvalid , //写数据有效信号
input wire axi_wready , //写数据准备信号
//output reg axi_wlast , //最后一次写信号
output wire wrfifo_en_ctrl , //写FIFO数据读使能控制位
output wire [25:0] axi_araddr , //读地址
output wire [3:0] axi_arlen , //读突发长度
input wire axi_arready , //读地址准备信号
output wire axi_arvalid , //读地址有效信号
input wire axi_rlast , //最后一次读信号
input wire axi_rvalid , //读数据有效信号
input wire [10:0] wfifo_rcount /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //写端口FIFO中的数据量
input wire [10:0] rfifo_wcount /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //读端口FIFO中的数据量
input wire [27:0] app_addr_rd_min , //读DDR3的起始地址
input wire [27:0] app_addr_rd_max , //读DDR3的结束地址
input wire [4:0] rd_bust_len /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //从DDR3中读数据时的突发长度
input wire [27:0] app_addr_wr_min /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //写DDR3的起始地址
input wire [27:0] app_addr_wr_max /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //写DDR3的结束地址
input wire [4:0] wr_bust_len /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/
);
//localparam define
localparam IDLE = 4'b0001 , //空闲状态
DDR3_DONE = 4'b0010 , //DDR3初始化完成状态
WRITE_ADDR = 4'b0011 , //写地址
WRITE_DATA = 4'b0100 , //写数据
READ_ADDR = 4'b0101 , //读地址
READ_DATA = 4'b0110 ; //读数据
//reg define
reg init_start /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ; //初始化完成信号
reg [31:0] init_addr ; //突发长度计数器
reg [27:0] axi_araddr_n ; //读地址计数
reg [27:0] axi_awaddr_n /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/; //写地址计数
reg [3:0] state_cnt /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ; //状态计数器
reg [3:0] next_state_cnt /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ; //状态计数器
reg [9:0] lenth_cnt ; //突发写次数计数器
reg [4:0] axi_awlen_temp /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ;
reg axi_awvalid_temp ;
reg axi_wvalid_temp ;
reg wrfifo_en_ctrl_temp ;
reg [4:0] axi_arlen_temp /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ;
reg axi_arvalid_temp ;
//wire define
wire [9:0] lenth_cnt_max; //最大突发次数
//*****************************************************
//** main code
//*****************************************************
assign axi_wstrb = {16{1'b1}};
//计算最大突发次数
assign lenth_cnt_max = app_addr_wr_max / (wr_bust_len * 4'd8);
//读写地址,16bit对应一个地址转换为一个字节对应一个地址
assign axi_araddr = {1'b0,axi_araddr_n[24:0]};
assign axi_awaddr = {1'b0,axi_awaddr_n[24:0]};
assign axi_awlen = axi_awlen_temp[3:0];
assign axi_awvalid = axi_awvalid_temp;
assign axi_wvalid = axi_wvalid_temp;
assign wrfifo_en_ctrl = wrfifo_en_ctrl_temp;
assign axi_arlen = axi_arlen_temp[3:0];
assign axi_arvalid = axi_arvalid_temp;
//稳定ddr3初始化信号
always @(posedge clk ) begin
if (rst_n == 1'd0)
init_start <= 1'b0;
else if (ddr_init_done)
init_start <= ddr_init_done;
else
init_start <= init_start;
end
//写地址模块
always @(posedge clk ) begin
if (rst_n == 1'd0) begin
axi_awaddr_n <= app_addr_wr_min;
axi_awlen_temp <= 5'b0;
axi_awvalid_temp <= 1'b0;
end
//DDR3初始化完成
else if (init_start) begin
axi_awlen_temp <= wr_bust_len - 1'b1;
//当写地址计数小于最后一次写地址起始位时
if (axi_awaddr_n < app_addr_wr_max - wr_bust_len * 5'd8) begin
//写地址有效信号和写地址准备信号都为1时
if (axi_awvalid_temp && axi_awready) begin
axi_awvalid_temp <= 1'b0; //拉低写地址有效信号
//写地址计数加一个突发长度所需的地址
axi_awaddr_n <= axi_awaddr_n + wr_bust_len * 5'd8;//wr_bust_len*128/16
end
//状态机处于写地址状态且写地址准备信号为1时
else if (state_cnt == WRITE_ADDR )
axi_awvalid_temp <= 1'b1; //拉高写地址有效信号
end
//当写地址计数等于最后一次写地址起始位时
else if (axi_awaddr_n == app_addr_wr_max - wr_bust_len * 5'd8) begin
if (axi_awvalid_temp && axi_awready) begin
axi_awvalid_temp <= 1'b0;
axi_awaddr_n <= app_addr_wr_min; //写地址计数清零(回到写起始地址)
end
else if (state_cnt == WRITE_ADDR)
axi_awvalid_temp <= 1'b1;
end
else
axi_awvalid_temp <= 1'b0;
end
else begin
axi_awaddr_n <= axi_awaddr_n;
axi_awlen_temp <= 5'b0;
axi_awvalid_temp <= 1'b0;
end
end
//写数据模块
always @(posedge clk ) begin
if (rst_n == 1'd0) begin
axi_wvalid_temp <= 1'b0 ;
//axi_wlast <= 1'b0 ;
init_addr <= 32'd0 ;
lenth_cnt <= 8'd0 ;
wrfifo_en_ctrl_temp <= 1'b0;
end
else begin
//DDR3初始化完成
if (init_start) begin
//当突发写次数计数器小于最大突发次数时
if (lenth_cnt < lenth_cnt_max) begin
if (axi_wvalid_temp && axi_wready && init_addr < wr_bust_len - 2'd2) begin
init_addr <= init_addr + 1'b1;
wrfifo_en_ctrl_temp <= 1'b0;
end
//因为写DDR时已经提前让FIFO准备好第一个数据,所以使能在写结尾要减少一个使能周期
else if (axi_wvalid_temp && axi_wready && init_addr == wr_bust_len - 2'd2) begin
//axi_wlast <= 1'b1;
wrfifo_en_ctrl_temp <= 1'b1; //提前一个时钟周期拉高
init_addr <= init_addr + 1'b1;
end
//当突发长度计数器等于一次突发长度时
else if (axi_wvalid_temp && axi_wready && init_addr == wr_bust_len - 2'd1) begin
axi_wvalid_temp <= 1'b0;
//axi_wlast <= 1'b0;
wrfifo_en_ctrl_temp <= 1'b0;
lenth_cnt <= lenth_cnt + 1'b1; //突发写次数计数器加1
init_addr <= 32'd0;
end
else if (state_cnt == WRITE_DATA )
axi_wvalid_temp <= 1'b1;
else
lenth_cnt <= lenth_cnt;
end
else begin
axi_wvalid_temp <= 1'b0 ;
//axi_wlast <= 1'b0 ;
init_addr <= init_addr;
lenth_cnt <= 8'd0 ;
end
end
else begin
axi_wvalid_temp <= 1'b0 ;
// axi_wlast <= 1'b0 ;
init_addr <= 32'd0;
lenth_cnt <= 8'd0 ;
end
end
end
//读地址模块
always @(posedge clk ) begin
if (rst_n == 1'd0) begin
axi_araddr_n <= app_addr_rd_min;
axi_arlen_temp <= 5'b0;
axi_arvalid_temp <= 1'b0;
end
//DDR3初始化完成
else if(init_start) begin
axi_arlen_temp <= rd_bust_len - 1'b1;
//当读地址计数小于最后一次读地址起始位时
if (axi_araddr_n < app_addr_rd_max - rd_bust_len * 5'd8) begin
if (axi_arready && axi_arvalid_temp) begin
axi_arvalid_temp <= 1'b0;
axi_araddr_n <= axi_araddr_n + rd_bust_len * 5'd8;
end
else if(state_cnt == READ_ADDR)
axi_arvalid_temp <= 1'b1;
end
//当读地址计数等于最后一次读地址起始位时
else if (axi_araddr_n == app_addr_rd_max - rd_bust_len * 5'd8) begin
if (axi_arready && axi_arvalid_temp) begin
axi_arvalid_temp <= 1'b0;
axi_araddr_n <= app_addr_rd_min;
end
else if(state_cnt==READ_ADDR)
axi_arvalid_temp <= 1'b1;
end
else
axi_arvalid_temp <= 1'b0;
end
else begin
axi_araddr_n <= app_addr_rd_min;
axi_arlen_temp <= 5'b0;
axi_arvalid_temp <= 1'b0;
end
end
//DDR3读写逻辑实现模块
always@(posedge clk)
begin
if(rst_n == 1'd0)
state_cnt <= IDLE;
else
state_cnt <= next_state_cnt;
end
always @(*) begin
case(state_cnt)
IDLE:begin
if(init_start)
next_state_cnt <= DDR3_DONE ;
else
next_state_cnt <= IDLE;
end
DDR3_DONE:begin
if(wfifo_rcount >= wr_bust_len)
next_state_cnt <= WRITE_ADDR; //跳到写地址操作
else if(rfifo_wcount < rd_bust_len)
next_state_cnt <= READ_ADDR; //跳到读地址操作
else
next_state_cnt <= DDR3_DONE;
end
WRITE_ADDR:begin
if(axi_awvalid_temp && axi_awready)
next_state_cnt <= WRITE_DATA; //跳到写数据操作
else
next_state_cnt <= WRITE_ADDR; //条件不满足,保持当前值
end
WRITE_DATA:begin
if(axi_wvalid_temp && axi_wready && init_addr == wr_bust_len - 1)
next_state_cnt <= DDR3_DONE; //写到设定的长度跳到等待状态
else
next_state_cnt <= WRITE_DATA; //写条件不满足,保持当前值
end
READ_ADDR:begin
if(axi_arvalid_temp && axi_arready)
next_state_cnt <= READ_DATA; //跳到读数据操作
else
next_state_cnt <= READ_ADDR; //条件不满足,保持当前值
end
READ_DATA:begin
if(axi_rlast) //读到设定的地址长度
next_state_cnt <= DDR3_DONE; //则跳到空闲状态
else
next_state_cnt <= READ_DATA; //否则保持当前值
end
default:begin
next_state_cnt <= IDLE;
end
endcase
end
endmodule
下面展示一些 内联代码片
。
ddr3 fifo 控制模块
module ddr3_fifo_ctrl(
input rst_n , //复位信号
input wr_clk , //wfifo时钟
input rd_clk /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //rfifo时钟
input clk_100 , //用户时钟
input datain_valid , //数据有效使能信号
input [15:0] datain , //有效数据
input [127:0] rfifo_din , //用户读数据
input rdata_req , //请求像素点颜色数据输入
input rfifo_wren , //从ddr3读出数据的有效使能
input wfifo_rden , //wfifo读使能
output [127:0] wfifo_dout , //用户写数据
output [10:0] wfifo_rcount , //rfifo剩余数据计数
output [10:0] rfifo_wcount , //wfifo写进数据计数
output [15:0] pic_data //有效数据
);
rd_fifo u_rd_fifo (
.wr_clk (clk_100 ), // input
.wr_rst (rst_n ), // input
.wr_en (rfifo_wren ), // input
.wr_data (rfifo_din ), // input [127:0]
.wr_full ( ), // output
.wr_water_level (rfifo_wcount), // output
.almost_full ( ), // output
.rd_clk (rd_clk ), // input
.rd_rst (rst_n ), // input
.rd_en (rdata_req ),
.rd_data (pic_data ), // output [15:0]
.rd_empty ( ), // output
.rd_water_level ( ), // output
.almost_empty ( ) // output
);
wr_fifo u_wr_fifo (
.wr_clk (wr_clk ), // input
.wr_rst (rst_n ), // input
.wr_en (datain_valid),
.wr_data (datain ), //input [15:0]
.wr_full ( ), // output
.wr_water_level ( ), // output
.almost_full ( ), // output
.rd_clk (clk_100 ), // input
.rd_rst (rst_n ), // input
.rd_en (wfifo_rden ), // input
.rd_data (wfifo_dout ), // output [127:0]
.rd_empty ( ), // output
.rd_water_level (wfifo_rcount), // output
.almost_empty ( ) // output
);
endmodule
下面展示一些 内联代码片
。
ddr3自加数测试模块
module ddr_test(
input clk_50m /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //时钟
input rst_n , //复位,低有效
output reg wr_en /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //写使能
output reg [15:0] wr_data , //写数据
output reg rd_en /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //读使能
input [15:0] rd_data /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //读数据
input [27:0] data_max /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ , //写入ddr的最大数据量
input ddr3_init_done, //ddr3初始化完成信号
output reg error_flag //ddr3读写错误
);
//reg define
reg init_done_d0;
reg init_done_d1;
reg [27:0] wr_cnt /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ; //写操作计数器
reg [27:0] rd_cnt /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ; //读操作计数器
reg rd_valid /* synthesis syn_preserve=1 */ /*synthesis PAP_MARK_DEBUG = "true"*/ ; //读数据有效标志
reg [27:0] rd_cnt_d0 ;
//*****************************************************
//** main code
//*****************************************************
//同步ddr3初始化完成信号
always @(posedge clk_50m ) begin
if(rst_n == 1'd0) begin
init_done_d0 <= 1'b0 ;
init_done_d1 <= 1'b0 ;
end
else begin
init_done_d0 <= ddr3_init_done;
init_done_d1 <= init_done_d0;
end
end
//对读计数器做一拍延时使数据对齐
always @(posedge clk_50m ) begin
if(rst_n == 1'd0)
rd_cnt_d0 <= 28'd0;
else
rd_cnt_d0 <= rd_cnt;
end
//ddr3初始化完成之后,写操作计数器开始计数
always @(posedge clk_50m ) begin
if(rst_n == 1'd0)
wr_cnt <= 28'd0;
else if(init_done_d1 && (wr_cnt < data_max ))
wr_cnt <= wr_cnt + 1'b1;
else
wr_cnt <= wr_cnt;
//wr_cnt <= 28'd0;
end
//ddr3写端口FIFO的写使能、写数据
always @(posedge clk_50m ) begin
if(rst_n == 1'd0) begin
wr_en <= 1'b0;
wr_data <= 16'd0;
end
else if(wr_cnt >= 11'd0 && (wr_cnt < data_max )&&init_done_d1) begin
wr_en <= 1'b1; //写使能拉高
wr_data <= wr_cnt[15:0]; //写入数据
end
else begin
wr_en <= 1'b0;
wr_data <= 16'd0;
end
end
//写入数据完成后,开始读操作
always @(posedge clk_50m ) begin
if(rst_n == 1'd0)
rd_en <= 1'b0;
else if(wr_cnt >= data_max ) //写数据完成
rd_en <= 1'b1; //读使能
else
rd_en <= rd_en;
end
//对读操作计数
always @(posedge clk_50m ) begin
if(rst_n == 1'd0)
rd_cnt <= 28'd0;
else if(rd_en) begin
if(rd_cnt < data_max - 1'd1)
rd_cnt <= rd_cnt + 1'd1;
else
rd_cnt <= 28'd0;
end
end
//第一次读取的数据无效,后续读操作所读取的数据才有效
always @(posedge clk_50m ) begin
if(rst_n == 1'd0)
rd_valid <= 1'b0;
else if(rd_cnt >= data_max - 1'd1 ) //等待第一次读操作结束
rd_valid <= 1'b1; //后续读取的数据有效
else
rd_valid <= rd_valid;
end
//读数据有效时,若读取数据错误,给出标志信号
always @(posedge clk_50m ) begin
if(rst_n == 1'd0)
error_flag <= 1'b0;
else if(wr_en)
error_flag <= 1'b0;
else if(rd_valid && ((rd_data[15:0] != rd_cnt_d0[15:0])) )
error_flag <= 1'b1; //若读取的数据错误,将错误标志位拉高
else
error_flag <= error_flag;
end
endmodule