写模块跟着视频看了一个多星期,一开始始终有点弄不清楚,现在记录一下理解的过程。
阅读文档信息:
首先阅读文档信息,了解SDRAM写过程的状态转换和时序图
SDRAM整体状态流程如图所示:
在SDRAM整体系统中,若要进入写模块,则需要从idle状态首先激活一行(row_acttive),再进入写状态(write),发送precharge命令跳出写状态。
WRITEA 状态不使用,因为当处于 WRITEA 状态时,它会自动的进入到 Precharge 状态。想要继续进行写操作就要先再次激活行,也就是说 WRITEA 比在 WRITE 状态的工作效率要低很多。
SDRAM写模块时序图如图所示:
观察时序图可知,在发送active命令时同时发送A0-A12,BANK信息,其中A0-A12指定了行信息,也就是说在ACT命令(激活状态)下,确定了要写入数据的行(SDRAM的数据是一行一行的写),BANK命令确定了要写入数据的SDRAM的块。也就是说在ACT命令下已经确定了bank和row;经过tRCD(至少20ns)时间后,再能发送write命令,发送write命令的同时也发送col命令(col命令也通过A0-A9发送,此时A10为低电平),也同时发送bank命令和数据。设置突发长度为4,等待4个周期的时间写入数据,才可以进行precharge命令退出写模式。
SDRAM写模块状态如图所示(写模块内部状态转移,与SDRAM整体的状态无关):
dle,w_req状态承接上下部分,接收到来自顶层模块的写触发信号(w_trig)后,由idle状态到w_req,此时w_req状态向顶层模块发送req信号,接收到写使能(w_en )信号后进入到act状态,此时也接收到row地址和bank地址信息。由时序图可知经过20ns后进入write状态。在write状态下,当数据写完或者刷新时间到的时候或者指定的行数据写完的时候,需要跳出write状态,此时进入pre状态。在pre状态下,数据写完进入idle状态;当刷新请求来的时候要进行刷新,在顶层模块的设计下刷新结束后会进入仲裁模块,此时需要重新发送写请求,接收到写使能;在指定的一行写完后需要重新激活,此时不需要外部的写使能信号再传输进来。
写模块代码如下:
其中burst_cnt用来计数突发长度,write->pre状态,break_cnt用来计数从pre->act状态(至少20ns)
module sdram_wirte(input sclk ,input srst ,//communicate with topinput w_en ,output wire w_req ,output reg flag_w_end ,//input ref_req ,input w_trig ,//write interfaceoutput reg [3:0] w_cmd ,output reg [11:0] w_addr ,output wire [1:0] bank_addr,output reg [15:0] w_data
);//==========================================================
//======= define parameter and internal signal ========
//==========================================================
//define state
localparam s_idle = 5'b00001;
localparam s_req = 5'b00010;
localparam s_act = 5'b00100;
localparam s_wr = 5'b01000;
localparam s_pre = 5'b10000;//
localparam cmd_nop = 5'b0111;
localparam cmd_pre = 5'b0010;
localparam cmd_ref = 5'b0001;
localparam cmd_act = 5'b0011;
localparam cmd_wr = 5'b0100;reg flag_wr;
reg[4:0] state;reg flag_act_end;
reg flag_pre_end;
reg sd_row_end;
reg[1:0] burst_cnt;
reg[1:0] burst_cnt_t;
reg wr_data_end;reg[3:0] act_cnt;
reg[3:0] break_cnt;
reg[6:0] col_cnt;reg[11:0] row_addr;
wire[8:0] col_addr;//==========================================================
//==================== main code ====================
//==========================================================
//flag_wr
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)flag_wr <= 1'b0;else if(w_trig == 1'b1 &&flag_wr == 1'b0)flag_wr <= 1'b1;else if(wr_data_end == 1'b1)flag_wr <= 1'b0;
end// w_cmd
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)w_cmd <= cmd_nop;else case(state)s_act:if(act_cnt == 'd0)w_cmd <= cmd_act;elsew_cmd <= cmd_nop;s_wr:if(burst_cnt == 'd0)w_cmd <= cmd_wr;elsew_cmd <= cmd_nop;s_pre:if(break_cnt == 'd0)w_cmd <= cmd_pre;elsew_cmd <= cmd_nop;default:w_cmd <= cmd_nop;endcase
end//burst_cnt
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)burst_cnt <= 'd0;else if(state == s_wr)burst_cnt <= burst_cnt + 1'b1;elseburst_cnt <= 'd0;
end//burst_cnt
always@(posedge sclk )beginburst_cnt_t <= burst_cnt;
end//state
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)state <= s_idle;else case(state)s_idle:if(w_trig == 1'b1)state <= s_req;elsestate <= s_idle;s_req:if(w_en == 1'b1)state <= s_act ;elsestate <= s_req;s_act:if(flag_act_end == 1'b1)state <= s_wr;elsestate <= s_act;s_wr:if(wr_data_end == 1'b1)state <= s_pre;else if(ref_req == 1'b1 && burst_cnt_t == 'd3 &&flag_wr == 1'b1)state <= s_pre;else if(sd_row_end == 1'b1 && flag_wr == 1'b1)state <= s_pre;s_pre:if(ref_req == 1'b1 && flag_wr == 1'b1)state <= s_req;else if(flag_pre_end == 1'b1 &&flag_wr == 1'b1)state <= s_act;else if(wr_data_end == 1'b1)state <= s_idle;default:state <= s_idle;endcase
end//flag_act_end
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)flag_act_end <= 1'b0;else if (act_cnt == 'd3) // 3?flag_act_end <= 1'b1;elseflag_act_end <= 1'b0;
end//flag_pre_end
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)flag_pre_end <= 1'b0;else if (break_cnt == 'd3)flag_pre_end <= 1'b1;elseflag_pre_end <= 1'b0;
end//break_cnt
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)break_cnt <= 'd0;else if(state == s_pre)break_cnt <= break_cnt +1'b1;elsebreak_cnt <= 'd0;
endalways@(posedge sclk or negedge srst)beginif(srst == 1'b0)wr_data_end <= 'd0;else if(row_addr == 'd1 && col_addr == 'd511)\wr_data_end <= 1'b1;elsewr_data_end <= 1'b0;
endalways@(posedge sclk or negedge srst)beginif(srst == 1'b0)col_cnt <= 'd0;else if(col_addr == 'd511)col_cnt <= 'd0;else if(burst_cnt_t == 'd3)col_cnt <= col_cnt + 1'b1;
endalways@(posedge sclk or negedge srst)beginif(srst == 1'b0)row_addr <= 'd0;else if(sd_row_end == 1'b1)row_addr <= row_addr +'b1;
end//w_addr
always@(*)begincase(state)s_act:if(act_cnt == 'd0)w_addr <= row_addr;s_wr:w_addr <= {3'b000,col_addr};s_pre:if(break_cnt == 'd0)w_addr <= {12'b0100_0000_0000};endcase
endalways@(posedge sclk or negedge srst )beginif(srst == 1'b0)sd_row_end <= 1'b0;else if(col_addr == 'd510)sd_row_end <= 1'b1;else sd_row_end <= 1'b0;
endalways@(posedge sclk or negedge srst )beginif(srst == 1'b0)flag_w_end <= 1'b0;else if((state == s_pre && ref_req == 1'b1)|| (state == s_pre && wr_data_end == 1'b1))flag_w_end <= 1'b1;else flag_w_end <= 1'b0;
endassign bank_addr = 2'b00;
assign col_addr = {col_cnt,burst_cnt_t};
assign w_req = state[1];//产生测试数据
always@(*)begincase(burst_cnt_t):0: w_data <= 'd3;1: w_data <= 'd5;2: w_data <= 'd7;3: w_data <= 'd9;endcase
endendmodule
顶层模块代码:
在测试中添加了一个w_trig信号
疑问:实际上w_trig信号如何产生?fpga input?按下按键?
module sdram_top(input sclk,input srst,//sdram interfaceoutput wire sdram_clk,output wire sdram_cke,output wire sdram_cs_n,output wire sdram_cas_n,output wire sdram_ras_n,output wire sdram_we_n,output wire [1:0] sdram_bank,output reg [11:0] sdram_addr,output wire [1:0] sdram_dqm,inout [15:0] sdram_dq,//input w_trig
);//==========================================================
//======= define parameter and internal signal ========
//==========================================================//define state machine
reg [4:0] state;
localparam idle = 5'b00001;
localparam arbit = 5'b00010;
localparam aref = 5'b00100;
localparam write = 5'b01000;
localparam read = 5'b10000;//init module
reg[3:0] sd_cmd;
wire flag_init_end;
wire [3:0] init_cmd;
wire [11:0] init_addr;//refresh module
reg ref_en;
wire ref_req;
wire flag_ref_end;
wire [3:0] cmd_reg;
wire [11:0] ref_addr;//write modulereg w_en ; wire w_req ; wire flag_w_end;wire[3:0] w_cmd ;wire[11:0] w_addr ;wire[1:0] w_bank_addr;wire[15:0] w_data;//==========================================================
//==================== main code ====================
//==========================================================
always@(posedge sclk or negedge srst) beginif(srst == 1'b0)state <= idle;elsecase(state)idle:if(flag_init_end == 1'b1)state <= arbit;elsestate <= idle;arbit:if(ref_en == 1'b1)state <= aref;else if(w_en == 1'b1)state <= write;elsestate <= arbit;aref:if(flag_ref_end == 1'b1)state <= arbit;elsestate <= aref;write:if(flag_w_end == 1'b1)state <= arbit;elsestate <= write;default: state <= idle;endcase
endalways@(*)
begincase(state)idle:beginsd_cmd <= init_cmd;sdram_addr <= init_addr;endaref:beginsd_cmd <= cmd_reg;sdram_addr <= ref_addr;endwrite:beginsd_cmd <= w_cmd;sdram_addr <= w_addr;enddefault:beginsd_cmd <= 4'b0111;//nopsdram_addr <= 'd0;endendcase
end//ref_en
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)ref_en <= 1'b0;else if(state == arbit && ref_req == 1'b1)ref_en <= 1'b1;else ref_en <= 1'b0;
end//w_en
always@(posedge sclk or negedge srst)beginif(srst == 1'b0)w_en <= 1'b0;else if(state == arbit && ref_en == 1'b0 && w_req == 1'b1)w_en <= 1'b1;elsew_en <= 1'b0;
endassign sdram_cke = 1'b1;
//assign sdram_addr = (state == idle) ?init_addr:ref_addr;
//assign {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = (state == idle) ?init_cmd:cmd_reg;
assign {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = sd_cmd;
assign sdram_dqm = 2'b00;
assign sdram_clk = ~sclk; //内部时钟上升沿采集命令,命令又是由系统时钟上升沿产生的??(为了保证采样时刻处在数据中间时刻)
assign sdram_dq = (state == write)?w_data:{16{1'bz}};
assign sdram_bank = (state == write)?w_bank_addr:2'b00;sdram_init sdram_init_inst(.sclk (sclk) ,.srst (srst) ,.cmd_reg (init_cmd) , .sdram_addr (init_addr) , .flag_init_end (flag_init_end)
); sdram_aref sdram_aref(.sclk (sclk),.srst (srst),//commmunicate with arbit.ref_en (ref_en),.ref_req (ref_req),.flag_ref_end (flag_ref_end),//others .flag_init_end (flag_init_end),.cmd_reg (cmd_reg),.ref_addr (ref_addr)
);sdram_wirte sdram_wirte_inst(.sclk (sclk),.srst (srst),//communicate with top .w_en (w_en ),.w_req (w_req ),.flag_w_end (flag_w_end),// .ref_req (ref_req),.w_trig (w_trig ),//write interface .w_cmd (w_cmd ),.w_addr (w_addr ),.bank_addr (w_bank_addr ),.w_data (w_data)
);endmodule
测试代码:
`timescale 1ns/1nsmodule tb_sdram_top;reg sclk;reg srst;
//----------------------------------------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 w_trig;
//----------------------------------------initial begin w_trig <= 0;#205000w_trig <= 'b1;#20w_trig <= 'b0;
endinitial beginsclk <= 1;srst <= 0;#100srst <=1;
endalways #10 sclk <= ~sclk;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;//1 M sdram_top sdram_top_inst(.sclk (sclk ),.srst (srst ),.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 ),.w_trig (w_trig)
);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