LATTICE进阶篇DDR2--(2)详解IPUG35---基于官方例程

前言

本章主要讲述根据《DDR & DDR2 SDRAM Controller IP Cores User’s Guide 》数据手册,配合ddr2的demo仿真,学习DDR2的IP核时序控制。

器件:Lattice ECP3

环境:Win10 + Diamond3.13 + ModelSim SE-64 10.5

一、下载DDR2UG数据手册

方法1:通过Diamond联网下载

方法2:前往lattice官网下载数据手册IPUG35

《DDR & DDR2 SDRAM Controller IP Cores Users Guide

https://www.latticesemi.com/Search.aspx?&lcid=9&q=IPUG35&t=480

二、分析数据手册

接下来让我们开始研究《DDR & DDR2 SDRAM Controller IP Cores User’s Guide 》

2.1用户命令CMD

2.2DDR初始化

系统复位后,只初始化一次,在上电完成,时钟稳定后,

用户等待200us后产生一个init_start高电平信号给CORE,当CORE完成初始化时序,会发送一个时钟周期的init_done高电平信号,当init_done信号出现高电平,init_start信号拉低。

如果我们不对MR或者EMR寄存器进行配置,则CORE会按照默认配置进行初始化。

DEMO解析--DDR初始化代码

//********************************************************************/

// Init_start generation

// - in simulation mode, wait for 128 clock cycles

// - in synthesis mode, wait for +200us from reset_n deassertion

//********************************************************************/

