当前位置:   article > 正文

XILINX关于DDR2的IP的读写控制仿真_vivado ddr2读写仿真

vivado ddr2读写仿真

最近没事。看了一下ddr2的读写访问。

平台:ISE

芯片:V4,XC4VLX25,ff668,-10

语言:VerilogHDL

参考文件:ug086.ug086.pdf • 查看器 • 文档门户 (xilinx.com)

关于DDR2的MIG核的新建

新建IP:参见UG086的using mig。输入频率200mhz,突发长度BL=4。

新建MIG IP核,此页面为IP的版本信息与FPGA器件。右下角可以下载IP的说明书。

MIG IP有几种不同的输出选项,这里我们选择create design。对于多控制器的应用,可以下下面munber of controllers中选择,这里我们使用一个。

这里可以选择PIN兼容的FPGA,这里只需要支持我们的FPGA就行了。不用选择。

内存选择界面。界面显示了FPGA支持的内存,可以看到支持ddr2和ddr。我们这里选择为ddr2。

capture method捕获方式,一般对于高频采样serdes时钟,而我们这里使用direct clocking时钟。frequency选择控制器输入的时钟。我们外部晶振提供了200MHZ时钟,这里输入200mhz。

Memory Type,Memory Part选择内存类型,和内存型号,我们这里选择的内存型号为MT47H256M8XX-3,也就是默认随便选择的。容量大小256Mbit*8=2Gbit=256MB。

create custom part创建自定义部件这里默认就好了。

data width选择为16位。后面部分保持默认就好。

data mask关于数据掩码,ddr2的访问都是以突发的形式连续访问同一行的相邻几个单元,数据掩码屏蔽了不需要的数据。

内存选择,此页面需要关注的是burst length突发长度。ddr2支持4和8的突发长度。这里可以修改。对于这两种突发模式,在对ddr2进行读写操作的时候地址和数据对应关系,对于突发长度为4,每个地址对应于数据fifo的两次写入,对于突发长度为8,每个地址对应于数据fifo的四次写入。其他参数默认即可。

FPGA选择,这里默认选择为使用DCM,即时钟需要经过DCM后输入到DDR2。系统时钟选择为单端,根据实际选择。

选择UCF引脚。这里我们只做仿真,不设置,如果有UCF引脚可以直接点击Read UCF File。

保持默认分配的引脚。

总结说明。之后一路next,生成IP。

关于DDR2的MIG核的仿真

打开仿真工程,添加文件如下。

E:\code1\FPGAzero_bace\Virtex4\DDR2_TEST\ddr2_test\ipcore_dir\ddr2\user_design\sim文件夹下的全部文件。文件添加成功后效果如下。

添加后效果如下,官方的例程结构是一个很好的参考工程。可以方便大家理解DDR2内部信号的传输。

可以看到成功拉高init_done初始化成功信号。通过ug086可以知道,test_bench_00模块交替执行了8个写入命令和8个读出命令。我们选择的突发长度为4,所以执行写入了32个数据字(16个上升沿数据字和16个下降沿数据字)。而对于突发长度为8的设计,执行8个写入命令将执行写入64个数据字。

在test_bench模块中,addr_gen和data_gen产生地址和数据,ddr2_cmp_rd_data模块验证收到的数据是否正确。

感兴趣的同学可以研究一下他们的测试代码。

下面我们使用自己的代码来代替官方的这部分仿真代码实现自由读写ddr2。

