SDRAM读写控制器,这里分为三个部分,分别是SDRAM的基本操作实现,SDRAM控制器,封装成FIFO,以方便使用。
一、SDRAM的基本操作:初始化模块、自动刷新模块、写操作模块、读操作模块、SDRAM仲裁模块,顶层模块。
1、初始化模块
上图是初始化模块的时序图。有图可知,SDRAM上电并且时钟稳定后,SDRAM首先的延迟等待100us,等待期间只能赋予禁止指令或者空操作指令。等待延时完成后,需要对SDRAM所有bank进行一次预充电操作(A10)置为高电平。然后进入预充电完成等待时间tRP,等待完成后至少执行两次自动刷新指令。之后对SDRAM进行加载模式寄存器。
时序分析图
寄存器模式配置
/*Sdram 初始化
1、工作时钟定为100Mhz
2、SDRAM型号:W9825G6DH 4M x 4banks x 16bits
3、 行地址位宽:13
4、列地址位宽:9
5、数据位宽:16
6、Sdram 的自动刷新功能:每隔 64ms/2^12=15625 个时钟周期,给出刷新命令。
*/
module sdram_init
(
input wire sys_clk ,
input wire sys_rst_n ,
output reg [3:0] cmd_init ,//操作指令(包括片选信号,行选同步信号,列选同步信号,使能信号)
output reg [1:0] ba_init ,//板块地址
output reg [12:0] addr_init ,//地址总线
output wire init_end //初始化完成标志
);
//格雷码
parameter INIT_IDLE = 3'b000, //初始状态
INIT_PRE = 3'b001, //预充电状态
INIT_TRP = 3'b011, //预充电等待时间状态(12-20ns),具体时间看数据手册
INIT_AR = 3'b010, //自动刷新状态
INIT_TRF = 3'b110, //自动刷新等待时间状态(66ns)
INIT_MRS = 3'b111, //寄存器配置状态
INIT_TMRD = 3'b101, //寄存器配置到active状态等待时间,两个工作时钟周期
INIT_END = 3'b100; //初始化完成状态
//初始等待时间计数
parameter CNT_200 = 15'd20000;
//等待时间周期
parameter TRP = 2'd2,//15~20ns预充电等待时间
TRFC = 3'd7,//66ns自动刷新等待时间
TMRD = 2'd3;//3tclk寄存器配置等待时间
parameter NOP = 4'b0111, //空操作指令
AUTO_RFE= 4'b0001, //自动刷新指令
MREG_ST = 4'b0000, //配置寄存器指令
PCHARGE = 4'b0010; //预充电指令
reg [2:0] init_state;
reg [14:0] cnt_200us;
wire wait_end; //初始等待时间结束(200us),高电平有效
reg [2:0] cnt_clk;
reg cnt_clk_rst;
wire trp_end;
wire trfc_end;
wire tmrd_end;
reg [3:0] cnt_aref;
//sdram初始化状态机
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
init_state <= INIT_IDLE;
else case(init_state)
INIT_IDLE: //系统上电后,在初始状态等待200us跳转到预充电状态
if(wait_end)
init_state <= INIT_PRE;
else
init_state <= init_state;
INIT_PRE : //预充电状态,直接跳转到预充电等待状态
init_state <= INIT_TRP;
INIT_TRP : //预充电等待状态,等待结束,跳转到自动刷新状态
if(trp_end)
init_state <= INIT_AR;
else
init_state <= init_state;
INIT_AR : //自动刷新状态,直接跳转到自动刷新等待状态
init_state <= INIT_TRF;
INIT_TRF : //自动刷新8次,自动跳转到模式寄存器设置状态
if(trfc_end & cnt_aref == 4'd8)
init_state <= INIT_MRS;
else if(trfc_end)
init_state <= INIT_AR;
else
init_state <= init_state;
INIT_MRS : //模式寄存器设置状态,直接跳转到模式寄存器设置等待状态
init_state <= INIT_TMRD;
INIT_TMRD: //模式寄存器设置等待状态,等待结束,跳到初始化完成状态
if(tmrd_end)
init_state <= INIT_END;
else
init_state <= init_state;
INIT_END : //初始化完成状态,保持此状态
init_state <= INIT_END;
default:init_state <= INIT_IDLE;
endcase
//计时200us
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_200us <= 15'd0;
else if(cnt_200us == CNT_200)
cnt_200us <= CNT_200;
else
cnt_200us <= cnt_200us + 1'b1;
//wait_end:上电后200us等待结束标志
assign wait_end = (cnt_200us == CNT_200 - 1'b1)?1'b1:1'b0;
//cnt_clk:时钟周期计数,记录初始化各状态等待时间
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 3'd0;
else if(cnt_clk_rst)
cnt_clk <= 3'd0;
else
cnt_clk <= cnt_clk + 1'b1;
//cnt_clk_rst:时钟周期计数复位标志
always@(*)
begin
case(init_state)
INIT_IDLE : cnt_clk_rst <= 1'b1;
INIT_TRP : cnt_clk_rst <= (trp_end)?1'b1:1'b0;
INIT_TRF : cnt_clk_rst <= (trfc_end)?1'b1:1'b0;
INIT_TMRD : cnt_clk_rst <= (tmrd_end)?1'b1:1'b0;
INIT_END : cnt_clk_rst <= 1'b1;
default : cnt_clk_rst <= 1'b0;
endcase
end
//trp_end,trc_end,tmrd_end:等待结束标志
assign trp_end = (cnt_clk == TRP & init_state == INIT_TRP)?1'b1:1'b0;
assign trfc_end = (cnt_clk == TRFC & init_state == INIT_TRF)?1'b1:1'b0;
assign tmrd_end = (cnt_clk == TMRD & init_state == INIT_TMRD)?1'b1:1'b0;
//对自动刷新计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_aref <= 4'd0;
else if(init_state == INIT_AR)
cnt_aref <= cnt_aref + 1'b1;
else if(init_state == INIT_IDLE)
cnt_aref <= 4'd0;
//输出配置
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
cmd_init <= NOP;
ba_init <= 2'b11;
addr_init<= 13'h1fff;
end
else case(init_state)
INIT_IDLE,INIT_TRF,INIT_TRP,INIT_TMRD,INIT_END:
begin
cmd_init <= NOP;
ba_init <= 2'b11;
addr_init<= 13'h1fff;
end
INIT_PRE:
begin
cmd_init <= PCHARGE;
ba_init <= 2'b11;
addr_init<= 13'h1fff;//预充电时,A10为高电平对所有bank进行预充电
end
INIT_AR:
begin
cmd_init <= AUTO_RFE;
ba_init <= 2'b11;
addr_init<= 13'h1fff;
end
INIT_MRS:
begin
cmd_init <= MREG_ST;
ba_init <= 2'b00; //配置寄存器模式时bank地址给00
addr_init<=
{
//地址辅助配置模式寄存器,参数不同,配置的模式不同
3'b000, //A12-A10:预留
1'b0, //A9=0:读写方式,0:突发读&突发写,1:突发读&单写
2'b00, //{A8,A7}=00:标准模式,默认
3'b011, //{A6,A5,A4}=011:CAS潜伏期,010:2,011:3,其他:保留
1'b0, //A3=0:突发传输方式,0:顺序,1:隔行
3'b111 //{A2,A1,A0}=111:突发长度,000:单字节,001:2字节
//010:4字节,011:8字节,111:整页,其他:保留
};
end
default:
begin
cmd_init <= NOP;
ba_init <= 2'b11;
addr_init<= 13'h1fff;
end
endcase
assign init_end = (init_state == INIT_END)?1'b1:1'b0;
endmodule
观察仿真模型结果,初始化操作的状态跳转和写入的操作指令正确。初始化对所有bank进行预充电,经过8次自动刷新后开始配置寄存器模式,对地址线A0~A12配置,参照数据手册可知,列选通潜伏期为3,突发传输方式为顺序突发,突发长度为整页突发。
2、自动刷新模块
自动刷新时序
状态转移图
/*Sdram自动刷新
1、工作时钟定为100Mhz
2、SDRAM型号:W9825G6DH 4M x 4banks x 16bits
3、 行地址位宽:13
4、列地址位宽:9
5、数据位宽:16
6、Sdram 的自动刷新功能:每隔 64ms/2^13=7812.5 ns,给出刷新命令。
*/
module sdram_aref
(
input wire sys_clk ,//系统时钟,频率100MHz
input wire sys_rst_n ,//复位信号,低电平有效
input wire init_end ,//初始化结束信号
input wire aref_en ,//自动刷新使能,由仲裁模块返回
output reg [3:0] aref_cmd ,//自动刷新阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n}
output reg [1:0] aref_ba ,//自动刷新阶段Bank地址
output reg [12:0] aref_addr ,//地址数据,辅助预充电操作,A12-A0,13位地址
output wire aref_end ,//自动刷新结束标志
output reg aref_req //向仲裁模块发送自动刷新请求信号
);
parameter CNT_MAX = 10'd750;
parameter AREF_IDLE = 3'b000, //初始状态
AREF_PCH = 3'b001, //预充电
AREF_TRP = 3'b011, //预充电等待
AUTO_REF = 3'b010, //自动刷新指令写入
AREF_TRF = 3'b110, //自动刷新等待时间
AREF_END = 3'b111; //结束状态
parameter TRP = 2'd2,//15~20ns
TRFC = 3'd7;//66ns
parameter NOP = 4'b0111, //空操作指令
AUTOREF= 4'b0001, //自动刷新指令
PCHARGE = 4'b0010; //预充电指令
reg [9:0] cnt_ref; //刷新周期,去7500ns,计时750次,给仲裁预留时间
wire aref_ack; //自动刷新响应信号
reg [3:0] cnt_clk;
reg [2:0] aref_state;
reg cnt_clk_rst; //等待时间计数复位
wire trp_end;
wire trf_end;
reg [1:0] cnt_aref; //自动刷新次数,至少两次
//cnt_ref:刷新计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_ref <= 10'd0;
else if(cnt_ref >= CNT_MAX -1'b1)
cnt_ref <= 10'd0;
else if(init_end)
cnt_ref <= cnt_ref + 1'b1;
assign aref_ack = (aref_state == AREF_PCH)?1'b1:1'b0;
//aref_req:自动刷新请求
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
aref_req <= 1'b0;
else if(cnt_ref == CNT_MAX - 1'b1)
aref_req <= 1'b1;
else if(aref_ack == 1'b1)
aref_req <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
aref_state <= AREF_IDLE;
else case(aref_state)
AREF_IDLE : //初始化结束且自动刷新使能有效,跳转到预充电状态
if(init_end && aref_en)
aref_state <= AREF_PCH;
else
aref_state <=aref_state;
AREF_PCH : //预充电状态,直接跳转到预充电等待状态
aref_state <= AREF_TRP;
AREF_TRP :
if(trp_end) //预充电等待结束,跳转到自动刷新状态
aref_state <= AUTO_REF;
else
aref_state <= aref_state;
AUTO_REF : //直接跳转到自动刷新等待状态
aref_state <= AREF_TRF;
AREF_TRF : //自动刷新等待完成且自动刷新两次,跳转到结束状态。不满足两次则跳转到自动刷新状态
if(trf_end && (cnt_aref == 2'd2))
aref_state <= AREF_END;
else if(trf_end)
aref_state <= AUTO_REF;
AREF_END : //直接跳转到初始状态
aref_state <= AREF_IDLE;
default:aref_state <= AREF_IDLE;
endcase
//cnt_clk:时钟周期计数,记录初始化各状态等待时间
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 3'd0;
else if(cnt_clk_rst)
cnt_clk <= 3'd0;
else
cnt_clk <= cnt_clk + 1'b1;
always@(*)
begin
case(aref_state)
AREF_IDLE : cnt_clk_rst <= 1'b1;
AREF_TRP : cnt_clk_rst <= (trp_end)?1'b1:1'b0;
AREF_TRF : cnt_clk_rst <= (trf_end)?1'b1:1'b0;
AREF_END : cnt_clk_rst <= 1'b1;
default : cnt_clk_rst <= 1'b0;
endcase
end
//预充电等待时间结束信号
assign trp_end = (cnt_clk == TRP & aref_state == AREF_TRP)?1'b1:1'b0;
//自动刷新等待时间结束信号
assign trf_end = (cnt_clk == TRFC & aref_state == AREF_TRF)?1'b1:1'b0;
//自动刷新计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_aref <= 2'd0;
else if(aref_state == AUTO_REF)
cnt_aref <= cnt_aref + 1'b1;
else if(aref_state == AREF_IDLE)
cnt_aref <= 2'd0;
/* always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
aref_cmd <= NOP;
else case(aref_state)
AREF_IDLE,AREF_TRP,AREF_TRF,AREF_END:
aref_cmd <= NOP;
AREF_PCH:
aref_cmd <= PCHARGE;
AUTO_REF:
aref_cmd <= AUTOREF;
default: aref_cmd <= NOP;
endcase */
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
aref_cmd <= NOP;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
else
case(aref_state)
AREF_IDLE,AREF_TRP,AREF_TRF: //执行空操作指令
begin
aref_cmd <= NOP;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
AREF_PCH: //预充电指令
begin
aref_cmd <= PCHARGE;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
AUTO_REF: //自动刷新指令
begin
aref_cmd <= AUTOREF;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
AREF_END: //一次自动刷新完成
begin
aref_cmd <= NOP;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
default:
begin
aref_cmd <= NOP;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
endcase
/* assign aref_ba = 2'b11;
assign aref_addr = 13'h1fff; */
assign aref_end = (aref_state == AREF_END)?1'b1:1'b0;
endmodule
SDRAM规定的刷新时间为64ms,也就是在该时间内要对所有的行进行刷新,这里SDRAM对应的行地址为13位宽,对应8192行,64ms/8192=7812.5ns,那么必须大约7.8us的时间就要发出一次自动刷新命令,这是为了保证SDRAM内的数据能够上电后一直保存。以工作时钟频率为100MHz,一个时钟就是10ns,则需要780个时钟周期刷新一次,但是在设计的过程中,我们只是让他计数到750,因为当计数到该值时发出自动刷新请求会经仲裁模块,这里留一下余量。
而且在此过程中有一问题,就是自动刷新的命令会不会影响到读写操作,假设我就以7812.5ns为周期进行自动刷新,当读写操作还在进行着时,刷新请求来到了,这时难道让我们打断读写操作?这肯定是不行的,要等待读写操作完成后再发出自动刷新请求。再假设每次刷新请求操作都超过7812.5ns,那么总时长就超过64ms了,数据可能会丢失。所里这里需要留有余量,设定在7500ns刷新一次,这时余量就为三百多ns,假设你的读写突发长度为10,读写完总共花的时间为100ns,远小于余量,这时就不会出现上诉情况,但如果读写突发长度为整页突发或者大于余量/时钟周期的突发长度(321/10=32),这时也有可能会出现数据丢失的情况。
自动刷新模块的仿真情况,当刷新计数器计数到最大值时自动刷新请求拉高3、SDRAM写操作模块
写操作时序图,页突发
当初始化完成且写使能信号拉高时,开始写激活,激活指定bank的某一行,经过写激活等待tRCD,开始写入数据,这时需要给列首地址A0~A8和已激活的bank地址,页突发可以写入页突发终止指令。
写模块状态转移图
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 ,//包括bank地址,行地址,列地址,fifo传入
input wire [15:0] wr_data ,//待写入sdram的数据,由fifo传入
input wire [9:0] wr_burst_len ,//整页突发,这里10突发终止,如果没有突发终止会写满一行
//输出到仲裁模块
output wire wr_ack ,
output wire wr_end ,//一次突发写结束
output reg [3:0] write_cmd ,
output reg [1:0] write_ba ,
output reg [12:0] write_addr ,
output reg wr_sdram_en ,//写sdram数据有效使能,与wr_ack
output wire [15:0] wr_sdram_data
);
parameter WR_IDEL = 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 ;//一次突发写结束
parameter TRCD_CLK = 10'd2 , //激活周期
TRP_CLK = 10'd2 ; //预充电周期
parameter NOP = 4'b0111,//空操作
ACTIVE = 4'b0011,//激活
WRITE = 4'b0100,//写
STOP = 4'b0110,//突发停止
P_CHARGE = 4'b0010;//预充电
reg [2:0] write_state ;
reg [3:0] cnt_clk ;
reg cnt_clk_rst ;
wire trcd_end ;//激活等待周期结束
wire twrite_end ;//突发写结束
wire trp_end ;//预充电结束
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
write_state <= WR_IDEL;
else case(write_state)
WR_IDEL : //初始化完成且仲裁模块返回写使能,转激活状态
if(init_end && wr_en)
write_state <= WR_ACTIVE;
else
write_state <= write_state;
WR_ACTIVE : //激活状态直接跳转到激活等待
write_state <= WR_TRCD;
WR_TRCD : //激活等待结束,跳转到写命令状态
if(trcd_end)
write_state <= WR_WRITE;
else
write_state <= write_state;
WR_WRITE : //写指令状态直接跳转到写数据状态
write_state <= WR_DATA;
WR_DATA : //突发写结束,跳转到预充电状态
if(twrite_end)
write_state <= WR_PRE;
else
write_state <= write_state;
WR_PRE : //预充电状态直接跳转到预充电等待状态
write_state <= WR_TRP;
WR_TRP : //预充电等待结束,跳转到结束状态
if(trp_end)
write_state <= WR_END;
else
write_state <= write_state;
WR_END : //完成一次突发写,跳回到初始状态
write_state <= WR_IDEL;
default: write_state <= WR_IDEL;
endcase
//周期计数,对各个状态的等待时间周期计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 4'd0;
else if(cnt_clk_rst)
cnt_clk <= 4'd0;
else
cnt_clk <= cnt_clk + 1'b1;
always@(*)
begin
case(write_state)
WR_IDEL : cnt_clk_rst <= 1'b1;
WR_TRCD : cnt_clk_rst <= (trcd_end)? 1'b1:1'b0;
WR_WRITE: cnt_clk_rst <= 1'b1;
WR_DATA : cnt_clk_rst <= (twrite_end)? 1'b1:1'b0;
WR_TRP : cnt_clk_rst <= (trp_end)? 1'b1:1'b0;
WR_END : cnt_clk_rst <= 1'b1;
default : cnt_clk_rst <= 1'b0;
endcase
end
assign trcd_end = (write_state == WR_TRCD && cnt_clk == TRCD_CLK)? 1'b1:1'b0;
assign twrite_end = (write_state == WR_DATA && cnt_clk == wr_burst_len - 1'b1)? 1'b1:1'b0;
assign trp_end = (write_state == WR_TRP && cnt_clk == TRP_CLK)? 1'b1:1'b0;
//一次突发写结束
assign wr_end = (write_state == WR_END)? 1'b1:1'b0;
//sdram操作指令
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
write_cmd <= NOP;
write_ba <= 2'b11;
write_addr<= 13'h1fff;
end
else case(write_state)
WR_IDEL,WR_TRCD,WR_TRP,WR_END :
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 <= {4'b0000,wr_addr[8:0]};
end
WR_WRITE :
begin
write_cmd <= WRITE;
write_ba <= 2'b00;
write_addr<= 13'h0000;
end
WR_DATA :
if(twrite_end)
write_cmd <= STOP;
else begin
write_cmd <= NOP;
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
default :
begin
write_cmd <= NOP;
write_ba <= 2'b11;
write_addr<= 13'h1fff;
end
endcase
//wr_ack:输出传到fifo控制模块
assign wr_ack = ( write_state == WR_WRITE)
|| ((write_state == WR_DATA)
&& (cnt_clk <= (wr_burst_len - 2'd2)));
//wr_sdram_en:写SDRAM数据有效信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_sdram_en <= 1'b0;
else
wr_sdram_en <= wr_ack;
assign wr_sdram_data = (wr_sdram_en)? wr_data:16'd0;
endmodule
仿真结果写入十个数据后执行了突发终止指令
4、SDRAM读操作模块
页突发读模式时序图
发送读激活命令给SDRAM,选中要激活的bank,读指令后等待tRCD后通过地址总线传入行地址,等待列选通潜伏期,由配置寄存器模式的选定,然后开始读取数据,读完所需数据之后写入读突发终止指令,与写模块不同,当读突发终止指令执行后,还会读取潜伏期个数的数据。入潜伏期为3,则突发终止后还会读出三个数据。
读模块状态转移图
读突发长度为10,读数据状态要保持读突发长度加上列选通潜伏期的个数,10+3。读突发终止指令则要在读数据状态第八个时钟周期开始写入。
module sdram_read
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire init_end ,//初始化完成信号,由初始化模块传入
input wire rd_en ,//写使能,由仲裁模块传入
input wire [23:0] rd_addr ,//包括bank地址,行地址,列地址
input wire [15:0] rd_data ,//读出sdram的数据,由fifo传入
input wire [9:0] rd_burst_len ,//整页突发,这里10突发终止,如果没有突发终止会读满一行
output wire rd_ack ,//读sdram有效信号
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 //sdram读出数据
);
parameter RD_IDLE = 4'b0000,
RD_ACTIVE= 4'b0001, //读激活
RD_TRCD = 4'b0011, //读激活等待
RD_READ = 4'b0010, //读指令写入
RD_CL = 4'b0110, //列选通潜伏期
RD_DATA = 4'b0111, //读数据
RD_PRE = 4'b0101, //预充电
RD_TRP = 4'b0100, //预充电等待
RD_END = 4'b1100; //结束一次突发读
parameter TRCD_CLK = 10'd2 , //激活周期
CL_CLK = 10'd3 , //列选通潜伏期
TRP_CLK = 10'd2 ; //预充电周期
parameter NOP = 4'b0111,//空操作
ACTIVE = 4'b0011,//激活
READ = 4'b0101,//读指令
STOP = 4'b0110,//突发停止
P_CHARGE = 4'b0010;//预充电
reg [15:0] rd_data_reg ;
reg [3:0] read_state ;
reg [3:0] cnt_clk ;
reg cnt_clk_rst ;
wire trcd_end ;//激活等待周期结束
wire trp_end ;//预充电结束
wire tread_end ;
wire tcl_end ;//列选通等待结束
wire stop_end ;//读突发终止
//对数据打1拍,rd_data同步在sys_clk下,只是相位不同步,所以能直接打拍
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_data_reg <= 16'd0;
else
rd_data_reg <= rd_data;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
read_state <= RD_IDLE;
else case(read_state)
RD_IDLE : //初始化完成且仲裁模块返回写使能,转激活状态
if(init_end && rd_en)
read_state <= RD_ACTIVE;
else
read_state <= read_state;
RD_ACTIVE : //激活状态直接跳转到激活等待
read_state <= RD_TRCD;
RD_TRCD : //激活等待结束,跳转到读命令状态
if(trcd_end)
read_state <= RD_READ;
else
read_state <= read_state;
RD_READ : //读指令状态直接跳转到列选通潜伏期状态
read_state <= RD_CL;
RD_CL : //列选通潜伏期结束,跳转到读数据状态
if(tcl_end)
read_state <= RD_DATA;
else
read_state <= read_state;
RD_DATA : //突发读结束,跳转到预充电状态
if(tread_end)
read_state <= RD_PRE;
else
read_state <= read_state;
RD_PRE : //预充电状态直接跳转到预充电等待状态
read_state <= RD_TRP;
RD_TRP : //预充电等待结束,跳转到结束状态
if(trp_end)
read_state <= RD_END;
else
read_state <= read_state;
RD_END : //完成一次突发读,跳回到初始状态
read_state <= RD_IDLE;
default: read_state <= RD_IDLE;
endcase
//周期计数,对各个状态的等待时间周期计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 4'd0;
else if(cnt_clk_rst)
cnt_clk <= 4'd0;
else
cnt_clk <= cnt_clk + 1'b1;
always@(*)
begin
case(read_state)
RD_IDLE : cnt_clk_rst <= 1'b1;
RD_TRCD : cnt_clk_rst <= (trcd_end)? 1'b1:1'b0;
RD_READ: cnt_clk_rst <= 1'b1;
RD_CL : cnt_clk_rst <= (tcl_end)? 1'b1:1'b0;
RD_DATA : cnt_clk_rst <= (tread_end)? 1'b1:1'b0;
RD_TRP : cnt_clk_rst <= (trp_end)? 1'b1:1'b0;
RD_END : cnt_clk_rst <= 1'b1;
default : cnt_clk_rst <= 1'b0;
endcase
end
assign trcd_end = (read_state == RD_TRCD && cnt_clk == TRCD_CLK)? 1'b1:1'b0;
assign tread_end = (read_state == RD_DATA && cnt_clk == rd_burst_len + 10'd2)? 1'b1:1'b0;
assign trp_end = (read_state == RD_TRP && cnt_clk == TRP_CLK)? 1'b1:1'b0;
assign tcl_end = (read_state == RD_CL && cnt_clk == CL_CLK - 1'b1)? 1'b1:1'b0;
assign stop_end = (read_state == RD_DATA && cnt_clk == rd_burst_len - 10'd1 - CL_CLK)? 1'b1:1'b0;
//一次突发写结束
assign rd_end = (read_state == RD_END)? 1'b1:1'b0;
//sdram操作指令
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
read_cmd <= NOP;
read_ba <= 2'b11;
read_addr<= 13'h1fff;
end
else case(read_state)
RD_IDLE,RD_TRCD,RD_TRP,RD_END,RD_CL :
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 <= {4'b0000,rd_addr[8:0]};
end
RD_READ :
begin
read_cmd <= READ;
read_ba <= 2'b00;
read_addr<= 13'h0000;
end
RD_DATA :
if(stop_end)
read_cmd <= STOP;
else begin
read_cmd <= NOP;
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;
end
default :
begin
read_cmd <= NOP;
read_ba <= 2'b11;
read_addr<= 13'h1fff;
end
endcase
//rd_ack:读SDRAM有效信号
//输出会少一位数据,
assign rd_ack = ( read_state == RD_DATA)
&& ((cnt_clk >= 10'd1)
&& (cnt_clk < (rd_burst_len + 1'd1)));
//这个不会读漏
/* assign rd_ack = ( read_state == RD_DATA)
&& ((cnt_clk >= 10'd0)
&& (cnt_clk < (rd_burst_len))); */
assign rd_sdram_data = (rd_ack)? rd_data_reg:16'd0;
endmodule
5、SDRAM仲裁模块
前面四个模块都与用户接口和SDRAM建立起了双向数据通信,如果多个指令一起发送请求,如果没有优先级,就有可能造成冲突,从而导致SDRAM工作出错,因为就需要引入仲裁模块,对前面四个模块进行优先级的划分。
当初始化完成时,跳转到仲裁状态,当读写与刷新,其中有一个为有效,其他两个无效,则执行操作,当执行操作之后,回到仲裁状态,若两路或多路请求信号同时有效,优先执行优先级较高的操作,默认优先级为自动刷新操作>数据写操作>数据读操作。
module sdram_arbit
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire init_end ,
input wire [3:0] init_cmd ,
input wire [1:0] init_ba ,
input wire [12:0] init_addr ,
input wire aref_req ,
input wire aref_end ,
input wire [3:0] aref_cmd ,
input wire [1:0] aref_ba ,
input wire [12:0] aref_addr ,
input wire wr_req ,
input wire wr_end ,
input wire [3:0] wr_cmd ,
input wire [1:0] wr_ba ,
input wire [12:0] wr_addr ,
input wire wr_sdram_en ,
input wire [15:0] wr_data ,
input wire rd_req ,
input wire rd_end ,
input wire [3:0] rd_cmd ,
input wire [1:0] rd_ba ,
input wire [12:0] rd_addr ,
output reg aref_en ,
output reg wr_en ,
output reg rd_en ,
output wire sdram_cke ,
output wire sdram_cs_n ,
output wire sdram_ras_n ,
output wire sdram_cas_n ,
output wire sdram_we_n ,
output reg [1:0] sdram_ba ,
output reg [12:0] sdram_addr ,
inout wire [15:0] sdram_dq
);
reg [4:0] state;
reg [3:0] sdram_cmd;
parameter IDLE = 5'b00001,
ARBIT = 5'b00010,
WRITE = 5'b00100,
READ = 5'b01000,
A_REF = 5'b10000;
parameter NOP = 4'b0111; //空操作指令
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE :
if(init_end)
state <= ARBIT;
else
state <= IDLE;
ARBIT :
if(aref_req)
state <= A_REF;
else if(wr_req)
state <= WRITE;
else if(rd_req)
state <= READ;
else
state <= ARBIT;
WRITE :
if(wr_end)
state <= ARBIT;
else
state <= WRITE;
READ :
if(rd_end)
state <= ARBIT;
else
state <= READ;
A_REF :
if(aref_end)
state <= ARBIT;
else
state <= A_REF;
default:state <= IDLE;
endcase
always@(*)
case(state)
IDLE :
begin
sdram_cmd <= init_cmd;
sdram_ba <= init_ba;
sdram_addr <= init_addr;
end
// ARBIT :
// begin
// sdram_cmd <= NOP;
// sdram_ba <= 2'b11;
// sdram_addr <= 13'h1fff;
// end
WRITE :
begin
sdram_cmd <= wr_cmd;
sdram_ba <= wr_ba;
sdram_addr <= wr_addr;
end
READ :
begin
sdram_cmd <= rd_cmd;
sdram_ba <= rd_ba;
sdram_addr <= rd_addr;
end
A_REF :
begin
sdram_cmd <= aref_cmd;
sdram_ba <= aref_ba;
sdram_addr <= aref_addr;
end
default:
begin
sdram_cmd <= NOP;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
endcase
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
aref_en <= 1'b0;
else if((aref_req == 1'b1)&& (state == ARBIT))
aref_en <= 1'b1;
else if(aref_end == 1'b1)
aref_en <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_en <= 1'b0;
else if((aref_req == 1'b0) && (wr_req == 1'b1) && (state == ARBIT))
wr_en <= 1'b1;
else if(wr_end == 1'b1)
wr_en <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if((aref_req == 1'b0) && (rd_req == 1'b1) && (state == ARBIT))
rd_en <= 1'b1;
else if(rd_end == 1'b1)
rd_en <= 1'b0;
assign {sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = sdram_cmd;
assign sdram_cke = 1'b1;
assign sdram_dq = (wr_sdram_en)? wr_data: 16'bz;
endmodule
把五个合成一个顶层模块
module sdram_ctrl
(
//时钟、复位,初始化端口
input wire sys_clk ,
input wire sys_rst_n ,
output wire init_end ,
//sdram write
input wire [23:0] wr_sdram_addr,
input wire [15:0] wr_sd_data,
input wire [9:0] wr_burst_len,
input wire wr_sdram_req,//写SDRAM请求
output wire wr_sdram_ack,//写SDRAM有效信号
//sdram read
input wire [23:0] rd_sdram_addr,
input wire [9:0] rd_burst_len,
input wire rd_sdram_req,
output wire rd_sdram_ack,
output wire [15:0] rd_sdram_out,
//sdram引脚接口
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_ba ,
output wire [12:0] sdram_addr ,
inout wire [15:0] sdram_dq
);
wire [3:0] init_cmd ;
wire [1:0] init_ba ;
wire [12:0] init_addr;
wire aref_req ;
wire aref_end ;
wire [3:0] aref_cmd ;
wire [1:0] aref_ba ;
wire [12:0] aref_addr;
wire wr_end ;
wire [3:0] wr_cmd ;
wire [1:0] wr_ba ;
wire [12:0] wr_addr;
wire wr_sdram_en;
wire [15:0] wr_sdram_data;
wire rd_end ;
wire [3:0] rd_cmd ;
wire [1:0] rd_ba ;
wire [12:0] rd_addr;
wire aref_en;
wire wr_en;
wire rd_en;
sdram_init sdram_init_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.cmd_init (init_cmd),//操作指令(包括片选信号,行选同步信号,列选同步信号,使能信号)
.ba_init (init_ba),//板块地址
.addr_init (init_addr),//地址总线
.init_end (init_end) //初始化完成标志
);
sdram_aref sdram_aref_inst
(
.sys_clk (sys_clk),//系统时钟,频率100MHz
.sys_rst_n (sys_rst_n),//复位信号,低电平有效
.init_end (init_end),//初始化结束信号
.aref_en (aref_en),//自动刷新使能,由仲裁模块返回
.aref_cmd (aref_cmd),//自动刷新阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n}
.aref_ba (aref_ba),//自动刷新阶段Bank地址
.aref_addr (aref_addr),//地址数据,辅助预充电操作,A12-A0,13位地址
.aref_end (aref_end),//自动刷新结束标志
.aref_req (aref_req) //向仲裁模块发送自动刷新请求信号
);
sdram_write sdram_write_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.init_end (init_end),//初始化完成信号,由初始化模块传入
.wr_en (wr_en),//写使能,由仲裁模块传入
.wr_addr (wr_sdram_addr),//包括bank地址,行地址,列地址
.wr_data (wr_sd_data),//待写入sdram的数据,由fifo传入
.wr_burst_len (wr_burst_len),//整页突发,这里10突发终止,如果没有突发终止会写满一行
.wr_ack (wr_sdram_ack),//写sdram有效信号
.wr_end (wr_end),//一次突发写结束
.write_cmd (wr_cmd),
.write_ba (wr_ba),
.write_addr (wr_addr),
.wr_sdram_en (wr_sdram_en),
.wr_sdram_data (wr_sdram_data)
);
sdram_read sdram_read_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.init_end (init_end),//初始化完成信号,由初始化模块传入
.rd_en (rd_en),//写使能,由仲裁模块传入
.rd_addr (rd_sdram_addr),//包括bank地址,行地址,列地址
.rd_data (sdram_dq),//读出sdram的数据,由sdram读出
.rd_burst_len (rd_burst_len),//整页突发,这里10突发终止,如果没有突发终止会读满一行
.rd_ack (rd_sdram_ack),//读sdram有效信号
.rd_end (rd_end),//一次突发读结束
.read_cmd (rd_cmd),
.read_ba (rd_ba),
.read_addr (rd_addr),
.rd_sdram_data (rd_sdram_out) //sdram读出数据
);
sdram_arbit sdram_arbit
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.init_end (init_end ),
.init_cmd (init_cmd ),
.init_ba (init_ba ),
.init_addr (init_addr),
.aref_req (aref_req ),
.aref_end (aref_end ),
.aref_cmd (aref_cmd ),
.aref_ba (aref_ba ),
.aref_addr (aref_addr),
.wr_req (wr_sdram_req),
.wr_end (wr_end ),
.wr_cmd (wr_cmd ),
.wr_ba (wr_ba ),
.wr_addr (wr_addr ),
.wr_sdram_en (wr_sdram_en),
.wr_data (wr_sdram_data),
.rd_req (rd_sdram_req),
.rd_end (rd_end),
.rd_cmd (rd_cmd),
.rd_ba (rd_ba),
.rd_addr (rd_addr),
.aref_en (aref_en),
.wr_en (wr_en),
.rd_en (rd_en),
.sdram_cke (sdram_cke),
.sdram_cs_n (sdram_cs_n),
.sdram_ras_n (sdram_ras_n),
.sdram_cas_n (sdram_cas_n),
.sdram_we_n (sdram_we_n),
.sdram_ba (sdram_ba),
.sdram_addr (sdram_addr),
.sdram_dq (sdram_dq)
);
endmodule
二、添加FIFO控制模块
异步FIFO模块能够解决多bit数据跨时钟域处理的问题,在SDRAM操作冲突时可以将数据寄存在FIFO以防止数据被覆盖或者丢失,为数据读写模块提供SDRAM读写地址,产生读写请求。引入FIFO,相当于把对SDRAM的操作封装成了一个FIFO。
module sdram_fifo_ctrl
(
input wire sys_clk ,//
input wire sys_rst_n ,//
input wire wr_rst ,
input wire [9:0] wr_burst_len ,//
input wire wr_fifo_clk ,//写fifo的时钟
input wire wr_fifo_req ,//写fifo的写请求
input wire [15:0] wr_fifo_data ,//写入写FIFO的数据,由外部传入,如ad采集
input wire [23:0] sdram_wr_b_addr ,//
input wire [23:0] sdram_wr_e_addr ,//
input wire rd_rst ,
input wire rd_fifo_clk ,//读fifo时钟,50M
input wire rd_fifo_req ,//读fifo读请求
input wire [23:0] sdram_rd_b_addr ,//sdram读数据首地址
input wire [9:0] rd_burst_len ,//
input wire [23:0] sdram_rd_e_addr ,//
input wire read_valid ,//
input wire init_end ,//初始化完成信号,由sdram_ctrl传入
input wire sdram_wr_ack ,//sdram写数据有效信号,提前一个时钟周期,写fifo的读请求
input wire sdram_rd_ack ,//sdram读数据有效信号,读FIFO的写请求
input wire [15:0] sdram_data_out ,//由sdram读出到读fifo中缓存
output reg sdram_wr_req ,//sdram写数据请求
output reg [23:0] sdram_wr_addr ,//sdram写地址,首地址开始写,写突发
output wire [15:0] sdram_data_in ,//写fifo的输出,待写入sdram的数据
output reg sdram_rd_req ,//sdram读数据请求
output reg [23:0] sdram_rd_addr ,//sdram读地址,首地址开始读
output wire [15:0] rd_fifo_rd_data ,//由读fifo读出缓存的数据
output wire [9:0] rd_fifo_num //对sdram读出到fifo的数据计数
);
reg wr_ack_reg;
wire wr_ack_fall;
reg rd_ack_reg;
wire rd_ack_fall;
wire [9:0] wr_fifo_num;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_ack_reg <= 1'b0;
else
wr_ack_reg <= sdram_wr_ack;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_ack_reg <= 1'b0;
else
rd_ack_reg <= sdram_rd_ack;
assign wr_ack_fall = (wr_ack_reg && ~sdram_wr_ack);
assign rd_ack_fall = (rd_ack_reg && ~sdram_rd_ack);
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
sdram_wr_addr <= 24'd0;
else if(wr_rst)
sdram_wr_addr <= sdram_wr_b_addr;
else if(wr_ack_fall)
begin
if(sdram_wr_addr < (sdram_wr_e_addr - wr_burst_len))
//不使用乒乓操作,一次突发写结束,更改写地址,未达到末地址,写地址累加
sdram_wr_addr <= sdram_wr_addr + wr_burst_len;
else //不使用乒乓操作,到达末地址,回到写起始地址
sdram_wr_addr <= sdram_wr_b_addr;
end
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
sdram_rd_addr <= 24'd0;
else if(rd_rst)
sdram_rd_addr <= sdram_rd_b_addr;
else if(rd_ack_fall)
begin
if(sdram_rd_addr < (sdram_rd_e_addr - rd_burst_len))
//不使用乒乓操作,一次突发写结束,更改写地址,未达到末地址,写地址累加
sdram_rd_addr <= sdram_rd_addr + rd_burst_len;
else //不使用乒乓操作,到达末地址,回到写起始地址
sdram_rd_addr <= sdram_rd_b_addr;
end
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
sdram_wr_req <= 1'b0;
sdram_rd_req <= 1'b0;
end
else if(init_end)
begin
if(wr_fifo_num >= wr_burst_len)
begin
sdram_wr_req <= 1'b1;
sdram_rd_req <= 1'b0;
end
else if(rd_fifo_num < rd_burst_len && read_valid)
begin
sdram_wr_req <= 1'b0;
sdram_rd_req <= 1'b1;
end
else
begin
sdram_wr_req <= 1'b0;
sdram_rd_req <= 1'b0;
end
end
else
begin
sdram_wr_req <= 1'b0;
sdram_rd_req <= 1'b0;
end
sdram_fifo sdram_wr_fifo_inst (
.aclr ( ~sys_rst_n || wr_rst ),
.data ( wr_fifo_data ),
.rdclk ( sys_clk ),
.rdreq ( sdram_wr_ack ),
.wrclk ( wr_fifo_clk ),
.wrreq ( wr_fifo_req ),
.q ( sdram_data_in ),
.rdusedw ( wr_fifo_num ),
.wrusedw ( )
);
sdram_fifo sdram_rd_fifo_inst (
.aclr ( ~sys_rst_n || rd_rst ),
.data ( sdram_data_out ),
.rdclk ( rd_fifo_clk ),
.rdreq ( rd_fifo_req ),
.wrclk ( sys_clk ),
.wrreq ( sdram_rd_ack ),
.q ( rd_fifo_rd_data ),
.rdusedw ( ),
.wrusedw ( rd_fifo_num )
);
endmodule
仿真文本
`timescale 1ns/1ns
module tb_uart_sdram();
//wire define
wire tx ;
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_ba ;
wire [12:0] sdram_addr ;
wire [1:0] sdram_dqm ;
wire [15:0] sdram_dq ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg rx ;
reg [7:0] data_mem [30:0] ; //data_mem是一个存储器,相当于一个ram
//读取sim文件夹下面的data.txt文件,并把读出的数据定义为data_mem
initial
$readmemh("E:/intelFPGA/data/uart_sdram/sim/test_data.txt",data_mem);
//时钟、复位信号
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#200
sys_rst_n <= 1'b1 ;
end
always #10 sys_clk = ~sys_clk;
initial
begin
rx <= 1'b1;
#200
rx_byte();
end
task rx_byte();
integer j;
for(j=0;j<30;j=j+1)
rx_bit(data_mem[j]);
endtask
task rx_bit(input[7:0] data); //data是data_mem[j]的值。
integer i;
for(i=0;i<10;i=i+1)
begin
case(i)
0: rx <= 1'b0 ; //起始位
1: rx <= data[0];
2: rx <= data[1];
3: rx <= data[2];
4: rx <= data[3];
5: rx <= data[4];
6: rx <= data[5];
7: rx <= data[6];
8: rx <= data[7]; //上面8个发送的是数据位
9: rx <= 1'b1 ; //停止位
endcase
#(52*20); //一个波特时间=ssys_clk周期*波特计数器
end
endtask
//重定义defparam,用于修改参数,缩短仿真时间
//defparam uart_sdram_inst.uart_rx_inst.BAUD_CNT_END = 52;
//defparam uart_sdram_inst.uart_rx_inst.BAUD_CNT_END_HALF = 26;
//defparam uart_sdram_inst.uart_tx_inst.BAUD_CNT_END = 52;
defparam uart_sdram_inst.CLK_FREQ = 50_000_0;
defparam uart_sdram_inst.fifo_read_inst.BAUD_CNT = 52;
defparam uart_sdram_inst.fifo_read_inst.BAUD_HALF = 26;
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;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//-------------uart_sdram_inst-------------
uart_sdram uart_sdram_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.rx (rx ),
.tx (tx ),
.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_ba (sdram_ba ),
.sdram_addr (sdram_addr ),
.sdram_dqm (sdram_dqm ),
.sdram_dq (sdram_dq )
);
//-------------sdram_model_plus_inst-------------
sdram_model_plus sdram_model_plus_inst(
.Dq (sdram_dq ),
.Addr (sdram_addr ),
.Ba (sdram_ba ),
.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
把采集数据写入写FIFO中,等待SDRAM控制模块SDRAM写数据有效信号拉高时,开始从写FIFO读出传到SDRAM。当SDRAM读数据有效信号拉高时,开始从SDRAM读出数据写入到读FIFO中,上位机再从读FIFO中读出数据。以上为学习内容,具体可参考野火教程。