FPGA操控SDRAM.docx
《FPGA操控SDRAM.docx》由会员分享,可在线阅读,更多相关《FPGA操控SDRAM.docx(30页珍藏版)》请在冰豆网上搜索。
![FPGA操控SDRAM.docx](https://file1.bdocx.com/fileroot1/2023-2/13/c3503b5e-f950-4430-a03d-98d0031eec68/c3503b5e-f950-4430-a03d-98d0031eec681.gif)
FPGA操控SDRAM
FPGA操控SDRAM
所用FPGA芯片:
EP2C8Q208C8
所用内存:
H57V2562GTR-75C
顶层模块:
modulesdram
(
clk,
ext_rst_n,
/**********************/
sdramclk,//SDRAM的引脚
ledpio,//发光二极管,打酱油的
addr,
ba,
cas_n,
cke,
cs_n,
dq,
dqm,
ras_n,
we_n,
/*****************/
flash_nce_n,
sram_nce_n
/******************/
);
/******************/
inputclk;
inputext_rst_n;
output[7:
0]ledpio;//八位流水
/**************************/
output[12:
0]addr;
output[1:
0]ba;
outputcas_n;
outputcke;
outputcs_n;
inout[15:
0]dq;
output[1:
0]dqm;
outputras_n;
outputwe_n;
outputsdramclk;
/**************************/
outputflash_nce_n=1'b1;
outputsram_nce_n=1'b1;
assigncs_n=1'b0;
assigncke=1'b1;
assigndqm=2'b00;
/****************************/
wirewr_n;
wire[4:
0]cmd;
wirerefquest;
wirerfinish;
wirewfinish;
sys_cmds1
(
.clk(clk_100m),
.rst_n(ext_rst_n),
.wr_n(wr_n),//由数据产生模块决定现在是读还是写状态
.cmd(cmd),
.refquest(refquest),
.rfinish(rfinish),//读完成标志位,每完成一次读操作出现一次上升沿
.wfinish(wfinish)//写完成标志位,每完成一次写操作出现一次上升沿
);
/***************************/
wire[12:
0]rowadd;
wire[8:
0]caadd;
wire[15:
0]indata;
wire[15:
0]outdata;
sys_ctrls2
(
.clk(clk_100m),
.cmd(cmd),
.rowadd(rowadd),//行地址输入
.caadd(caadd),//列地址输入
.indata(indata),//数据输入
.outdata(outdata),//从内存读回数据后,将数据输出
.addr(addr),
.ba(ba),
.cas_n(cas_n),
.dq(dq),
.ras_n(ras_n),
.we_n(we_n),
.refquest(refquest)
);
assignledpio=outdata[7:
0];
/*****************************/
wireclk_100m;
pll100mp1(
.inclk0(clk),
.c0(clk_100m),
.c1(sdramclk));
/*****************************/
dataproduced1
(
.clk(clk_100m),
.wr_n(wr_n),
.data(indata),
.rowadd(rowadd),
.caadd(caadd),
.rst_n(ext_rst_n),
.rfinish(rfinish),//读完成标志位,每完成一次读操作出现一次上升沿
.wfinish(wfinish)//写完成标志位,每完成一次写操作出现一次上升沿
);
/******************************/
Endmodule
内存状态控制模块:
modulesys_cmd
(
clk,
rst_n,
wr_n,
cmd,
refquest,//刷新请求位,为高时请求外部执行刷新操作
rfinish,//读完成标志位,每完成一次读操作出现一次上升沿
wfinish//写完成标志位,每完成一次写操作出现一次上升沿
);
/*************************/
parametercnt_200us=20000;//SDRAM在开机时的初始化过程,上电后要有200us的输入稳定期
parameterinicycle=8;//200us以后就是要对所有L-Bank预充电,再往后给SDRAM8次的刷新命令
parametertRFC=6;//预刷新操作需要的等待时间要比预充电长的多,大约需要6个时钟周期(60ns)
parametertRP=2;//在发出预充电命令之后,要经过一段时间才能允许发送RAS行有效命令打开
//新的工作行,这个间隔被称为tRP(PrechargecommandPeriod,预充电有效周期)。
parametertRCD=3;//在发送列读写命令时必须要与行有效命令有一个间隔
//这个间隔被定义为tRCD,即RAStoCASDelay(RAS至CAS延迟),
parameterCL=3;
parametertWR=2;//数据的真正写入需要一定的周期。
为了保证数据的可靠写入,
//都会留出足够的写入/校正时间(tWR,WriteRecoveryTime)
/***************************/
parameterINI=5'b00001;
parameterININOP=5'b00010;
parameterPRECHARGEALL=5'b00011;//预充电
parameterPRECHARGEALLNOP=5'b00100;//预充电
parameterMRS=5'b00101;
parameterMRSNOP=5'b00111;
parameterREFRESH=5'b01000;
parameterREFRESHNOP=5'b01001;
//以上四个状态是用来初始化的
parameterRAS=5'b01010;
parameterRASNOP=5'b01011;
parameterCAS=5'b01100;
parameterCASNOP=5'b01101;
parameterR_W_PRECHARGE=5'b11110;//由于SDRAM的寻址具有独占性,所以在进行完读写操作后,
//如果要对同一L-Bank的另一行进行寻址,就要将原来有效(工作)的行关闭,重新发送行/列地址?
//L-Bank关闭现有工作行,准备打开新行的操作就是预充电(Precharge)。
//预充电可以通过命令控制,也可以通过辅助设定让芯片在每次读写操作之后自动进行预充电。
?
parameterR_W_PRECHARGENOP=5'b10000;
/***************************/
parameterRE=5'b01110;//确定当前是读状态还是写状态
parameterWR=5'b01111;
parameterREWAIT=5'b10001;
parameterREFIN=5'b10010;
parameterWRFIN=5'b10011;
/*******************************/
parameterRECNT=782;//刷新周期为7.8125μs
/***************************/
inputclk;
inputrst_n;
inputwr_n;
output[4:
0]cmd;
outputrefquest;//刷新请求位,为高时请求外部执行刷新操作
outputrfinish;//读完成标志位,每完成一次读操作出现一次上升沿
outputwfinish;//写完成标志位,每完成一次写操作出现一次上升沿
/************************/
assigncmd=state;
reg[4:
0]state;
reg[15:
0]cnt;
reg[3:
0]refreshcnt;//初始化要八个刷新周期,每个刷新周期70ns
/***************************/
reg[15:
0]Tcnt;//刷新周期为7.8125μs
reg[3:
0]Tcout;//而每次刷新所占用的时间为9个时钟周期
regrefquest;//刷新请求位,为高时请求外部执行刷新操作
regrefinish;//刷新完成标志位,高表示刷新完成,可以正常操作,否则要等待
regrfinish;//读完成标志位,每完成一次读操作出现一次上升沿
regwfinish;//写完成标志位,每完成一次写操作出现一次上升沿
always@(posedgeclkornegedgerst_n)
if(!
rst_n)
begin
Tcnt<=16'b0;
Tcout<=4'b0;
refquest<=1'b0;
refinish<=1'b1;
end
elseif(Tcnt>=RECNT)
begin
if(Tcout>=9)
begin
refquest<=1'b0;
refinish<=1'b1;//发出刷新请求并等待九个时钟周期后,刷新操作完成
Tcout<=4'b0;
Tcnt<=16'b0;
end
else
begin
refquest<=1'b1;
refinish<=1'b0;
Tcout<=Tcout+1'b1;
Tcnt<=Tcnt;
end
end
else
Tcnt<=Tcnt+1'b1;
/*****************************/
always@(posedgeclkornegedgerst_n)
begin
if(!
rst_n)
begin
state<=INI;
cnt<=16'b0;
refreshcnt<=4'b0;
rfinish<=1'b0;
wfinish<=1'b0;
end
elseif(refinish)
begin
case(state)
INI:
begin
if(cnt>=cnt_200us)//上电后要有200us的输入稳定期
begin
cnt<=16'b0;
state<=ININOP;
end
else
begin
cnt<=cnt+1'b1;
state<=state;
end
end
ININOP:
state<=PRECHARGEALL;
/****************************************/
PRECHARGEALL:
//所有L-Bank预充电
begin
state<=PRECHARGEALLNOP;
end
PRECHARGEALLNOP:
state<=REFRESH;
/****************************************/
REFRESH:
//预充电完成了就要连续进行八个预刷新
if(refreshcnt>=inicycle)//给SDRAM8次的刷新命令
begin
refreshcnt<=4'b0;
state<=MRS;
end
else
begin
state<=REFRESHNOP;
refreshcnt<=refreshcnt+1'b1;
end
REFRESHNOP:
if(cnt>=tRFC)//预刷新操作需要的等待时间要比预充电长的多,大约需要6个时钟周期(60ns)
begin
cnt<=16'b0;
state<=REFRESH;
end
else
begin
cnt<=cnt+1'b1;
state<=state;
end
/****************************************/
MRS:
//模式寄存器设置
state<=MRSNOP;
MRSNOP:
state<=RAS;
/****************************************/
RAS:
//行有效
begin
state<=RASNOP;
rfinish<=1'b0;//读完成标志位,每完成一次读操作出现一次上升沿
wfinish<=1'b0;//写完成标志位,每完成一次写操作出现一次上升沿
end
RASNOP:
//在发送列读写命令时必须要与行有效命令有一个间隔
//这个间隔被定义为tRCD,即RAStoCASDelay(RAS至CAS延迟),
if(cnt>=tRCD)
begin
if(wr_n)
state<=RE;//wr_n为高,对内存进行读操作
else
state<=WR;//wr_n为低,对内存进行写操作
cnt<=16'b0;
end
else
begin
cnt<=cnt+1'b1;
state<=state;
end
/****************************************/
RE:
//列读写
state<=REWAIT;
REWAIT:
//在CAS发出之后,仍要经过一定的时间才能有数据输出,
//从CAS与读取命令发出到第一笔数据输出的这段时间,被定义为CL(CASLatency,CAS潜伏期)。
if(cnt>=CL)
begin
cnt<=16'b0;
state<=REFIN;
end
else
begin
cnt<=cnt+1'b1;
state<=state;
end
REFIN:
begin
state<=R_W_PRECHARGE;
rfinish<=1'b1;//读完成标志位,每完成一次读操作出现一次上升沿
end
/****************************************/
WR:
//列读写
state<=WRFIN;
WRFIN:
//数据的真正写入需要一定的周期。
为了保证数据的可靠写入,
//都会留出足够的写入/校正时间(tWR,WriteRecoveryTime)
if(cnt>=tWR)
begin
cnt<=16'b0;
wfinish<=1'b1;//写完成标志位,每完成一次写操作出现一次上升沿
state<=R_W_PRECHARGE;
end
else
begin
cnt<=cnt+1'b1;
state<=state;
end
/********************************/
R_W_PRECHARGE:
state<=R_W_PRECHARGENOP;
R_W_PRECHARGENOP:
if(cnt>=tRP)//在发出预充电命令之后,要经过一段时间才能允许发送RAS行有效命令打开
//新的工作行,这个间隔被称为tRP(PrechargecommandPeriod,预充电有效周期)。
begin
cnt<=16'b0;
state<=RAS;
end
else
begin
cnt<=cnt+1'b1;
state<=state;
end
/****************************************/
endcase
end
else
state<=state;
end
/***************************/
Endmodule
操作内存接口模块:
modulesys_ctrl
(
clk,
cmd,
rowadd,//行地址输入
caadd,//列地址输入
indata,//数据输入
outdata,//从内存读回数据后,将数据输出
/*************************/
addr,
ba,
cas_n,
dq,
ras_n,
we_n,
/***************************/
refquest
);
/***************************/
inputclk;
inputrefquest;//刷新请求位,出现上升沿发出一条刷新命令
input[4:
0]cmd;
input[12:
0]rowadd;
input[8:
0]caadd;
input[15:
0]indata;
output[15:
0]outdata;
/************************/
output[12:
0]addr;
output[1:
0]ba;
outputcas_n;
inout[15:
0]dq;
outputras_n;
outputwe_n;//为低时,向内存写数据
/******************************/
reg[12:
0]addr;
reg[1:
0]ba;//这里只对第零个bank进行操作
regcas_n;
reg[15:
0]wrdata;//写数据寄存器
reg[15:
0]redata;//读数据寄存器
regras_n;
regwe_n;
/**************************/
parameterINI=5'b00001;
parameterININOP=5'b00010;
parameterPRECHARGEALL=5'b00011;//预充电
parameterPRECHARGEALLNOP=5'b00100;//预充电
parameterMRS=5'b00101;
parameterMRSNOP=5'b00111;
parameterREFRESH=5'b01000;
parameterREFRESHNOP=5'b01001;
//以上四个状态是用来初始化的
parameterRAS=5'b01010;
parameterRASNOP=5'b01011;
parameterCAS=5'b01100;
parameterCASNOP=5'b01101;
parameterR_W_PRECHARGE=5'b10000;//由于SDRAM的寻址具有独占性,所以在进行完读写操作后,
//如果要对同一L-Bank的另一行进行寻址,就要将原来有效(工作)的行关闭,重新发送行/列地址。
parameterR_W_PRECHARGENOP=5'b10101;
/***************************/
parameterRE=5'b01110;//确定当前是读状态还是写状态
parameterWR=5'b01111;
parameterREFIN=5'b10010;
parameterREWAIT=5'b10011;
parameterWRFIN=5'b10100;
/*******************************/
reg[15:
0]outdata;
always@(posedgeclk)
begin
wrdata<=indata;
outdata<=redata;
end
/***********************************/
assigndq=we_n?
16'bz:
wrdata;//为低时,向内存写数据,为高时从内存读数据(先设置为输出高阻态)
regrefquest_temp1;//检测refquest的上升沿
regrefquest_temp2;
always@(posedgeclk)
begin
refquest_temp1<=refquest;
refquest_temp2<=refquest_temp1;
if(~refquest_temp2&refquest_temp1)//刷新请求位,出现上升沿发出一条刷新命令
begin
ras_n=1'b0;
cas_n=1'b0;
we_n=1'b1;
end
elseif(!
refquest)//refquest为高时,还处于刷新状态
begin
case(cmd)
ININOP:
begin
ras_n<=1'b1;
cas_n<=1'b1;