論理回路デザイン
ArchiTek home page
テストの考え方

PHYのモデル化

DDRの初期化とリフレッシュ

Initial flow

テストの開始と結果

テストモジュールサンプル(RTL)

/* **************************** MODULE PREAMBLE ********************************

        Copyright (c) 2012, ArchiTek
        This document constitutes confidential and proprietary information
        of ArchiTek. All rights reserved.
        // 基本クロックと4位相のクロックを生成する最上位モジュール*/

// ***************************** MODULE HEADER *********************************

`timescale 1ps / 1ps

module test;

// ************************ PARAMETER DECLARATIONS *****************************

        parameter       D       = 1;
        parameter       DCLK    = 2500;
        parameter       DCLK2   = DCLK/2;
        parameter       DCLK4   = DCLK/4;

// ************************** LOCAL DECLARATIONS *******************************

        reg             clk, clk_n;
        reg             reset_n;
        reg             ddr_clk_0, ddr_clk_90, ddr_clk_180, ddr_clk_270;

// ****************************** MODULE BODY **********************************

// -----------------------------------------------------------------------------
initial begin clk=0; forever begin #(DCLK2) clk=~clk; end end
initial begin clk_n=1; forever begin #(DCLK2) clk_n=~clk_n; end end

// -----------------------------------------------------------------------------
initial begin reset_n=0; #D reset_n=0; #(DCLK*3.5) reset_n=1; end

// -----------------------------------------------------------------------------
initial begin ddr_clk_0  =0; #(DCLK4*0) forever begin #(DCLK2) ddr_clk_0  =~ddr_clk_0;   end end
initial begin ddr_clk_90 =0; #(DCLK4*1) forever begin #(DCLK2) ddr_clk_90 =~ddr_clk_90;  end end
initial begin ddr_clk_180=0; #(DCLK4*2) forever begin #(DCLK2) ddr_clk_180=~ddr_clk_180; end end
initial begin ddr_clk_270=0; #(DCLK4*3) forever begin #(DCLK2) ddr_clk_270=~ddr_clk_270; end end

// -----------------------------------------------------------------------------
top top_0 (
        clk, clk_n, reset_n,
        ddr_clk_0, ddr_clk_90, ddr_clk_180, ddr_clk_270
        );

// ************************** FUNCTIONS and TASKS ******************************

endmodule

// *****************************************************************************
/* **************************** MODULE PREAMBLE ********************************

        Copyright (c) 2012, ArchiTek
        This document constitutes confidential and proprietary information
        of ArchiTek. All rights reserved.
*/

// ***************************** MODULE HEADER *********************************

module top (
        clk, clk_n, reset_n,
        ddr_clk_0, ddr_clk_90, ddr_clk_180, ddr_clk_270
        );

// ************************ PARAMETER DECLARATIONS *****************************

        parameter       ST      = 10000;                // Simulation interval

// ***************************** I/O DECLARATIONS ******************************

        input           clk;
        input           clk_n;
        input           reset_n;
        input           ddr_clk_0;
        input           ddr_clk_90;
        input           ddr_clk_180;
        input           ddr_clk_270;

// ************************** LOCAL DECLARATIONS *******************************

        // 通常アクセスマスター信号
        wire            iReq;
        wire            iGnt;
        wire            iRxw;
        wire    [31:0]  iAddr;
        wire            iWrStrb;
        wire            iWrAck;
        wire    [15:0]  iWrData;
        wire    [1:0]   iWrMask;
        wire            iRdStrb;
        wire            iRdAck;
        wire    [15:0]  iRdData;

        // 割り込みアクセスマスター信号
        wire            pReq;
        wire            pGnt;
        wire    [31:0]  pAddr;

        // sdcモジュールのDDR信号
        wire            sd_cs_n;
        wire            sd_ras_n;
        wire            sd_cas_n;
        wire            sd_we_n;
        wire            sd_cke          = 1'b1;
        wire    [2:0]   sd_ba;
        wire    [15:0]  sd_addr;
        wire            sd_odt          = 1'b0;
        wire            sd_reset_n      = 1'b1;
        wire            sd_wdvldt;
        wire            sd_wdvldd;
        wire            sd_wdvld        = sd_wdvldt | sd_wdvldd;
        wire    [15:0]  sd_wd;
        wire    [1:0]   sd_wdqm;
        wire            sd_rdvld;
        wire    [15:0]  sd_rd;

        // ACタイミングの設定
        wire    [7:0]   reg_pACCW       = 8'h18;
        wire    [7:0]   reg_pACCR       = 8'h16;
        wire    [7:0]   reg_pCOM        = 8'h4f;
        wire    [4:0]   reg_pFAW        = 5'h12;
        wire    [4:0]   reg_pRRD        = 5'h03;
        wire    [4:0]   reg_dTWR        = 5'h0b;
        wire    [4:0]   reg_dTRW        = 5'h06;
        wire    [4:0]   reg_dWL         = 5'h07;
        wire    [4:0]   reg_dRL         = 5'h08;

        // I/Oから見たDDR信号
        wire            ddr_cs_n;
        wire            ddr_ras_n;
        wire            ddr_cas_n;
        wire            ddr_we_n;
        wire            ddr_cke;
        wire    [2:0]   ddr_ba;
        wire    [15:0]  ddr_addr;
        wire            ddr_odt;
        wire            ddr_reset_n;
        wire    [7:0]   ddr_dq;
        wire            ddr_dqm;
        wire            ddr_dqs_p;
        wire            ddr_dqs_n;
        wire            ddr_rdqs_n;
        wire    [7:0]   ddr_din;
        wire    [7:0]   ddr_dout;
        wire    [7:0]   ddr_oe;
        wire            ddr_dqsout;
        wire            ddr_dqsin;
        wire            ddr_dqs_poe;
        wire            ddr_dqs_noe;
        wire            ddr_dqs_pbus;
        wire            ddr_clk_p;
        wire            ddr_clk_n;

        // シミュレーション用変数
        reg     [1:0]   rst;
        reg             reset;

        reg     [31:0]  tsc;
        reg             ts_req;
        wire            ts_gnt;
        reg     [31:0]  ts_addr;
        reg             ts_hold;
        wire            ts_sdram;
        reg             ts_sdc;
        reg             ts_omit;

        reg             omit;

        reg     [31:0]  addr;
        reg     [31:0]  data0;
        reg     [31:0]  data1;

        reg     [31:0]  reqCnt;
        reg     [31:0]  gntCnt;

        integer         i;

// ******************************** MODULE BODY ********************************

// -----------------------------------------------------------------------------
// Reset
always @(posedge clk or negedge reset_n)
        if (!reset_n)
                rst     <= #1 2'b11;
        else
                rst     <= #1 {rst[0], 1'b0};

always @(posedge clk or negedge reset_n)
        if (!reset_n)
                reset   <= #1 1'b1;
        else
                reset   <= #1 rst[1];

// -----------------------------------------------------------------------------
// Time Count
// タスクのカウンター
always @(posedge clk or negedge reset_n)
        if (!reset_n)
                tsc     <= #1 32'd0;
        else if (~&tsc & !ts_hold)
                tsc     <= #1 tsc + 32'd1;

always @(posedge clk or negedge reset_n)
        if (!reset_n)
                omit    <= #1 1'b1;
        else
                omit    <= #1 ts_omit;

// DDR制御の非IDLE、終了判定に使用
assign ts_sdram         = |sdc_0.cntl_0.state;

// -----------------------------------------------------------------------------
// Parameter Access
assign pReq             = ts_req;
assign pAddr            = ts_addr;

assign ts_gnt           = pGnt;

// -----------------------------------------------------------------------------
// Initialize Memory
// DDRモデル内のメモリ値をインクリメンタルデータに初期化、DDRモデルが変われば変数名等変更が必要
always @(ts_sdc)
        if (ts_sdc)
                for (i=0; i<'h10_0000; i=i+1) begin             // 8MB
                        addr            = 8*i;
                        data0           = 2*i;
                        data1           = 2*i + 1;

                        sdram_0.memory[{addr[5:3], addr[27:13], addr[12:6]}]
                                        = {data1, data0};
                end

// -----------------------------------------------------------------------------
// Initialize Master
// マスターの内のメモリ値をインクリメンタルデータに初期化
always @(ts_sdc)
        if (ts_sdc)
                for (i=0; i<'h10_0000; i=i+1) begin             // 8MB
                        addr            = i;
                        data0           = 2*i;
                        data1           = 2*i + 1;

                        master_0.mem[addr]
                                        = {data1, data0};
        end

// -----------------------------------------------------------------------------
// Master
// 上位アドレスは変化しないようマスク(メモリ消費量や同じアドレスへのR/Wが重なる確率を上げるため)
master #(32'h000ffff8, 1, 2) master_0 (
        iReq, iGnt, iRxw, iAddr,
        iWrStrb, iWrAck, 1'b0, iWrData, iWrMask,
        iRdStrb, iRdAck, 1'b0, iRdData,
        omit,
        reset, clk
        );

// -----------------------------------------------------------------------------
// Body
// RowアドレスはiAddrの13bit目からマッピング
sdc #(13) sdc_0 (
        .iReq                           (iReq),
        .iGnt                           (iGnt),
        .iRxw                           (iRxw),
        .iAddr                          (iAddr),
        .iWrAck                         (iWrAck),
        .iWrData                        (iWrData),
        .iWrMask                        (iWrMask),
        .iRdAck                         (iRdAck),
        .iRdData                        (iRdData),
        .pReq                           (pReq),
        .pGnt                           (pGnt),
        .pAddr                          (pAddr),
        .cs_n                           (sd_cs_n),
        .ras_n                          (sd_ras_n),
        .cas_n                          (sd_cas_n),
        .we_n                           (sd_we_n),
        .addr                           (sd_addr),
        .ba                             (sd_ba),
        .wdvld                          (sd_wdvldt),
        .wdvldd                         (sd_wdvldd),
        .wd                             (sd_wd),
        .wdqm                           (sd_wdqm),
        .rdvld                          (sd_rdvld),
        .rd                             (sd_rd),
        .reg_pACCW                      (reg_pACCW),
        .reg_pACCR                      (reg_pACCR),
        .reg_pCOM                       (reg_pCOM),
        .reg_pFAW                       (reg_pFAW),
        .reg_pRRD                       (reg_pRRD),
        .reg_dTWR                       (reg_dTWR),
        .reg_dTRW                       (reg_dTRW),
        .reg_dWL                        (reg_dWL),
        .reg_dRL                        (reg_dRL),
        .clk                            (clk),
        .clk_n                          (clk_n),
        .reset_n                        (reset_n)
        );

// -----------------------------------------------------------------------------
// Statistics
// 使用効率を出すため、リクエスト期間と受付期間を計測
always @(posedge clk)
        if (reset) begin
                reqCnt          <= #1 32'd0;
                gntCnt          <= #1 32'd0;
        end
        else if (!omit) begin
                reqCnt          <= #1 reqCnt + 32'd1;
                gntCnt          <= #1 gntCnt + (iReq & iGnt);
        end

// -----------------------------------------------------------------------------
// Clock
// ここから先はI/O部品
dobuf1 cbuf_0 (
        .datain                         (clk),
        .dataout                        (ddr_clk_p),
        .dataout_b                      (ddr_clk_n)
        );

// -----------------------------------------------------------------------------
// Command Buffer
obuf26 abuf_0 (
        .datain_h                       ({sd_cs_n, sd_ras_n, sd_cas_n, sd_we_n,
                                          sd_cke, sd_ba, sd_addr,
                                          sd_odt, sd_reset_n
                                        }),
        .datain_l                       ({sd_cs_n, sd_ras_n, sd_cas_n, sd_we_n,
                                          sd_cke, sd_ba, sd_addr,
                                          sd_odt, sd_reset_n
                                        }),
        .dataout                        ({ddr_cs_n, ddr_ras_n, ddr_cas_n, ddr_we_n,
                                          ddr_cke, ddr_ba, ddr_addr,
                                          ddr_odt, ddr_reset_n
                                        }),
        .outclock                       (ddr_clk_180)
        );

// -----------------------------------------------------------------------------
// DQM
obuf1 qbuf_0 (
        .datain_h                       (sd_wdqm[0]),
        .datain_l                       (sd_wdqm[1]),
        .dataout                        (ddr_dqm),
        .outclock                       (ddr_clk_270)
        );

// -----------------------------------------------------------------------------
// DQS
diobuf1 sbuf_0 (
        .datain                         (ddr_dqsout),
        .dataout                        (ddr_dqsin),
        .dataio                         (ddr_dqs_p),
        .dataio_b                       (ddr_dqs_n),
        .oe                             (ddr_dqs_poe),
        .oe_b                           (ddr_dqs_noe)
        );

// -----------------------------------------------------------------------------
// Data Lane
// AlteraのALTDQ_DQSを模したモデル
dlyd dlyd_0 (
        .bidir_dq_input_data_in         (ddr_din),
        .bidir_dq_oe_in                 ({8{sd_wdvld}}),
        .bidir_dq_output_data_in_high   (sd_wd[7:0]),
        .bidir_dq_output_data_in_low    (sd_wd[15:8]),
        .dll_delayctrlin                (6'h00),
        .dq_output_reg_clk              (ddr_clk_270),
        .dq_output_reg_clkena           (1'b1),
        .dqs_enable_in                  (sd_rdvld),
        .dqs_input_data_in              (ddr_dqsin),
        .dqs_oe_in                      (sd_wdvld),
        .dqs_output_data_in_high        (sd_wdvldd),
        .dqs_output_data_in_low         (1'b0),
        .dqs_output_reg_clk             (ddr_clk_0),
        .dqs_output_reg_clkena          (1'b1),
        .dqsn_oe_in                     (sd_wdvld),
        .bidir_dq_input_data_out_high   (sd_rd[15:8]),
        .bidir_dq_input_data_out_low    (sd_rd[7:0]),
        .bidir_dq_oe_out                (ddr_oe),
        .bidir_dq_output_data_out       (ddr_dout),
        .dqs_bus_out                    (ddr_dqs_pbus),
        .dqs_oe_out                     (ddr_dqs_poe),
        .dqs_output_data_out            (ddr_dqsout),
        .dqsn_oe_out                    (ddr_dqs_noe)
        );

iobuf8 dbuf_0 (
        .datain                         (ddr_dout),
        .oe                             (ddr_oe),
        .dataio                         (ddr_dq),
        .dataout                        (ddr_din)
        );

// -----------------------------------------------------------------------------
// SDRAM
// USEMがセットされていれば、付録のddr.vを使う
// DDRモデルはMicronのddr.vを使うことを前提にしている(SpeedバージョンはSG25E)
// Micronのddr.vは同じディレクトリにddr2_parameters.vhが必要
`ifdef USEM
ddr_emu sdram_0 (
        ddr_clk_p, ddr_clk_n,
        ddr_cke, ddr_cs_n, ddr_ras_n, ddr_cas_n, ddr_we_n, ddr_dqm,
        ddr_ba, ddr_addr, ddr_dq, ddr_dqs_p, ddr_dqs_n, ddr_rdqs_n, ddr_odt
        );