always@(posedge k_clk or negedge reset_n) begin

    if (reset_n == 1'b0) begin

        init_cnt        <=  16'h0;

        init_srvcd      <=  1'b0;

        init_start_hit  <=  1'b0;

        init_start      <=  1'b0;

    end

    else begin

        init_cnt    <=  init_cnt + 1;

`ifdef RTL_SIM

        if (init_cnt[6] && !init_srvcd) begin       // to save the simulation run time

`else

        if (init_cnt[15] && init_cnt[13] && !init_srvcd) begin

`endif

            init_srvcd      <=  1'b1;

            init_start_hit  <=  1'b1;

        end

        else

            init_start_hit  <=  1'b0;

        if (init_start_hit)

            init_start      <=  1'b1;

        if (init_done)

            init_start      <=  1'b0;

    end

end

assign init_start = init_start_req && pll_lock && dll_lock;

2.3DDR的自刷新模式

该模式一般在IP核设置为使能,不需要写代码,直接使用,了解即可。

内存控制器自带自刷新模式,一旦使能,ext_auto_ref被用户拉高,来强制CORE产生一连串的刷新指令,当CORE产生刷新指令后,ext_auto_ref_acklag拉高一个时钟周期,当ext_auto_ref_acklag信号出现高电平,ext_auto_ref信号拉低。

2.4DDR的命令和地址控制

 一旦内存初始化完成,IP核就会等待用户命令来访问内存。用户需要向IP核提供命令和地址以及控制信号。命令和地址是按照以下描述的过程传递给IP核:

  1. IP核输出的cmd_rdy信号拉高一个时钟周期,代表其已准备好接收用户命令。
  2. IP核输出的cmd_rdy信号拉高时,如果用户逻辑也拉高了cmd_valid信号,IP核输就会将cmd输入作为有效的用户命令进行处理。

如果cmd_valid没有被拉高,cmd和addr输入将变为无效,IP核将忽略它们。

  1. 当cmd_rdy信号为低时,cmdaddrcmd_valid将变为无效,IP核将会忽略它们。
  2. cmd_rdy信号再次拉高,则准备执行下一个命令。

DEMO解析--DDR的命令和地址控制代码

1.在初始化完成后,IP核发出cmd_rdy信号。

2.在初始化完成后,一直拉高cmd_valid信号。

3.cmd_rdy拉低时,就应该准备好addr burst_count的信号。

4.每出现一次cmd_rdy,状态机就跳转一次。

PS:此处的CMD=6LOAD_MR)在实际的工程中是不需要我们去写的,一旦配置好DDR2IP核,就会自动产生,实际的工程中我们只需要去控制写命令和读命令,DEMO工程中并没有配置DDR2IP核,而是调用该IP的一些源文件,与实际工程有所不同。

// ==============================================================================

// state assignments & defines

// ==============================================================================

`define     IDLE            0

`define     CONF_MR         1

`define     CONF_EMR        2

`define     WRITE_CMD       3

`define     READ_CMD        4

 

// ==============================================================================

// Demo configuration generation

// ==============================================================================

assign  BL_MODE     =   switch[0];

assign  ODT_SEL     =   switch[2:1];

assign  CMD_BRST    =   switch[5:3];

assign  DATA_MODE   =   switch[6];      // 1: PRBS,     0: Sequential

assign  DEMO_MODE   =   switch[7];      // 1: Non-stop, 0: Single Wr/Rd

assign  MR_DATA     =   BL_MODE ? 16'h0643 : 16'h0642;  // 1: BL8, 0: BL4

assign  EMR_DATA    =   (ODT_SEL == 2'b11) ? 16'h2004 : // 75-ohm

                        (ODT_SEL == 2'b10) ? 16'h2040 : // 150-ohm

                        (ODT_SEL == 2'b01) ? 16'h2044 : // 50-ohm

                                             16'h2000 ; // disabled

assign  data_mask   =   {`DSIZE/8{1'b0}};

`ifdef BRST_CNT_EN  

 assign burst_count =   (CMD_BRST == 3'b100) ? 5'd16 :

                        (CMD_BRST == 3'b011) ? 5'd8  :

                        (CMD_BRST == 3'b010) ? 5'd4  :

                        (CMD_BRST == 3'b001) ? 5'd2  :

                        (CMD_BRST == 3'b000) ? 5'd1  :

                                               5'd0;    // 32-command burst in all other cases

`endif

 

//*******************************************************************************/

//  Address and Command Generation

//*******************************************************************************/

assign  cmd_gone    =   cmd_rdy && cmd_valid;

 

// ==============================================================================

// Command generation state machine

// ==============================================================================

always @(posedge k_clk or negedge reset_n) begin

    if (reset_n == 1'b0) begin

        cmdgen          <=  `IDLE;

        cmd             <=  4'h6;

        cmd_valid       <=  1'b0;

        addr            <=  {`ADDR_WIDTH{1'b0}};    

    end

    else begin

        case (cmdgen)

        `IDLE : begin           // :0

            if (init_done) begin

                cmdgen      <=  `CONF_MR;

                cmd_valid   <=  1'b1;  

            end

            else begin

                cmdgen      <=  `IDLE;

            end

        end

        `CONF_MR : begin        // :1

            cmd         <=  4'h6;               // LOAD_MR command for MR

            addr        <=  MR_DATA;

            if (cmd_gone) begin

                cmdgen      <=  `CONF_EMR;

            end

            else

                cmdgen      <=  `CONF_MR;

        end

        `CONF_EMR : begin       // :2

            addr        <=  EMR_DATA;           // LOAD_MR command for EMR

            if (cmd_gone) begin

                cmdgen      <=  `WRITE_CMD;

            end

            else

                cmdgen      <=  `CONF_EMR;

        end

        `WRITE_CMD : begin      // :3

            cmd         <=  4'h2;               // WRITE command

            addr        <=  addr_wr;

            if (cmd_gone) begin

                cmdgen      <=  `READ_CMD;

            end

            else begin

                cmdgen      <=  `WRITE_CMD;

            end

        end

        `READ_CMD : begin       // :4

            cmd         <=  4'h1;               // READ command    

            addr        <=  addr_rd;

            if (cmd_gone) begin

                if (DEMO_MODE) begin

                    cmdgen      <=  `WRITE_CMD;

                end

                else begin

                    cmdgen      <=  `IDLE;      // Run only one Write-Read cycle

                    cmd_valid   <=  1'b0;

                end

            end

            else begin

                cmdgen      <=  `READ_CMD;

            end

        end

        endcase

    end

end

 

 

2.5DDR的写数据控制

1.在IP核接收到写命令之后,当它准备好接收要写入内存的数据时,IP核会拉高data_rdy信号。

2.一旦data_rdy被拉高,在data_rdy信号拉高后的一到两个时钟周期内(由WrRqDDelay决定),写数据线上必须接收到有效数据。

实际的工程中,写数据延迟是通过IP核配置的。

DEMO解析--Write Data产生代码

 

1.当data_rdy为高时,就发送数据。(时序逻辑,会延后1拍)

// ==============================================================================

// state assignments & defines

// ==============================================================================

 

`define     WAIT_RDY        1

 

//********************************************************************/

//

//  Data Generation and Result Chcker

//

//********************************************************************/

always @(posedge k_clk or negedge reset_n) begin

    if (reset_n == 1'b0) begin

        datagen         <=  `IDLE;

        write_data      <=  {`DSIZE{1'b0}};

    end

    else begin

        case (datagen)

            `IDLE : begin

                if (init_done)

                    datagen     <= `WAIT_RDY;

                else

                    datagen     <= `IDLE;

            end

       

            `WAIT_RDY : begin

                if (data_rdy) begin

                    write_data  <=  curr_data;

                    datagen     <= `WAIT_RDY;

                end

                else begin

                    datagen     <= `WAIT_RDY;

                end

            end

        endcase

    end

end

assign  gen_data        =   (datagen == `WAIT_RDY) && data_rdy;

assign  idle_data       =   (datagen == `IDLE);

 

 

2.6DDR的读数据控制

1.当IP核接收读命令后,IP核就会产生read_data_valid信号,此时有效读取数据已经在read_data总线上。

与写数据不同,读数据是没有读数据延迟。

EMO解析--Read Data代码

1.read_data没什么好讲的,按照时序正常接收就行。

 

// ==============================================================================

// Sequential expected data generator

// ==============================================================================

always @(posedge k_clk or negedge reset_n) begin

    if (reset_n == 1'b0) begin

        chk_seq_data32          <=  32'h1;

        read_data_valid_d       <=  1'b0;

        read_data_valid_d2      <=  1'b0;

        read_data_valid_d3      <=  1'b0;

    end

    else begin

        read_data_valid_d       <=  read_data_valid;    

        read_data_valid_d2      <=  read_data_valid_d;

        read_data_valid_d3      <=  read_data_valid_d2;

        if (read_data_valid_d) begin

            chk_seq_data32      <=   chk_seq_data32 +1;

        end

    end

end

assign  expect_data =   (DATA_MODE == 1'b1) ?  chk_rndm_data : chk_seq_data;

always @(posedge k_clk or negedge reset_n) begin

    if (reset_n == 1'b0) begin

        read_data_d         <=  {`DSIZE{1'b0}};

        read_data_2d        <=  {`DSIZE{1'b0}};

        expect_data_d       <=  {`DSIZE{1'b0}};

    end

    else begin

        read_data_d         <=  read_data;

        read_data_2d        <=  read_data_d;

        expect_data_d       <=  expect_data;

    end

end

  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值