耗时一周,终于完成sdram简易控制器的所有代码设计,其中感谢开源骚客 – 邓堪文老师在b站发布的相关视频学习教材;其中仿真模块及所使用到的sdram仿真文件来源于开源骚客;
因为时间较为紧迫,其中就不做代码的一些注释;
在设计中,总结一哈要主要的几个问题:
1. 仲裁模块中if/else语句中的先后顺序很重要,其中根据仿真的时序图经过多次修改;
2. 仲裁模块决定刷新/写/读操作的重要部分,要重点理清楚各模块产生请求信号后产生使能信号的先后顺序;
3. 读/写状态结束后应及时赋予PRE命令,否则导致读数据有误(具体可查看芯片手册相关介绍);
本设计中采用写3行数据后进行数据的读取,代码如下:
初始化init模块:
module sdram_init(
sys_clk,
sys_rst_n,
init_cmd,
init_addr,
init_end
);input sys_clk,sys_rst_n;
output reg [3:0] init_cmd;
output wire [12:0] init_addr;
output wire init_end;reg [13:0] cnt_200us;
wire flag_200us;
reg [3:0] cnt_cmd;localparam delay_200us = 10000; //200us
//SDRAM init_cmd//
localparam NOP = 4'b0111;
localparam PRE = 4'b0010;
localparam AREF = 4'b0001;
localparam MSET = 4'b0000; always@(posedge sys_clk or negedge sys_rst_n)
begin if(!sys_rst_n)cnt_200us <= 0;else if (flag_200us == 0)cnt_200us <= cnt_200us+1;
endassign flag_200us = (cnt_200us >= delay_200us) ? 1'b1:1'b0;always@(posedge sys_clk or negedge sys_rst_n)
begin if(!sys_rst_n)cnt_cmd <= 0;else if ((flag_200us == 1) && (init_end == 0))cnt_cmd <= cnt_cmd+1;
endalways@(posedge sys_clk or negedge sys_rst_n)
begin if(!sys_rst_n)init_cmd <= NOP ;else if (flag_200us == 1)case (cnt_cmd)0: init_cmd <= PRE;1: init_cmd <= AREF;5: init_cmd <= AREF;9: init_cmd <= MSET;default:init_cmd <= NOP;endcase
end//sdram addr//
//always@(posedge sys_clk or negedge sys_rst_n)
//begin
// if(!sys_rst_n)
// init_addr <= 0;
// else if (flag_200us == 1)
// case (cnt_init_cmd)assign init_addr = (init_cmd == MSET) ? 13'b0_0000_0011_0010:12'b0_0100_0000_0010;
assign init_end = (cnt_cmd >= 'd10) ? 1'b1:1'b0;endmodule
刷新模块代码:
module sdram_aref(
sys_clk,
sys_rst_n,
init_end,
ref_en,
ref_req,
ref_end,
ref_cmd,
ref_addr
);input sys_clk,sys_rst_n,ref_en,init_end;
output reg ref_req;
output wire ref_end;
output reg [3:0] ref_cmd;
output [12:0] ref_addr; //4M*4banks*16bit//localparam Delay_15us = 399;
localparam NOP = 4'b0111;
localparam PRE = 4'b0010;
localparam AREF = 4'b0001; reg [3:0] cnt_cmd;
reg [9:0] cnt_ref;
reg ref_flag;always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)cnt_ref <= 1'b0;else if (cnt_ref == Delay_15us)cnt_ref <= 1'b0;else if (init_end == 1'b1)cnt_ref <= cnt_ref + 1'b1;
endalways@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)ref_req <= 0;else if (ref_en == 1 && ref_req == 1)ref_req <= 0;else if (cnt_ref >= Delay_15us)ref_req <= 1;
end
//assign ref_req = (cnt_ref == Delay_15us) ? 1'b1:1'b0;always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)ref_flag <= 0;else if (ref_en == 1)ref_flag <= 1;else if (ref_end == 1)ref_flag <= 0;
endalways@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)cnt_cmd <= 0;else if (ref_flag == 1)cnt_cmd <= cnt_cmd+1;elsecnt_cmd <= 0;
endalways@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)ref_cmd <= NOP;else case (cnt_cmd)1:ref_cmd <= PRE;2:ref_cmd <= AREF;default:ref_cmd <= NOP;endcase
endassign ref_end = (cnt_cmd >= 5)? 1'b1:1'b0;
assign ref_addr = 13'b0_0100_0000_0000;endmodule
写模块代码:
module sdram_write(
sys_clk,
sys_rst_n,
//ref_end,
ref_req,
wr_en,
wr_trig,wr_data,
wr_addr,
wr_cmd,
bank_addr,
wr_end,
wr_req
);input sys_clk;
input sys_rst_n;
//input ref_end;
input ref_req;
input wr_en;
input wr_trig;output reg [5:0] wr_data;
output reg [12:0] wr_addr;
output reg [3:0] wr_cmd;
output [1:0] bank_addr;
output reg wr_end;
output reg wr_req;
//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;
//cmd//
localparam PRE = 4'b0010;
localparam AREF = 4'b0001;
localparam ACT = 4'b0011;
localparam NOP = 4'b0111;
localparam MSET = 4'b0000;
localparam WR = 4'b0100;
localparam RD = 4'b0101;reg cnt_act;
reg [1:0] burst_cnt;
reg [4:0] state;
reg [8:0] col_cnt;
reg col_full;
reg [13:0] row_addr; //change//
reg wr_data_end;
reg wr_flag;assign bank_addr = 2'b00;
//wr_req//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)wr_req <= 0;else if (wr_en == 1 && wr_req == 1)wr_req <= 0;else if (wr_trig == 1 || wr_end ==1 && wr_flag ==1)wr_req <= 1;
end
//wr_flag//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)wr_flag <= 0;else if (wr_en ==1 && wr_flag ==0)wr_flag <= 1;else if (wr_data_end == 1)wr_flag <= 0;
end
//state//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)state <= s_idle;else case (state)s_idle:beginif (wr_req == 1 )state <= s_req;elsestate <= s_idle;ends_req:beginif (wr_en == 1 )state <= s_act;elsestate <= s_req;ends_act:beginif (cnt_act ==1 )state <= s_wr;else if ((wr_data_end == 1)) //change//state <= s_idle;elsestate <= s_act;ends_wr:begin //change//if ((burst_cnt == 3 && ref_req ==1) || (wr_data_end == 1))state <= s_idle;else if (burst_cnt == 3 || col_full == 1)state <= s_pre;elsestate <= s_wr;ends_pre:beginif(wr_data_end == 1)state <= s_idle;elsestate<=s_act;enddefault: state <= s_idle;endcase
end
//cnt_act//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)cnt_act <= 0;else if (state == s_act)cnt_act <= cnt_act +1;
end
//burst_cnt//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)burst_cnt <= 0;else if (state == s_wr)burst_cnt <= burst_cnt +1;
end
//wr_cmd//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)wr_cmd <= NOP;else if (wr_en == 1)wr_cmd <= ACT;else if (cnt_act == 1)wr_cmd <= WR;else if ((burst_cnt == 3 || col_full == 1))wr_cmd <= PRE;elsewr_cmd <= NOP;
end
//wr_end//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)wr_end <= 0;else if ((burst_cnt == 3 && ref_req ==1) || (wr_data_end == 1))wr_end <= 1;else wr_end <= 0; //change//
end
//col_cnt//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)col_cnt <= 0;else if (state == s_wr)col_cnt <= col_cnt +1;
end
//col_full//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)col_full <=0;else if (col_cnt == 511)col_full <=1;else //change//col_full <= 0;
end
//wr_data//
always@(*)
begincase (burst_cnt)0 : wr_data <= 3;1 : wr_data <= 5;2 : wr_data <= 7;3 : wr_data <= 9;endcase
end
//wr_addr//
always@(*)
begincase (state)s_act : wr_addr <= row_addr;s_wr : wr_addr <= {4'b000,col_cnt};s_pre : wr_addr <= 13'b0_0100_0000_0000;endcase
end
//row_addr//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)row_addr <= 0;else if (col_full == 1)row_addr <= row_addr +1;
end
//wr_data_end//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)wr_data_end <= 0;else if (row_addr == 'd3 && col_cnt == 'd511)wr_data_end <= 1;else wr_data_end <= 0;
endendmodule
读模块代码:
module sdram_read(
sys_clk,
sys_rst_n,
ref_req,
rd_en,
rd_trig,//rd_data,
rd_addr,
rd_cmd,
bank_addr,
rd_end,
rd_req
);input sys_clk;
input sys_rst_n;
//input ref_end;
input ref_req;
input rd_trig;
input rd_en;//output reg [5:0] rd_data;
output reg [12:0] rd_addr;
output reg [3:0] rd_cmd;
output [1:0] bank_addr;
output reg rd_end;
output reg rd_req;
//state//
localparam s_idle = 5'b00001;
localparam s_req = 5'b00010;
localparam s_act = 5'b00100;
localparam s_rd = 5'b01000;
localparam s_pre = 5'b10000;
//cmd//
localparam PRE = 4'b0010;
localparam AREF = 4'b0001;
localparam ACT = 4'b0011;
localparam NOP = 4'b0111;
localparam MSET = 4'b0000;
localparam WR = 4'b0100;
localparam RD = 4'b0101;reg cnt_act;
reg [1:0] burst_cnt;
reg [4:0] state;
reg [8:0] col_cnt;
reg col_full;
reg [13:0] row_addr;
reg rd_data_end;
reg rd_flag;assign bank_addr = 2'b00;//rd_req//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)rd_req <= 0;else if (rd_en == 1 && rd_req == 1)rd_req <= 0;else if (rd_trig == 1 || rd_end ==1 && rd_flag ==1)rd_req <= 1;
end//rd_flag//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)rd_flag <= 0;else if (rd_en ==1 && rd_flag ==0)rd_flag <= 1;else if (rd_data_end == 1)rd_flag <= 0;
end
//state//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)state <= s_idle;else case (state)s_idle:beginif (rd_req == 1 )state <= s_req;elsestate <= s_idle;ends_req:beginif (rd_en == 1 )state <= s_act;elsestate <= s_req;ends_act:beginif (cnt_act ==1 )state <= s_rd;else if ((rd_data_end == 1))state <= s_idle;elsestate <= s_act;ends_rd:beginif ((burst_cnt == 3 && ref_req ==1) || (rd_data_end == 1))state <= s_idle;else if (burst_cnt == 3 || col_full == 1)state <= s_pre;elsestate <= s_rd;ends_pre:beginif(rd_data_end == 1)state <= s_idle;elsestate<=s_act;enddefault: state <= s_idle;endcase
end
//cnt_act//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)cnt_act <= 0;else if (state == s_act)cnt_act <= cnt_act +1;
end
//burst_cnt//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)burst_cnt <= 0;else if (state == s_rd)burst_cnt <= burst_cnt +1;
end
//rd_cmd//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)rd_cmd <= NOP;else if (rd_en == 1)rd_cmd <= ACT;else if (cnt_act == 1)rd_cmd <= RD;else if ((burst_cnt == 3 || col_full == 1))rd_cmd <= PRE;elserd_cmd <= NOP;
end
//rd_end//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)rd_end <= 0;else if ((burst_cnt == 3 && ref_req ==1) || (rd_data_end == 1))rd_end <= 1;elserd_end <= 0;
end
//col_cnt//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)col_cnt <= 0;else if (state == s_rd)col_cnt <= col_cnt +1;
end
//col_full//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)col_full <=0;else if (col_cnt == 511)col_full <=1;elsecol_full <= 0;
end
rd_data//
//always@(*)
//begin
// case (burst_cnt)
// 0 : rd_data <= 3;
// 1 : rd_data <= 5;
// 2 : rd_data <= 7;
// 3 : rd_data <= 9;
// endcase
//end
//rd_addr//
always@(*)
begincase (state)s_act : rd_addr <= row_addr;s_rd : rd_addr <= {4'b000,col_cnt};s_pre : rd_addr <= 13'b0_0100_0000_0000;endcase
end
//row_addr//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)row_addr <= 0;else if (col_full == 1)row_addr <= row_addr +1;
end
//rd_data_end//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)rd_data_end <= 0;else if (row_addr == 'd3 && col_cnt == 'd511)rd_data_end <= 1;else rd_data_end <= 0;
endendmodule
顶层模块:
module sdram_top(
sys_clk,
sys_rst_n,
wr_trig,
rd_trig,sdram_clk,
sdram_cke,
sdram_cs_n,
sdram_ras_n,
sdram_cas_n,
sdram_we_n,
sdram_bank,
sdram_addr,
sdram_dqm,
sdram_dq
);input sys_clk;
input sys_rst_n;
input wr_trig;
input rd_trig;output sdram_clk;
output sdram_cke;
output sdram_cs_n;
output sdram_ras_n;
output sdram_cas_n;
output sdram_we_n;
output reg [1:0] sdram_bank;
output reg [12:0] sdram_addr;
output [1:0] sdram_dqm;
inout [15:0] sdram_dq;reg [3:0] sdram_cmd;
assign {sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = sdram_cmd;
assign sdram_dqm = 2'b00;
assign sdram_cke = 1'b1;
assign sdram_clk = ~sys_clk;//define//
wire [3:0] init_cmd;
wire [12:0] init_addr;
wire init_end;reg ref_en;
wire ref_req;
wire ref_end;
wire [3:0] ref_cmd;
wire [12:0] ref_addr;reg wr_en;
wire [5:0] wr_data;
wire [12:0] wr_addr;
wire [3:0] wr_cmd;
wire [1:0] wr_bank_addr;
wire wr_end;
wire wr_req;reg rd_en;
wire [5:0] rd_data;
wire [12:0] rd_addr;
wire [3:0] rd_cmd;
wire [1:0] rd_bank_addr;
wire rd_end;
wire rd_req;//ARBIT_STATE//
localparam s_IDLE = 5'b00001;
localparam s_ARBIT = 5'b00010;
localparam s_AREF = 5'b00100;
localparam s_WR = 5'b01000;
localparam s_READ = 5'b10000;
reg [4:0] state;always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)state <= s_IDLE;else case (state)s_IDLE:beginif (init_end == 1)state <= s_ARBIT;elsestate <= s_IDLE;ends_ARBIT:beginif (ref_req == 1)state <= s_AREF;else if (wr_req == 1)state <= s_WR;else if (rd_req == 1)state <= s_READ;ends_AREF:beginif (ref_end == 1)state <= s_ARBIT;elsestate <= s_AREF;ends_WR:beginif (wr_end == 1)state <= s_ARBIT;elsestate <= s_WR;ends_READ:beginif (rd_end == 1)state <= s_ARBIT;elsestate <= s_READ;enddefault:state <= s_IDLE;endcase
end
//cmd//
localparam NOP = 4'b0111;
always@(*)begincase (state)s_IDLE:beginsdram_cmd <= init_cmd;sdram_addr <= init_addr;sdram_bank <= 2'b00;ends_AREF:beginsdram_cmd <= ref_cmd;sdram_addr <= ref_addr;sdram_bank <= 2'b00;ends_WR:beginsdram_cmd <= wr_cmd;sdram_addr <= wr_addr;sdram_bank <= wr_bank_addr;ends_READ:beginsdram_cmd <= rd_cmd;sdram_addr <= rd_addr;sdram_bank <= rd_bank_addr;enddefault:beginsdram_cmd <= NOP;sdram_addr <= 0;sdram_bank <= 2'b00;endendcase
end
assign sdram_dq = (state == s_WR)?wr_data:{16{1'bz}};
//ref_en//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)ref_en <= 0;else if (ref_en == 1 && ref_req == 1)ref_en <= 0;else if (state == s_AREF && ref_req == 1)ref_en <= 1;
end
//wr_en//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)wr_en <= 0;else if (wr_en == 1 && wr_req == 1) //change//wr_en <= 0;else if (state == s_WR && wr_req == 1)wr_en <= 1;
end
//rd_en//
always@(posedge sys_clk or negedge sys_rst_n)
beginif (!sys_rst_n)rd_en <= 0;else if (rd_en == 1 && rd_req == 1)rd_en <= 0;else if (state == s_READ && rd_req == 1)rd_en <= 1;
endsdram_init init(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.init_cmd(init_cmd),.init_addr(init_addr),.init_end(init_end)
);sdram_aref aref(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.init_end(init_end),.ref_en(ref_en),.ref_req(ref_req),.ref_end(ref_end),.ref_cmd(ref_cmd),.ref_addr(ref_addr)
);sdram_write write(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),
//ref_end,.ref_req(ref_req),.wr_en(wr_en),.wr_trig(wr_trig),.wr_data(wr_data),.wr_addr(wr_addr),.wr_cmd(wr_cmd),.bank_addr(wr_bank_addr),.wr_end(wr_end),.wr_req(wr_req)
);sdram_read read(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.ref_req(ref_req),.rd_en(rd_en),.rd_trig(rd_trig),//rd_data,.rd_addr(rd_addr),.rd_cmd(rd_cmd),.bank_addr(rd_bank_addr),.rd_end(rd_end),.rd_req(rd_req)
);endmodule
时序图仿真如下:

欢迎指出问题,一起讨论。