`else
`define x8
`define sg25E
ddr2 sdram_0 (
        ddr_clk_p, ddr_clk_n,
        ddr_cke, ddr_cs_n, ddr_ras_n, ddr_cas_n, ddr_we_n, ddr_dqm,
        ddr_ba, ddr_addr[14:0], ddr_dq, ddr_dqs_p, ddr_dqs_n, ddr_rdqs_n, ddr_odt
        );
`endif

// **************************** FUNCTIONS and TASKS ****************************

// DDRの初期化、マスターアクセスの許可(omit=0)、リフレッシュの挿入、終了処理を行う
always @(
        tsc or
        ts_gnt or ts_sdram or
        omit or
        reqCnt or gntCnt
        ) begin

        ts_req          = 1'b0;
        ts_addr         = 32'h00000000;
        ts_hold         = 1'b0;
        ts_sdc          = 1'b0;
        ts_omit         = omit;

case (tsc)

// -----------------------------------------------------------------------------
// Initialize SDRAM
// -----------------------------------------------------------------------------
// DDR2:
//      AL=3, CL=5, WR=6, RL=AL+CL=8, RL=AL+CL=8, WL=RL-1=7
//      tRP=5(12.5ns), tRRD=3(7.5ns), tRFC=79/28000(197.5ns/70us)
//      tRAS=16(40ns), tRC=22(55ns), tWTR=3(7.5ns), tFAW=18(45ns)
//
//              FAW     = tFAW                  = 18
//              RRD     = tRRD                  = 3
//              CP      = tRFC                  = 79
//              APR     = tRC                   = 22
//                      = tRAS+tRP              = 21
//                      = 2+RL+BL/2+tRP         = 19
//              APW     = tRC                   = 22
//                      = tRAS+tRP              = 21
//                      = 2+WL+BL/2+tRP+WR      = 24
//              RCL     = RL                    = 8
//              WCL     = WL                    = 7
//              TRW     = RL-WL+BL/2+1          = 6
//              TWR     = WL+BL/2-AL+tWTR       = 11
//
1:              begin
                        $display("");
                        $display("********************");
                        $display("* Initialize Start *");
                        $display("********************");
                end

