sdram读写控制器
实验目标:
设计并实现一个 SDRAM 数据读写控制器,使用 PC 机通过串口向 SDRAM 写入 10 字
节数据,并将写入的 10 字节数据读出,通过串口回传至 PC 机,在串口助手上位机上打印
显示回传数据。
框图设计:
第一部分:
sdram基本操作实的实现sdram_ctrl
要实现数据的读写,还要有初始化和刷新操作。所以该模块要有分别产生这四条指令的模块。
由于时序冲突问题,刷新,和读写指令存在优先级的问题。所以还应有仲裁模块。
如下:
指令产生模块应该有指令端口,bank地址端口,行列地址端口,结束标志信号。
刷新模块的请求信号传向仲裁模块,反馈给刷新模块一个使能信号。
写指令模块的写数据,写地址,写突发长度。必不可少。
需要注意的是有几个输入端口是外界给的,有几个输出端口直接输出出去。
整体:
第二部分:
sdram控制器sdram_top
fifo控制模块,实现sdram读写数据的跨时钟域处理。
第三部分:
uart_sdram。uart与sdram的数据交互与最顶层模块设计。
指令:
1,取消设备选择:1XXX;
2,无操作指令:0111;
3,配置模式指令:0000;+ addr A12~A0
4,预充电指令:0010;+ addr
5,自动刷新与自刷新指令:0001;
6,激活指令:0011;+addr/bank
7,写指令:0100;+bank/col;
8,读指令:0101;+bank/col;
明天完成初始化模块,加油!!!
这个sdram读写控制器工程确实挺大的。
代码设计:
sdram_ctrl:
sdram_init:
设计思路:
1 预充电指令,在写入0010指令+A10 == 1 之前,先等待200us,加载稳定时钟,并传递空操作指令。
2 写入预充电之后要等待TRP = 2 ,并传递空操作指令0111。
3 TRP时间结束后,写入自动刷新指令0001,并等待TRC = 7 ,并传递空操作指令。
4 TRC时间结束后,再次刷新指令。
5 TRC时间结束后,模式配置寄存器指令0000。+A11~A0;并等待TMRD = 时间,并传递空操作。
6 等待TMRD结束后,完成初始化。
时序图:
代码:
// 初始化模块,产生控指令时序,预充电时序,刷新时序,模式配置时序。sys_clk == 100mHz
module sdram_init(
input wire sys_clk ,
input wire sys_rst_n ,
output reg [3:0] init_cmd ,
output reg [1:0] init_ba ,
output reg [12:0] init_addr ,
output reg init_end
);
// parameter
parameter T_POWER = 200_00 , // 等待时钟稳定。200us。
T_RP = 2 , // 2个时钟周期的预充电时间。
T_RFC = 7 , // 7个时钟周期的刷新时间。
T_MRD = 3 , // 3个时钟周期的模式配置时间。
MAX_CNT_AR= 8 ; // 刷新次数。
localparam INIT_NOP = 4'b0111 , // 空指令
INIT_PREC = 4'b0010 , // 预充电
INIT_REF = 4'b0001 , // 自动刷新
INIT_MOD = 4'b0000 ; // 模式配置
localparam INIT_IDLE = 8'b0000_0001 ,
INIT_PRE = 8'b0000_0010 , // 预充电
INIT_TRP = 8'b0000_0100 ,
INIT_AR = 8'b0000_1000 , // 刷新
INIT_TFR = 8'b0001_0000 ,
INIT_MRS = 8'b0010_0000 , // 模式配置
INIT_TMRD = 8'b0100_0000 ,
INIT_ENDS = 8'b1000_0000 ; // 完成
// reg signal define
reg [7:0] state_c ;
reg [7:0] state_n ;
reg [15:0] cnt_init ;
reg [3:0] cnt_ar ; // 记录刷新次数,以产生状态跳转条件。
// wire signal define
wire INIT_IDLEtoINIT_PRE ;
wire INIT_TRPtoINIT_AR ;
wire INIT_TFRtoINIT_MRS ;
wire INIT_TFRtoINIT_AR ;
wire INIT_TMRDtoINIT_ENDS;
/************************************************************************/
// reg [7:0] state_c ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= INIT_IDLE ;
else
state_c <= state_n ;
end
// reg [7:0] state_n ;
always @(*) begin
case(state_c)
INIT_IDLE : if(INIT_IDLEtoINIT_PRE)
state_n = INIT_PRE ;
else
state_n = INIT_IDLE ;
INIT_PRE : state_n = INIT_TRP ;
INIT_TRP : if(INIT_TRPtoINIT_AR)
state_n = INIT_AR ;
else
state_n = INIT_TRP ;
INIT_AR : state_n = INIT_TFR ;
INIT_TFR : if(INIT_TFRtoINIT_MRS)
state_n = INIT_MRS ;
else if(INIT_TFRtoINIT_AR) // 没有达到规定的刷新次数,回到刷新指令。
state_n = INIT_AR ;
else
state_n = INIT_TFR ;
INIT_MRS : state_n = INIT_TMRD ;
INIT_TMRD : if(INIT_TMRDtoINIT_ENDS)
state_n = INIT_ENDS ;
else
state_n = INIT_TMRD ;
INIT_ENDS : state_n = INIT_ENDS ; // 保持在此状态。
default : state_n = INIT_IDLE ;
endcase
end
assign INIT_IDLEtoINIT_PRE = (state_c == INIT_IDLE) && (cnt_init == T_POWER - 1) ;
assign INIT_TRPtoINIT_AR = (state_c == INIT_TRP ) && (cnt_init == T_RP - 1) ;
assign INIT_TFRtoINIT_MRS = (state_c == INIT_TFR ) && ((cnt_ar == MAX_CNT_AR - 1) && (cnt_init == T_RFC - 1)) ;
assign INIT_TFRtoINIT_AR = (state_c == INIT_TFR ) && ((cnt_ar != MAX_CNT_AR - 1) && (cnt_init == T_RFC - 1)) ;
assign INIT_TMRDtoINIT_ENDS = (state_c == INIT_TMRD) && (cnt_init == T_MRD - 1) ;
// reg [15:0] cnt_init ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_init <= 16'd0 ;
else
case (state_c)
INIT_IDLE : if(cnt_init == T_POWER - 1)
cnt_init <= 16'd0 ;
else
cnt_init <= cnt_init + 1'b1 ;
INIT_PRE : cnt_init <= 16'd0 ;
INIT_TRP : if(cnt_init == T_RP - 1)
cnt_init <= 16'd0 ;
else
cnt_init <= cnt_init + 1'b1 ;
INIT_AR : cnt_init <= 16'd0 ;
INIT_TFR : if(cnt_init == T_RFC - 1)
cnt_init <= 16'd0 ;
else
cnt_init <= cnt_init + 1'b1 ;
INIT_MRS : cnt_init <= 16'd0 ;
INIT_TMRD : if(cnt_init == T_MRD - 1)
cnt_init <= 16'd0 ;
else
cnt_init <= cnt_init + 1'b1 ;
INIT_ENDS : cnt_init <= 16'd0 ;
default : cnt_init <= 16'd0 ;
endcase
end
// reg [3:0] cnt_ar ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_ar <= 4'd0 ;
else if((state_c == INIT_TFR) && (cnt_init == T_RFC - 1) && (cnt_ar == MAX_CNT_AR - 1))
cnt_ar <= 4'd0 ;
else if((state_c == INIT_TFR) && (cnt_init == T_RFC - 1))
cnt_ar <= cnt_ar + 1'b1 ;
else
cnt_ar <= cnt_ar ;
end
// reg [3:0] init_cmd ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
init_cmd <= INIT_NOP ;
else
case (state_c)
INIT_IDLE : init_cmd <= INIT_NOP ;
INIT_PRE : init_cmd <= INIT_PREC;
INIT_TRP : init_cmd <= INIT_NOP ;
INIT_AR : init_cmd <= INIT_REF ;
INIT_TFR : init_cmd <= INIT_NOP ;
INIT_MRS : init_cmd <= INIT_MOD ;
INIT_TMRD : init_cmd <= INIT_NOP ;
INIT_ENDS : init_cmd <= INIT_NOP ;
default : init_cmd <= INIT_NOP ;
endcase
end
// reg [1:0] init_ba ,
// reg [12:0] init_addr ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
init_ba <= 2'b11 ;
init_addr <= 13'h1FFF ;
end
else if(state_c == INIT_MRS) begin
init_ba <= 2'b00 ;
init_addr <= 13'b000_0_00_011_0_111 ;
end
else begin
init_ba <= 2'b11 ;
init_addr <= 13'h1FFF ;
end
end
// reg init_end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
init_end <= 1'b0 ;
else if(state_c == INIT_ENDS)
init_end <= 1'b1 ;
else
init_end <= init_end ;
end
endmodule
仿真:
`timescale 1ns/1ns
module test_sdram_init ();
reg sys_clk ; // 100mHz
reg sys_rst_n ;
wire [3:0] init_cmd ;
wire [1:0] init_ba ;
wire [12:0] init_addr ;
wire init_end ;
sdram_init sdram_init_inst(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n) ,
.init_cmd ( init_cmd ) ,
.init_ba ( init_ba ) ,
.init_addr ( init_addr) ,
.init_end ( init_end )
);
parameter CYCLE = 10 ; // 10ns
initial begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#(CYCLE * 250_00) ;
$stop ;
end
always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule
sdram_aref:
设计思路:
分析时序图可知,刷新模块要做的是先传预充电时序,然后传刷新时序>=2次。简化版的初始化。
自动刷新周期为7.5us。设参数MAX_CNT_REF = 750.
在初始化结束信号拉高后,cnt_750us开始计数,计满后aref_req拉高,等待仲裁模块给出aref_en拉高,开始进行刷新状态机跳转。当跳转到预充电命令时,说明已经开始刷新了,aref_req拉低。
根据时序,绘制状态跳转。
当进入结束状态时,发出一个周期的aref_end==1信号,仲裁模块把aref_en拉低。完成一次刷新。
cnt_750us计数器是在init_end==1后循环计数的。
cnt_ar记录刷新次数,要刷新两次才可以。
时序图:
代码:
// 模块说明:刷新模块,在初始化完成后,以750us为周期进行刷新时序的输出(要受仲裁模块控制),但刷新周期不会大于750us。
module sdram_aref(
input wire sys_clk ,
input wire sys_rst_n ,
input wire init_end ,
input wire aref_en ,
output reg aref_req ,
output reg [3:0] aref_cmd ,
output wire [1:0] aref_ba ,
output wire [12:0] aref_addr ,
output wire aref_end
);
// parameter
parameter MAX_CNT_750US = 750 ;
// localparam
localparam T_RP = 2 , // 2个时钟周期的预充电时间。
T_RFC = 7 , // 7个时钟周期的刷新时间。
MAX_CNT_AR = 2 , // 刷新次数。
AREF_NOP = 4'b0111 , // 空指令
AREF_PREC = 4'b0010 , // 预充电
AREF_REFC = 4'b0001 ; // 自动刷新,指令与状态机命名冲突了,后面加个C。
localparam AREF_IDLE = 6'b00_0001 ,
AREF_PCHA = 6'b00_0010 ,
AREF_TRP = 6'b00_0100 ,
AREF_REF = 6'b00_1000 ,
AREF_TRF = 6'b01_0000 ,
AREF_END = 6'b10_0000 ;
// reg signal define
reg [5:0] state_c ;
reg [5:0] state_n ;
reg [9:0] cnt_750us ;
reg [3:0] cnt_aref ;
reg cnt_ar ;
// wire signal define
wire AREF_IDLEtoAREF_PCHA;
wire AREF_TRPtoAREF_REF ;
wire AREF_TRFtoAREF_REF ;
wire AREF_TRFtoAREF_END ;
/*************************************************************/
// reg [5:0] state_c ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= AREF_IDLE ;
else
state_c <= state_n ;
end
// reg [5:0] state_n ;
always @(*) begin
case(state_c)
AREF_IDLE : if(AREF_IDLEtoAREF_PCHA)
state_n = AREF_PCHA ;
else
state_n = AREF_IDLE ;
AREF_PCHA : state_n = AREF_TRP ;
AREF_TRP : if(AREF_TRPtoAREF_REF)
state_n = AREF_REF ;
else
state_n = AREF_TRP ;
AREF_REF : state_n = AREF_TRF ;
AREF_TRF : if(AREF_TRFtoAREF_END)
state_n = AREF_END ;
else if(AREF_TRFtoAREF_REF)
state_n = AREF_REF ;
else
state_n = AREF_TRF ;
AREF_END : state_n = AREF_IDLE ;
default : state_n = AREF_IDLE ;
endcase
end
assign AREF_IDLEtoAREF_PCHA = (state_c == AREF_IDLE) && ( aref_en ) ;
assign AREF_TRPtoAREF_REF = (state_c == AREF_TRP ) && ( cnt_aref ) ;
assign AREF_TRFtoAREF_REF = (state_c == AREF_TRF ) && ( cnt_aref == 6 && ~cnt_ar ) ;
assign AREF_TRFtoAREF_END = (state_c == AREF_TRF ) && ( cnt_aref == 6 && cnt_ar ) ;
// reg [9:0] cnt_750us ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_750us <= 10'd0 ;
else if(init_end)
begin
if(cnt_750us == MAX_CNT_750US - 1)
cnt_750us <= 10'd0 ;
else
cnt_750us <= cnt_750us + 1'b1 ;
end
else
cnt_750us <= 10'd0 ;
end
// reg [3:0] cnt_aref ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_aref <= 4'd0 ;
else
case (state_c)
AREF_IDLE : cnt_aref <= 4'd0 ;
AREF_PCHA : cnt_aref <= 4'd0 ;
AREF_TRP : if(cnt_aref == T_RP -1)
cnt_aref <= 4'd0 ;
else
cnt_aref <= cnt_aref + 1'b1 ;
AREF_REF : cnt_aref <= 4'd0 ;
AREF_TRF : if(cnt_aref == T_RFC - 1)
cnt_aref <= 4'd0 ;
else
cnt_aref <= cnt_aref + 1'b1 ;
AREF_END : cnt_aref <= 4'd0 ;
default : cnt_aref <= 4'd0 ;
endcase
end
// reg cnt_ar ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_ar <= 1'b0 ;
else if(state_c == AREF_TRF && cnt_aref == T_RFC - 1) // 由于是一位宽的,就不用写归零情况了。
cnt_ar <= cnt_ar + 1'b1 ;
else
cnt_ar <= cnt_ar ;
end
// output signal describe
// reg aref_req ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
aref_req <= 1'b0 ;
else if(cnt_750us == MAX_CNT_750US - 1)
aref_req <= 1'b1 ;
else if(state_c == AREF_PCHA)
aref_req <= 1'b0 ;
else
aref_req <= aref_req ;
end
// reg [3:0] aref_cmd ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
aref_cmd <= 4'd0 ;
else
case (state_c)
AREF_IDLE : aref_cmd <= AREF_NOP ;
AREF_PCHA : aref_cmd <= AREF_PREC;
AREF_TRP : aref_cmd <= AREF_NOP ;
AREF_REF : aref_cmd <= AREF_REFC;
AREF_TRF : aref_cmd <= AREF_NOP ;
AREF_END : aref_cmd <= AREF_NOP ;
default : aref_cmd <= AREF_NOP ;
endcase
end
// wire [1:0] aref_ba ;
assign aref_ba = 2'b11 ;
// reg [12:0] aref_addr ;
assign aref_addr = 13'h1FFF ;
// wire aref_end ;
assign aref_end = (state_c == AREF_END) ? 1'b1 : 1'b0 ;
endmodule
仿真:
在仿真代码中模拟了一小部分仲裁。
`timescale 1ns/1ns
module test_sdram_aref ();
reg sys_clk ; // 50mHz
reg sys_rst_n ;
reg aref_en ; // 自刷新模块的输入,应该是仲裁模块传出。
wire [3:0] init_cmd ; // 初始化模块的输出。
wire [1:0] init_ba ;
wire [12:0] init_addr ;
wire init_end ;
wire aref_req ; // 自刷新模块输出
wire [3:0] aref_cmd ;
wire [1:0] aref_ba ;
wire [12:0] aref_addr ;
wire aref_end ;
wire [3:0] sdram_cmd ;
wire [1:0] sdram_ba ;
wire [12:0] sdram_addr ;
assign sdram_cmd = (init_end) ? aref_cmd : init_cmd ; // 经过仲裁后的命令与地址。
assign sdram_ba = (init_end) ? aref_ba : init_ba ;
assign sdram_addr = (init_end) ? aref_addr : init_addr ;
wire c0_sig ; // pll的输出。
wire c1_sig ;
wire c2_sig ;
wire locked_sig ;
wire rst_n ; // 全局复位。
assign rst_n = sys_rst_n && (locked_sig) ;
sdram_aref sdram_aref_inst(
.sys_clk ( c1_sig ) ,
.sys_rst_n ( rst_n ) ,
.init_end ( init_end ) ,
.aref_en ( aref_en ) ,
.aref_req ( aref_req ) ,
.aref_cmd ( aref_cmd ) ,
.aref_ba ( aref_ba ) ,
.aref_addr ( aref_addr ) ,
.aref_end ( aref_end )
);
clk_gen clk_gen_inst (
.areset (~sys_rst_n ) ,
.inclk0 ( sys_clk ) ,
.c0 ( c0_sig ) , // 50
.c1 ( c1_sig ) , // 100
.c2 ( c2_sig ) , // 100+shift30
.locked ( locked_sig )
);
sdram_model_plus sdram_model_plus_inst(
.Dq ( ) ,
.Addr ( sdram_addr ) ,
.Ba ( sdram_ba ) ,
.Clk ( c2_sig ) ,
.Cke ( 1'b1 ) ,
.Cs_n ( sdram_cmd[3]) ,
.Ras_n ( sdram_cmd[2]) ,
.Cas_n ( sdram_cmd[1]) ,
.We_n ( sdram_cmd[0]) ,
.Dqm ( 2'b00 ) ,
.Debug ( 1'b1 )
);
sdram_init sdram_init_inst(
.sys_clk ( c1_sig ) ,
.sys_rst_n ( rst_n ) ,
.init_cmd ( init_cmd ) ,
.init_ba ( init_ba ) ,
.init_addr ( init_addr) ,
.init_end ( init_end )
);
parameter CYCLE = 20 ; // 10ns
defparam sdram_model_plus_inst.addr_bits = 13 ;
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 ;
// aref_en 仲裁
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
aref_en <= 1'b0 ;
else if(init_end && aref_req)
aref_en <= 1'b1 ;
else if(aref_end)
aref_en <= 1'b0 ;
else
aref_en <= aref_en ;
end
initial begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#(CYCLE * 250_00) ;
end
always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule
sdram_write:
设计思路:
指令:激活指令0011,空指令0111,写指令0100,突发停止指令0110(也就是中止页写的指令)。
由于使用的模式是突发一页的读写模式,给出列首地址后,会自动生成下一个地址。
1,传送激活指令+bank地址+列地址,激活特定行。0011
2,等待tRCD,激活时间。此过程中,传送空指令。0111
3.tRCD时间结束后,传送写指令,同时A8~A0开始传送列地址(首地址),DQ开始传送要写入的数据。
4,在最后一个数据写入后的下一个时钟周期写入突发中止指令。
5,突发中止指令写入后,页写突发操作完成。0110
但是在设计的时候,在页写突发指令完成后加了预充电指令。
设计状态机时,把突发中止指令放进了写数据状态的最后。
一个细节:在写指令传送时,DQ已经开始传递第一个数据了。
sdram_en用作三态门的写数据使能,输出使能。
第一部分:状态机。
第二部分:数据写响应设计。wr_ack。
第三部分:各输出信号的设计与实现。
时序图:
代码:
// 模块说明:写数据模块,在初始化完成后,fifo_ctrl模块产生wr_req信号传给仲裁模块,仲裁模块传回写使能信号,开始写数据是时序传输。
// input wire [23:0] write_addr , // 高两位是bank地址,中间13位为数据写入位置的行地址,后9位为列首地址。
// input wire [9:0] write_burst_len , // 写突发长度由fifo_ctrl模块给出。
// output wire wr_sdram_en , // 用作三态输出使能信号。
// wr_ack 传给fifo_ctrl模块,用来做读数据使能,告知fifo_ctrl模块,已经开始读时序了,请求信号可以拉低了。
// wr_burst_len , // 写突发长度,控制WR_DATA状态下传输数据的个数。每一行有512个存储单元。
module sdram_write(
input wire sys_clk ,
input wire sys_rst_n ,
input wire init_end ,
input wire wr_en ,
input wire [23:0] wr_addr ,
input wire [15:0] wr_data ,
input wire [9:0] wr_burst_len , // 写突发长度,控制WR_DATA状态下传输数据的个数。
output reg [3:0] write_cmd ,
output reg [1:0] write_ba ,
output reg [12:0] write_addr ,
output wire wr_end ,
output wire wr_sdram_en ,
output wire [15:0] wr_sdram_data ,
output wire wr_ack
);
// parameter
parameter TRCD = 2 ,
TRP = 2 ,
NOP = 4'b0111 ,
ACTIVE = 4'b0011 ,
WRITE = 4'b0100 ,
B_STOP = 4'b0110 ,
P_CHARGE = 4'b0010 ;
// localparam
localparam WR_IDLE = 3'b000 ,
WR_ACTIVE = 3'b001 ,
WR_TRCD = 3'b011 ,
WR_WRITE = 3'b010 ,
WR_DATA = 3'b110 ,
WR_PRE = 3'b111 ,
WR_TRP = 3'b101 ,
WR_END = 3'b100 ;
// reg signal define
reg [2:0] state_c ;
reg [2:0] state_n ;
reg [9:0] cnt_wr ; // 位宽注意。
reg cnt_wr_rst ; // 用组合逻辑赋值。
// wire dignal define
wire trcd_end ;
wire twrite_end ;
wire trp_end ;
wire WR_IDLEtoWR_ACTIVE ;
wire WR_TRCDtoWR_WRITE ;
wire WR_DATAtoWR_PRE ;
wire WR_TRPtoWR_END ;
/********************************************************************/
// reg [2:0] state_c ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= WR_IDLE ;
else
state_c <= state_n ;
end
// reg [2:0] state_n ;
always @(*) begin
case(state_c)
WR_IDLE : if(WR_IDLEtoWR_ACTIVE)
state_n = WR_ACTIVE ;
else
state_n = WR_IDLE ;
WR_ACTIVE : state_n = WR_TRCD ;
WR_TRCD : if(WR_TRCDtoWR_WRITE)
state_n = WR_WRITE ;
else
state_n = WR_TRCD ;
WR_WRITE : state_n = WR_DATA ;
WR_DATA : if(WR_DATAtoWR_PRE)
state_n = WR_PRE ;
else
state_n = WR_DATA ;
WR_PRE : state_n = WR_TRP ;
WR_TRP : if(WR_TRPtoWR_END)
state_n = WR_END ;
else
state_n = WR_TRP ;
WR_END : state_n = WR_IDLE ;
default : state_n = WR_IDLE ;
endcase
end
assign WR_IDLEtoWR_ACTIVE = (state_c == WR_IDLE) && (wr_en ) && (init_end);
assign WR_TRCDtoWR_WRITE = (state_c == WR_TRCD) && (trcd_end ) && (init_end);
assign WR_DATAtoWR_PRE = (state_c == WR_DATA) && (twrite_end) && (init_end);
assign WR_TRPtoWR_END = (state_c == WR_TRP ) && (trp_end ) && (init_end);
// reg [9:0] cnt_wr ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_wr <= 10'd0 ;
else if(cnt_wr_rst)
cnt_wr <= 10'd0 ;
else
cnt_wr <= cnt_wr + 1'b1 ;
end
// reg cnt_wr_rst ;
always @(*) begin
case(state_c)
WR_IDLE : cnt_wr_rst = 1'b1 ;
WR_ACTIVE : cnt_wr_rst = trcd_end ;
WR_TRCD : cnt_wr_rst = trcd_end ;
WR_WRITE : cnt_wr_rst = 1'b1 ;
WR_DATA : cnt_wr_rst = twrite_end ;
WR_PRE : cnt_wr_rst = trp_end ;
WR_TRP : cnt_wr_rst = trp_end ;
WR_END : cnt_wr_rst = 1'b1 ;
default : cnt_wr_rst = 1'b1 ;
endcase
end
// wire trcd_end ;
assign trcd_end = (state_c == WR_TRCD && cnt_wr == TRCD) ? 1'b1 : 1'b0 ;
// wire twrite_end ;
assign twrite_end = (state_c == WR_DATA && cnt_wr == (wr_burst_len - 1)) ? 1'b1 : 1'b0 ;
// wire trp_end ;
assign trp_end = (state_c == WR_TRP && cnt_wr == TRP) ? 1'b1 : 1'b0 ;
// output signal describe
// wire wr_end ,
assign wr_end = (state_c == WR_END) ? 1'b1 : 1'b0 ;
// reg [3:0] write_cmd ,
// reg [1:0] write_ba ,
// reg [12:0] write_addr ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
write_cmd <= NOP ;
write_ba <= 2'b11 ;
write_addr <= 13'h1FFF ;
end else begin
case (state_c)
WR_IDLE :
begin
write_cmd <= NOP ;
write_ba <= 2'b11 ;
write_addr <= 13'h1FFF ;
end
WR_ACTIVE:
begin
write_cmd <= ACTIVE ;
write_ba <= wr_addr[23:22] ;
write_addr <= wr_addr[21:9 ] ; //wr_addr[23:0]
end
WR_TRCD :
begin
write_cmd <= NOP ;
write_ba <= 2'b11 ;
write_addr <= 13'h1FFF ;
end
WR_WRITE :
begin
write_cmd <= WRITE ;
write_ba <= wr_addr[23:22] ;
write_addr <= {4'b0000,wr_addr[ 8:0 ]} ;
end
WR_DATA :
if(!twrite_end)
begin
write_cmd <= NOP ;
write_ba <= 2'b11 ;
write_addr <= 13'h1FFF ;
end
else
begin
write_cmd <= B_STOP ;
write_ba <= 2'b11 ;
write_addr <= 13'h1FFF ;
end
WR_PRE :
begin
write_cmd <= P_CHARGE ; // 预充电指令,一开始忘记赋值了。
write_ba <= wr_addr[23:22] ;
write_addr <= 13'h0400 ;
end
WR_TRP :
begin
write_cmd <= NOP ;
write_ba <= 2'b11 ;
write_addr <= 13'h1FFF ;
end
WR_END :
begin
write_cmd <= NOP ;
write_ba <= 2'b11 ;
write_addr <= 13'h1FFF ;
end
default :
begin
write_cmd <= NOP ;
write_ba <= 2'b11 ;
write_addr <= 13'h1FFF ;
end
endcase
end
end
// wire wr_sdram_en ,
assign wr_sdram_en = (state_c == WR_DATA) ? 1'b1 : 1'b0 ;
// reg [15:0] wr_sdram_data ,
assign wr_sdram_data = (wr_sdram_en) ? wr_data : 16'd0 ;
// wire wr_ack
assign wr_ack = (((state_c == WR_WRITE) || (state_c == WR_DATA)) && (cnt_wr <= wr_burst_len - 2)) ? 1'b1 : 1'b0 ;
endmodule
`timescale 1ns/1ns
module test_sdram_write ();
reg sys_clk ; // 50mHz
reg sys_rst_n ;
wire [3:0] init_cmd ;
wire [1:0] init_ba ;
wire [12:0] init_addr ;
wire init_end ;
wire [15:0] sdram_data ;
wire [3:0] sdram_cmd ;
wire [1:0] sdram_ba ;
wire [12:0] sdram_addr ;
wire c0_sig ;
wire c1_sig ;
wire c2_sig ;
wire locked_sig ;
wire rst_n ;
assign rst_n = sys_rst_n && (locked_sig) ;
reg wr_en ;
reg [15:0] wr_data ;
wire [3:0] write_cmd ;
wire [1:0] write_ba ;
wire [12:0] write_addr ;
wire wr_end ;
wire wr_sdram_en ;
wire [15:0] wr_sdram_data ;
wire wr_ack ;
reg wr_en_flag ;
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_en_flag <= 1'b0 ;
else if(wr_end)
wr_en_flag <= ~wr_en_flag ;
end
// always @(posedge c1_sig or negedge sys_rst_n) begin
// if(~sys_rst_n)
// wr_en <= 1'b0 ;
// else if(wr_end)
// wr_en <= 1'b0 ;
// else if(init_end && !wr_en_flag)
// wr_en <= 1'b1 ;
// else
// wr_en <= wr_en ;
// end
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_en <= 1'b0 ;
else if(wr_end)
wr_en <= 1'b0 ;
else if(init_end)
wr_en <= 1'b1 ;
else
wr_en <= wr_en ;
end
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_data <= 16'd0 ;
else if(wr_ack == 0)
wr_data <= 16'd0 ;
else
wr_data <= wr_data + 1'b1 ;
end
assign sdram_cmd = (init_end) ? write_cmd : init_cmd ;
assign sdram_ba = (init_end) ? write_ba : init_ba ;
assign sdram_addr = (init_end) ? write_addr : init_addr ;
assign sdram_data = (wr_sdram_en) ? wr_data : 16'hzzzz ;
sdram_write sdram_write_inst(
.sys_clk ( c1_sig ) ,
.sys_rst_n ( rst_n ) ,
.init_end ( init_end ) ,
.wr_en ( wr_en ) ,
.wr_addr ( 24'd0 ) ,
.wr_data ( wr_data ) ,
.wr_burst_len ( 10 ) , // 写突发长度,控制WR_DATA状态下传输数据的个数。
.write_cmd ( write_cmd ) ,
.write_ba ( write_ba ) ,
.write_addr ( write_addr ) ,
.wr_end ( wr_end ) ,
.wr_sdram_en ( wr_sdram_en ) ,
.wr_sdram_data ( wr_sdram_data ) ,
.wr_ack ( wr_ack )
);
clk_gen clk_gen_inst (
.areset (~sys_rst_n ) ,
.inclk0 ( sys_clk ) ,
.c0 ( c0_sig ) , // 50
.c1 ( c1_sig ) , // 100
.c2 ( c2_sig ) , // 100+shift30
.locked ( locked_sig )
);
sdram_model_plus sdram_model_plus_inst(
.Dq ( sdram_data ) ,
.Addr ( sdram_addr ) ,
.Ba ( sdram_ba ) ,
.Clk ( c2_sig ) ,
.Cke ( 1'b1 ) ,
.Cs_n ( sdram_cmd[3]) ,
.Ras_n ( sdram_cmd[2]) ,
.Cas_n ( sdram_cmd[1]) ,
.We_n ( sdram_cmd[0]) ,
.Dqm ( 2'b00 ) ,
.Debug ( 1'b1 )
);
sdram_init sdram_init_inst(
.sys_clk ( c1_sig ) ,
.sys_rst_n ( rst_n ) ,
.init_cmd ( init_cmd ) ,
.init_ba ( init_ba ) ,
.init_addr ( init_addr) ,
.init_end ( init_end )
);
parameter CYCLE = 20 ; // 10ns
defparam sdram_model_plus_inst.addr_bits = 13 ;
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 ;
initial begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#(CYCLE * 250_00) ;
$stop ;
end
always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule
仿真:
sdram_read:
设计思路:
1,传送激活指令(0011),同时传递bank地址与行地址,激活特定行。
2,然后进入激活等待状态,等待时间tRCD,此过程中传送空指令。
3, 然后传动读指令,同时A8~A0写入列地址。
4,然后跳转到潜伏状态,3个clk,结束后,开始读数据。
5,进入读数据状态。设读取的数据为N。
6,从读指令周期开始计数,N个周期后,写入突发中止指令。
7,突发中止指令写入后,DQ继续传送2个周期数据。数据传输完成后,sdram页突发读操作完成。
8,然后要发预充电命令,进入预充电等待状态,结束后进入读结束状态。
第一部分状态机;
第二部分读突发中止指令应该何时写入?
assign rd_bust_flag = (state_c == RD_DATA && cnt_clk == rd_bust_len - CL - 1).
提前CL个时钟周期传送中止指令,又因为从零开始计数。
第三部分输出信号。
注意,写计数器时,先写归零条件,把它放在最高优先级。
本模块偏移时钟向前移动和向后移动会造成很大的不同。
由于我一开始设置的向后移动30,导致潜伏期状态,读数据状态,ack响应信号,与官方教程里的代码很不一样。
由于时序图是仿照官方画的,和代码有很多不相符合的地方,但是对于代码而言只需改动两个地方。
tcl_end与rd_bust_flag!!!!!!!!!!!
时序图:
代码:
// 读时序产生模块。先传送激活指令,然后传送读指令,然后读突发中止指令,然后预充电指令。
// 注意rd_data的跨时钟域处理。
module sdram_read(
input wire sys_clk ,
input wire sys_rst_n ,
input wire init_end ,
input wire [23:0] rd_addr ,
input wire [15:0] rd_data ,
input wire [9:0] rd_burst_len ,
input wire rd_en ,
output wire rd_end ,
output reg [3:0] read_cmd ,
output reg [1:0] read_ba ,
output reg [12:0] read_addr ,
output wire [15:0] rd_sdram_data ,
output wire rd_ack
);
// parameter
parameter NOP = 4'b0111 , // 空指令
P_CHARGE = 4'b0010 , // 预充电指令
ACTIVE = 4'b0011 , // 激活指令
READ = 4'b0101 , // 读指令
B_STOP = 4'b0110 ; // 读中止指令
parameter T_RCD = 2 , // 激活周等待期
T_CL = 3 , // 列潜伏
T_RP = 2 ; // 预充电
localparam RD_IDLE = 9'b0_0000_0001 , // 空闲状态
RD_ACTIVE = 9'b0_0000_0010 , // 激活状态
RD_TRCD = 9'b0_0000_0100 , // 激活等待状态
RD_READ = 9'b0_0000_1000 , // 传送读指令状态
RD_CL = 9'b0_0001_0000 , // 潜伏状态
RD_DATA = 9'b0_0010_0000 , // 读数据状态
RD_PRE = 9'b0_0100_0000 , // 预充电指令传送状态
RD_TRP = 9'b0_1000_0000 , // 预充电等待状态
RD_END = 9'b1_0000_0000 ; // 读结束
// reg signal define
// reg [15:0] rd_data_reg ;
reg [9:0] state_c ;
reg [9:0] state_n ; // 用的组合逻辑
reg [9:0] cnt_read ;
reg cnt_rd_rst ; // 用的组合逻辑
// wire signal define
wire trcd_end ;
wire tcl_end ;
wire tread_end ;
wire trp_end ;
wire rd_bust_flag;
wire RD_IDLEtoRD_ACTIVE ;
wire RD_TRCDtoRD_READ ;
wire RD_CLtoRD_DATA ;
wire RD_DATAto_RD_PRE ;
wire RD_TRPtoRD_END ;
/****************************************************************************/
// // reg [15:0] rd_data_reg ;
// always @(posedge sys_clk or negedge sys_rst_n) begin
// if(~sys_rst_n)
// rd_data_reg <= 16'd0 ;
// else
// rd_data_reg <= rd_data ;
// end
// reg [9:0] state_c ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= RD_IDLE ;
else
state_c <= state_n ;
end
// reg [9:0] state_n ;
always @(*) begin
case (state_c)
RD_IDLE : if(RD_IDLEtoRD_ACTIVE)
state_n = RD_ACTIVE ;
else
state_n = RD_IDLE ;
RD_ACTIVE : state_n = RD_TRCD ;
RD_TRCD : if(RD_TRCDtoRD_READ) // 可以写成三元运算符:state_n <= (RD_TRCDtoRD_READ) ? RD_READ : RD_TRCD ;
state_n = RD_READ ;
else
state_n = RD_TRCD ;
RD_READ : state_n = RD_CL ;
RD_CL : if(RD_CLtoRD_DATA)
state_n = RD_DATA ;
else
state_n = RD_CL ;
RD_DATA : if(RD_DATAto_RD_PRE)
state_n = RD_PRE ;
else
state_n = RD_DATA ;
RD_PRE : state_n = RD_TRP ;
RD_TRP : if(RD_TRPtoRD_END)
state_n = RD_END ;
else
state_n = RD_TRP ;
RD_END : state_n = RD_IDLE ;
default : state_n = RD_IDLE ;
endcase
end
assign RD_IDLEtoRD_ACTIVE = ( state_c == RD_IDLE ) && ( rd_en ) && ( init_end ) ;
assign RD_TRCDtoRD_READ = ( state_c == RD_TRCD ) && ( trcd_end ) && ( init_end ) ;
assign RD_CLtoRD_DATA = ( state_c == RD_CL ) && ( tcl_end ) && ( init_end ) ;
assign RD_DATAto_RD_PRE = ( state_c == RD_DATA ) && ( tread_end) && ( init_end ) ;
assign RD_TRPtoRD_END = ( state_c == RD_TRP ) && ( trp_end ) && ( init_end ) ;
// reg [9:0] cnt_read ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_read <= 10'd0 ;
else if(cnt_rd_rst)
cnt_read <= 10'd0 ;
else
cnt_read <= cnt_read + 1'b1 ;
end
// wire cnt_rd_rst ;
always @(*) begin
case(state_c)
RD_IDLE : cnt_rd_rst = 1'b1 ;
RD_ACTIVE : cnt_rd_rst = trcd_end ;
RD_TRCD : cnt_rd_rst = trcd_end ;
RD_READ : cnt_rd_rst = tcl_end ;
RD_CL : cnt_rd_rst = tcl_end ;
RD_DATA : cnt_rd_rst = tread_end ;
RD_PRE : cnt_rd_rst = trp_end ;
RD_TRP : cnt_rd_rst = trp_end ;
RD_END : cnt_rd_rst = 1'b1 ;
default : cnt_rd_rst = 1'b1 ;
endcase
end
// wire trcd_end ;T_RP
assign trcd_end = (state_c == RD_TRCD && cnt_read == T_RCD) ? 1'b1 : 1'b0 ;
// wire tcl_end ;
assign tcl_end = (state_c == RD_CL && cnt_read == T_CL - 1) ? 1'b1 : 1'b0 ; // 注意这里时序图多画了一个时钟周期。要计数到2,而不是3。
// wire tread_end ;
assign tread_end= (state_c == RD_DATA && cnt_read == rd_burst_len) ? 1'b1 : 1'b0 ;
// wire trp_end ;
assign trp_end = (state_c == RD_TRP && cnt_read == T_RP) ? 1'b1 : 1'b0 ;
// wire rd_bust_flag;
assign rd_bust_flag = (state_c == RD_DATA && cnt_read == (rd_burst_len - T_CL)) ? 1'b1 : 1'b0 ;
// // output
// wire rd_end ;
assign rd_end = (state_c == RD_END) ? 1'b1 : 1'b0 ;
// reg [3:0] read_cmd ;
// reg [1:0] read_ba ;
// reg [12:0] read_addr ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
begin
read_cmd <= NOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
else
case (state_c)
RD_IDLE :
begin
read_cmd <= NOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
RD_ACTIVE :
begin
read_cmd <= ACTIVE ;
read_ba <= rd_addr[23:22] ;
read_addr <= rd_addr[21:9] ;
end
RD_TRCD :
begin
read_cmd <= NOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
RD_READ :
begin
read_cmd <= READ ;
read_ba <= rd_addr[23:22] ;
read_addr <= rd_addr[8:0] ;
end
RD_CL :
begin
read_cmd <= NOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
RD_DATA :
if(!rd_bust_flag)
begin
read_cmd <= NOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
else
begin
read_cmd <= B_STOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
RD_PRE :
begin
read_cmd <= P_CHARGE ;
read_ba <= rd_addr[23:22] ;
read_addr <= 13'h0400 ; // 辅助预充电,选中所有bank
end
RD_TRP :
begin
read_cmd <= NOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
RD_END :
begin
read_cmd <= NOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
default :
begin
read_cmd <= NOP ;
read_ba <= 2'b11 ;
read_addr <= 13'h1FFF ;
end
endcase
end
// wire [15:0] rd_sdram_data;
assign rd_sdram_data = (rd_ack) ? rd_data : 16'd0 ; // rd_data_reg
// wire rd_ack ;
assign rd_ack = (state_c == RD_DATA && (cnt_read >= 1 && cnt_read <= rd_burst_len)) ? 1'b1 : 1'b0 ;
endmodule
`timescale 1ns/1ns
module test_sdram_read ();
//
reg rd_en ;
wire rd_end ;
wire [3:0] read_cmd ;
wire [1:0] read_ba ;
wire [12:0] read_addr ;
wire [15:0] rd_sdram_data ;
wire rd_ack ;
//
reg sys_clk ; // 50mHz
reg sys_rst_n ;
wire [3:0] init_cmd ;
wire [1:0] init_ba ;
wire [12:0] init_addr ;
wire init_end ;
wire [15:0] sdram_data ;
wire [3:0] sdram_cmd ;
wire [1:0] sdram_ba ;
wire [12:0] sdram_addr ;
wire c0_sig ;
wire c1_sig ;
wire c2_sig ;
wire locked_sig ;
wire rst_n ;
assign rst_n = sys_rst_n && (locked_sig) ;
reg wr_en ;
reg [15:0] wr_data ;
wire [3:0] write_cmd ;
wire [1:0] write_ba ;
wire [12:0] write_addr ;
wire wr_end ;
wire wr_sdram_en ;
wire [15:0] wr_sdram_data ;
wire wr_ack ;
reg wr_en_flag ;
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_en_flag <= 1'b0 ;
else if(wr_end)
wr_en_flag <= ~wr_en_flag ;
end
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_en <= 1'b0 ;
else if(wr_end)
wr_en <= 1'b0 ;
else if(init_end && !wr_en_flag)
wr_en <= 1'b1 ;
else
wr_en <= wr_en ;
end
// always @(posedge c1_sig or negedge sys_rst_n) begin
// if(~sys_rst_n)
// wr_en <= 1'b0 ;
// else if(wr_end)
// wr_en <= 1'b0 ;
// else if(init_end)
// wr_en <= 1'b1 ;
// else
// wr_en <= wr_en ;
// end
reg rd_en_flag ;
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
rd_en_flag <= 1'b0 ;
else if(rd_end)
rd_en_flag <= ~rd_en_flag ;
end
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
rd_en <= 1'b0 ;
else if(rd_end || rd_en_flag)
rd_en <= 1'b0 ;
else if(init_end == 1 && wr_en == 0 && wr_en_flag == 1 && ~rd_en_flag)
rd_en <= 1'b1 ;
else
rd_en <= rd_en ;
end
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_data <= 16'd0 ;
else if(wr_ack == 0)
wr_data <= 16'd0 ;
else
wr_data <= wr_data + 1'b1 ;
end
assign sdram_cmd = (init_end) ? (( wr_en ) ? write_cmd : read_cmd ) : init_cmd ;
assign sdram_ba = (init_end) ? (( wr_en ) ? write_ba : read_ba ) : init_ba ;
assign sdram_addr = (init_end) ? (( wr_en ) ? write_addr : read_addr) : init_addr ;
assign sdram_data = (wr_sdram_en) ? wr_data : 16'hzzzz ;
sdram_read sdram_read_inst(
.sys_clk ( c1_sig ) ,
.sys_rst_n ( rst_n ) ,
.init_end ( init_end ) ,
.rd_addr ( 24'd0 ) ,
.rd_data ( sdram_data ) ,
.rd_burst_len ( 10'd20 ) ,
.rd_en ( rd_en ) ,
.rd_end ( rd_end ) ,
.read_cmd ( read_cmd ) ,
.read_ba ( read_ba ) ,
.read_addr ( read_addr ) ,
.rd_sdram_data ( rd_sdram_data) ,
.rd_ack ( rd_ack )
);
sdram_write sdram_write_inst(
.sys_clk ( c1_sig ) ,
.sys_rst_n ( rst_n ) ,
.init_end ( init_end ) ,
.wr_en ( wr_en ) ,
.wr_addr ( 24'd0 ) ,
.wr_data ( wr_data ) ,
.wr_burst_len ( 10 ) , // 写突发长度,控制WR_DATA状态下传输数据的个数。
.write_cmd ( write_cmd ) ,
.write_ba ( write_ba ) ,
.write_addr ( write_addr ) ,
.wr_end ( wr_end ) ,
.wr_sdram_en ( wr_sdram_en ) ,
.wr_sdram_data ( wr_sdram_data ) ,
.wr_ack ( wr_ack )
);
clk_gen clk_gen_inst (
.areset (~sys_rst_n ) ,
.inclk0 ( sys_clk ) ,
.c0 ( c0_sig ) , // 50
.c1 ( c1_sig ) , // 100
.c2 ( c2_sig ) , // 100+shift30
.locked ( locked_sig )
);
sdram_model_plus sdram_model_plus_inst(
.Dq ( sdram_data ) ,
.Addr ( sdram_addr ) ,
.Ba ( sdram_ba ) ,
.Clk ( c2_sig ) ,
.Cke ( 1'b1 ) ,
.Cs_n ( sdram_cmd[3]) ,
.Ras_n ( sdram_cmd[2]) ,
.Cas_n ( sdram_cmd[1]) ,
.We_n ( sdram_cmd[0]) ,
.Dqm ( 2'b00 ) ,
.Debug ( 1'b1 )
);
sdram_init sdram_init_inst(
.sys_clk ( c1_sig ) ,
.sys_rst_n ( rst_n ) ,
.init_cmd ( init_cmd ) ,
.init_ba ( init_ba ) ,
.init_addr ( init_addr) ,
.init_end ( init_end )
);
parameter CYCLE = 20 ; // 10ns
defparam sdram_model_plus_inst.addr_bits = 13 ;
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 ;
initial begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#(CYCLE * 250_00) ;
$stop ;
end
always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule
仿真:
sdram_arbit:
设计思路:
仲裁模块通过状态机协调各个模块的正常进行,每项操作的实现都需要对sdram写入对应操作指令,且各项操作指令不同,为了方便赋值与准确传输指令,内部声明sdram_cmd信号,用来保存不同状态下对应的指令。
如状态机所示:
we_n:写使能有效信号,低电平有效,使能写操作和预充电。
时序图:
看着状态机与之前画的时序图就能写出来代码。
代码:
module sdram_arbit(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [3:0] init_cmd ,
input wire [1:0] init_ba ,
input wire [12:0] init_addr ,
input wire init_end ,
input wire aref_req ,
input wire [3:0] aref_cmd ,
input wire [1:0] aref_ba ,
input wire [12:0] aref_addr ,
input wire aref_end ,
input wire read_req ,
input wire [3:0] read_cmd ,
input wire [1:0] read_ba ,
input wire [12:0] read_addr ,
input wire read_end ,
input wire write_req ,
input wire [3:0] write_cmd ,
input wire [1:0] write_ba ,
input wire [12:0] write_addr,
input wire wr_end ,
input wire wr_sdram_en ,
input wire [15:0] wr_sdram_data ,
output wire sdram_cke , // 时钟使能
output wire sdram_csn ,
output wire sdram_ras ,
output wire sdram_cas ,
output wire sdram_wen , // 写使能。
output reg [1:0] sdram_bank,
output reg [12:0] sdram_addr,
inout wire [15:0] sdram_dq ,
output wire aref_en ,
output wire write_en ,
output wire read_en
);
// localparam
localparam NOP = 4'b0111 ;
localparam INIT = 5'b0_0001 ,
ARBIT = 5'b0_0010 , // 仲裁状态
WRITE = 5'b0_0100 ,
READ = 5'b0_1000 ,
AREF = 5'b1_0000 ;
// reg signal define
reg [4:0] state_c ;
reg [4:0] state_n ;
reg [3:0] sdram_cmd ; // 组合逻辑
// wire signal define
wire INITtoARBIT ;
wire ARBITtoAREF ;
wire ARBITtoWRITE;
wire ARBITtoREAD ;
wire WRITEtoARBIT;
wire READtoARBIT ;
wire AREFtoARBIT ;
/****************************************************************/
// reg [4:0] state_c ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= INIT ;
else
state_c <= state_n ;
end
// reg [4:0] state_n ;
always @(*) begin
case(state_c)
INIT :
if(INITtoARBIT)
state_n = ARBIT ;
else
state_n = INIT ;
ARBIT :
if(ARBITtoAREF)
state_n = AREF ;
else if(ARBITtoWRITE)
state_n = WRITE ;
else if(ARBITtoREAD)
state_n = READ ;
else
state_n = ARBIT ;
WRITE :
if(WRITEtoARBIT)
state_n = ARBIT ;
else
state_n = WRITE ;
READ :
if(READtoARBIT)
state_n = ARBIT ;
else
state_n = READ ;
AREF : if(AREFtoARBIT)
state_n = ARBIT ;
else
state_n = AREF ;
default: state_n = ARBIT ;
endcase
end
assign INITtoARBIT = ( state_c == INIT ) && ( init_end ) ;
assign ARBITtoAREF = ( state_c == ARBIT ) && ( aref_req ) ;
assign ARBITtoWRITE = ( state_c == ARBIT ) && ( write_req ) ;
assign ARBITtoREAD = ( state_c == ARBIT ) && ( read_req ) ;
assign WRITEtoARBIT = ( state_c == WRITE ) && ( wr_end ) ;
assign READtoARBIT = ( state_c == READ ) && ( read_end ) ;
assign AREFtoARBIT = ( state_c == AREF ) && ( aref_end ) ;
// output
// wire sdram_cke ,
assign sdram_cke = 1'b1 ;
// wire sdram_csn ,
// wire sdram_ras ,
// wire sdram_cas ,
// wire sdram_wen ,
assign sdram_csn = sdram_cmd[3] ;
assign sdram_ras = sdram_cmd[2] ;
assign sdram_cas = sdram_cmd[1] ;
assign sdram_wen = sdram_cmd[0] ;
// assign {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = sdram_cmd;
// reg [3:0] sdram_cmd ;
// reg [1:0] sdram_bank,
// reg [12:0] sdram_addr,
always @(*) begin
case (state_c)
INIT :
begin
sdram_cmd = init_cmd ;
sdram_bank = init_ba ;
sdram_addr = init_addr ;
end
ARBIT :
begin
sdram_cmd = NOP ;
sdram_bank = 2'b11 ;
sdram_addr = 13'h1FFF ;
end
WRITE :
begin
sdram_cmd = write_cmd ;
sdram_bank = write_ba ;
sdram_addr = write_addr ;
end
READ :
begin
sdram_cmd = read_cmd ;
sdram_bank = read_ba ;
sdram_addr = read_addr ;
end
AREF :
begin
sdram_cmd = aref_cmd ;
sdram_bank = aref_ba ;
sdram_addr = aref_addr ;
end
default:
begin
sdram_cmd = NOP ;
sdram_bank = 2'b11 ;
sdram_addr = 13'h1FFF ;
end
endcase
end
// wire [15:0] sdram_dq ,
assign sdram_dq = (wr_sdram_en) ? wr_sdram_data : 16'hzzzz ;
// wire aref_en ,
assign aref_en = (state_c == AREF) ? 1'b1 : 1'b0 ; // 灵活运用三段式状态机。
// wire write_en ,
assign write_en = (state_c == WRITE) ? 1'b1 : 1'b0 ;
// wire read_en
assign read_en = (state_c == READ) ? 1'b1 : 1'b0 ;
endmodule
`timescale 1ns/1ns
module test_sdram_ctrl();
reg sys_clk ; // 50mHz
reg sys_rst_n ;
wire c0_sig ;
wire c1_sig ;
wire c2_sig ;
wire locked_sig ;
wire rst_n ;
assign rst_n = sys_rst_n && (locked_sig) ;
wire wr_end ; // 需要从sdram_write模块引出。
assign wr_end = sdram_ctrl_inst.sdram_write_inst.wr_end ;
// sdram_ctrl模块输入端口信号。
reg sdram_wr_req ;
reg [23:0] sdram_wr_addr ;
reg [15:0] sdram_wr_data ;
reg [9:0] sdram_wr_bust ;
reg sdram_rd_req ;
reg [23:0] sdram_rd_addr ;
reg [9:0] sdram_rd_bust ;
// sdram_ctrl模块的输出信号。
wire sdram_sck ;
wire sdram_cke ;
wire sdram_csn ;
wire sdram_ras ;
wire sdram_cas ;
wire sdram_wen ;
wire [1:0] sdram_bank ;
wire [12:0] sdram_addr ;
wire [15:0] sdram_dq ;
wire sdram_wr_ack ;
wire sdram_rd_ack ;
wire sdram_init_end;
wire [15:0] sdram_data_out;
clk_gen clk_gen_inst (
.areset (~sys_rst_n ) ,
.inclk0 ( sys_clk ) ,
.c0 ( c0_sig ) , // 50
.c1 ( c1_sig ) , // 100
.c2 ( c2_sig ) , // 100+shift30
.locked ( locked_sig )
);
sdram_ctrl sdram_ctrl_inst(
.sys_clk ( c1_sig ) ,
.sys_rst_n ( rst_n ) ,
.clk_shift ( c2_sig ) ,
.sdram_wr_req ( sdram_wr_req ) ,
.sdram_wr_addr ( sdram_wr_addr ) ,
.sdram_wr_data ( sdram_wr_data ) ,
.sdram_wr_bust ( sdram_wr_bust ) ,
.sdram_rd_req ( sdram_rd_req ) ,
.sdram_rd_addr ( sdram_rd_addr ) ,
.sdram_rd_bust ( sdram_rd_bust ) ,
.sdram_sck ( sdram_sck ) ,
.sdram_cke ( sdram_cke ) , // 时钟使能
.sdram_csn ( sdram_csn ) ,
.sdram_ras ( sdram_ras ) ,
.sdram_cas ( sdram_cas ) ,
.sdram_wen ( sdram_wen ) , // 写使能。
.sdram_bank ( sdram_bank ) ,
.sdram_addr ( sdram_addr ) ,
.sdram_dq ( sdram_dq ) ,
.sdram_wr_ack ( sdram_wr_ack ) ,
.sdram_rd_ack ( sdram_rd_ack ) ,
.sdram_init_end ( sdram_init_end ) ,
.sdram_data_out ( sdram_data_out )
);
sdram_model_plus sdram_model_plus_inst(
.Dq ( sdram_dq ) ,
.Addr ( sdram_addr ) ,
.Ba ( sdram_bank ) ,
.Clk ( sdram_sck ) ,
.Cke ( sdram_cke ) ,
.Cs_n ( sdram_csn ) ,
.Ras_n ( sdram_ras ) ,
.Cas_n ( sdram_cas ) ,
.We_n ( sdram_wen ) ,
.Dqm ( 2'b00 ) ,
.Debug ( 1'b1 )
);
parameter CYCLE = 20 ; // 10ns
defparam sdram_model_plus_inst.addr_bits = 13 ;
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 ;
defparam sdram_ctrl_inst.sdram_aref_inst.MAX_CNT_750US = 75 ;
/**********************************************************************/
reg init_end_reg1 ;
reg init_end_reg2 ;
wire wr_flag ;
always @(posedge c1_sig or negedge rst_n) begin
if(~rst_n) begin
init_end_reg1 <= 1'b0 ;
init_end_reg2 <= 1'b0 ;
end
else begin
init_end_reg1 <= sdram_init_end ;
init_end_reg2 <= init_end_reg1 ;
end
end
assign wr_flag = init_end_reg1 && ~init_end_reg2 ;
// reg sdram_wr_req ;
always @(posedge c1_sig or negedge rst_n) begin
if(~rst_n)
sdram_wr_req <= 1'b0 ;
else if(wr_flag) // 初始化完成之后第一个始终周期就拉高写亲求。
sdram_wr_req <= 1'b1 ;
else if(sdram_wr_ack) // sdram_ctrl回应给FIFO控制模块一个写响应信号后,写请求就可以拉低了。
sdram_wr_req <= 1'b0 ;
else
sdram_wr_req <= sdram_wr_req ;
end
// reg [23:0] sdram_wr_addr ;
// reg [15:0] sdram_wr_data ;
// reg [9:0] sdram_wr_bust ;
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~sys_rst_n)
sdram_wr_data <= 16'd0 ;
else if(sdram_wr_ack == 0)
sdram_wr_data <= 16'd0 ;
else
sdram_wr_data <= sdram_wr_data + 1'b1 ;
end
initial begin
sdram_wr_addr <= 24'd0 ; // bank00,行13'd0,列9'd0;
sdram_wr_bust <= 10'd20 ; // 写入10个数据。
end
// reg sdram_rd_req ;
// always @(posedge c1_sig or negedge sys_rst_n) begin
// if(~rst_n)
// sdram_rd_req <= 1'b0;
// else if(wr_end) // 写操作结束后拉高读请求信号。
// sdram_rd_req >= 1'b1 ;
// else if(sdram_rd_ack) // 读响应信号拉高后,读请求信号拉低。
// sdram_rd_req <= 1'b0;
// else
// sdram_rd_req <= sdram_rd_req ;
// end
always @(posedge c1_sig or negedge sys_rst_n) begin
if(~rst_n)
sdram_rd_req <= 1'b0 ;
else if(wr_end)
sdram_rd_req <= 1'b1 ;
else
sdram_rd_req <= sdram_rd_req ;
end
// reg [23:0] sdram_rd_addr ;
// reg [9:0] sdram_rd_bust ;
initial begin
sdram_rd_addr <= 24'd0 ;
sdram_rd_bust <= 10'd13 ;
end
initial begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#(CYCLE * 250_00) ;
$stop ;
end
always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule
仿真:
fifo_ctrl:
设计思路:
这个模块要想写好,需要先明确输入端口具体信号是什么样子的。
我在代码部分有详细说明。
第一部分:
输入端口的设计
写数据控制部分:fifo_ctrl模块先把uart传输接收到的数据,传输进(write_fifo)中。然后用wr_ack做(write_fifo)的读使能,并把数据传输给sdram_data_in[15:0]。
读数据控制部分:fifo_ctrl模块先从sdram_ctrl中把数据读出来,并存进(read_fifo)中。然后根据rd_fifo_rd_req把(read_fifo)中的数据读出来。
1,wr_fifo_wr_clk:写FIFO时钟。
2,wr_fifo_wr_req:写fifo,写请求。应该连接uart_rx,的po_falg端口。
3,wr_fifo_wr_data:写进fifo中的数据,应该连接uart_rx,的po_data端口。
4,sdram_wr_b_addr[23:0]:先把从uart_rx模块接收到的数据存进写fifo做跨时钟域处理,然后写进sdram中。这个sdram_wr_b_addr是写数据进sdram的首地址。
5,sdram_wr_e_addr[23:0]:是末地址。
6,wr_rst:地址复位信号,高电平有效。
7,wr_burst_len[9:0]:写突发长度,由顶层自己设置一个数据写入。
读数据部分:先从sdram_ctrl模块中读出数据,然后写进FIFO控制模块中的读fifo存储器。
8,rd_fifo_rd_clk:读fifo的读时钟。
9,rd_fifo_rd_req:读FIFO的读请求信号。串口数据发送模块产生,读取fifo控制模块中“读FIFO”中的数据。
10,sdram_rd_b_addr[23:0],需要先读取SDRAM、中的数据,然后写进读fifo。这个信号时读sdram的首地址。
11,sdram_rd_e_addr[23:0],末地址。
12,rd_rst,读地址复位信号。
13,rd_valid:由顶层模块传入,高电平有效,表示可进行sdram读操作。
14,rd_burst_len[9:0]:读数据突发长度。
15:init_end:初始化结束信号。
16:sdram_rd_ack与sdram_wr_ack,分别为sdram对应操作的响应信号。
17:sdram_data_out[15:0]:从sdram中读出的数据,传递给fifo控制模块中的读fifo模块。为了通过uart_tx模块发送数据,做跨时钟域同步处理。
第二部分:
内部变量的设计
满足数据通路,时序对齐,怎么设计都行。明天完成这个fifo_ctrl模块。
时序图:
代码:
编译时遇到一个时序逻辑中产生锁存器的情况:
时序逻辑,复位某变量时,如果给该变量赋值某变量(非常量),会产生锁存器。
改成常量就好了。
// 该模块的作用是辅助sdram_ctrl模块进行数据的读写。
// 将要写进sdram模块的数据进行跨时钟域同步处理。
// 通过uart接收模块(50Mhz)接收(写)数据,缓存进write_fifo中。然后大于突发长度时,产生写请求信号,把写数据,写进sdram。
// 通过uart发送模块(50Mhz)发送(读)数据。读数据是通过saram读模块(100Mhz)读取到的缓存进read_fifo的(读)数据。
// 这个读数据的读请求信号是当,初始化完成,写操作完成,read_fifo中数据量小于读突发长度,那么读请求拉高。从sdram中读数据,并写进read_fifo中。
// uart发送模块产生读请求信号,读取read_fifo中的数据,并传输给上位机。
// 该模块部分代码设计与野火官方有很大不同,与模块框图有不一致的地方。但是谁规定一定要完全仿照别人的东西了?
module fifo_ctrl(
input wire clk_50Mhz , // write_fifo的输入端时钟,read_fifo的输出端时钟。
input wire clk_100Mhz , // 大多数信号的时钟,write_fifo的输出端时钟,read_fifo的输入端时钟。
input wire sys_rst_n , // 系统复位
input wire init_end , // 一切请求在初始化完成之后产生?其实没有必要,因为你即使产生了,也不会被响应。
// 写数据进写write_fifo
input wire wr_fifo_wr_req , // 与uart_rx模块的po_flag相连。
input wire [15:0] wr_fifo_wr_data , // 与uart_rx模块的po_data相连。
// 把数据从write_fifo中读出来并写进sdram
output reg sdram_wr_req , // 当write_fifo中的二数据量大于一次写突发长度时,即可拉高。不大于时拉低。也就是说如果write_fifo中数据比写突发长度大很多倍,写请求会一直拉高。直到write_fifo中数据小于写突发长度时拉低。
output reg [23:0] sdram_wr_addr , // 输出一次突发写sdram的首地址。
output wire [15:0] sdram_data_in , // 输出突发写,数据。连接到write_fifo的读出数据端口。
input wire [23:0] sdram_wr_b_addr , // 写进sdram的首末地址。2bit,bank地址,9bit列地址,中间13bit的行地址。
input wire [23:0] sdram_wr_e_addr , // 写进sdram的首末地址。
input wire sdram_wr_ack , // 用来复位写进sdram地址的(sdram_wr_addr),它的下降沿表示一次写突发数据完成,可以用此下降沿作数据更新依据。因为采用突发方式写数据(写突发长度是任意的)。写完后需要更新地址。除非你想一直在相同地址范围内写数据,那就不用更新写地址,就不用复位。
input wire wr_rst ,
input wire [9:0] wr_bust_len , // 写数据突发长度。
// 从sdram中读数据
output reg sdram_rd_req , // 申请读sdram中数据的读请求。
output reg [23:0] sdram_rd_addr , // 一次读突发的,读首地址。
input wire [23:0] rd_fifo_b_addr , // 外界传入的读突发的首末地址。作为sdram_rd_addr的赋值参考依据。
input wire [23:0] rd_fifo_e_addr , // 外界传入的读突发的首末地址。作为sdram_rd_addr的赋值参考依据。
input wire sdram_rd_ack , // 用来复位读突发地址。用来做read_fifo的写入数据使能。
input wire rd_rst , // 与wr_rst一样,是外界传输的
input wire [9:0] rd_bust_len , // 读突发长度。
input wire [15:0] sdram_data_out , // 从sdram中读数据。
// 从read_fifo中读出数据。传给下一个模块。
output wire [15:0] rd_fifo_rd_data , // 从read_fifo中读出的数据。连接到read_fifo的数据输出端口。
output wire [9:0] rd_fifo_num , // read_fifo中“存储”的数据个数,传给外界的一个读模块,用来产生read_fifo的读请求模块。
input wire rd_valid , // 读数据有效信号,sdram_top凭空产生的。个人认为没什么用。类似于使能信号?但是下面都有请求信号了。
input wire rd_fifo_rd_req // 下一个模块产生的,根据read_fifo中存储的数据个数。
); // 仿真时记得把rd_valid信号拉高;rd_rst ;wr_rst 信号相对于sys_rst_n 取反。
// 例化间连线。
wire [9:0] wrusedw_write_num1 ; // write_fifo中存储的数据个数。
wire [9:0] wrusedw_write_num2 ; // write_fifo中存储的数据个数。
wire [9:0] wrusedw_read_num1 ; // read_fifo中存储的数据个数。
wire [9:0] wrusedw_read_num2 ; // read_fifo中存储的数据个数。
wire wr_ack_dly1_nege ;
wire rd_ack_dly1_nege ;
// reg signal define
reg wr_ack_dly1 ;
reg rd_ack_dly1 ;
// wr_ack_dly1 rd_ack_dly1
always @(posedge clk_100Mhz or negedge sys_rst_n) begin
if(~sys_rst_n)
begin
wr_ack_dly1 <= 1'b0 ;
rd_ack_dly1 <= 1'b0 ;
end
else
begin
wr_ack_dly1 <= sdram_wr_ack ;
rd_ack_dly1 <= sdram_rd_ack ;
end
end
// wr_ack_dly1_nege rd_ack_dly1_nege
assign wr_ack_dly1_nege = (~sdram_wr_ack) && (wr_ack_dly1);
assign rd_ack_dly1_nege = (~sdram_rd_ack) && (rd_ack_dly1);
// reg sdram_wr_req
always @(posedge clk_100Mhz or negedge sys_rst_n) begin
if(~sys_rst_n)
sdram_wr_req <= 1'b0 ;
else if(init_end && wrusedw_write_num1 >= wr_bust_len)
sdram_wr_req <= 1'b1 ;
else
sdram_wr_req <= 1'b0 ;
end
// reg [23:0] sdram_wr_addr , // 输出一次突发写sdram的首地址
// always @(posedge clk_100Mhz or negedge sys_rst_n) begin
// if(~sys_rst_n)
// sdram_wr_addr <= sdram_wr_b_addr ;
// else if((sdram_wr_addr < (sdram_wr_e_addr - sdram_wr_b_addr - wr_bust_len)) && wr_ack_dly1_nege)
// sdram_wr_addr <= sdram_wr_addr + wr_bust_len;
// else if(wr_ack_dly1_nege)
// sdram_wr_addr <= sdram_wr_b_addr ;
// else
// sdram_wr_addr <= sdram_wr_addr ;
// end
always @(posedge clk_100Mhz or negedge sys_rst_n) begin
if(~sys_rst_n)
sdram_wr_addr <= 24'd0 ;
else if(wr_rst == 1)
sdram_wr_addr <= sdram_wr_b_addr ;
else if(wr_ack_dly1_nege)
begin
if(sdram_wr_addr < (sdram_wr_e_addr - sdram_wr_b_addr - wr_bust_len))
sdram_wr_addr <= sdram_wr_addr + wr_bust_len;
else
sdram_wr_addr <= sdram_wr_b_addr ;
end
else
sdram_wr_addr <= sdram_wr_addr ;
end
// reg sdram_rd_req , // 申请读sdram中数据的读请求。每完成一次写操作,或者读操作后,若read_fifo中的数据小于rd_bust_len,则需拉高读请求。
always @(posedge clk_100Mhz or negedge sys_rst_n) begin
if(~sys_rst_n)
sdram_rd_req <= 1'b0 ;
else
if(rd_valid == 1)
begin
if(wrusedw_read_num1 < rd_bust_len && (wr_ack_dly1_nege || rd_ack_dly1_nege))
sdram_rd_req <= 1'b1 ;
else if(wrusedw_read_num1 >= rd_bust_len && (wr_ack_dly1_nege || rd_ack_dly1_nege))
sdram_rd_req <= 1'b0 ;
else
sdram_rd_req <= sdram_rd_req ;
end
else
sdram_rd_req <= 1'b0 ;
end
// reg [23:0] sdram_rd_addr , // 一次读突发的,读首地址。
always @(posedge clk_100Mhz or negedge sys_rst_n) begin
if(~sys_rst_n)
sdram_rd_addr <= 24'd0 ;
else if(rd_rst == 1)
sdram_rd_addr <= rd_fifo_b_addr ;
else if(sdram_rd_addr < (rd_fifo_e_addr - rd_fifo_b_addr - rd_bust_len) && rd_ack_dly1_nege)
sdram_rd_addr <= sdram_rd_addr + rd_bust_len ;
else if(sdram_rd_addr >= (rd_fifo_e_addr - rd_fifo_b_addr - rd_bust_len) && rd_ack_dly1_nege)
sdram_rd_addr <= rd_fifo_b_addr ;
else
sdram_rd_addr <= sdram_rd_addr ;
end
// wire [9:0] rd_fifo_num , // read_fifo中“存储”的数据个数,传给外界的一个读模块,用来产生read_fifo的读请求模块。
assign rd_fifo_num = wrusedw_read_num1 ;
// 例化write_fifo read_fifo
fifo_data_16x1024 write_fifo_data_16x1024(
.data ( wr_fifo_wr_data ) ,
.wrclk ( clk_50Mhz ) ,
.wrreq ( wr_fifo_wr_req ) ,
.wrusedw ( wrusedw_write_num1 ) , // ???
.rdclk ( clk_100Mhz ) ,
.rdreq ( sdram_wr_ack ) ,
.q ( sdram_data_in ) ,
.rdusedw ( wrusedw_write_num2 ) // ???
);
fifo_data_16x1024 read_fifo_data_16x1024(
.data ( sdram_data_out ) ,
.wrclk ( clk_100Mhz ) ,
.wrreq ( sdram_rd_ack ) ,
.wrusedw ( wrusedw_read_num1 ) , // ???
.rdclk ( clk_50Mhz ) ,
.rdreq ( rd_fifo_rd_req ) ,
.q ( rd_fifo_rd_data ) ,
.rdusedw ( wrusedw_read_num2 ) // ???
);
endmodule
仿真:
没有单独仿真。
sdram_top:
设计思路
该模块例化了fifo_ctrl模块与sdram_ctrl模块。
例化完后对sdram_top进行仿真验证:
通过uart_rx模块,先传输10个数据进write_fifo
等待sdram完成初始化,然后拉高写请求,把write_fifo中的10个数据写进sdram中。
写完后仲裁模块响应读请求,然后读取10个数据进read_fifo
然后拉高rd_fifo_rd_req读读fifo请求信号,把read_fifo中存的10个数据读出来。
但是要保持read_fifo存储器中存有10个数据,会再次拉高读请求,从sdram中读出10个数据进read_fifo。
之后不会再次拉高rd_fifo_rd_req,除非有其他控制信号干预,想要再次读取数据时会拉高。
代码:
module sdram_top(
input wire clk_50Mhz ,
input wire clk_100Mhz ,
input wire clk_shift ,
input wire sys_rst_n ,
input wire wr_fifo_wr_req ,
input wire [15:0] wr_fifo_wr_data ,
input wire [23:0] sdram_wr_b_addr ,
input wire [23:0] sdram_wr_e_addr ,
input wire wr_rst ,
input wire [9:0] wr_bust_len ,
input wire [23:0] rd_fifo_b_addr ,
input wire [23:0] rd_fifo_e_addr ,
input wire rd_rst ,
input wire [9:0] rd_bust_len ,
input wire rd_valid ,
input wire rd_fifo_rd_req ,
output wire [15:0] rd_fifo_rd_data ,
output wire [9:0] rd_fifo_num ,
output wire sdram_sck , // 偏移+30度的时钟。
output wire sdram_cke , // 时钟使能
output wire sdram_csn ,
output wire sdram_ras ,
output wire sdram_cas ,
output wire sdram_wen , // 写使能。
output wire [1:0] sdram_bank ,
output wire [12:0] sdram_addr ,
inout wire [15:0] sdram_dq ,
output wire [1:0] sdram_dqm // 数据掩码。
);
assign sdram_dqm = 2'b00 ;
// 例化间连线
wire sdram_init_end ;
wire sdram_wr_req ;
wire [23:0] sdram_wr_addr ;
wire [15:0] sdram_data_in ;
wire sdram_wr_ack ;
wire sdram_rd_ack ;
wire sdram_rd_req ;
wire [23:0] sdram_rd_addr ;
wire [15:0] sdram_data_out ;
fifo_ctrl fifo_ctrl_inst(
.clk_50Mhz ( clk_50Mhz ) , // write_fifo的输入端时钟,read_fifo的输出端时钟。
.clk_100Mhz ( clk_100Mhz ) , // 大多数信号的时钟,write_fifo的输出端时钟,read_fifo的输入端时钟。
.sys_rst_n ( sys_rst_n ) , // 系统复位
.init_end ( sdram_init_end ) , // 一切请求在初始化完成之后产生?其实没有必要,因为你即使产生了,也不会被响应。
// 写数据进写write_fifo
.wr_fifo_wr_req ( wr_fifo_wr_req ) , // 与uart_rx模块的po_flag相连。
.wr_fifo_wr_data ( wr_fifo_wr_data ) , // 与uart_rx模块的po_data相连。
// 把数据从write_fifo中读出来并写进sdram
.sdram_wr_req ( sdram_wr_req ) , // 当write_fifo中的二数据量大于一次写突发长度时,即可拉高。不大于时拉低。也就是说如果write_fifo中数据比写突发长度大很多倍,写请求会一直拉高。直到write_fifo中数据小于写突发长度时拉低。
.sdram_wr_addr ( sdram_wr_addr ) , // 输出一次突发写sdram的首地址。
.sdram_data_in ( sdram_data_in ) , // 输出突发写,数据。连接到write_fifo的读出数据端口。
.sdram_wr_b_addr ( sdram_wr_b_addr ) , // 写进sdram的首末地址。2bit,bank地址,9bit列地址,中间13bit的行地址。
.sdram_wr_e_addr ( sdram_wr_e_addr ) , // 写进sdram的首末地址。
.sdram_wr_ack ( sdram_wr_ack ) , // 用来复位写进sdram地址的(sdram_wr_addr),它的下降沿表示一次写突发数据完成,可以用此下降沿作数据更新依据。因为采用突发方式写数据(写突发长度是任意的)。写完后需要更新地址。除非你想一直在相同地址范围内写数据,那就不用更新写地址,就不用复位。
.wr_rst ( wr_rst ) ,
.wr_bust_len ( wr_bust_len ) , // 写数据突发长度。
// 从sdram中读数据
.sdram_rd_req ( sdram_rd_req ) , // 申请读sdram中数据的读请求。
.sdram_rd_addr ( sdram_rd_addr ) , // 一次读突发的,读首地址。
.rd_fifo_b_addr ( rd_fifo_b_addr ) , // 外界传入的读突发的首末地址。作为sdram_rd_addr的赋值参考依据。
.rd_fifo_e_addr ( rd_fifo_e_addr ) , // 外界传入的读突发的首末地址。作为sdram_rd_addr的赋值参考依据。
.sdram_rd_ack ( sdram_rd_ack ) , // 用来复位读突发地址。用来做read_fifo的写入数据使能。
.rd_rst ( rd_rst ) , // 与wr_rst一样,是外界传输的
.rd_bust_len ( rd_bust_len ) , // 读突发长度。
.sdram_data_out ( sdram_data_out ) , // 从sdram中读数据。
// 从read_fifo中读出数据。传给下一个模块。
.rd_fifo_rd_data ( rd_fifo_rd_data ) , // 从read_fifo中读出的数据。连接到read_fifo的数据输出端口。
.rd_fifo_num ( rd_fifo_num ) , // read_fifo中“存储”的数据个数,传给外界的一个读模块,用来产生read_fifo的读请求模块。
.rd_valid ( rd_valid ) , // 读数据有效信号,sdram_top凭空产生的。个人认为没什么用。类似于使能信号?但是下面都有请求信号了。
.rd_fifo_rd_req ( rd_fifo_rd_req ) // 下一个模块产生的,根据read_fifo中存储的数据个数。
);
sdram_ctrl sdram_ctrl_inst(
.sys_clk ( clk_100Mhz ) ,
.sys_rst_n ( sys_rst_n ) ,
.clk_shift ( clk_shift ) ,
.sdram_wr_req ( sdram_wr_req ) ,
.sdram_wr_addr ( sdram_wr_addr ) ,
.sdram_wr_data ( sdram_data_in ) ,
.sdram_wr_bust ( wr_bust_len ) ,
.sdram_rd_req ( sdram_rd_req ) ,
.sdram_rd_addr ( sdram_rd_addr ) ,
.sdram_rd_bust ( rd_bust_len ) ,
.sdram_sck ( sdram_sck ) , // 偏移+30度的时钟。
.sdram_cke ( sdram_cke ) , // 时钟使能
.sdram_csn ( sdram_csn ) ,
.sdram_ras ( sdram_ras ) ,
.sdram_cas ( sdram_cas ) ,
.sdram_wen ( sdram_wen ) , // 写使能。
.sdram_bank ( sdram_bank ) ,
.sdram_addr ( sdram_addr ) ,
.sdram_dq ( sdram_dq ) ,
.sdram_wr_ack ( sdram_wr_ack ) ,
.sdram_rd_ack ( sdram_rd_ack ) ,
.sdram_init_end ( sdram_init_end ) ,
.sdram_data_out ( sdram_data_out )
);
endmodule
仿真:
`timescale 1ns/1ns
module test_sdram_top ();
reg sys_clk_50Mhz ;
reg sys_rst_n ;
wire c0_sig ;
wire c1_sig ;
wire c2_sig ;
wire locked_sig ;
wire rst_n ;
reg rx ;
wire po_flag ;
wire [7:0] po_data ;
reg rd_fifo_rd_req ;
wire [15:0] rd_fifo_rd_data ;
wire [9:0] rd_fifo_num ;
wire sdram_sck ;
wire sdram_cke ;
wire sdram_csn ;
wire sdram_ras ;
wire sdram_cas ;
wire sdram_wen ;
wire [1:0] sdram_bank ;
wire [12:0] sdram_addr ;
wire [15:0] sdram_dq ;
wire [1:0] sdram_dqm ;
wire [9:0] rd_bust_len ;
wire [9:0] wr_bust_len ;
reg rd_flag ;
// 例化
clk_gen clk_gen_inst (
.areset ( ~sys_rst_n ) ,
.inclk0 ( sys_clk_50Mhz ) ,
.c0 ( c0_sig ) , // 50m
.c1 ( c1_sig ) , // 100m
.c2 ( c2_sig ) , // 100m shift
.locked ( locked_sig )
);
uart_rx uart_rx_inst(
.sys_clk ( c0_sig ) ,
.sys_rst_n ( rst_n ) ,
.rx ( rx ) ,
.po_flag ( po_flag ) ,
.po_data ( po_data )
);
sdram_top sdram_top_inst(
.clk_50Mhz ( c0_sig ) ,
.clk_100Mhz ( c1_sig ) ,
.clk_shift ( c2_sig ) ,
.sys_rst_n ( rst_n ) ,
.wr_fifo_wr_req ( po_flag ) ,
.wr_fifo_wr_data ( {8'h00,po_data} ) ,
.sdram_wr_b_addr ( 24'd0 ) ,
.sdram_wr_e_addr ( 24'd10 ) ,
.wr_rst ( ~rst_n ) ,
.wr_bust_len ( wr_bust_len ) ,
.rd_fifo_b_addr ( 24'd0 ) ,
.rd_fifo_e_addr ( 24'd10 ) ,
.rd_rst ( ~rst_n ) ,
.rd_bust_len ( rd_bust_len ) ,
.rd_valid ( 1'b1 ) ,
.rd_fifo_rd_req ( rd_fifo_rd_req) ,
.rd_fifo_rd_data (rd_fifo_rd_data) ,
.rd_fifo_num ( rd_fifo_num ) ,
.sdram_sck ( sdram_sck ) , // 偏移+30度的时钟。
.sdram_cke ( sdram_cke ) , // 时钟使能
.sdram_csn ( sdram_csn ) ,
.sdram_ras ( sdram_ras ) ,
.sdram_cas ( sdram_cas ) ,
.sdram_wen ( sdram_wen ) , // 写使能。
.sdram_bank ( sdram_bank ) ,
.sdram_addr ( sdram_addr ) ,
.sdram_dq ( sdram_dq ) ,
.sdram_dqm ( sdram_dqm ) // 数据掩码。
);
sdram_model_plus sdram_model_plus_inst(
.Dq ( sdram_dq ) ,
.Addr ( sdram_addr ) ,
.Ba ( sdram_bank ) ,
.Clk ( sdram_sck ) ,
.Cke ( sdram_cke ) ,
.Cs_n ( sdram_csn ) ,
.Ras_n ( sdram_ras ) ,
.Cas_n ( sdram_cas ) ,
.We_n ( sdram_wen ) ,
.Dqm ( {2'b00,sdram_dqm} ) ,
.Debug ( 1'b1 )
);
/****************************************************************/
parameter CYCLE = 20 ;
defparam uart_rx_inst.CLK_UART = 50_000_0 ;
defparam sdram_top_inst.sdram_ctrl_inst.sdram_aref_inst.MAX_CNT_750US = 200 ;
defparam sdram_model_plus_inst.addr_bits = 13 ;
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 ;
// rst_n rd_bust_len wr_bust_len
assign rst_n = sys_rst_n && locked_sig ;
assign rd_bust_len = 10'd10 ;
assign wr_bust_len = 10'd10 ;
// rx
task rx_bit ;
input [7:0] data ;
integer i ;
for (i = 0;i <= 9 ;i = i + 1 ) begin
case (i)
0: rx <= 1'b0 ;
1: rx <= data[i - 1];
2: rx <= data[i - 1];
3: rx <= data[i - 1];
4: rx <= data[i - 1];
5: rx <= data[i - 1];
6: rx <= data[i - 1];
7: rx <= data[i - 1];
8: rx <= data[i - 1];
9: rx <= 1'b1 ;
default: rx <= 1'b1 ;
endcase
#(CYCLE * 52) ;
end
endtask
// rd_fifo_rd_req
// always @(posedge c0_sig or negedge rst_n) begin
// if(~rst_n)
// rd_fifo_rd_req <= 1'b0 ;
// else if(rd_fifo_num == rd_bust_len)
// rd_fifo_rd_req <= 1'b1 ;
// else if(rd_fifo_num == 0)
// rd_fifo_rd_req <= 1'b0 ;
// else
// rd_fifo_rd_req <= rd_fifo_rd_req ;
// end
always @(posedge c0_sig or negedge rst_n) begin
if(~rst_n)
rd_flag <= 1'b1 ;
else if(rd_fifo_num == rd_bust_len && rd_flag == 1'b1)
rd_flag <= 1'b0 ;
else
rd_flag <= rd_flag ;
end
always @(posedge c0_sig or negedge rst_n) begin
if(~rst_n)
rd_fifo_rd_req <= 1'b0 ;
else if(rd_fifo_num == rd_bust_len && rd_flag)
rd_fifo_rd_req <= 1'b1 ;
else if(rd_fifo_num == 0)
rd_fifo_rd_req <= 1'b0 ;
else
rd_fifo_rd_req <= rd_fifo_rd_req ;
end
initial begin
sys_clk_50Mhz = 1'b1 ;
sys_rst_n <= 1'b0 ;
rx <= 1'b1 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#( CYCLE * 100 ) ;
rx_bit(8'b1) ;
rx_bit(8'd2) ;
rx_bit(8'd3) ;
rx_bit(8'd4) ;
rx_bit(8'd5) ;
rx_bit(8'd6) ;
rx_bit(8'd7) ;
rx_bit(8'd8) ;
rx_bit(8'd9) ;
rx_bit(8'd9) ;
end
always #(CYCLE / 2) sys_clk_50Mhz = ~sys_clk_50Mhz ;
endmodule
uart_sdram:
read_fifo:
设计思路:
从read_fifo中读出的10个数据时50Mhz时钟下的,需要先存进fifo,然后由波特率产生标志信号,作为fifo的读标志。把标志打拍与读出的数据作为po_flag_tx与po_flag_tx。传给uart_tx模块。
修改uart_tx模块,产生一个data_over信号,传递完一帧数据的最后一位拉高一个时钟周期,作为read_fifo的读信号。
时序图:
代码:
module fifo_read(
input wire sys_clk , // clk_50M
input wire sys_rst_n ,
input wire [7:0] rd_fifo_rd_data ,
input wire [9:0] rd_fifo_num ,
input wire [9:0] bust_num , // 作为顶层模块设置并输入,为固定参数,表示本模块一次读取读 FIFO 中数据个数,参数数值与数据读模块中读突发长度一致,防止数据读空。
input wire data_over ,
output reg read_en , // read_en的设计可以参考仿真模块里的设计。
output wire [7:0] tx_data ,
output reg tx_flag
);
parameter BPS = 9600 ,
BIT = 10 ;
reg fifo_256_wren ; // 经过观察read_en与rd_fifo_rd_data的关系,打一拍,会与rd_fifo_rd_data对齐,做fifo的写使能。
wire [7:0] wrusedw ; // 写进本模块fifo中的数据。当wrusedw == bust_len - 2时,read_en拉低。
reg rd_fifo256_en ; // fifo256开始进行读的使能信号。
reg rd_fifo256_en_r ; // fifo256开始进行读的使能信号。打拍
reg rd_fifo256 ; // fifo256读请求信号。
wire empty ;
// fifo_256_wren
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
fifo_256_wren <= 1'b0 ;
else
fifo_256_wren <= read_en ;
end
// rd_fifo256_en
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
rd_fifo256_en <= 1'b0 ;
else if(empty == 1)
rd_fifo256_en <= 1'b0 ;
else if(!empty && !read_en && fifo_256_wren)
rd_fifo256_en <= 1'b1 ;
end
// rd_fifo256_en_r
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
rd_fifo256_en_r <= 1'b0 ;
else
rd_fifo256_en_r <= rd_fifo256_en ;
end
// rd_fifo256
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
rd_fifo256 <= 1'b0 ;
else if(rd_fifo256_en) begin
if((~rd_fifo256_en_r) || (!empty && data_over))
rd_fifo256 <= 1'b1 ;
else
rd_fifo256 <= 1'b0 ;
end
else
rd_fifo256 <= 1'b0 ;
end
// output signal describe
// read_en
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
read_en <= 1'b0 ;
else if(rd_fifo_num == bust_num)
read_en <= 1'b1 ;
else if(wrusedw == bust_num - 3) // 提前拉低。
read_en <= 1'b0 ;
else
read_en <= read_en ;
end
// wire [7:0] tx_data ,
// reg tx_flag
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
tx_flag <= 1'b0 ;
else
tx_flag <= rd_fifo256 ;
end
// 例化
fifo_data_8x256 fifo_data_8x256_inst(
.wrclk ( sys_clk ) , // 写时钟与读时钟貌似是相同的。
.wrreq ( fifo_256_wren ) , // 写请求。
.data ( rd_fifo_rd_data ) ,
.rdclk ( sys_clk ) ,
.rdreq ( rd_fifo256 ) ,
.rdempty ( empty ),
.q ( tx_data ) ,
.rdusedw ( ) ,
.wrusedw ( wrusedw )
);
endmodule
仿真:
仍然对顶层模块仿真,测试文件稍作修改。
把read_fifo模块与uart_tx加进去。
`timescale 1ns/1ns
module test_sdram_top ();
reg sys_clk_50Mhz ;
reg sys_rst_n ;
wire c0_sig ;
wire c1_sig ;
wire c2_sig ;
wire locked_sig ;
wire rst_n ;
reg rx ;
wire po_flag ;
wire [7:0] po_data ;
reg rd_fifo_rd_req ;
wire [15:0] rd_fifo_rd_data ;
wire [9:0] rd_fifo_num ;
wire sdram_sck ;
wire sdram_cke ;
wire sdram_csn ;
wire sdram_ras ;
wire sdram_cas ;
wire sdram_wen ;
wire [1:0] sdram_bank ;
wire [12:0] sdram_addr ;
wire [15:0] sdram_dq ;
wire [1:0] sdram_dqm ;
wire [9:0] rd_bust_len ;
wire [9:0] wr_bust_len ;
reg rd_flag ;
wire data_over ;
wire read_en ;
wire [7:0] tx_data ;
wire tx_flag ;
wire tx ;
// 例化
clk_gen clk_gen_inst (
.areset ( ~sys_rst_n ) ,
.inclk0 ( sys_clk_50Mhz ) ,
.c0 ( c0_sig ) , // 50m
.c1 ( c1_sig ) , // 100m
.c2 ( c2_sig ) , // 100m shift
.locked ( locked_sig )
);
uart_rx uart_rx_inst(
.sys_clk ( c0_sig ) ,
.sys_rst_n ( rst_n ) ,
.rx ( rx ) ,
.po_flag ( po_flag ) ,
.po_data ( po_data )
);
sdram_top sdram_top_inst(
.clk_50Mhz ( c0_sig ) ,
.clk_100Mhz ( c1_sig ) ,
.clk_shift ( c2_sig ) ,
.sys_rst_n ( rst_n ) ,
.wr_fifo_wr_req ( po_flag ) ,
.wr_fifo_wr_data ( {8'h00,po_data} ) ,
.sdram_wr_b_addr ( 24'd0 ) ,
.sdram_wr_e_addr ( 24'd10 ) ,
.wr_rst ( ~rst_n ) ,
.wr_bust_len ( wr_bust_len ) ,
.rd_fifo_b_addr ( 24'd0 ) ,
.rd_fifo_e_addr ( 24'd10 ) ,
.rd_rst ( ~rst_n ) ,
.rd_bust_len ( rd_bust_len ) ,
.rd_valid ( 1'b1 ) ,
.rd_fifo_rd_req ( read_en ) , // rd_fifo_rd_req
.rd_fifo_rd_data (rd_fifo_rd_data) ,
.rd_fifo_num ( rd_fifo_num ) ,
.sdram_sck ( sdram_sck ) , // 偏移+30度的时钟。
.sdram_cke ( sdram_cke ) , // 时钟使能
.sdram_csn ( sdram_csn ) ,
.sdram_ras ( sdram_ras ) ,
.sdram_cas ( sdram_cas ) ,
.sdram_wen ( sdram_wen ) , // 写使能。
.sdram_bank ( sdram_bank ) ,
.sdram_addr ( sdram_addr ) ,
.sdram_dq ( sdram_dq ) ,
.sdram_dqm ( sdram_dqm ) // 数据掩码。
);
sdram_model_plus sdram_model_plus_inst(
.Dq ( sdram_dq ) ,
.Addr ( sdram_addr ) ,
.Ba ( sdram_bank ) ,
.Clk ( sdram_sck ) ,
.Cke ( sdram_cke ) ,
.Cs_n ( sdram_csn ) ,
.Ras_n ( sdram_ras ) ,
.Cas_n ( sdram_cas ) ,
.We_n ( sdram_wen ) ,
.Dqm ( {2'b00,sdram_dqm} ) ,
.Debug ( 1'b1 )
);
fifo_read fifo_read_inst(
.sys_clk ( c0_sig ) , // clk_50M
.sys_rst_n ( rst_n ) ,
.rd_fifo_rd_data ( rd_fifo_rd_data[7:0] ) ,
.rd_fifo_num ( rd_fifo_num ) ,
.bust_num ( rd_bust_len ) ,
.data_over ( data_over ) ,
.read_en ( read_en ) , // read_en的设计可以参考仿真模块里的设计。
.tx_data ( tx_data ) ,
.tx_flag ( tx_flag )
);
uart_tx uart_tx_inst(
.sys_clk ( c0_sig ) ,
.sys_rst_n ( rst_n ) ,
.pi_data ( tx_data ) ,
.pi_flag ( tx_flag ) ,
.data_over ( data_over ) ,
.tx ( tx )
);
/****************************************************************/
parameter CYCLE = 20 ;
defparam uart_tx_inst.CLK_UART = 50_000_0 ;
defparam uart_rx_inst.CLK_UART = 50_000_0 ;
defparam sdram_top_inst.sdram_ctrl_inst.sdram_aref_inst.MAX_CNT_750US = 200 ;
defparam sdram_model_plus_inst.addr_bits = 13 ;
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 ;
// rst_n rd_bust_len wr_bust_len
assign rst_n = sys_rst_n && locked_sig ;
assign rd_bust_len = 10'd10 ;
assign wr_bust_len = 10'd10 ;
// rx
task rx_bit ;
input [7:0] data ;
integer i ;
for (i = 0;i <= 9 ;i = i + 1 ) begin
case (i)
0: rx <= 1'b0 ;
1: rx <= data[i - 1];
2: rx <= data[i - 1];
3: rx <= data[i - 1];
4: rx <= data[i - 1];
5: rx <= data[i - 1];
6: rx <= data[i - 1];
7: rx <= data[i - 1];
8: rx <= data[i - 1];
9: rx <= 1'b1 ;
default: rx <= 1'b1 ;
endcase
#(CYCLE * 52) ;
end
endtask
// rd_fifo_rd_req
// always @(posedge c0_sig or negedge rst_n) begin
// if(~rst_n)
// rd_fifo_rd_req <= 1'b0 ;
// else if(rd_fifo_num == rd_bust_len)
// rd_fifo_rd_req <= 1'b1 ;
// else if(rd_fifo_num == 0)
// rd_fifo_rd_req <= 1'b0 ;
// else
// rd_fifo_rd_req <= rd_fifo_rd_req ;
// end
always @(posedge c0_sig or negedge rst_n) begin
if(~rst_n)
rd_flag <= 1'b1 ;
else if(rd_fifo_num == rd_bust_len && rd_flag == 1'b1)
rd_flag <= 1'b0 ;
else
rd_flag <= rd_flag ;
end
always @(posedge c0_sig or negedge rst_n) begin
if(~rst_n)
rd_fifo_rd_req <= 1'b0 ;
else if(rd_fifo_num == rd_bust_len && rd_flag)
rd_fifo_rd_req <= 1'b1 ;
else if(rd_fifo_num == 2)
rd_fifo_rd_req <= 1'b0 ;
else
rd_fifo_rd_req <= rd_fifo_rd_req ;
end
initial begin
sys_clk_50Mhz = 1'b1 ;
sys_rst_n <= 1'b0 ;
rx <= 1'b1 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#( CYCLE * 100 ) ;
rx_bit(8'b1) ;
rx_bit(8'd2) ;
rx_bit(8'd3) ;
rx_bit(8'd4) ;
rx_bit(8'd5) ;
rx_bit(8'd6) ;
rx_bit(8'd7) ;
rx_bit(8'd8) ;
rx_bit(8'd9) ;
rx_bit(8'd10) ;
end
always #(CYCLE / 2) sys_clk_50Mhz = ~sys_clk_50Mhz ;
endmodule
top:
对下列模块进行例化。
文章来源:https://www.toymoban.com/news/detail-794817.html
代码:
module top(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,
output wire tx ,
output wire sdram_cke ,
output wire sdram_sck ,
output wire sdram_csn ,
output wire sdram_ras ,
output wire sdram_cas ,
output wire sdram_wen ,
output wire [1:0] sdram_bank ,
output wire [12:0] sdram_addr ,
inout wire [15:0] sdram_dq ,
output wire [1:0] sdram_dqm
);
// 例化间连线
wire clk_50Mhz ;
wire clk_100Mhz ;
wire clk_100Mhz_shift;
wire rst_n ;
wire po_flag ;
wire [7:0] po_data ;
wire read_en ;
wire [7:0] tx_data ;
wire tx_flag ;
wire [15:0] rd_fifo_rd_data ;
wire [9:0] rd_fifo_num ;
wire data_over ;
// 例化
clk_gen clk_gen_inst(
.areset ( ~sys_rst_n ) ,
.inclk0 ( sys_clk ) ,
.c0 ( clk_50Mhz ) ,
.c1 ( clk_100Mhz ) ,
.c2 ( clk_100Mhz_shift ) ,
.locked ( rst_n )
);
uart_rx uart_rx_inst(
.sys_clk ( clk_50Mhz ) ,
.sys_rst_n ( rst_n ) ,
.rx ( rx ) ,
.po_flag ( po_flag ) ,
.po_data ( po_data )
);
sdram_top sdram_top_inst(
.clk_50Mhz ( clk_50Mhz ) ,
.clk_100Mhz ( clk_100Mhz ) ,
.clk_shift ( clk_100Mhz_shift ) ,
.sys_rst_n ( rst_n ) ,
.wr_fifo_wr_req ( po_flag ) ,
.wr_fifo_wr_data ( {8'd0,po_data} ) ,
.sdram_wr_b_addr ( 24'd0 ) ,
.sdram_wr_e_addr ( 24'd10 ) ,
.wr_rst ( ~rst_n ) ,
.wr_bust_len ( 10'd10 ) ,
.rd_fifo_b_addr ( 24'd0 ) ,
.rd_fifo_e_addr ( 24'd10 ) ,
.rd_rst ( ~rst_n ) ,
.rd_bust_len ( 10'd10 ) ,
.rd_valid ( 1'b1 ) ,
.rd_fifo_rd_req ( read_en ) ,
.rd_fifo_rd_data ( rd_fifo_rd_data ) ,
.rd_fifo_num ( rd_fifo_num ) ,
.sdram_cke ( sdram_cke ) ,
.sdram_sck ( sdram_sck ) ,
.sdram_csn ( sdram_csn ) ,
.sdram_ras ( sdram_ras ) ,
.sdram_cas ( sdram_cas ) ,
.sdram_wen ( sdram_wen ) ,
.sdram_bank ( sdram_bank ) ,
.sdram_addr ( sdram_addr ) ,
.sdram_dq ( sdram_dq ) ,
.sdram_dqm ( sdram_dqm )
);
fifo_read fifo_read_inst(
.sys_clk ( clk_50Mhz ) ,
.sys_rst_n ( rst_n ) ,
.rd_fifo_rd_data ( rd_fifo_rd_data[7:0] ) ,
.rd_fifo_num ( rd_fifo_num ) ,
.bust_num ( 10'd10 ) ,
.data_over ( data_over ) ,
.read_en ( read_en ) ,
.tx_data ( tx_data ) ,
.tx_flag ( tx_flag )
);
uart_tx uart_tx_inst(
.sys_clk ( clk_50Mhz ) ,
.sys_rst_n ( rst_n ) ,
.pi_data ( tx_data ) ,
.pi_flag ( tx_flag ) ,
.data_over ( data_over ) ,
.tx ( tx )
);
endmodule
仿真:
`timescale 1ns/1ns
module test_top();
reg sys_clk_50Mhz ;
reg sys_rst_n ;
reg rx ;
wire tx ;
wire sdram_cke ;
wire sdram_sck ;
wire sdram_csn ;
wire sdram_ras ;
wire sdram_cas ;
wire sdram_wen ;
wire [1:0] sdram_bank ;
wire [12:0] sdram_addr ;
wire [15:0] sdram_dq ;
wire [1:0] sdram_dqm ;
top top_inst(
.sys_clk ( sys_clk_50Mhz ) ,
.sys_rst_n ( sys_rst_n ) ,
.rx ( rx ) ,
.tx ( tx ) ,
.sdram_cke ( sdram_cke ) ,
.sdram_sck ( sdram_sck ) ,
.sdram_csn ( sdram_csn ) ,
.sdram_ras ( sdram_ras ) ,
.sdram_cas ( sdram_cas ) ,
.sdram_wen ( sdram_wen ) ,
.sdram_bank ( sdram_bank ) ,
.sdram_addr ( sdram_addr ) ,
.sdram_dq ( sdram_dq ) ,
.sdram_dqm ( sdram_dqm )
);
sdram_model_plus sdram_model_plus_inst(
.Dq ( sdram_dq ) ,
.Addr ( sdram_addr ) ,
.Ba ( sdram_bank ) ,
.Clk ( sdram_sck ) ,
.Cke ( sdram_cke ) ,
.Cs_n ( sdram_csn ) ,
.Ras_n ( sdram_ras ) ,
.Cas_n ( sdram_cas ) ,
.We_n ( sdram_wen ) ,
.Dqm ( {2'b00,sdram_dqm} ) ,
.Debug ( 1'b1 )
);
/******************************************************/
parameter CYCLE = 20 ;
defparam top_inst.uart_rx_inst.CLK_UART = 50_000_0 ;
defparam top_inst.uart_tx_inst.CLK_UART = 50_000_0 ;
defparam top_inst.uart_tx_inst.SUB_1K = 100 ;
defparam top_inst.sdram_top_inst.sdram_ctrl_inst.sdram_aref_inst.MAX_CNT_750US = 200 ;
defparam sdram_model_plus_inst.addr_bits = 13 ;
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 ;
// rx
task rx_bit ;
input [7:0] data ;
integer i ;
for (i = 0;i <= 9 ;i = i + 1 ) begin
case (i)
0: rx <= 1'b0 ;
1: rx <= data[i - 1];
2: rx <= data[i - 1];
3: rx <= data[i - 1];
4: rx <= data[i - 1];
5: rx <= data[i - 1];
6: rx <= data[i - 1];
7: rx <= data[i - 1];
8: rx <= data[i - 1];
9: rx <= 1'b1 ;
default: rx <= 1'b1 ;
endcase
#(CYCLE * 52) ;
end
endtask
initial begin
sys_clk_50Mhz = 1'b1 ;
sys_rst_n <= 1'b0 ;
rx <= 1'b1 ;
#(CYCLE) ;
sys_rst_n <= 1'b1 ;
#( CYCLE * 100 ) ;
rx_bit(8'b1) ;
rx_bit(8'd2) ;
rx_bit(8'd3) ;
rx_bit(8'd4) ;
rx_bit(8'd5) ;
rx_bit(8'd6) ;
rx_bit(8'd7) ;
rx_bit(8'd8) ;
rx_bit(8'd9) ;
rx_bit(8'd10) ;
end
always #(CYCLE / 2) sys_clk_50Mhz = ~sys_clk_50Mhz ;
endmodule
上板验证:
文章来源地址https://www.toymoban.com/news/detail-794817.html
到了这里,关于FPGA project : sdram的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!