下面分析一下sim_tb_top,该文件是对ddr2的仿真,top端设置了时钟的产生。

  1. //***************************************************************************
  2. // Clock generation and reset
  3. //***************************************************************************
  4. initial
  5. sys_clk = 1'b0;
  6. always
  7. sys_clk = #(CLK_PERIOD_NS/2) ~sys_clk;
  8. //设置系统时钟CLK_PERIOD_NS= 5000.0 / 1000.0;时间刻度是ns,即系统时钟5ns翻
  9. //转一次,sys_clk=100MHZ.
  10. //差分时钟sys_clk_p/n也是一致,其中n相差180°
  11. assign sys_clk_p = sys_clk;
  12. assign sys_clk_n = ~sys_clk;
  13. initial
  14. sys_clk200 = 1'b0;
  15. always
  16. sys_clk200 = #(TCYC_200/2) ~sys_clk200;
  17. //TCYC_200=5,sys_clk200为200mhz时钟。
  18. assign clk200_p = sys_clk200;
  19. assign clk200_n = ~sys_clk200;
  20. //pci 时钟
  21. initial
  22. pci_clk = 1'b0;
  23. always
  24. pci_clk = # 15 ~pci_clk;
  25. assign pci_rst = ~sys_rst_n;
  26. initial begin
  27. sys_rst_n = 1'b0;
  28. #200;
  29. sys_rst_n = 1'b1;
  30. end
  31. //测试复位200ns后拉高
  32. assign sys_rst_out = `RESET_ACTIVE_LOW ? sys_rst_n : ~sys_rst_n;
  33. //输出到DDR的复位高有效

模拟产生ddr的控制信号。

  1. // =============================================================================
  2. // BOARD Parameters
  3. // 这部分模拟产生ddr的控制信号
  4. // =============================================================================
  5. // These parameter values can be changed to model varying board delays
  6. // between the Virtex-4 device and the memory model
  7. always @( * ) begin
  8. ddr2_clk_sdram <= #(TPROP_PCB_CTRL) ddr2_clk_fpga;
  9. ddr2_clk_n_sdram <= #(TPROP_PCB_CTRL) ddr2_clk_n_fpga;
  10. ddr2_address_sdram <= #(TPROP_PCB_CTRL) ddr2_address_fpga;
  11. ddr2_ba_sdram <= #(TPROP_PCB_CTRL) ddr2_ba_fpga;
  12. ddr2_ras_n_sdram <= #(TPROP_PCB_CTRL) ddr2_ras_n_fpga;
  13. ddr2_cas_n_sdram <= #(TPROP_PCB_CTRL) ddr2_cas_n_fpga;
  14. ddr2_we_n_sdram <= #(TPROP_PCB_CTRL) ddr2_we_n_fpga;
  15. ddr2_cs_n_sdram <= #(TPROP_PCB_CTRL) ddr2_cs_n_fpga;
  16. ddr2_cke_sdram <= #(TPROP_PCB_CTRL) ddr2_cke_fpga;
  17. ddr2_odt_sdram <= #(TPROP_PCB_CTRL) ddr2_odt_fpga;
  18. ddr2_dm_sdram_tmp <= #(TPROP_PCB_DATA) ddr2_dm_fpga;//DM signal generation
  19. end
  20. assign ddr2_dm_sdram = ddr2_dm_sdram_tmp;
  21. genvar dqwd;
  22. generate
  23. for (dqwd = 0;dqwd < `DATA_WIDTH;dqwd = dqwd+1) begin : dq_delay
  24. WireDelay #
  25. (
  26. .Delay_g (TPROP_PCB_DATA),
  27. .Delay_rd (TPROP_PCB_DATA_RD)
  28. )
  29. u_delay_dq
  30. (
  31. .A (ddr2_dq_fpga[dqwd]),
  32. .B (ddr2_dq_sdram[dqwd]),
  33. .reset (sys_rst_n)
  34. );
  35. end
  36. endgenerate
  37. genvar dqswd;
  38. generate
  39. for (dqswd = 0;dqswd < `DATA_STROBE_WIDTH;dqswd = dqswd+1) begin : dqs_delay
  40. WireDelay #
  41. (
  42. .Delay_g (TPROP_DQS),
  43. .Delay_rd (TPROP_DQS_RD)
  44. )
  45. u_delay_dqs
  46. (
  47. .A (ddr2_dqs_fpga[dqswd]),
  48. .B (ddr2_dqs_sdram[dqswd]),
  49. .reset (sys_rst_n)
  50. );
  51. WireDelay #
  52. (
  53. .Delay_g (TPROP_DQS),
  54. .Delay_rd (TPROP_DQS_RD)
  55. )
  56. u_delay_dqs_n
  57. (
  58. .A (ddr2_dqs_n_fpga[dqswd]),
  59. .B (ddr2_dqs_n_sdram[dqswd]),
  60. .reset (sys_rst_n)
  61. );
  62. end
  63. endgenerate

例化了ddr2的mig IP核,这部分有对外部ddr2芯片控制的接口,与内部用户侧接口。其中用户侧接口是我们重要关注的地方。

  1. //***************************************************************************
  2. // FPGA memory controller
  3. // 例化DDRIP核。
  4. //***************************************************************************
  5. ddr2_top u_mem_controller
  6. (
  7. .sys_clk (sys_clk_p),
  8. .idly_clk_200 (clk200_p),
  9. .sys_reset_in_n (sys_rst_out),
  10. .cntrl0_ddr2_ras_n (ddr2_ras_n_fpga),
  11. .cntrl0_ddr2_cas_n (ddr2_cas_n_fpga),
  12. .cntrl0_ddr2_we_n (ddr2_we_n_fpga),
  13. .cntrl0_ddr2_cs_n (ddr2_cs_n_fpga),
  14. .cntrl0_ddr2_cke (ddr2_cke_fpga),
  15. .cntrl0_ddr2_odt (ddr2_odt_fpga),
  16. .cntrl0_ddr2_dm (ddr2_dm_fpga),
  17. .cntrl0_ddr2_dq (ddr2_dq_fpga),
  18. .cntrl0_ddr2_dqs (ddr2_dqs_fpga),
  19. .cntrl0_ddr2_dqs_n (ddr2_dqs_n_fpga),
  20. .cntrl0_ddr2_ck (ddr2_clk_fpga),
  21. .cntrl0_ddr2_ck_n (ddr2_clk_n_fpga),
  22. .cntrl0_ddr2_ba (ddr2_ba_fpga),
  23. .cntrl0_ddr2_a (ddr2_address_fpga),
  24. //用户侧信号
  25. .cntrl0_clk_tb (clk0_tb),//用户侧输出时钟
  26. .cntrl0_reset_tb (rst0_tb),//用户复位信号高有效
  27. .cntrl0_wdf_almost_full (wdf_almost_full),
  28. .cntrl0_af_almost_full (af_almost_full),
  29. .cntrl0_burst_length_div2 (burst_length_div2),//突发长度
  30. .cntrl0_read_data_valid (read_data_valid),//读取读FIFO的数据有效
  31. .cntrl0_read_data_fifo_out (read_data_fifo_out),//ddr读取的数据存在读fifo中
  32. .cntrl0_app_af_addr (app_af_addr),
  33. .cntrl0_app_af_wren (app_af_wren),
  34. .cntrl0_app_wdf_data (app_wdf_data),
  35. .cntrl0_app_wdf_wren (app_wdf_wren),
  36. .cntrl0_app_mask_data (app_mask_data),
  37. .cntrl0_init_done (init_done)
  38. );

此部分模拟了数据位宽不同的存储器。

  1. //***************************************************************************
  2. // Memory model instances
  3. // 模拟外部模型的实例
  4. // 实现了数据位宽为16,8,4的存储器模拟
  5. //
  6. //***************************************************************************
  7. genvar i, j;
  8. generate
  9. if (DEVICE_WIDTH == 16) begin
  10. // if memory part is x16
  11. if ( REG_ENABLE ) begin
  12. // if the memory part is Registered DIMM
  13. for(j = 0; j < `CS_WIDTH; j = j+1) begin : gen_chips
  14. for(i = 0; i < `DATA_STROBE_WIDTH/2; i = i+1) begin : gen_bytes
  15. ddr2_model u_mem0
  16. (
  17. .ck (ddr2_clk_sdram[`CLK_WIDTH*i/`DATA_STROBE_WIDTH]),
  18. .ck_n (ddr2_clk_n_sdram[`CLK_WIDTH*i/`DATA_STROBE_WIDTH]),
  19. .cke (ddr2_cke_reg[j]),
  20. .cs_n (ddr2_cs_n_reg[j]),
  21. .ras_n (ddr2_ras_n_reg),
  22. .cas_n (ddr2_cas_n_reg),
  23. .we_n (ddr2_we_n_reg),
  24. .dm_rdqs (ddr2_dm_sdram[(2*(i+1))-1 : i*2]),
  25. .ba (ddr2_ba_reg),
  26. .addr (ddr2_address_reg),
  27. .dq (ddr2_dq_sdram[(16*(i+1))-1 : i*16]),
  28. .dqs (ddr2_dqs_sdram[(2*(i+1))-1 : i*2]),
  29. .dqs_n (ddr2_dqs_n_sdram[(2*(i+1))-1 : i*2]),
  30. .rdqs_n (),
  31. .odt (ddr2_odt_reg[j])
  32. );
  33. end
  34. end
  35. end
  36. ....

最后就是用户侧部分,我们自定义了一个模块来对ddr2进行读写操作。

  1. // synthesizable test bench provided for wotb designs
  2. // 这部分代码做了一个ddr数据传输的测试。也就是我们需要看的用户侧接口,我们控制
  3. // 这个用户侧接口实现对ddr的读写
  4. // ddr2_top_test_bench_0 test_bench_00
  5. // (
  6. // .clk (clk0_tb),
  7. // .reset (rst0_tb),
  8. // .wdf_almost_full (wdf_almost_full),
  9. // .af_almost_full (af_almost_full),
  10. // .burst_length_div2 (burst_length_div2),
  11. // .read_data_valid (read_data_valid),
  12. // .read_data_fifo_out (read_data_fifo_out),
  13. // .init_done (init_done),
  14. // .app_af_addr (app_af_addr),
  15. // .app_af_wren (app_af_wren),
  16. // .app_wdf_data (app_wdf_data),
  17. // .app_mask_data (app_mask_data),
  18. // .app_wdf_wren (app_wdf_wren),
  19. // .error (error)
  20. // );
  21. ddr2_top_ctr u_ddr2_top_ctr (
  22. .pci_clk (pci_clk ),
  23. .pci_rst (pci_rst ),
  24. .clk (clk0_tb ),
  25. .reset (rst0_tb ),
  26. .wdf_almost_full (wdf_almost_full ),
  27. .af_almost_full (af_almost_full ),
  28. .burst_length_div2 (burst_length_div2 ),
  29. .read_data_valid (read_data_valid ),
  30. .read_data_fifo_out (read_data_fifo_out ),
  31. .init_done (init_done ),
  32. .app_af_addr (app_af_addr ),
  33. .app_af_wren (app_af_wren ),
  34. .app_wdf_data (app_wdf_data ),
  35. .app_mask_data (app_mask_data ),
  36. .app_wdf_wren (app_wdf_wren ),
  37. .error (error )
  38. );

关于DDR2接口与结构分析

下面结合ug086介绍DDR2 SDRAM系统和用户界面信号

系统接口信号是用户提供给FPGA的时钟和复位信号。

singal name

方向

描述

sys_clk_p, sys_clk_n

Input

差分输入时钟

clk200_p, clk200_n

Input

idelay_ctrl逻辑中使用的差分时钟。

sys_reset_in_n

Input

复位低有效

用户侧信号

singal name

方向

描述

CLK_TB

Output

用户时钟

RESET_TB

Output

用户复位

BURST_LENGTH_DIV2[2:0]

Output

该信号决定了每个写地址的数据突发长度

010突发长度为4

100突发长度为8

WDF_ALMOST_FULL

Output

该信号表示写入数据FIFO的ALMOST_FULL状态

APP_WDF_DATA[2n-1:0]

Input

用户侧写数据

APP_MASK_DATA[2m-1:0]

Input

数据掩码

APP_WDF_WREN

Input

写数据使能

AF_ALMOST_FULL

Output

该信号表示地址FIFO的ALMOST_FULL状态

APP_AF_ADDR[35:0]

Input

用户地址和动态命令组合体。

bit[31:0]为芯片选择,行地址,列地址。

bit[34:32]为命令选择。

001自动刷新

010预充电

100写

101读

APP_AF_WREN

Input

写地址使能

READ_DATA_FIFO_OUT[2n-1:0]

Output

读数据

READ_DATA_VALID

Output

读数据有效

INIT_DONE

INIT_DONE

初始化成功

关于ddr2的整体结构。可以看手册上的描述如下。

关于DDR2读写时序分析

拥有三个通道,写地址fifo通道,写数据fifo通道,读数据fifo通道。

  1. 一个命令地址FIFO总线,他接收来自用户读写命令以及相关的内存地址。

  1. 一个写数据FIFO总线,他接收来自用户写入的数据。

  1. 一个读数据FIFO总线,他保存从内存上读出的数据。

用户操作的注意事项。

  1. 在校准完成之前需要保持以下接口信号为低:app_af_wren,app_wdf_wren,app_wdf_data,app_mask_data未保持将出现校准错误。

  1. 发出写入命令时,第一个写入数据字必须在发出写入命令后不超过两个时钟周期写入写入数据FIFO。

  1. 使用用户侧时钟同步。

用户侧写入接口结构。

对于突发长度为4和突发长度为8的用户侧时序。

用户侧读出侧结构。

用户侧读出突发长度为4和突发长度为8的时序。

需要注意的是,在读命令和读地址发送出去后,需要至少25个时钟周期才会接收到读出的数据。

这里ddr还支持连续读写突发。此部分暂时先不介绍了。

关于DDR2读写控制设计

根据DDR2的结构,最左侧为用户接口侧,也就是我们控制读写的时候需要控制的信号。右边部分是核的部分,了解核内结构可以让我快速控制DDR读写。

首先我们用户侧输入的数据地址,进入FIFO缓存,DDR2的地址是公用的。读写地址都是通过app_af_addr写,写数据和读数据有专门的缓存。

根据文档的介绍,app_af_addr[34:32]三位代表着读或者写DDR2,当我们发送地址到地址FIFO的时候,DDR2_SDRAM_controller读出地址FIFO里面的值,并解析是读取还是写入DDR2。根据文档的要求,写入命令发出成功后,数据不能超过两个时钟周期写入。所以测试代码里面直接生成地址的同时生成数据。

需要注意的是,在写入的时候,根据新建IP核时候生成的突发长度值不同,地址数据的读写也不同。

  1. // 写:特性,当BL=4(burst_length_div2=3'b010)地址每写入一次,数据要写入两次
  2. // 当BL=8(burst_length_div2=3'b100)地址每写入一次,数据要写入四次
  3. // 读:特性,当BL=4(burst_length_div2=3'b010)写一个地址读出两个数据
  4. // 当BL=8(burst_length_div2=3'b100)写一个地址读出四个数据

分析官方给到的接口。

  1. //system clock
  2. input pci_clk ,//pci侧时钟33mhz
  3. input pci_rst ,
  4. //---------------------------------------
  5. //DDR2用户侧信号
  6. //---------------------------------------
  7. //时钟
  8. input wire clk ,//200MHZ
  9. input wire reset ,//ACTIVE HIGH
  10. //ddr内部fifo相关信号
  11. input wire wdf_almost_full ,
  12. input wire af_almost_full ,
  13. input wire[2:0] burst_length_div2 ,//burst_length_div2=3'b010,(BL=4)
  14. input wire read_data_valid ,
  15. input wire[31:0] read_data_fifo_out ,
  16. input wire init_done ,
  17. //输出数据地址信号
  18. output wire[35:0] app_af_addr ,
  19. output wire app_af_wren ,
  20. output wire[31:0] app_wdf_data ,
  21. output wire[3:0] app_mask_data ,
  22. output wire app_wdf_wren ,
  23. output wire error

内部信号接口分别为,fifo的状态信号以及,初始化信号。以及给到了FIFO的写使能和数据,最后设置了掩码和错误信号。

通过控制这些信号已经可以控制DDR2的读写,但是为了我们自己程序更加方便,我们需要把这个接口该的更加通用一点。方便我们更加准确的控制DDR2。

下面我准备这样来定义几个接口。

  1. //write and read request
  2. reg ddr2_write_req ;//写请求
  3. reg ddr2_read_req ;//读请求
  4. reg [31:0] ddr2_write_len ;//突发写数据长度
  5. reg [31:0] ddr2_read_len ;//突发读数据长度
  6. reg [31:0] ddr2_wr_brust_addr ;//突发写首地址
  7. reg [31:0] ddr2_rd_brust_addr ;//突发读首地址
  8. reg [35:0] app_af_addr_r ;//地址信号
  9. reg app_af_wren_r ;//地址使能
  10. reg [31:0] write_data ;//写数据
  11. reg write_data_en ;//写数据使能

我准备来这样实现更加通用的接口,我们知道,DDR2是通过地址线的高位来给到DDR2控制器的读写信息。我们每次在配置地址信息的时候,需要配置这个地址信息是读还是写,非常的麻烦。于是把读写请求提出来。用DDR2传输一个数据的话则白白浪费了性能,一般都是突发传输一段数据,所以定义了,突发传输数据长度和突发读写的地址代表从哪个地方开始读写。同时读写完成需要对上层一个反馈,于是接出了两个完成信号。表示一阶段的任务完成。

设置状态机。

  1. //定义状态机
  2. localparam IDLE = 3'b001;
  3. localparam WRITE = 3'b010;
  4. localparam WRITE_END = 3'b011;
  5. localparam READ = 3'b100;
  6. localparam READ_WIAT = 3'b101;
  7. localparam READ_END = 3'b110;

设置相关的计数器。

其实我这种方式也浪费了DDR2的性能。具体表现在DDR2读取数据的时候,一次突发数据会延迟25个时钟周期出来。这里我采用了比较保守的,牺牲了一部分性能,保证数据传输的完全性。毕竟DDR2传输本就不是很快,再快一点可以直接上DDR3了。后面思考一下如何把这部分性能也利用起来。

  1. //count
  2. reg [2:0] state ;
  3. reg [2:0] brust_count ;//突发个数计数器
  4. reg [31:0] addr_cnt ;//地址计数
  5. reg [31:0] wr_data_cnt ;//写数据计数
  6. reg [31:0] rd_data_cnt ;//读数据计数

对于DDR2的地址,读写数据,上文中提到过,在新建IP的时候有数据突发长度设置,我们设置了BL=4,反应在时序里面就是传递一个地址,伴随着两个数据。所以定义了突发个数计数器。这里我为了区分读写,设置了地址使能,读写数据个数计数,地址个数计数。

赋值DDR2端口信号。

  1. assign app_af_addr = (app_af_wren_r == 1'b1) ? app_af_addr_r : 36'd0;
  2. assign app_af_wren = app_af_wren_r;
  3. assign app_wdf_wren = write_data_en;
  4. assign app_wdf_data = write_data;
  5. assign app_mask_data = {4{1'b0}};
  6. //assign error //暂时不用
  7. assign brust_len = burst_length_div2;//BL、这里还是赋原始值
  8. //完成信号
  9. assign ddr2_write_finish = (state == WRITE_END);
  10. assign ddr2_read_finish = (state == READ_END);

设置状态机。

  1. always@(posedge clk ,posedge reset)
  2. begin
  3. if(reset == 1'b1)
  4. begin
  5. state <= IDLE;
  6. brust_count <= 3'b0;
  7. write_data_en <= 1'b0;
  8. app_af_wren_r <= 1'b0;
  9. addr_cnt <= 32'b0;
  10. wr_data_cnt <= 32'b0;
  11. rd_data_cnt <= 32'b0;
  12. app_af_addr_r <= 36'b0;
  13. write_data <= 32'b0;
  14. end
  15. else if(init_done == 1'b1)
  16. begin
  17. case(state)
  18. IDLE:
  19. begin
  20. if(ddr2_write_req)
  21. begin
  22. state <= WRITE;
  23. app_af_addr_r <= {1'b0,3'b100,32'b0};
  24. brust_count <= brust_len;
  25. end
  26. else if(ddr2_read_req)
  27. begin
  28. state <= READ;
  29. app_af_addr_r <= {1'b0,3'b101,32'b0};
  30. app_af_wren_r <= 1'b1;
  31. end
  32. else
  33. begin
  34. state <= IDLE;
  35. brust_count <= 3'b0;
  36. end
  37. end
  38. WRITE:
  39. begin
  40. if(wdf_almost_full == 1'b0 && af_almost_full == 1'b0)
  41. begin
  42. if(wr_data_cnt == 32'd128 && addr_cnt == 32'd64)//数据到达128个
  43. begin
  44. wr_data_cnt <= 32'b0;
  45. addr_cnt <= 32'b0;
  46. app_af_wren_r <= 1'b0;
  47. write_data_en <= 1'b0;
  48. write_data <= 32'b0;
  49. app_af_addr_r <= 36'b0;
  50. state <= WRITE_END;
  51. end
  52. else//这一段三个计数器来表示,代码问题产生了丢失一个数据线改动,已改正
  53. begin
  54. //对写数据赋值
  55. if( brust_count == 3'b010)
  56. begin
  57. wr_data_cnt <= 32'd0;
  58. write_data_en <= 1'b0;
  59. write_data <= 32'd0;
  60. end
  61. else
  62. begin
  63. wr_data_cnt <= wr_data_cnt + 1'b1;
  64. write_data_en <= 1'b1;
  65. write_data <= write_data + 1'b1;
  66. end
  67. //对写地址赋值
  68. if(brust_count == 3'b001)
  69. begin
  70. addr_cnt <= addr_cnt;
  71. app_af_wren_r <= 1'b1;
  72. end
  73. else if(brust_count == 3'b010)
  74. begin
  75. addr_cnt <= 32'd0;
  76. app_af_wren_r <= 1'b0;
  77. end
  78. else
  79. begin
  80. addr_cnt <= addr_cnt + 1'b1;
  81. app_af_addr_r <= app_af_addr_r + 32'd4;//ddr为256*8,一个地址存储1byte,存储32bit的数据,所以加4
  82. app_af_wren_r <= 1'b0;
  83. end
  84. //这里对brust_count进行赋值
  85. if (brust_count[2:0] != 3'b000)
  86. brust_count[2:0] <= brust_count[2:0] - 1'b1;
  87. else
  88. brust_count[2:0] <= brust_len - 1'b1;
  89. end
  90. end
  91. else
  92. begin
  93. app_af_wren_r <= 1'b0;
  94. write_data_en <= 1'b0;
  95. end
  96. end
  97. WRITE_END:
  98. begin
  99. state <= IDLE;
  100. end
  101. READ:
  102. begin
  103. if( af_almost_full == 1'b0 )
  104. begin
  105. if(addr_cnt == 32'd64)//地址到达64个
  106. begin
  107. addr_cnt <= 32'b0;
  108. app_af_wren_r <= 1'b0;
  109. app_af_addr_r <= 36'b0;
  110. state <= READ_WIAT;
  111. end
  112. else
  113. begin
  114. addr_cnt <= addr_cnt + 1'b1;
  115. app_af_addr_r <= app_af_addr_r + 32'd4;
  116. app_af_wren_r <= 1'b1;
  117. end
  118. end
  119. else
  120. app_af_wren_r <= 1'b0;
  121. if(read_data_valid == 1'b1)
  122. begin
  123. if(rd_data_cnt == 32'd128)
  124. rd_data_cnt <= 32'b0;
  125. else
  126. rd_data_cnt <= rd_data_cnt + 1'b1;
  127. end
  128. else
  129. rd_data_cnt <= rd_data_cnt;
  130. end
  131. READ_WIAT://数据已经出来,但是读数据计数器没有开始工作。
  132. begin
  133. if(read_data_valid == 1'b1)
  134. begin
  135. if(rd_data_cnt == 32'd128)
  136. begin
  137. rd_data_cnt <= 32'b0;
  138. state <= READ_END;
  139. end
  140. else
  141. rd_data_cnt <= rd_data_cnt + 1'b1;
  142. end
  143. else
  144. rd_data_cnt <= rd_data_cnt;
  145. end
  146. READ_END:
  147. state <= IDLE;
  148. default:
  149. state <= IDLE;
  150. endcase
  151. end
  152. end

下面看看仿真。

读数据

分析问题原因,是应为我们使用的地址位计数器brust_count。本意是在brust_count第一次等于1的时候,地址从零开始。后面累加。

方法:仔细分析brust_count信号。利用该信号在过程中的变化来改正。

优化代码....

写状态,在计数器数值达到要求复位计数信号。其次通过控制brust_count来实现对数据地址的精确控制。为了解决上述问题,将数值和地址分开赋值,地址阶段在brust_count=1时置高使能,其他清零。并在brust_count=0的时候累加地址。

在读的时候,加了个在进入读状态的时候就开始拉高地址使能,这时候保证读的第一地址是0。

再仿真

写正常

读:

读数据:

接下来,将这个模块改为一个更加通用的接口。

根据之前的设计经验,ddr2我们可以给他来两个FIFO来缓存数据,即在数据写入ddr之前,先写入写入FIFO缓存,等待DDR工作完成。读取FIFO里面的数据。

同理在读出数据的时候,读出来的数据先进读取FIFO缓存,方便后续的对数据使用。之所以用FIFO缓存是因为FIFO是一个基本的逻辑单元,处理跨时钟域数据非常方便。对于低速的数据我们这样做,对于速率高的情况下可以考虑采用DMA。

建立测试代码块。

  1. // *********************************************************************************/
  2. // Project Name :
  3. // Author : i_huyi
  4. // Email : i_huyi@qq.com
  5. // Creat Time : 2021/6/16 9:46:21
  6. // File Name : .v
  7. // Module Name :
  8. // Called By :
  9. // Abstract : 此模块的作用是构建一个双缓存的结构,来实现DDR2数据传输的高速
  10. // 控制。
  11. // 方案的设计为,外部(pci)侧写入的数据先缓存在写入FIFO中,当写入的数据值达到
  12. // 阈值,启动ddr将数据传输到外部ddr存储,等待从ddr里面读出数据。
  13. // 从外部ddr里面读出的数据要经过PCI传输给上位机,先在读出数据fifo中缓存。等待
  14. // PCI读出。
  15. // 因为ddr和PCI侧是跨时钟处理,使用fifo比较方便。
  16. // 第一步,先实现小规模,低速率传输。
  17. // 第二步,学习pci的突发传输,将读数据FIFO内部的数据通过突发传输给pci总线。
  18. //
  19. // CopyRight(c) 2020, xxx xxx xxx Co., Ltd..
  20. // All Rights Reserved
  21. //
  22. // *********************************************************************************/
  23. // Modification History:
  24. // 1. initial
  25. // *********************************************************************************/
  26. // *************************
  27. // MODULE DEFINITION
  28. // *************************
  29. `timescale 1 ns / 1 ps
  30. module ddr2_top_ctr#(
  31. parameter U_DLY = 1
  32. )
  33. (
  34. //system clock
  35. input pci_clk ,//pci侧时钟33mhz
  36. input pci_rst ,
  37. //---------------------------------------
  38. //DDR2用户侧信号
  39. //---------------------------------------
  40. //时钟
  41. input wire clk ,//200MHZ
  42. input wire reset ,//ACTIVE HIGH
  43. //ddr内部fifo相关信号
  44. input wire wdf_almost_full ,
  45. input wire af_almost_full ,
  46. input wire[2:0] burst_length_div2 ,//burst_length_div2=3'b010,(BL=4)
  47. input wire read_data_valid ,
  48. input wire[31:0] read_data_fifo_out ,
  49. input wire init_done ,
  50. //输出数据地址信号
  51. output wire[35:0] app_af_addr ,
  52. output wire app_af_wren ,
  53. output wire[31:0] app_wdf_data ,
  54. output wire[3:0] app_mask_data ,
  55. output wire app_wdf_wren ,
  56. output wire error
  57. );
  58. //--------------------------------------
  59. // localparam
  60. //--------------------------------------
  61. //--------------------------------------
  62. // register
  63. //--------------------------------------
  64. //设置写入数据寄存器
  65. reg [31:0] wr_fifo_wr_data ;
  66. reg wr_fifo_wr_data_en ;
  67. wire wr_fifo_almost_full ;//写fifo的满信号
  68. //读FIFO的读取数据
  69. reg rd_fifo_rd_en ;
  70. wire [31:0] rd_fifo_rd_data ;
  71. wire rd_fifo_rd_data_vaild;
  72. wire rd_fifo_almost_full ;
  73. wire rd_fifo_almost_empty;
  74. //--------------------------------------
  75. // wire
  76. //--------------------------------------
  77. //自定义ddr2读写控制器的接口
  78. reg ddr2_write_req ;
  79. reg ddr2_read_req ;
  80. wire [31:0] ddr2_write_len ;
  81. wire [31:0] ddr2_read_len ;
  82. wire [31:0] ddr2_wr_burst_addr ;
  83. wire [31:0] ddr2_rd_burst_addr ;
  84. wire [31:0] wr_burst_data ;
  85. wire wr_burst_data_vaild ;
  86. wire [31:0] rd_burst_data ;
  87. wire rd_burst_data_vaild ;
  88. wire wr_burst_data_req ;
  89. wire ddr2_write_finish ;
  90. wire ddr2_read_finish ;
  91. ddr2用户侧信号
  92. //wire clk ;//200MHZ
  93. //wire reset ;//ACTIVE HIGH
  94. //wire wdf_almost_full ;
  95. //wire af_almost_full ;
  96. //wire [2:0] burst_length_div2 ;//burst_length_div2=3'b010;(BL=4)
  97. //wire read_data_valid ;
  98. //wire [31:0] read_data_fifo_out ;
  99. //wire init_done ;
  100. //wire [35:0] app_af_addr ;
  101. //wire app_af_wren ;
  102. //wire [31:0] app_wdf_data ;
  103. //wire [3:0] app_mask_data ;
  104. //wire app_wdf_wren ;
  105. //wire error ;
  106. //--------------------------------------
  107. // assign
  108. //--------------------------------------
  109. //设置突发读写个数
  110. assign ddr2_write_len = 32'd128;
  111. assign ddr2_read_len = 32'd128;
  112. assign ddr2_wr_burst_addr = 32'd0;
  113. assign ddr2_rd_burst_addr = 32'd0;
  114. //------------------------------------------------------------
  115. //------------------------------------------------------------
  116. //模拟写入fifo的写入数据
  117. always@(posedge pci_clk ,posedge pci_rst)
  118. begin
  119. if(pci_rst == 1'b1)
  120. begin
  121. wr_fifo_wr_data <= 32'b0;
  122. wr_fifo_wr_data_en <= 1'b0;
  123. end
  124. else if(wr_fifo_almost_full == 1'b1)
  125. begin
  126. wr_fifo_wr_data <= 32'b0;
  127. wr_fifo_wr_data_en <= 1'b0;
  128. end
  129. else
  130. begin
  131. wr_fifo_wr_data <= wr_fifo_wr_data + 32'b1;
  132. wr_fifo_wr_data_en <= 1'b1;
  133. end
  134. end
  135. //产生读写ddr请求信号,写FIFO满写,写完读。
  136. always@(posedge clk ,posedge reset)
  137. begin
  138. if(reset == 1'b1)
  139. begin
  140. ddr2_write_req <= 1'b0;
  141. ddr2_read_req <= 1'b0;
  142. end
  143. else if(wr_fifo_almost_full == 1'b1)
  144. begin
  145. ddr2_write_req <= 1'b1;
  146. ddr2_read_req <= 1'b0;
  147. end
  148. else if(ddr2_write_finish == 1'b1)
  149. begin
  150. ddr2_write_req <= 1'b0;
  151. ddr2_read_req <= 1'b1;
  152. end
  153. else if(ddr2_read_finish == 1'b1)
  154. begin
  155. ddr2_write_req <= 1'b0;
  156. ddr2_read_req <= 1'b0;
  157. end
  158. else
  159. begin
  160. ddr2_write_req <= 1'b0;
  161. ddr2_read_req <= 1'b0;
  162. end
  163. end
  164. always@(posedge pci_clk, posedge pci_rst)
  165. begin
  166. if(pci_rst == 1'b1)
  167. rd_fifo_rd_en <= 1'b0;
  168. else if(rd_fifo_almost_full == 1'b1)
  169. rd_fifo_rd_en <= 1'b1;
  170. else if(rd_fifo_almost_empty == 1'b1)
  171. rd_fifo_rd_en <= 1'b0;
  172. else
  173. rd_fifo_rd_en <= rd_fifo_rd_en;
  174. end
  175. //------------------------------------------------------------
  176. //------------------------------------------------------------
  177. //例化DDR2读写控制模块
  178. ddr2_burst_ctr u_ddr2_burst_ctr (
  179. .ddr2_write_req (ddr2_write_req ),
  180. .ddr2_read_req (ddr2_read_req ),
  181. .ddr2_write_len (ddr2_write_len ),
  182. .ddr2_read_len (ddr2_read_len ),
  183. .ddr2_wr_burst_addr (ddr2_wr_burst_addr ),
  184. .ddr2_rd_burst_addr (ddr2_rd_burst_addr ),
  185. .wr_burst_data (wr_burst_data ),
  186. .wr_burst_data_vaild (wr_burst_data_vaild ),
  187. .rd_burst_data (rd_burst_data ),
  188. .rd_burst_data_vaild (rd_burst_data_vaild ),
  189. .wr_burst_data_req (wr_burst_data_req ),
  190. .ddr2_write_finish (ddr2_write_finish ),
  191. .ddr2_read_finish (ddr2_read_finish ),
  192. .clk (clk ),
  193. .reset (reset ),
  194. .wdf_almost_full (wdf_almost_full ),
  195. .af_almost_full (af_almost_full ),
  196. .burst_length_div2 (burst_length_div2 ),
  197. .read_data_valid (read_data_valid ),
  198. .read_data_fifo_out (read_data_fifo_out ),
  199. .init_done (init_done ),
  200. .app_af_addr (app_af_addr ),
  201. .app_af_wren (app_af_wren ),
  202. .app_wdf_data (app_wdf_data ),
  203. .app_mask_data (app_mask_data ),
  204. .app_wdf_wren (app_wdf_wren ),
  205. .error (error )
  206. );
  207. //ddr数据写入缓存FIFO
  208. ddr2_wr_fifo u_ddr2_wr_fifo (
  209. .rst (pci_rst ), // input rst
  210. .wr_clk (pci_clk ), // input wr_clk
  211. .rd_clk (clk ), // input rd_clk
  212. .din (wr_fifo_wr_data ), // input [31 : 0] din
  213. .wr_en (wr_fifo_wr_data_en ), // input wr_en
  214. .rd_en (wr_burst_data_req ), // input rd_en
  215. .dout (wr_burst_data ), // output [31 : 0] dout
  216. .full ( ), // output full
  217. .almost_full (wr_fifo_almost_full ), // output almost_full
  218. .empty ( ), // output empty
  219. .almost_empty ( ), // output almost_empty
  220. .valid (wr_burst_data_vaild ), // output valid
  221. .rd_data_count ( ), // output [9 : 0] rd_data_count
  222. .wr_data_count ( )// output [9 : 0] wr_data_count
  223. );
  224. //ddr数据读出缓存FIFO
  225. ddr2_rd_fifo u_ddr2_rd_fifo (
  226. .rst (pci_rst ), // input rst
  227. .wr_clk (clk ), // input wr_clk
  228. .rd_clk (pci_clk ), // input rd_clk
  229. .din (rd_burst_data ), // input [31 : 0] din
  230. .wr_en (rd_burst_data_vaild ), // input wr_en
  231. .rd_en (rd_fifo_rd_en ), // input rd_en
  232. .dout (rd_fifo_rd_data ), // output [31 : 0] dout
  233. .full ( ), // output full
  234. .almost_full (rd_fifo_almost_full ), // output almost_full
  235. .empty ( ), // output empty
  236. .almost_empty (rd_fifo_almost_empty ), // output almost_empty
  237. .valid (rd_fifo_rd_data_vaild ), // output valid
  238. .rd_data_count ( ), // output [9 : 0] rd_data_count
  239. .wr_data_count ( )// output [9 : 0] wr_data_count
  240. );
  241. //------------------------------------------------------------
  242. //------------------------------------------------------------
  243. endmodule

ddr用户侧控制器。

  1. // *********************************************************************************/
  2. // Project Name :
  3. // Author : i_huyi
  4. // Email : i_huyi@qq.com
  5. // Creat Time : 2021/6/15 9:06:55
  6. // File Name : .v
  7. // Module Name :
  8. // Called By :
  9. // Abstract :
  10. //
  11. // CopyRight(c) 2020, xxx xxx xxx Co., Ltd..
  12. // All Rights Reserved
  13. //
  14. // *********************************************************************************/
  15. // Modification History:
  16. // 1. initial
  17. // *********************************************************************************/
  18. // *************************
  19. // MODULE DEFINITION
  20. // *************************
  21. `timescale 1 ns / 1 ps
  22. module ddr2_burst_ctr#(
  23. parameter U_DLY = 1
  24. )
  25. (
  26. //---------------------------------------
  27. //ddr2 read and write ctr
  28. //---------------------------------------
  29. input wire ddr2_write_req ,//写请求
  30. input wire ddr2_read_req ,//读请求
  31. input wire[31:0] ddr2_write_len ,//突发写数据长度
  32. input wire[31:0] ddr2_read_len ,//突发读数据长度
  33. input wire[31:0] ddr2_wr_burst_addr ,//突发写首地址
  34. input wire[31:0] ddr2_rd_burst_addr ,//突发读首地址
  35. //数据信号
  36. input wire[31:0] wr_burst_data ,//写数据
  37. input wire wr_burst_data_vaild ,//写数据有效
  38. output wire[31:0] rd_burst_data ,//读数据
  39. output wire rd_burst_data_vaild ,//读数据有效
  40. //数据准备信号
  41. output wire wr_burst_data_req ,//ddr准备写入数据在写期间有效
  42. //读写完成信号
  43. output wire ddr2_write_finish ,//写完成
  44. output wire ddr2_read_finish ,//读完成
  45. //---------------------------------------
  46. //DDR2用户侧信号
  47. //---------------------------------------
  48. //时钟
  49. input wire clk ,//200MHZ
  50. input wire reset ,//ACTIVE HIGH
  51. //ddr内部fifo相关信号
  52. input wire wdf_almost_full ,
  53. input wire af_almost_full ,
  54. input wire[2:0] burst_length_div2 ,//burst_length_div2=3'b010,(BL=4)
  55. input wire read_data_valid ,
  56. input wire[31:0] read_data_fifo_out ,
  57. input wire init_done ,
  58. //输出数据地址信号
  59. output wire[35:0] app_af_addr ,
  60. output wire app_af_wren ,
  61. output wire[31:0] app_wdf_data ,
  62. output wire[3:0] app_mask_data ,
  63. output wire app_wdf_wren ,
  64. output wire error
  65. );
  66. //--------------------------------------
  67. // localparam
  68. //--------------------------------------
  69. //定义状态机
  70. localparam IDLE = 3'b001 ;
  71. localparam WRITE = 3'b010 ;
  72. localparam WRITE_END = 3'b011 ;
  73. localparam READ = 3'b100 ;
  74. localparam READ_WIAT = 3'b101 ;
  75. localparam READ_END = 3'b110 ;
  76. //--------------------------------------
  77. // register
  78. //--------------------------------------
  79. reg [35:0] app_af_addr_r ;//地址信号
  80. reg app_af_wren_r ;//地址使能
  81. //count
  82. reg [2:0] state ;
  83. reg [2:0] burst_count ;//突发个数计数器
  84. reg [31:0] addr_cnt ;//地址计数
  85. reg [31:0] wr_data_cnt ;//写数据计数
  86. reg [31:0] rd_data_cnt ;//读数据计数
  87. //ddr准备写入数据在写期间有效延时
  88. reg wr_burst_data_req_r ;
  89. //--------------------------------------
  90. // wire
  91. //--------------------------------------
  92. wire [2:0] burst_len ;//突发长度
  93. //--------------------------------------
  94. // assign
  95. //--------------------------------------
  96. assign app_af_addr = (app_af_wren_r == 1'b1) ? app_af_addr_r : 36'd0;
  97. assign app_af_wren = app_af_wren_r;
  98. assign app_wdf_wren = wr_burst_data_vaild;
  99. assign app_wdf_data = wr_burst_data;
  100. assign app_mask_data = {4{1'b0}};
  101. //assign error //暂时不用
  102. assign burst_len = burst_length_div2;//BL、这里还是赋原始值
  103. //完成信号
  104. assign ddr2_write_finish = (state == WRITE_END);
  105. assign ddr2_read_finish = (state == READ_END);
  106. //读出的数据
  107. assign rd_burst_data = read_data_fifo_out;
  108. assign rd_burst_data_vaild = read_data_valid;
  109. //ddr准备写入数据在写期间有效
  110. assign wr_burst_data_req = wr_burst_data_req_r;
  111. //------------------------------------------------------------
  112. //------------------------------------------------------------
  113. //------------------------------------------------------------
  114. //------------------------------------------------------------
  115. always@(posedge clk, posedge reset)
  116. begin
  117. if(reset == 1'b1)
  118. begin
  119. state <= IDLE;
  120. burst_count <= 3'b0;
  121. addr_cnt <= 32'b0;
  122. wr_data_cnt <= 32'b0;
  123. rd_data_cnt <= 32'b0;
  124. app_af_addr_r <= 36'b0;
  125. app_af_wren_r <= 1'b0;
  126. wr_burst_data_req_r <= 1'b0;
  127. end
  128. else if(init_done == 1'b1)
  129. begin
  130. case(state)
  131. IDLE:
  132. begin
  133. //接收到读写请求将读写首地址赋值
  134. if(ddr2_write_req)
  135. begin
  136. state <= WRITE;
  137. app_af_addr_r <= {1'b0,3'b100,ddr2_wr_burst_addr};
  138. burst_count <= burst_len;
  139. end
  140. else if(ddr2_read_req)
  141. begin
  142. state <= READ;
  143. app_af_addr_r <= {1'b0,3'b101,ddr2_rd_burst_addr};
  144. app_af_wren_r <= 1'b1;
  145. end
  146. else
  147. begin
  148. state <= IDLE;
  149. burst_count <= 3'b0;
  150. end
  151. end
  152. WRITE:
  153. begin
  154. //写状态包括地址和数据
  155. //判断数据FIFO和地址FIFO是否满
  156. //判断地址和数据个数是否达到设置个数
  157. if(wdf_almost_full == 1'b0 && af_almost_full == 1'b0)
  158. begin
  159. if(wr_data_cnt == ddr2_write_len && addr_cnt == {1'b0,ddr2_write_len[31:1]} )
  160. begin
  161. wr_data_cnt <= 32'b0;
  162. addr_cnt <= 32'b0;
  163. app_af_wren_r <= 1'b0;
  164. app_af_addr_r <= 36'b0;
  165. state <= WRITE_END;
  166. wr_burst_data_req_r <= 1'b0;
  167. end
  168. //数据从外部进入,我们根据外部数据有效
  169. //判断写数据个数
  170. //这里应该在设置一个数据有效标志
  171. //告诉外界准备数据
  172. else
  173. begin
  174. wr_burst_data_req_r <= 1'b1;
  175. if(wr_burst_data_vaild)
  176. wr_data_cnt <= wr_data_cnt + 1'b1;
  177. else
  178. wr_data_cnt <= wr_data_cnt;
  179. if(burst_count == 3'b001)
  180. begin
  181. addr_cnt <= addr_cnt;
  182. app_af_wren_r <= 1'b1;
  183. end
  184. else if(burst_count == 3'b010)
  185. begin
  186. addr_cnt <= 32'b0;
  187. app_af_wren_r <= 1'b0;
  188. end
  189. else
  190. begin
  191. addr_cnt <= addr_cnt + 1'b1;
  192. app_af_addr_r <= app_af_addr_r + 32'd4;
  193. app_af_wren_r <= 1'b0;
  194. end
  195. //对burst_count进行赋值
  196. if(burst_count[2:0] != 3'b000)
  197. burst_count[2:0] <= burst_count[2:0] - 1'b1;
  198. else
  199. burst_count[2:0] <= burst_len - 1'b1;
  200. end
  201. end
  202. else
  203. begin
  204. app_af_wren_r <= 1'b0;
  205. wr_burst_data_req_r <= 1'b0;
  206. end
  207. end
  208. WRITE_END:
  209. begin
  210. state <= IDLE;
  211. end
  212. READ:
  213. begin
  214. if(af_almost_full == 1'b0)
  215. begin
  216. //地址个数为突发传输长度的一半
  217. if(addr_cnt == {1'b0,ddr2_read_len[31:1]})
  218. begin
  219. addr_cnt <= 32'b0;
  220. app_af_wren_r <= 1'b0;
  221. app_af_addr_r <= 36'b0;
  222. state <= READ_WIAT;
  223. end
  224. else
  225. begin
  226. addr_cnt <= addr_cnt + 1'b1;
  227. app_af_addr_r <= app_af_addr_r + 32'd4;
  228. app_af_wren_r <= 1'b1;
  229. end
  230. end
  231. else
  232. app_af_wren_r <= 1'b0;
  233. if(read_data_valid == 1'b1)
  234. begin
  235. if(rd_data_cnt == ddr2_read_len)
  236. rd_data_cnt <= 32'b0;
  237. else
  238. rd_data_cnt <= rd_data_cnt + 1'b1;
  239. end
  240. else
  241. rd_data_cnt <= rd_data_cnt;
  242. end
  243. READ_WIAT://读地址写入,读出的数据延后出来
  244. begin
  245. if(read_data_valid == 1'b1)
  246. begin
  247. if(rd_data_cnt == ddr2_read_len )
  248. begin
  249. rd_data_cnt <= 32'b0;
  250. state <= READ_END;
  251. end
  252. else
  253. rd_data_cnt <= rd_data_cnt+1'b1;
  254. end
  255. else
  256. rd_data_cnt <= rd_data_cnt;
  257. end
  258. READ_END:
  259. state <= IDLE;
  260. default:
  261. state <= IDLE;
  262. endcase
  263. end
  264. end
  265. //------------------------------------------------------------
  266. //------------------------------------------------------------
  267. //------------------------------------------------------------
  268. //------------------------------------------------------------
  269. endmodule

仿真搭建。

在sim_tb_top中把自己写的测试部分代码例化。运行仿真。

写入

读出命令地址

读出的数据。

另外可以从仿真中看出来DDR2用户控制部分还有很大的空闲期间。此期间既没有写命令也没有读命令。这部分就是浪费的性能。思考一下后续怎么将此部分利用起来。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/820005
推荐阅读
相关标签
  

闽ICP备14008679号