本文相对简单,只供自己看看就行。从其它的博客找了个代码,然后记录下仿真波形。
直接使用quartus生成的DDR2 IP核,然后实现循环 -->写入burst长度的数据后读出。
代码数据的传输是32位,实际使用了两片IC。因此IP核也是32位交互。
burst=4,是IC固有的=预取值。而代码中LEN为用户一次想要读取数据的长度的。与local_size和burst_length无关。
分为4个状态: IDLE、WRITER、WRITING、READR、READING。WRITER和READR状态下,开始读写burst的第一个数据,其余的在WRITING和READING状态读写。
local_ready:很重要的一个指示信号。要进行读写操作,必须在IP核(ddr2)准备好的情况下进行。因此,需要一直对此信号进行检测判断。
写入地址(mem_local_addr),一开始为1,当读到的数据长度rdatalen=5时(初始值=1),mem_local_addr <= mem_local_addr + 4;
命令
连续写
连续读
读完写
如下图所示:每次先写入连续4个数据,(例如1,2,3,4),然后再依次读出。
读过程及下一次写
顶层代码:
module ddr2wrtest (
input clk , // Clock
input global_reset_n , // Asynchronous reset active low
output [ 12: 0] mem_addr ,
output [ 2: 0] mem_ba ,
output mem_cas_n ,
output mem_cke ,
inout mem_clk ,
inout mem_clk_n ,
output mem_cs_n ,
output [ 1: 0] mem_dm ,
inout [ 15: 0] mem_dq ,
inout [ 1: 0] mem_dqs ,
output mem_odt ,
output mem_ras_n ,
output mem_we_n
);
parameter LEN = 8; // 突发长度 4
parameter IDEL = 5'b00001; // 空闲态
parameter WRITER = 5'b00010; // 准备写
parameter WRITING = 5'b00100; // 写状态
parameter READR = 5'b01000; // 准备读
parameter READING = 5'b10000; // 读状态
reg [ 5: 0] state;
//**********************与IP相连部分********************//
reg [ 24: 0] mem_local_addr =1 ;
wire mem_local_init_done ;
wire local_burstbegin_sig ;
wire [ 31: 0] mem_local_rdata ;
wire mem_local_rdata_valid ;
wire mem_local_read_req ;
wire mem_local_ready ;
wire [ 3: 0] mem_local_size ;
reg [ 31: 0] mem_local_wdata ;
wire mem_local_write_req ;
wire phy_clk ;
wire reset_phy_clk_n ;
//*****************************************************//
wire rst_n ;
reg [ 3: 0] rdatalen ; // 读突发长度计数器
reg [ 3: 0] wdatalen ; // 写突发长度计数器
reg [100: 0] state_name ; // 状态名
assign rst_n = reset_phy_clk_n&&mem_local_init_done;
assign mem_local_size = LEN;
assign local_burstbegin_sig = ((state == WRITER&&mem_local_ready) || (state == READR&&mem_local_ready))?1:0;
assign mem_local_write_req = (state == WRITING)?1:0;
assign mem_local_read_req = (state == READR&&mem_local_ready)?1:0;
ddr2 ddr2_inst
(
//==============================《 》===============================
.aux_full_rate_clk ( ), //全速率时钟
.aux_half_rate_clk ( ), //半速率时钟
//==============================《 用户操作信号 》===============================
.global_reset_n (global_reset_n ), //全局复位
.local_address (mem_local_addr ), //当前操作地址
.local_be (8'hff ), //数据掩码
.local_burstbegin (local_burstbegin_sig ), //突发起始信号
.local_init_done (mem_local_init_done ), //初始化完成信号
.local_rdata (mem_local_rdata ), //读数据总线
.local_rdata_valid (mem_local_rdata_valid ), //读有效标志
.local_read_req (mem_local_read_req ), //读请求,保持一个时钟周期
.local_ready (mem_local_ready ), //接受到请求
.local_refresh_ack ( ), //自动刷新
.local_size (mem_local_size ), //突发长度
.local_wdata (mem_local_wdata ), //写数据总线
.local_write_req (mem_local_write_req ), //写请求
//==============================《 与DDR2相连的信号 》===============================
.mem_addr (mem_addr ), //地址总线
.mem_ba (mem_ba ), //bank地址
.mem_cas_n (mem_cas_n ), //行选通
.mem_cke (mem_cke ), //时钟使能
.mem_clk (mem_clk ), //操作时钟
.mem_clk_n (mem_clk_n ), //反向时钟
.mem_cs_n (mem_cs_n ), //片选信号
.mem_dm (mem_dm ), //DDR2数据屏蔽信号
.mem_dq (mem_dq ), //数据总线
.mem_dqs (mem_dqs ), //数据选取脉冲信号
.mem_odt (mem_odt ), //片内终结信号
.mem_ras_n (mem_ras_n ), //行选通
.mem_we_n (mem_we_n ), //使能
//==============================《 用户操作信号 》===============================
.phy_clk (phy_clk ), //提供给用户的操作时钟
.pll_ref_clk (clk ), //给ddr2的输入时钟
.reset_phy_clk_n (reset_phy_clk_n ), //提供给用户的复位信号
.reset_request_n ( ), //当PLL锁定后为低电平
.soft_reset_n (1'b1 ) //软复位,不包括PLL的复位
);
always@(*)
case (state)
IDEL : state_name = "IDEL";
WRITER : state_name = "WRITER" ;
WRITING : state_name = "WRITING" ;
READR : state_name = "READR" ;
READING : state_name = "READING";
default : /* default */;
endcase
always@(posedge phy_clk or negedge rst_n)begin
if(!rst_n)
state <= IDEL;
else
case (state)
IDEL : state <= WRITER;
WRITER : if(mem_local_ready)
state <= WRITING;
else
state <= WRITER;
WRITING : if(wdatalen == LEN)
state <= READR;
else
state <= state;
READR : if(mem_local_ready)
state <= READING;
else
state <= READR;
READING : if(rdatalen == LEN)
state <= IDEL;
else
state <= state;
default : state <= state;
endcase
end
// 写的数据自加1
always@(posedge phy_clk or negedge rst_n)begin
if(!rst_n)
mem_local_wdata <= 1;
else if(state == WRITING && mem_local_ready)
mem_local_wdata <= mem_local_wdata + 1;
else
mem_local_wdata <= mem_local_wdata;
end
// 当前写的数据突发长度
always@(posedge phy_clk or negedge rst_n)begin
if(!rst_n)
wdatalen <= 1;
else if(state == WRITING && mem_local_ready)
wdatalen <= wdatalen + 1;
else if(state == WRITING && !mem_local_ready)
wdatalen <= wdatalen ;
else
wdatalen <= 1;
end
// 当前读的数据突发长度
always@(posedge phy_clk or negedge rst_n)begin
if(!rst_n)
rdatalen <= 1;
else if(state == READING && mem_local_rdata_valid)
rdatalen <= rdatalen + 1;
else
rdatalen <= 1;
end
// 操作地址自加
always@(posedge phy_clk or negedge rst_n)begin
if(!rst_n)
mem_local_addr <= 1;
else if(rdatalen == LEN)
mem_local_addr <= mem_local_addr + LEN;
end
endmodule
测试代码
`timescale 1ns/1ps
module test_tb ();
reg clk ;
reg global_reset_n;
wire [ 12: 0] mem_addr ;
wire [ 2: 0] mem_ba ;
wire mem_cas_n ;
wire mem_cke ;
wire mem_clk ;
wire mem_clk_n ;
wire mem_cs_n ;
wire [ 1: 0] mem_dm ;
wire [ 15: 0] mem_dq ;
wire [ 1: 0] mem_dqs ;
wire mem_odt ;
wire mem_ras_n ;
wire mem_we_n ;
ddr2wrtest u1 (
.clk (clk ), // Clock
.global_reset_n (global_reset_n ) , // Asynchronous reset active low
.mem_addr (mem_addr ),
.mem_ba (mem_ba ),
.mem_cas_n (mem_cas_n ),
.mem_cke (mem_cke ),
.mem_clk (mem_clk ),
.mem_clk_n (mem_clk_n ),
.mem_cs_n (mem_cs_n ),
.mem_dm (mem_dm ),
.mem_dq (mem_dq ),
.mem_dqs (mem_dqs ),
.mem_odt (mem_odt ),
.mem_ras_n (mem_ras_n ),
.mem_we_n (mem_we_n )
);
ddr2_mem_model mem (
.mem_dq (mem_dq),
.mem_dqs (mem_dqs),
.mem_dqs_n (mem_dqs_n),
.mem_addr (mem_addr),
.mem_ba (mem_ba),
.mem_clk (mem_clk),
.mem_clk_n (mem_clk_n),
.mem_cke (mem_cke),
.mem_cs_n (mem_cs_n),
.mem_ras_n (mem_ras_n),
.mem_cas_n (mem_cas_n),
.mem_we_n (mem_we_n),
.mem_dm (mem_dm),
.mem_odt (mem_odt)
);
always #10 clk = ~clk;
initial begin
clk = 0;
global_reset_n = 0;
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
@(posedge clk);
global_reset_n = 1;
end
endmodule