基于FPGA的SDRAM讀寫子產品設計
- SDRAM工作狀态轉移圖
- SDRAM寫時序圖
- SDRAM的讀時序圖
- SDRAM寫子產品狀态機
- SDRAM的讀寫操作代碼
- SDRAM讀寫測試子產品
- SDRAM仿真測試
- 參考文獻
- 總結
SDRAM工作狀态轉移圖
SDRAM晶片是按照一定的狀态時序操作的,是以我們為了更好的寫代碼需要清楚SDRAM的狀态轉移圖,如下:
其中粗線是執行完該狀态後,不需要我們給什麼指令,自動轉移到下一個狀态,但是細線需要我們給出相應的指令。
這裡引用Kevin老師對其中一個過程的描述,從“IDLE”狀态跳到“WRITE”狀态的路線以及從“WRITE”狀态跳到“IDLE”狀态的路線:
IDLE 狀态到 WRITE 狀态:
- 在 IDLE 狀态需要先給 ACT 指令激活某一行, 此時處于 Row Active 狀态;
- 在 Row Active 狀态之後, 給 Write 指令則會進入 WRITE 狀态;
-
在 WRITE 狀态後, 再給一次 Write 指令, 就可以繼續寫入資料。
WRITE 狀态到 IDLE 狀态:
-
在 WRITE 狀态給 PRE 指令, 則 SDRAM 将跳出 WRITE 狀态進入 Precharge
狀态;
-
在 Precharge 狀态後, 就會自動進入 IDLE 狀态了
要從 WRITE 狀态跳到 IDLE 狀态的一個原因是, 我們需要進行重新整理操作, 進入重新整理操作, 必須從 IDLE 狀态進入。我們注意到WRITE 狀态下邊還有一個 WRITEA 狀态,執行完一次WRITE狀态它會自動的進入到
Precharge 狀态,是以比在 WRITE 狀态的工作效率要低很多,也比較少使用。
SDRAM寫時序圖
從SDRAM的技術手冊中我們很容易找到對應的寫時序圖,如下:
上面對應的時間也可以從手冊中找到,在上兩篇關于SDRAM的文章中我們已經給出了每個時間的最小值。
SDRAM的讀時序圖
同理,我們給出SDRAM的讀時序圖,如下:
這裡是給出讀延遲2個時鐘的時序圖,注意我們模式寄存器中設定的是三個時鐘延遲。同時,我們注意,***SDRAM的狀态轉移圖,讀寫操作是對稱的,讀寫的時序也是對稱的,是以我們在寫完寫操作之後可以利用notepad++的全部替換功能,替換一下寫子產品的名稱、寫操作的指令、删除寫子產品的資料端口并增加都子產品的使能端口,便可以把寫子產品轉換成讀子產品。***是以接下來我們将以寫子產品為例進行設計SDRAM的讀寫子產品。
同學們也可以對比上面兩個時序圖與SDRAM的狀态轉移圖會發現兩者完美契合。
SDRAM寫子產品狀态機
假設我們現在需要往 SDRAM 中寫入兩行資料, 那什麼時候可以退出仲裁狀
态機的寫狀态:
- 資料已經寫完;
- SDRAM 需要進行重新整理操作;
- 資料未寫完, 需要激活下一行繼續寫。
為什麼按照上面的條件進行設計SDRAM的寫子產品,因為上面的條件比較齊全,可以将我們所需要考慮的情況考慮的比較完善。
這裡也是使用了Kevin的狀态機,但是沒有按照該老師的時序圖設計,因為自我感覺有了狀态機再看時序圖反倒有些多餘。
這裡雖然沒使用Kevin老師的時序圖,這裡也給出來,友善大家調試,對這裡有問題的同學可以參考該老師的教學視訊,但是還是支援大家自我完成該子產品的設計。
至此我們便完成了SDRAM的讀寫理論設計,接下來我們便完成該子產品的代碼設計。
SDRAM的讀寫操作代碼
SDRAM頂層子產品的設計,這裡包括仲裁子產品:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : sdram_top.v
// Create Time : 2020-02-09 17:22:24
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_top(
//System Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output wire sdram_clk ,
output wire sdram_cke ,
output reg sdram_cs_n ,
output reg sdram_cas_n ,
output reg sdram_ras_n ,
output reg sdram_we_n ,
output reg [ 1:0] sdram_bank ,
output reg [11:0] sdram_addr ,
output wire [ 1:0] sdram_dqm ,
inout [15:0] sdram_dq ,
//Others
input wr_trig ,
input rd_trig
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
localparam NOP = 4'b0111 ;
localparam IDLE = 5'b0_0001 ;
localparam ARBIT = 5'b0_0010 ;
localparam AREF = 5'b0_0100 ;
localparam WRITE = 5'b0_1000 ;
localparam READ = 5'b1_0000 ;
//sdram_init
wire [ 3:0] init_cmd ;
wire [11:0] init_addr ;
wire init_done ;
//AREF
wire aref_req ;
reg aref_en ;
wire aref_end ;
wire [ 3:0] aref_cmd ;
wire [11:0] aref_addr ;
//WRITE
wire [ 3:0] wr_cmd ;
wire [11:0] wr_addr ;
wire [ 1:0] wr_bank_addr ;
wire [15:0] wr_data ;
reg wr_en ;
wire wr_end ;
wire wr_req ;
//READ
wire [ 3:0] rd_cmd ;
wire [11:0] rd_addr ;
wire [ 1:0] rd_bank_addr ;
reg rd_en ;
wire rd_end ;
wire rd_req ;
wire rd_data_en ;
//ARBIT
reg [ 4:0] state ;
//Others
reg [15:0] sdram_dq1 ;
reg sdram_dq_en ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign sdram_dqm = 2'b00;
assign sdram_clk = ~sclk;
assign sdram_cke = 1'b1;
assign sdram_dq = sdram_dq_en == 1'b1 ? sdram_dq1 : 16'hzzzz;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE : if(init_done == 1'b1)
state <= ARBIT;
else
state <= state;
ARBIT : if(aref_req == 1'b1)
state <= AREF;
else if(wr_req == 1'b1)
state <= WRITE;
else if(rd_req == 1'b1)
state <= READ;
else
state <= state;
AREF : if(aref_end == 1'b1)
state <= ARBIT;
else
state <= state;
WRITE : if(wr_end == 1'b1)
state <= ARBIT;
else
state <= state;
READ : if(rd_end == 1'b1)
state <= ARBIT;
else
state <= state;
default : state <= IDLE;
endcase
always @(*)
case(state)
IDLE : begin
sdram_addr = init_addr;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = init_cmd;
sdram_dq_en = 1'b0;
end
AREF : begin
sdram_addr = aref_addr;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = aref_cmd;
end
WRITE : begin
sdram_addr = wr_addr;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = wr_cmd;
sdram_dq1 = wr_data;
sdram_bank = wr_bank_addr;
sdram_dq_en = 1'b1;
end
READ : begin
sdram_addr = rd_addr;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = rd_cmd;
sdram_bank = rd_bank_addr;
sdram_dq_en = 1'b0;
end
default : begin
sdram_addr = 12'd0;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = NOP;
sdram_dq1 = 16'd0;
sdram_bank = 2'b00;
sdram_dq_en = 1'b0;
end
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_en <= 1'b0;
else if(state == ARBIT && aref_req == 1'b1)
aref_en <= 1'b1;
else
aref_en <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_en <= 1'b0;
else if(state == ARBIT && aref_req == 1'b1)
wr_en <= 1'b0;
else if(state == ARBIT && wr_req == 1'b1)
wr_en <= 1'b1;
else
wr_en <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_en <= 1'b0;
else if(state == ARBIT && aref_req == 1'b1)
rd_en <= 1'b0;
else if(state == ARBIT && wr_req == 1'b1)
rd_en <= 1'b0;
else if(state == ARBIT && rd_req == 1'b1)
rd_en <= 1'b1;
else
rd_en <= 1'b0;
sdram_init sdram_init_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.sdram_cmd (init_cmd ),
.sdram_addr (init_addr ),
//Others
.init_done (init_done )
);
sdram_aref sdram_aref_inst(
//Sysytem Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.aref_cmd (aref_cmd ),
.aref_addr (aref_addr ),
//Others
.aref_req (aref_req ),
.aref_end (aref_end ),
.aref_en (aref_en ),
.init_done (init_done )
);
sdram_write sdram_write_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.wr_cmd (wr_cmd ),
.wr_addr (wr_addr ),
.bank_addr (wr_bank_addr ),
.wr_data (wr_data ),
//Communication Interfaces
.wr_trig (wr_trig ),
.wr_en (wr_en ),
.wr_end (wr_end ),
.wr_req (wr_req ),
.aref_req (aref_req )
);
sdram_read sdram_read_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.rd_cmd (rd_cmd ),
.rd_addr (rd_addr ),
.bank_addr (rd_bank_addr ),
//Communication Interfaces
.rd_trig (rd_trig ),
.rd_en (rd_en ),
.rd_end (rd_end ),
.rd_req (rd_req ),
.aref_req (aref_req ),
.rd_data_en (rd_data_en )
);
endmodule
SDRAM上電初始化子產品:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : sdram_init.v
// Create Time : 2020-02-09 16:20:31
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_init(
//System Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output reg [ 3:0] sdram_cmd ,
output reg [11:0] sdram_addr ,
//Others
output reg init_done
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
localparam DELAY_200US = 20000 ;
//SDRAM Command
localparam NOP = 4'b0111 ;
localparam PRE = 4'b0010 ;
localparam AREF = 4'b0001 ;
localparam MSET = 4'b0000 ;
reg [14:0] cnt_200us ;
reg flag_200us ;
reg [ 4:0] cnt_cmd ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_200us <= 11'd0;
else if(flag_200us == 1'b0)
cnt_200us <= cnt_200us + 1'b1;
else
cnt_200us <= cnt_200us;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_200us <= 1'b0;
else if(cnt_200us >= DELAY_200US)
flag_200us <= 1'b1;
else
flag_200us <= flag_200us;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_cmd <= 5'd0;
else if(flag_200us == 1'b1 && cnt_cmd <= 5'd19)
cnt_cmd <= cnt_cmd + 1'b1;
else
cnt_cmd <= cnt_cmd;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
sdram_cmd <= NOP;
else case(cnt_cmd)
1 : sdram_cmd <= PRE;
3 : sdram_cmd <= AREF;
11 : sdram_cmd <= AREF;
19 : sdram_cmd <= MSET;
default : sdram_cmd <= NOP;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
sdram_addr <= 12'b0100_0000_0000;
else if(cnt_cmd == 5'd19)
sdram_addr <= 12'b0000_0011_0010;
else
sdram_addr <= 12'b0100_0000_0000;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
init_done <= 1'b0;
else if(cnt_cmd > 5'd19)
init_done <= 1'b1;
else
init_done <= init_done;
endmodule
SDRAM自重新整理子產品:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : sdram_aref.v
// Create Time : 2020-02-10 13:34:03
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_aref(
//Sysytem Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output reg [ 3:0] aref_cmd ,
output wire [11:0] aref_addr ,
//Others
input init_done ,
output reg aref_req ,
output reg aref_end ,
input aref_en
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
localparam NOP = 4'b0111 ;
localparam PRE = 4'b0010 ;
localparam AREF = 4'b0001 ;
localparam DELAY_15US = 11'd1500 ;
reg aref_flag ;
reg [ 2:0] cnt_cmd ;
reg [10:0] cnt_15ms ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign aref_addr = 12'b0100_0000_0000;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_flag <= 1'b0;
else if(cnt_cmd >= 3'd7)
aref_flag <= 1'b0;
else if(aref_en == 1'b1)
aref_flag <= 1'b1;
else
aref_flag <= aref_flag;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_cmd <= 3'd0;
else if(cnt_cmd >= 3'd7)
cnt_cmd <= 3'd0;
else if(aref_flag == 1'b1)
cnt_cmd <= cnt_cmd + 1'b1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_cmd <= NOP;
else case(cnt_cmd)
1 : aref_cmd <= PRE;
4 : aref_cmd <= AREF;
default : aref_cmd <= NOP;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_end <= 1'b0;
else if(cnt_cmd >= 3'd7)
aref_end <= 1'b1;
else
aref_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_15ms <= 20'd0;
else if(cnt_15ms == DELAY_15US)
cnt_15ms <= 20'd0;
else if(init_done == 1'b1)
cnt_15ms <= cnt_15ms + 1'b1;
else
cnt_15ms <= 20'd0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_req <= 1'b0;
else if(cnt_15ms == DELAY_15US)
aref_req <= 1'b1;
else if(aref_en == 1'b1)
aref_req <= 1'b0;
else
aref_req <= aref_req;
endmodule
SDRAM寫子產品:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : sdram_write.v
// Create Time : 2020-02-10 20:05:26
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_write(
//System Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output reg [ 3:0] wr_cmd ,
output reg [11:0] wr_addr ,
output wire [ 1:0] bank_addr ,
output reg [15:0] wr_data ,
//Communication Interfaces
input wr_trig ,
input wr_en ,
output reg wr_end ,
output reg wr_req ,
input aref_req
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
// Define State
localparam S_IDLE = 5'b0_0001 ;
localparam S_REQ = 5'b0_0010 ;
localparam S_ACT = 5'b0_0100 ;
localparam S_WR = 5'b0_1000 ;
localparam S_PRE = 5'b1_0000 ;
// SDRAM Command
localparam CMD_NOP = 4'b0111 ;
localparam CMD_PRE = 4'b0010 ;
localparam CMD_AREF = 4'b0001 ;
localparam CMD_ACT = 4'b0011 ;
localparam CMD_WR = 4'b0100 ;
reg [ 4:0] state ;
reg flag_act_end ;
reg row_end ;
reg [ 1:0] burst_cnt ;
reg data_end ;
reg flag_row_end ;
reg flag_data_end ;
reg flag_aref_req ;
reg [ 8:0] col_addr ;
reg [ 1:0] burst_cnt_r ;
reg [11:0] row_addr ;
reg data_end_r ;
reg data_end_r2 ;
reg row_end_r ;
reg row_end_r2 ;
reg flag_s_wr ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign bank_addr = 2'b00;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= S_IDLE;
else case(state)
S_IDLE : if(wr_trig == 1'b1)
state <= S_REQ;
else
state <= state;
S_REQ : if(wr_en == 1'b1)
state <= S_ACT;
else
state <= state;
S_ACT : if(flag_act_end == 1'b1)
state <= S_WR;
else
state <= state;
S_WR : if(data_end_r == 1'b1 || row_end_r == 1'b1)
state <= S_PRE;
else if(burst_cnt_r == 2'd2 && aref_req == 1'b1)
state <= S_PRE;
else
state <= state;
S_PRE : if(flag_data_end == 1'b1)
state <= S_IDLE;
else if(flag_aref_req == 1'b1)
state <= S_REQ;
else if(flag_row_end == 1'b1)
state <= S_ACT;
else
state <= state;
default : state <= S_IDLE;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_aref_req <= 1'b0;
else if(state == S_PRE && aref_req == 1'b1)
flag_aref_req <= 1'b1;
else
flag_aref_req <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_act_end <= 1'b0;
else if(state == S_ACT && wr_cmd == CMD_ACT)
flag_act_end <= 1'b1;
else
flag_act_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
row_end <= 1'b0;
else if(col_addr == 9'd508 && burst_cnt == 2'd1)
row_end <= 1'b1;
else
row_end <= 1'b0;
always @(posedge sclk)begin
row_end_r <= row_end;
row_end_r2 <= row_end_r;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
burst_cnt <= 2'd0;
else if(state == S_WR)
burst_cnt <= burst_cnt + 1'b1;
else
burst_cnt <= 2'd0;
always @(posedge sclk)
burst_cnt_r <= burst_cnt;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
data_end <= 1'b0;
else if(row_addr == 12'd1 && col_addr == 9'd508 && burst_cnt == 2'd1)
data_end <= 1'b1;
else
data_end <= 1'b0;
always @(posedge sclk)begin
data_end_r <= data_end;
data_end_r2 <= data_end_r;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_row_end <= 1'b0;
else if(state == S_PRE && row_end_r2 == 1'b1)
flag_row_end <= 1'b1;
else
flag_row_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_data_end <= 1'b0;
else if(state == S_PRE && data_end_r2 == 1'b1)
flag_data_end <= 1'b1;
else
flag_data_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
col_addr <= 12'd0;
else if(state == S_WR && wr_cmd == CMD_WR && col_addr == 'd508)
col_addr <= 12'd0;
else if(state == S_WR && wr_cmd == CMD_WR)
col_addr <= col_addr + 3'd4;
else
col_addr <= col_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
row_addr <= 12'd0;
else if(data_end == 1'b1)
row_addr <= 12'd0;
else if(state == S_WR && wr_cmd == CMD_WR && col_addr == 'd508)
row_addr <= row_addr + 1'b1;
else
row_addr <= row_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_cmd <= CMD_NOP;
else case(state)
S_ACT : if(wr_cmd != CMD_ACT && flag_act_end == 1'b0)
wr_cmd <= CMD_ACT;
else
wr_cmd <= CMD_NOP;
S_WR : if(data_end == 1'b1 || row_end == 1'b1)
wr_cmd <= CMD_NOP;
else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
wr_cmd <= CMD_NOP;
else if(burst_cnt == 2'd0)
wr_cmd <= CMD_WR;
else
wr_cmd <= CMD_NOP;
S_PRE : if(wr_cmd != CMD_PRE)
wr_cmd <= CMD_PRE;
else
wr_cmd <= CMD_NOP;
default : wr_cmd <= CMD_NOP;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_addr <= 12'd0;
else case(state)
S_ACT : if(wr_cmd != CMD_ACT)
wr_addr <= row_addr;
else
wr_addr <= 12'd0;
S_WR : if(data_end == 1'b1 || row_end == 1'b1)
wr_addr <= 12'd0;
else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
wr_addr <= 12'd0;
else if(burst_cnt == 2'd0)
wr_addr <= {3'b000,col_addr};
else
wr_addr <= 12'd0;
S_PRE : wr_addr <= 12'b0100_0000_0000;
default : wr_addr <= 12'd0;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_s_wr <= 1'b0;
else if(state == S_WR)
flag_s_wr <= 1'b1;
else
flag_s_wr <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_data <= 16'd0;
else if(flag_s_wr == 1'b1)
wr_data <= wr_data + 1'b1;
else
wr_data <= wr_data;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_end <= 1'b0;
else if(flag_data_end == 1'b1)
wr_end <= 1'b1;
else if(state != S_IDLE && flag_aref_req == 1'b1 && wr_end == 1'b0)
wr_end <= 1'b1;
else
wr_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_req <= 1'b0;
else if(wr_en == 1'b1)
wr_req <= 1'b0;
else if(state == S_REQ)
wr_req <= 1'b1;
else
wr_req <= wr_req;
endmodule
SDRAM讀子產品:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : sdram_read.v
// Create Time : 2020-02-11 20:48:41
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_read(
//System Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output reg [ 3:0] rd_cmd ,
output reg [11:0] rd_addr ,
output wire [ 1:0] bank_addr ,
//Communication Interfaces
input rd_trig ,
input rd_en ,
output reg rd_end ,
output reg rd_req ,
input aref_req ,
output reg rd_data_en
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
// Define State
localparam S_IDLE = 5'b0_0001 ;
localparam S_REQ = 5'b0_0010 ;
localparam S_ACT = 5'b0_0100 ;
localparam S_RD = 5'b0_1000 ;
localparam S_PRE = 5'b1_0000 ;
// SDRAM Command
localparam CMD_NOP = 4'b0111 ;
localparam CMD_PRE = 4'b0010 ;
localparam CMD_AREF = 4'b0001 ;
localparam CMD_ACT = 4'b0011 ;
localparam CMD_RD = 4'b0101 ;
reg [ 4:0] state ;
reg flag_act_end ;
reg row_end ;
reg [ 1:0] burst_cnt ;
reg data_end ;
reg flag_row_end ;
reg flag_data_end ;
reg flag_aref_req ;
reg [ 8:0] col_addr ;
reg [ 1:0] burst_cnt_r ;
reg [11:0] row_addr ;
reg data_end_r ;
reg data_end_r2 ;
reg row_end_r ;
reg row_end_r2 ;
reg [ 3:0] cnt_data ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign bank_addr = 2'b00;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= S_IDLE;
else case(state)
S_IDLE : if(rd_trig == 1'b1)
state <= S_REQ;
else
state <= state;
S_REQ : if(rd_en == 1'b1)
state <= S_ACT;
else
state <= state;
S_ACT : if(flag_act_end == 1'b1)
state <= S_RD;
else
state <= state;
S_RD : if(data_end_r == 1'b1 || row_end_r == 1'b1)
state <= S_PRE;
else if(burst_cnt_r == 2'd2 && aref_req == 1'b1)
state <= S_PRE;
else
state <= state;
S_PRE : if(flag_data_end == 1'b1)
state <= S_IDLE;
else if(flag_aref_req == 1'b1)
state <= S_REQ;
else if(flag_row_end == 1'b1)
state <= S_ACT;
else
state <= state;
default : state <= S_IDLE;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_aref_req <= 1'b0;
else if(state == S_PRE && aref_req == 1'b1)
flag_aref_req <= 1'b1;
else
flag_aref_req <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_act_end <= 1'b0;
else if(state == S_ACT && rd_cmd == CMD_ACT)
flag_act_end <= 1'b1;
else
flag_act_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
row_end <= 1'b0;
else if(col_addr == 9'd508 && burst_cnt == 2'd1)
row_end <= 1'b1;
else
row_end <= 1'b0;
always @(posedge sclk)begin
row_end_r <= row_end;
row_end_r2 <= row_end_r;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
burst_cnt <= 2'd0;
else if(state == S_RD)
burst_cnt <= burst_cnt + 1'b1;
else
burst_cnt <= 2'd0;
always @(posedge sclk)
burst_cnt_r <= burst_cnt;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
data_end <= 1'b0;
else if(row_addr == 12'd1 && col_addr == 9'd508 && burst_cnt == 2'd1)
data_end <= 1'b1;
else
data_end <= 1'b0;
always @(posedge sclk)begin
data_end_r <= data_end;
data_end_r2 <= data_end_r;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_row_end <= 1'b0;
else if(state == S_PRE && row_end_r2 == 1'b1)
flag_row_end <= 1'b1;
else
flag_row_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_data_end <= 1'b0;
else if(state == S_PRE && data_end_r2 == 1'b1)
flag_data_end <= 1'b1;
else
flag_data_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
col_addr <= 12'd0;
else if(state == S_RD && rd_cmd == CMD_RD && col_addr == 'd508)
col_addr <= 12'd0;
else if(state == S_RD && rd_cmd == CMD_RD)
col_addr <= col_addr + 3'd4;
else
col_addr <= col_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
row_addr <= 12'd0;
else if(data_end == 1'b1)
row_addr <= 12'd0;
else if(state == S_RD && rd_cmd == CMD_RD && col_addr == 'd508)
row_addr <= row_addr + 1'b1;
else
row_addr <= row_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_cmd <= CMD_NOP;
else case(state)
S_ACT : if(rd_cmd != CMD_ACT && flag_act_end == 1'b0)
rd_cmd <= CMD_ACT;
else
rd_cmd <= CMD_NOP;
S_RD : if(data_end == 1'b1 || row_end == 1'b1)
rd_cmd <= CMD_NOP;
else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
rd_cmd <= CMD_NOP;
else if(burst_cnt == 2'd0)
rd_cmd <= CMD_RD;
else
rd_cmd <= CMD_NOP;
S_PRE : if(rd_cmd != CMD_PRE)
rd_cmd <= CMD_PRE;
else
rd_cmd <= CMD_NOP;
default : rd_cmd <= CMD_NOP;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_addr <= 12'd0;
else case(state)
S_ACT : if(rd_cmd != CMD_ACT)
rd_addr <= row_addr;
else
rd_addr <= 12'd0;
S_RD : if(data_end == 1'b1 || row_end == 1'b1)
rd_addr <= 12'd0;
else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
rd_addr <= 12'd0;
else if(burst_cnt == 2'd0)
rd_addr <= {3'b000,col_addr};
else
rd_addr <= 12'd0;
S_PRE : rd_addr <= 12'b0100_0000_0000;
default : rd_addr <= 12'd0;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_end <= 1'b0;
else if(flag_data_end == 1'b1)
rd_end <= 1'b1;
else if(state != S_IDLE && flag_aref_req == 1'b1 && rd_end == 1'b0)
rd_end <= 1'b1;
else
rd_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_req <= 1'b0;
else if(rd_en == 1'b1)
rd_req <= 1'b0;
else if(state == S_REQ)
rd_req <= 1'b1;
else
rd_req <= rd_req;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_data <= 4'd0;
else if(cnt_data == 4'd7)
cnt_data <= 4'd0;
else if(cnt_data >= 4'd1)
cnt_data <= cnt_data + 1'b1;
else if(rd_cmd == CMD_RD)
cnt_data <= cnt_data + 1'b1;
else
cnt_data <= 4'd0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_data_en <= 1'b0;
else if(cnt_data >= 4'd3 && cnt_data <= 4'd5)
rd_data_en <= 1'b1;
else
rd_data_en <= 1'b0;
endmodule
觀察上述子產品很容易發現讀寫子產品中最重要的就是上面的那個狀态機。
SDRAM讀寫測試子產品
這裡給出頂層測試子產品的編寫:
`timescale 1ns / 1ps
`define CLOCK 10
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : sdram_init_tb.v
// Create Time : 2020-02-09 17:10:08
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_init_tb;
reg sclk ;
reg rst_n ;
wire sdram_clk ;
wire sdram_cke ;
wire sdram_cs_n ;
wire sdram_cas_n ;
wire sdram_ras_n ;
wire sdram_we_n ;
wire [ 1:0] sdram_bank ;
wire [11:0] sdram_addr ;
wire [ 1:0] sdram_dqm ;
wire [15:0] sdram_dq ;
reg wr_trig ;
reg rd_trig ;
initial begin
sclk = 1'b0;
rst_n <= 1'b0;
wr_trig <= 1'b0;
rd_trig <= 1'b0;
#(100*`CLOCK);
rst_n <= 1'b1;
#(250_000)
wr_trig <= 1'b1;
#(`CLOCK)
wr_trig <= 1'b0;
#(20_000)
rd_trig <= 1'b1;
#(`CLOCK)
rd_trig <= 1'b0;
#(20_000);
wr_trig <= 1'b1;
#(`CLOCK)
wr_trig <= 1'b0;
#(20_000);
rd_trig <= 1'b1;
#(`CLOCK)
rd_trig <= 1'b0;
#(20_000);
$stop;
end
always #(`CLOCK/2) sclk = ~sclk;
sdram_top sdram_top_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.sdram_clk (sdram_clk ),
.sdram_cke (sdram_cke ),
.sdram_cs_n (sdram_cs_n ),
.sdram_cas_n (sdram_cas_n ),
.sdram_ras_n (sdram_ras_n ),
.sdram_we_n (sdram_we_n ),
.sdram_bank (sdram_bank ),
.sdram_addr (sdram_addr ),
.sdram_dqm (sdram_dqm ),
.sdram_dq (sdram_dq ),
//Others
.wr_trig (wr_trig ),
.rd_trig (rd_trig )
);
defparam sdram_model_plus_inst.addr_bits = 12;
defparam sdram_model_plus_inst.data_bits = 16;
defparam sdram_model_plus_inst.col_bits = 9;
defparam sdram_model_plus_inst.mem_sizes = 2*1024*1024; // 2M
sdram_model_plus sdram_model_plus_inst(
.Dq (sdram_dq ),
.Addr (sdram_addr ),
.Ba (sdram_bank ),
.Clk (sdram_clk ),
.Cke (sdram_cke ),
.Cs_n (sdram_cs_n ),
.Ras_n (sdram_ras_n ),
.Cas_n (sdram_cas_n ),
.We_n (sdram_we_n ),
.Dqm (sdram_dqm ),
.Debug (1'b1 )
);
endmodule
這裡為了部落格的簡潔性不再給出相應的仿真模型檔案,同學們可以關注前幾篇SDRAM的部落格,也可以進群下載下傳相應的工程。
SDRAM仿真測試
這裡也不給出具體的仿真圖形,因為資料太長,截圖不友善,感興趣的同學可以自己仿真試試,但是這裡要給出一個之前所說奇怪的實驗現象,就是4突發的最後一個資料的有限長度比一個時鐘周期短的多:
其實這裡隻需要下闆驗證一下,但是因為在家開發闆沒帶回來,也就無法驗證,這裡很有可能是仿真模型的問題。即使SDRAM就是有這個坑人的特性,我們也可以用SDRAM來捕捉讀資料,實在不行,我們還可以對sclk進行PLL移相操作。
參考文獻
[1]、開源騷客
總結
創作不易,認為文章有幫助的同學們可以關注點贊支援。(工程也都在群中)對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的群: