根据文档信息,读模块和写模块过程相似,所以直接在写模块上修改信号名称。
与写模块不同的是,读模块的数据输出在读命令发出后几个周期才开始输出。
读模块代码:
module sdram_read(
input sclk ,
input srst ,
//communicate with top
input rd_en ,
output wire rd_req ,
output reg flag_rd_end ,
//
input ref_req ,
input rd_trig ,
//write interface
output reg [3:0] rd_cmd ,
output reg [11:0] rd_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_rd = 5'b01000;
localparam s_pre = 5'b10000;
//
localparam cmd_nop = 4'b0111;
localparam cmd_pre = 4'b0010;
localparam cmd_ref = 4'b0001;
localparam cmd_act = 4'b0011;
localparam cmd_rd = 4'b0101;
reg flag_rd;
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 rd_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_rd
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
flag_rd <= 1'b0;
else if(rd_trig == 1'b1 &&flag_rd == 1'b0)
flag_rd <= 1'b1;
else if(rd_data_end == 1'b1)
flag_rd <= 1'b0;
end
// rd_cmd
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
rd_cmd <= cmd_nop;
else case(state)
s_act:
if(act_cnt == 'd0)
rd_cmd <= cmd_act;
else
rd_cmd <= cmd_nop;
s_rd:
if(burst_cnt == 'd0)
rd_cmd <= cmd_rd;
else
rd_cmd <= cmd_nop;
s_pre:
if(break_cnt == 'd0)
rd_cmd <= cmd_pre;
else
rd_cmd <= cmd_nop;
default:rd_cmd <= cmd_nop;
endcase
end
//burst_cnt
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
burst_cnt <= 'd0;
else if(state == s_rd)
burst_cnt <= burst_cnt + 1'b1;
else
burst_cnt <= 'd0;
end
//burst_cnt
always@(posedge sclk )begin
burst_cnt_t <= burst_cnt;
end
//state
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
state <= s_idle;
else case(state)
s_idle:
if(rd_trig == 1'b1)
state <= s_req;
else
state <= s_idle;
s_req:
if(rd_en == 1'b1)
state <= s_act ;
else
state <= s_req;
s_act:
if(flag_act_end == 1'b1)
state <= s_rd;
else
state <= s_act;
s_rd:
if(rd_data_end == 1'b1)
state <= s_pre;
else if(ref_req == 1'b1 && burst_cnt_t == 'd3 &&flag_rd == 1'b1)
state <= s_pre;
else if(sd_row_end == 1'b1 && flag_rd == 1'b1)
state <= s_pre;
s_pre:
if(ref_req == 1'b1 && flag_rd == 1'b1)
state <= s_req;
else if(flag_pre_end == 1'b1 &&flag_rd == 1'b1)//sd_row_end==1
state <= s_act;
else if(rd_data_end == 1'b1)
state <= s_idle;
default:state <= s_idle;
endcase
end
//flag_act_end
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
flag_act_end <= 1'b0;
else if (act_cnt == 'd3) // 3?
flag_act_end <= 1'b1;
else
flag_act_end <= 1'b0;
end
//flag_pre_end
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
flag_pre_end <= 1'b0;
else if (break_cnt == 'd3)
flag_pre_end <= 1'b1;
else
flag_pre_end <= 1'b0;
end
//break_cnt
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
break_cnt <= 'd0;
else if(state == s_pre)
break_cnt <= break_cnt +1'b1;
else
break_cnt <= 'd0;
end
//act_cnt
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
act_cnt <= 'd0;
else if(state == s_act)
act_cnt <= act_cnt +1'b1;
else
act_cnt <= 'd0;
end
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
rd_data_end <= 'd0;
else if(row_addr == 'd1 && col_addr == 'd511)
rd_data_end <= 1'b1;
else
rd_data_end <= 1'b0;
end
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
col_cnt <= 'd0;
else if(col_cnt == 'd511)
col_cnt <= 'd0;
else if(burst_cnt_t == 'd3)
col_cnt <= col_cnt + 1'b1;
end
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
row_addr <= 'd0;
else if(sd_row_end == 1'b1)
row_addr <= row_addr +'b1;
end
//rd_addr
always@(*)begin
case(state)
s_act:
if(act_cnt == 'd0)
rd_addr <= row_addr;
s_rd:
rd_addr <= {3'b000,col_addr};
s_pre:
if(break_cnt == 'd0)
rd_addr <= {12'b0100_0000_0000};
endcase
end
always@(posedge sclk or negedge srst )begin
if(srst == 1'b0)
sd_row_end <= 1'b0;
else if(col_addr == 'd510)
sd_row_end <= 1'b1;
else
sd_row_end <= 1'b0;
end
//flag_rd_end 有无意义?
always@(posedge sclk or negedge srst )begin
if(srst == 1'b0)
flag_rd_end <= 1'b0;
else if((state == s_pre && ref_req == 1'b1)
|| (state == s_pre && rd_data_end == 1'b1))
flag_rd_end <= 1'b1;
else
flag_rd_end <= 1'b0;
end
assign bank_addr = 2'b00;
assign col_addr = {col_cnt,burst_cnt_t};
assign rd_req = state[1];
//产生测试数据
/* always@(*)begin
case(burst_cnt_t)
0: w_data <= 'd3;
1: w_data <= 'd5;
2: w_data <= 'd7;
3: w_data <= 'd9;
endcase
end
*/
endmodule
顶层模块例化读模块:
module sdram_top(
input sclk,
input srst,
//sdram interface
output 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,
input rd_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 module
reg 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;
//read module
reg rd_en ;
wire rd_req ;
wire flag_rd_end ;
wire [3:0] rd_cmd ;
wire [11:0] rd_addr ;
wire [1:0] rd_bank_addr ;
//==========================================================
//==================== main code ====================
//==========================================================
always@(posedge sclk or negedge srst) begin
if(srst == 1'b0)
state <= idle;
else
case(state)
idle:
if(flag_init_end == 1'b1)
state <= arbit;
else
state <= idle;
arbit:
if(ref_en == 1'b1)
state <= aref;
else if(w_en == 1'b1)
state <= write;
else if(rd_en == 1'b1)
state <= read;
else
state <= arbit;
aref:
if(flag_ref_end == 1'b1)
state <= arbit;
else
state <= aref;
write:
if(flag_w_end == 1'b1)
state <= arbit;
else
state <= write;
read:
if(flag_rd_end == 1'b1)
state <= arbit;
else
state <= read;
default:
state <= idle;
endcase
end
always@(*)
begin
case(state)
idle:
begin
sd_cmd <= init_cmd;
sdram_addr <= init_addr;
end
aref:
begin
sd_cmd <= cmd_reg;
sdram_addr <= ref_addr;
end
write:
begin
sd_cmd <= w_cmd;
sdram_addr <= w_addr;
end
read:
begin
sd_cmd <= rd_cmd;
sdram_addr <= rd_addr;
end
default:begin
sd_cmd <= 4'b0111;//nop
sdram_addr <= 'd0;
end
endcase
end
//ref_en
always@(posedge sclk or negedge srst)begin
if(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)begin
if(srst == 1'b0)
w_en <= 1'b0;
else if(state == arbit && ref_en == 1'b0 && w_req == 1'b1)
w_en <= 1'b1;
else
w_en <= 1'b0;
end
//rd_en
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
rd_en <= 1'b0;
else if(state == arbit && ref_en == 1'b0 && w_req == 1'b0 && rd_req == 1'b1)
rd_en <= 1'b1;
else
rd_en <= 1'b0;
end
assign 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:rd_bank_addr;
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)
);
sdram_read sdram_read_inst(
.sclk (sclk),
.srst (srst),
//communicate with top
.rd_en (rd_en ),
.rd_req (rd_req ),
.flag_rd_end (flag_rd_end),
//
.ref_req (ref_req),
.rd_trig (rd_trig),
//write interface
.rd_cmd (rd_cmd ),
.rd_addr (rd_addr ),
.bank_addr (rd_bank_addr)
);
endmodule
测试代码中添加读触发信号,并进行初始化
`timescale 1ns/1ns
module 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;
reg rd_trig;
//----------------------------------------
initial begin
w_trig <= 0;
rd_trig<= 0;
#205000
w_trig <= 'b1;
#20
w_trig <= 'b0;
#226500
rd_trig <= 1'b1;
#20
rd_trig <= 1'b0;
end
initial begin
sclk = 1;
srst <= 0;
#100
srst <=1;
end
always #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),
.rd_trig (rd_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
在modelsim中用脚本文件仿真:
##create work library
vlib work
vlog "./tb_sdram_top.v"
vlog "../src/sdram_model_plus.v"
vlog "../src/sdram_top1.5.v"
vlog "../src/sdram_write.v"
vlog "../src/sdram_init.v"
vlog "../src/sdram_ref.v"
vlog "../src/sdram_read.v"
vsim -voptargs=+acc work.tb_sdram_top
# Set the window types
view wave
view structure
view signals
add wave -divider {sdram_top1.5}
add wave tb_sdram_top/sdram_top_inst/*
add wave -divider {sdram_write}
add wave tb_sdram_top/sdram_top_inst/sdram_wirte_inst/*
add wave -divider {sdram_read}
add wave tb_sdram_top/sdram_top_inst/sdram_read_inst/*
add wave -divider {tb_sdram_top}
add wave tb_sdram_top/sdram_model_plus_inst/*
.main clear
run 205us
到此,简易的SDRAM控制器已经完成了,通过SDRAM控制器的学习,大致了解了如何阅读文档信息;根据文档信息设计时序逻辑;学会了一些脚本语言的使用。后续还需要优化SDRAM控制器的设计。