10:             Write   (32'h04000200);         // (Precharge-all)
11:             Write   (32'h00001000);         // (EMRS1:0)
12:             Write   (32'h00002000);         // (EMRS2:0)
13:             Write   (32'h00003000);         // (EMRS3:0)
14:             Write   (32'h0b530000);         // (MRS:WR=6,DLL=1,CL=5,BL=8)
15:             Write   (32'h04000200);         // (Precharge-all)
16:             Write   (32'h00000400);         // (Refresh)
17:             Write   (32'h00000400);         // (Refresh)
18:             Write   (32'h0a530000);         // (MRS:WR=6,DLL=0,CL=5,BL=8)
19:             Write   (32'h03981000);         // (EMRS1:OCD=def,AL=3)
20:             Write   (32'h00181000);         // (EMRS1:OCD=exit,AL=3)

// -----------------------------------------------------------------------------
// SDRAM Initial Value Set
// -----------------------------------------------------------------------------
30:             ts_sdc  = 1'b1;

// -----------------------------------------------------------------------------
// Master Access Start
// -----------------------------------------------------------------------------
50:             begin
                        $display("");
                        $display("****************");
                        $display("* Access Start *");
                        $display("****************");

                        ts_omit = 1'b0;
                end

// -----------------------------------------------------------------------------
// Refresh Test
// -----------------------------------------------------------------------------
// リフレッシュを要求(実際はタイマー起動すべき)
293:            Write   (32'h00000400);         // (Refresh)

// -----------------------------------------------------------------------------
// Finalize
// -----------------------------------------------------------------------------
ST:             ts_omit = 1'b1;
ST+1:           ts_hold = iReq | ts_sdram;
ST+2:           begin
                        $display("");
                        $display("********************");
                        $display("* Access Terminate *");
                        $display("********************");
                        $display("");
                        // DDRの使用効率、4を掛けているのはバースト補正
                        $display("Density: %d/%d\t(%3d%% )", gntCnt<<2, reqCnt, 400*gntCnt/reqCnt);
                        $display("");

                        $finish;
                end
endcase

end

// -----------------------------------------------------------------------------
// Task
// -----------------------------------------------------------------------------
task Write;
input   [31:0]  Addr;
begin
        ts_req          = 1'b1;
        ts_addr         = Addr;
        ts_hold         = !ts_gnt;
end
endtask

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

endmodule

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

I/Oバッファ

/* **************************** MODULE PREAMBLE ********************************

        Copyright (c) 2011, ArchiTek
        This document constitutes confidential and proprietary information
        of ArchiTek. All rights reserved.
*/

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

module obuf1 (
        datain_h, datain_l, outclock, dataout
        );

        input           datain_h;
        input           datain_l;
        output          dataout;
        input           outclock;

        reg             dataout_h, dataout_l;

always @(posedge outclock) begin
        dataout_h       <= #1 datain_h;
        dataout_l       <= #1 datain_l;
end

assign dataout          = outclock ? dataout_h : dataout_l;

endmodule

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

module obuf26 (
        datain_h, datain_l, outclock, dataout
        );

        input   [25:0]  datain_h, datain_l;
        output  [25:0]  dataout;
        input           outclock;

        reg     [25:0]  dataout_h, dataout_l;

always @(posedge outclock) begin
        dataout_h       <= #1 datain_h;
        dataout_l       <= #1 datain_l;
end

assign dataout          = outclock ? dataout_h : dataout_l;

endmodule

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

module dobuf1 (
        datain, dataout, dataout_b
        );

        input           datain;
        output          dataout;
        output          dataout_b;

assign dataout          = datain;
assign dataout_b        = ~datain;

endmodule

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

module iobuf8 (
        datain, oe, dataio, dataout
        );

        input   [7:0]   datain;
        output  [7:0]   dataout;
        inout   [7:0]   dataio;
        input   [7:0]   oe;

assign dataout          = dataio;

assign dataio[7]        = !oe[7] ? 1'bz : datain[7];
assign dataio[6]        = !oe[6] ? 1'bz : datain[6];
assign dataio[5]        = !oe[5] ? 1'bz : datain[5];
assign dataio[4]        = !oe[4] ? 1'bz : datain[4];
assign dataio[3]        = !oe[3] ? 1'bz : datain[3];
assign dataio[2]        = !oe[2] ? 1'bz : datain[2];
assign dataio[1]        = !oe[1] ? 1'bz : datain[1];
assign dataio[0]        = !oe[0] ? 1'bz : datain[0];

endmodule

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

module diobuf1 (
        datain, dataout, dataio, dataio_b, oe, oe_b
        );

        input           datain;
        output          dataout;
        inout           dataio;
        inout           dataio_b;
        input           oe;
        input           oe_b;

assign dataout          = dataio;
assign dataio           = !oe ? 1'bz : datain;
assign dataio_b         = !oe ? 1'bz : ~datain;

endmodule

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

PHYモデル(RTL)

/* **************************** MODULE PREAMBLE ********************************

        Copyright (c) 2011, ArchiTek
        This document constitutes confidential and proprietary information
        of ArchiTek. All rights reserved.
*/

// ***************************** MODULE HEADER *********************************

module dlyd (
        bidir_dq_input_data_in, bidir_dq_oe_in,
        bidir_dq_output_data_in_high, bidir_dq_output_data_in_low,
        dll_delayctrlin,
        dq_output_reg_clk, dq_output_reg_clkena,
        dqs_enable_in, dqs_input_data_in, dqs_oe_in,
        dqs_output_data_in_high, dqs_output_data_in_low,
        dqs_output_reg_clk, dqs_output_reg_clkena,
        dqsn_oe_in,
        bidir_dq_input_data_out_high, bidir_dq_input_data_out_low,
        bidir_dq_oe_out, bidir_dq_output_data_out,
        dqs_bus_out, dqs_oe_out, dqs_output_data_out, dqsn_oe_out
        );

// *************************** I/O DECLARATIONS ********************************

        input   [7:0]   bidir_dq_input_data_in;
        input   [7:0]   bidir_dq_oe_in;
        input   [7:0]   bidir_dq_output_data_in_high;
        input   [7:0]   bidir_dq_output_data_in_low;
        input   [5:0]   dll_delayctrlin;
        input           dq_output_reg_clk;
        input           dq_output_reg_clkena;
        input           dqs_enable_in;
        input           dqs_input_data_in;
        input           dqs_oe_in;
        input           dqs_output_data_in_high;
        input           dqs_output_data_in_low;
        input           dqs_output_reg_clk;
        input           dqs_output_reg_clkena;
        input           dqsn_oe_in;
        output  [7:0]   bidir_dq_input_data_out_high;
        output  [7:0]   bidir_dq_input_data_out_low;
        output  [7:0]   bidir_dq_oe_out;
        output  [7:0]   bidir_dq_output_data_out;
        output          dqs_bus_out;
        output          dqs_oe_out;
        output          dqs_output_data_out;
        output          dqsn_oe_out;

// ************************** LOCAL DECLARATIONS *******************************

        reg     [7:0]   bidir_dq_input_data_out_high;
        reg     [7:0]   bidir_dq_input_data_out_low;
        reg     [7:0]   dq_data_latch;

        reg             dqs_bus_out;
        reg             dqsn_bus_out;

        reg             dqs;
        reg             en;
        reg     [7:0]   doea, doeb;
        reg     [7:0]   doa, dob;
        reg             soea, soeb;
        reg             soa, sob;
        reg             dqs_output_data_out;

// ****************************** MODULE BODY **********************************

// -----------------------------------------------------------------------------
// DQ Input
always @(posedge dqs_bus_out)
        dq_data_latch   <= #1 bidir_dq_input_data_in;

always @(negedge dqs_bus_out) begin
        bidir_dq_input_data_out_high
                        <= #1 bidir_dq_input_data_in;
        bidir_dq_input_data_out_low
                        <= #1 dq_data_latch;
end

always @(negedge dqs or posedge dqs_enable_in)
        if (dqs_enable_in)
                en      <= #1 1'b1;
        else
                en      <= #1 1'b0;

always @(dqs_input_data_in)
        dqs             <= #2 dqs_input_data_in;

always @(dqs or en) begin
        dqs_bus_out     = en & dqs;
        dqsn_bus_out    = ~en | ~dqs;
end

// -----------------------------------------------------------------------------
// DQ OE
always @(posedge dq_output_reg_clk)
        if (dq_output_reg_clkena)
                doea    <= #1 bidir_dq_oe_in;

always @(negedge dq_output_reg_clk)
        if (dq_output_reg_clkena)
                doeb    <= #1 doea;

assign bidir_dq_oe_out          = {8{doea & doeb}};

// -----------------------------------------------------------------------------
// DQ Out
always @(posedge dq_output_reg_clk)
        if (dq_output_reg_clkena)
                doa     <= #1 bidir_dq_output_data_in_high;

always @(posedge dq_output_reg_clk)
        if (dq_output_reg_clkena)
                dob     <= #1 bidir_dq_output_data_in_low;

assign bidir_dq_output_data_out = dq_output_reg_clk ? doa : dob;

// -----------------------------------------------------------------------------
// DQS OE
always @(posedge dqs_output_reg_clk)
        if (dqs_output_reg_clkena)
                soea    <= #1 dqs_oe_in;

always @(negedge dqs_output_reg_clk)
        if (dqs_output_reg_clkena)
                soeb    <= #1 soea;

assign dqs_oe_out               = soea;
assign dqsn_oe_out              = soea;

// -----------------------------------------------------------------------------
// DQS Out
always @(posedge dqs_output_reg_clk)
        if (dqs_output_reg_clkena)
                soa     <= #1 dqs_output_data_in_high;

always @(posedge dqs_output_reg_clk)
        if (dqs_output_reg_clkena)
                sob     <= #1 dqs_output_data_in_low;

always @(dqs_output_reg_clk or soa or sob)
        dqs_output_data_out
                        <= dqs_output_reg_clk ? soa : sob;

// ************************** FUNCTIONS and TASKS ******************************

endmodule

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

付録のDDRモデル(他のDDRモデルが重い場合の代わり)

/* **************************** MODULE PREAMBLE ********************************

        Copyright (c) 2011, ArchiTek
        This document constitutes confidential and proprietary information
        of ArchiTek. All rights reserved.
        // x8bit限定
*/

// ***************************** MODULE HEADER *********************************

module ddrse8 (
        ck,
        ck_n,
        cke,
        cs_n,
        ras_n,
        cas_n,
        we_n,
        dm_rdqs,
        ba,
        addr,
        dq,
        dqs,
        dqs_n,
        rdqs_n,
        odt
        );

// ************************ PARAMETER DECLARATIONS *****************************

        // DDR3なら1で動作、ただしreset_n端子は削ってある
        parameter       SPD     = 0;

// *************************** I/O DECLARATIONS ********************************

        input           ck;
        input           ck_n;
        input           cke;
        input           cs_n;
        input           ras_n;
        input           cas_n;
        input           we_n;
        input           dm_rdqs;
        input   [2:0]   ba;
        input   [15:0]  addr;
        inout   [7:0]   dq;
        inout           dqs;
        inout           dqs_n;
        output          rdqs_n;
        input           odt;

// ************************** LOCAL DECLARATIONS *******************************

        // Prepare
        reg     [63:0]  memory[0:33554431];             // 256MB

        reg             reg_bl;                         // Burst Length 0:4 1:8
        reg             reg_bt;                         // Burst Type 0:Seq 1:Inter
        reg     [3:0]   reg_cl;                         // Cas Latency
        reg     [3:0]   reg_cwl;                        // Cas Write Latency
        reg     [2:0]   reg_al;                         // Additive Latency
        wire    [3:0]   al      = reg_cl - reg_al;

        // Request
        reg     [14:0]  acta;
        reg     [9:0]   cola;
        reg     [2:0]   actba;
        reg             rxw;

        reg     [1:0]   vpip[0:31];
        reg     [1:0]   cpip[0:31];
        reg     [27:0]  apip[0:31];

        reg     [1:0]   ca;

        wire    [3:0]   cntl    = {cs_n, ras_n, cas_n, we_n};

        wire            msr     = (cntl == 4'b0000);
        wire            pre     = (cntl == 4'b0010);
        wire            act     = (cntl == 4'b0011);
        wire            com     = (cntl[3:1] == 3'b010);

        wire            put     = com | (|ca);

        // Write
        reg             wr;
        reg     [2:0]   waddpt;
        reg     [2:0]   waddnt;
        reg     [27:0]  lwaddr;

        wire    [4:0]   wl      = SPD ? (al + reg_cwl - 'h1) : (reg_al + reg_cl - 'h2);
        wire    [1:0]   wp      = cpip[wl];
        wire    [27:0]  waddr   = apip[wl];
        wire    [2:0]   waddp   = {wp, 1'b0} + waddr[2:0];
        wire    [2:0]   waddn   = {wp, 1'b1} + waddr[2:0];
        wire    [63:0]  wdata   = memory[lwaddr[27:3]];
        wire    [63:0]  wvldp   = {{56{1'b0}}, {8{~dm_rdqs}}} << (waddpt * 8);
        wire    [63:0]  wvldn   = {{56{1'b0}}, {8{~dm_rdqs}}} << (waddnt * 8);
        wire    [63:0]  wdp     = ~wvldp & wdata | wvldp & {8{dq}};
        wire    [63:0]  wdn     = ~wvldn & wdata | wvldn & {8{dq}};

        // Read
        reg             rd;
        reg     [2:0]   raddpt;
        reg     [2:0]   raddnt;
        reg     [63:0]  lrdata;

        reg     [7:0]   qp, qn;
        reg             doe;
        reg             soe;

        wire    [4:0]   rl      = SPD ? (al + reg_cl - 'h1) : (reg_al + reg_cl - 'h1);
        wire    [1:0]   rp      = cpip[rl];
        wire    [27:0]  raddr   = apip[rl];
        wire    [2:0]   raddp   = {rp, 1'b0} + raddr[2:0];
        wire    [2:0]   raddn   = {rp, 1'b1} + raddr[2:0];
        wire    [63:0]  rdata   = memory[raddr[27:3]];

        integer         i;

// ****************************** MODULE BODY **********************************

// -----------------------------------------------------------------------------
// Initialize
initial begin
        reg_bl  = 1'b1;
        reg_bt  = 1'b0;
        reg_cl  = 4'h3;
        reg_cwl = 4'h3;

        ca      = 2'h0;

        for (i=0; i<32; i=i+1) begin
                vpip[i] = 2'h0;
                cpip[i] = 2'h0;
                apip[i] = 28'h0000000;
        end
end

// -----------------------------------------------------------------------------
// Request
always @(posedge ck)                                    // REG
        if (msr & (ba[1:0] == 2'b00)) begin
                reg_bl  <= #1 SPD ? (addr[1:0] == 2'b00) : (addr[2:0] != 3'b010);
                reg_bt  <= #1 addr[3];
                reg_cl  <= #1 addr[6:4] + (SPD ? 'h4 : 'h0);
        end

always @(posedge ck)                                    // REG
        if (msr & (ba[1:0] == 2'b01))
                reg_al  <= #1 SPD ? {1'b0, addr[4:3]} : addr[5:3];

always @(posedge ck)                                    // REG
        if (msr & (ba[1:0] == 2'b10))
                reg_cwl <= #1 addr[5:3] + 'h5;

always @(posedge ck)                                    // ACT
        if (act) begin
                acta    <= #1 addr[14:0];
                actba   <= #1 ba;
        end

always @(posedge ck)                                    // COM
        if (com) begin
                cola    <= #1 addr[9:0];
                rxw     <= #1 cntl[0];
        end

always @(posedge ck)                                    // COM
        if (pre)
                ca      <= #1 2'h0;
        else if (put)
                ca      <= #1 (ca + 1'h1) & {reg_bl, 1'b1};

always @(posedge ck)                                    // COM & charge
        if (pre)
                for (i=0; i<32; i=i+1) begin
                        vpip[i] <= #1 2'h0;
                        cpip[i] <= #1 2'h0;
                        apip[i] <= #1 28'h0000000;
                end
        else begin
                for (i=31; i>0; i=i-1) begin
                        vpip[i] <= #1 vpip[i-1];
                        cpip[i] <= #1 cpip[i-1];
                        apip[i] <= #1 apip[i-1];
                end

                if (com) begin
                        vpip[0] <= #1 {put, cntl[0]};
                        cpip[0] <= #1 ca;
                        apip[0] <= #1 {actba, acta, addr[9:0]};
                end
                else begin
                        vpip[0] <= #1 {put, rxw};
                        cpip[0] <= #1 ca;
                end
        end

// -----------------------------------------------------------------------------
// Write
always @(negedge ck) begin
        wr      <= #1 (vpip[wl] == 2'b10);
        waddpt  <= #1 reg_bt ? ({wp, 1'b0} ^ waddr[2:0]) : {wp[1] ^ waddr[2], waddp[1:0]};
        waddnt  <= #1 reg_bt ? ({wp, 1'b1} ^ waddr[2:0]) : {wp[1] ^ waddr[2], waddn[1:0]};
        lwaddr  <= #1 waddr;
end

always @(posedge ck)
        if (wr)
                memory[lwaddr[27:3]]
                        <= #1 wdp;

always @(negedge ck)
        if (wr)
                memory[lwaddr[27:3]]
                        <= #1 wdn;

// -----------------------------------------------------------------------------
// Read
always @(negedge ck) begin
        rd      <= #1 (vpip[rl] == 2'b11);
        raddpt  <= #1 reg_bt ? ({rp, 1'b0} ^ raddr[2:0]) : {rp[1] ^ raddr[2], raddp[1:0]};
        raddnt  <= #1 reg_bt ? ({rp, 1'b1} ^ raddr[2:0]) : {rp[1] ^ raddr[2], raddn[1:0]};
        lrdata  <= #1 rdata;
end

always @(posedge ck)
        if (rd) case (raddpt)
                3'h0: qp <= #1 lrdata[7:0];
                3'h1: qp <= #1 lrdata[15:8];
                3'h2: qp <= #1 lrdata[23:16];
                3'h3: qp <= #1 lrdata[31:24];
                3'h4: qp <= #1 lrdata[39:32];
                3'h5: qp <= #1 lrdata[47:40];
                3'h6: qp <= #1 lrdata[55:48];
                3'h7: qp <= #1 lrdata[63:56];
        endcase

always @(negedge ck)
        if (rd) case (raddnt)
                3'h0: qn <= #1 lrdata[7:0];
                3'h1: qn <= #1 lrdata[15:8];
                3'h2: qn <= #1 lrdata[23:16];
                3'h3: qn <= #1 lrdata[31:24];
                3'h4: qn <= #1 lrdata[39:32];
                3'h5: qn <= #1 lrdata[47:40];
                3'h6: qn <= #1 lrdata[55:48];
                3'h7: qn <= #1 lrdata[63:56];
        endcase

always @(posedge ck)
        doe     <= #1 (vpip[rl] == 2'b11);

always @(posedge ck)
        soe     <= #1 (vpip[rl-1] == 2'b11) | (vpip[rl] == 2'b11);

assign dq       = doe ? (ck ? qp : qn) : 8'hzz;         // 8'hzz if bi-directional buffer
assign dqs      = soe ? ~rdqs_n : 1'bz;                 // 2'bzz if bi-directional buffer
assign dqs_n    = soe ? rdqs_n : 1'bz;                  // 2'bzz if bi-directional buffer
assign rdqs_n   = ck_n | ~doe | ~rd;

// ************************** FUNCTIONS and TASKS ******************************

endmodule

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

回路デザイン > 設計例 [DDR制御(論理)] > テスト    このページのTOP ▲

[1]
Micronのモデルが便利です。例えば、これなど。
[2]
LSIやFPGAでは、PLLやDLLを用いて生成します。実際には、ジッターや安定性など苦労するところですが、論理的にはいたって簡単に記述できます。
[3]
PHYがシーケンサーを持って自動的にする場合もありますし、DDR制御側にある程度の機能が必要になる場合もあります。

端子の遅延パラメータの変更と、試験的なアクセスによるデータの正否確認とフィードバックが一連の動作になります。
[4]
効率によっては、性能達成のためDual Channel化が必要になるなどコストに影響します。

システムにとって重要な要素なので、マスターのアドレッシング、複数マスターを用いた最適なアービトレーションなど、工夫のしどころです。