SDRAM操作(FPGA实现)
对SDRAM基本概念的介绍以及芯片手册说明,请参考上一篇文章SDRAM操作说明。
1. 说明
如图所示为状态机的简化图示,过程大概可以描述为:SDRAM(IS42S16320D)上电初始化完成后,进入“空闲”状态,此时一直监控外部控制模块给予的控制信号。初始化完成后,外部定时器开始定时,定时周期为SDRAM刷新周期(7.7us),一旦计数到刷新周期后,向状态机发送auto_ref_req(自动刷新请求),此时状态机进入“刷新”状态,这样就确保在无任何操作时,SDRAM能正常完成刷新。刷新完成后回到“空闲”状态。
当处于空闲状态时,接收到写命令(wr_en),进入“写”状态(有效接收读写命令的时刻有特殊要求,后面再详细说明),在full_page下连续写600个数据(100MHz,恰好耗时6us多一点,这样方便不用考虑定时刷新),写完之后,发送wr_done命令,进入“刷新”状态,相对于每次连续写完成后,提前刷新一次。此时,定时刷新的计数器清零,重新开始计数。
读多过程跟写过程类似,读完600个数据之后,手动完成刷新。
现在就来说一说,“空闲”状态接收读写命令的特殊要求。理论上充电周期为7.8125us,为保证600次读写在充电周期内完成,并且前后预留一些其他命令的时间,所以推荐在0~1us这个时间内接受读写命令,这样读写的时候专注读写就可以了。当然这是我的设计方式,如有更好的设计方式,那更好,欢迎分享。
2. 代码实现
状态机的代码如下所示,清晰的描述了各状态之间的跳变及其跳变条件。其中信号ctrl_valid即为上图中命令有效期的时间区间。在各状态描述的时序逻辑模块中,只是产生了读、写或刷新执行模块的使能信号,即在“写”状态的时候,使能写模块,完成相信的写操作。
always @ (posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
current_status <= IDLE;
end
else if(init_ing == 1'b0)
begin
current_status <= next_status;
end
else
begin
current_status <= IDLE;
end
end
always @ (rst_n or current_status or sdram_wrreq or sdram_rdreq or ref_req_auto or wr_done or rd_done or ref_done or ctrl_valid)
begin
next_status = 5'dx;
case(current_status)
IDLE:
begin
if(ref_req_auto == 1'b1) //收到自动刷新请求
begin
next_status = AUTO_REF;
end
else if(ctrl_valid == 1'b1 && sdram_wrreq == 1'b1)//在读写控制有效区内收到写请求
begin
next_status = WRITE;
end
else if(ctrl_valid == 1'b1 && sdram_rdreq == 1'b1) //在读写控制有效区内收到读请求
begin
next_status = READ;
end
else
begin
next_status = IDLE;
end
end
WRITE:
begin
if(wr_done == 1'b1)
begin
next_status = AUTO_REF;
end
else
begin
next_status = WRITE;
end
end
READ:
begin
if(rd_done == 1'b1)
begin
next_status = AUTO_REF;
end
else
begin
next_status = READ;
end
end
AUTO_REF:
begin
if(ref_done == 1'b1)
begin
next_status = IDLE;
end
else
begin
next_status = AUTO_REF;
end
end
default:
begin
next_status = IDLE;
end
endcase
end
//各个状态下的使能信号,以控制相应的模块执行相应的操作
always @ (posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
wr_start <= 1'b0;
rd_start <= 1'b0;
ref_start <= 1'b0;
end
else
begin
case(next_status)
IDLE:
begin
wr_start <= 1'b0;
rd_start <= 1'b0;
ref_start <= 1'b0;
end
WRITE:
begin
wr_start <= 1'b1;
rd_start <= 1'b0;
ref_start <= 1'b0;
end
READ:
begin
wr_start <= 1'b0;
rd_start <= 1'b1;
ref_start <= 1'b0;
end
AUTO_REF:
begin
wr_start <= 1'b0;
rd_start <= 1'b0;
ref_start <= 1'b1;
end
default:
begin
wr_start <= 1'b0;
rd_start <= 1'b0;
ref_start <= 1'b0;
end
endcase
end
end
以下给出写操作模块的部分代码,读操作和刷新同理。中间有些信号是我工程需要,参考一下思路即可。
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
cke_wr <= 1'b0;
cmd_wr <= NOP;
dqm_wr <= DQM_DIS;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= 4'd0;
end
else if(wr_start == 1'b1)
begin
case(status_wr)
4'd0:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
4'd1:
begin
cke_wr <= 1'b1;
cmd_wr <= ACT;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= row_addr; //行地址
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
4'd2: //4'd2和4'd3是为了延时T_RCD,即两个时钟
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
4'd3:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
4'd4:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b1; //用于写入第一个数据的时序标记
status_wr <= status_wr + 4'd1;
end
4'd5:
begin
cke_wr <= 1'b1;
cmd_wr <= WR;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= column_addr; //{A12A11,A10,column_address}
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
4'd6:
begin
if(sdram_wr_done == 1'b1) //用于增加NOP持续周期
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_DIS;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b1;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
else
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr;
end
end
4'd7:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_DIS;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= 4'd0;
end
default:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= 4'd0;
end
endcase
end
else
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= 4'd0;
end
end
参考文献
SDRAM驱动篇之简易SDRAM控制器的verilog代码实现
SDRAM操作(FPGA实现)的更多相关文章
- 用ModelSim仿真SDRAM操作
之前写了两篇关于Modelsim仿真的blog,其中模块管脚的命名可能让人觉得有些奇怪,其实不然,之前的两篇内容都是为了仿真SDRAM操作做铺垫的. 由于SDRAM的仿真过程相对比较复杂,也比较繁琐. ...
- [FPGA] 1、开发板使用和引脚连接
目录 1.注意事项 2.设备简介 3.引脚分配 注意事项: ① 插拔下载线时必须断电! ② Quartus II 软件和 NIOS 软件的版本必须一致,并安装在同一个目录下面,安装目录不要有中文和空格 ...
- 通过HPS控制FPGA端的GPIO
该笔记主要记录HPS端如何通过AXI Bridge控制FPGA端口的GPIO,主要是如何操作FPGA侧的Led 1.AXI Bridge AXIB主要包括H2FB.F2HB.LWH2F ...
- HPS端如何通过AXI Bridge控制FPGA端口的GPIO
该笔记主要记录HPS端如何通过AXI Bridge控制FPGA端口的GPIO,主要是如何操作FPGA侧的Led 1.AXI Bridge AXIB主要包括H2FB.F2HB.LWH2F ...
- 转载 基于NicheStack协议栈的TCP/IP实现
一.摘要 Altera软件NIOS II高版本(7.2版本以上,本例程中使用的是9.0版本)中实现TCP/IP所用的协议栈为NicheStack,常用的例程有2个,web_server和simple_ ...
- 【DSP开发】6455EMIF
外部设备连接接口包括外部存储器连接接口(EMIF).主机接口(HPI)等.外部存储器接口主要用来同并行存储器连接,这些存储器包括SDRAM.SBSRAM.Flash.SRAM存储器等,外部存储器接口 ...
- [转]DDR3基础知识介绍
本文转自:(4条消息) xilinx ddr3 MIG ip核使用详解_admiraion123的博客-CSDN博客 1,DDR3基本内容介绍1.1,DDR3简介DDR3全称double-data-r ...
- 【图像处理】【SEED-VPM】6.文件目录结构
———————————————————————————————————————————————————————————————————————— seed-vpm6467 \ Hardware Tes ...
- cavium octeon 处理器启动总线Bootbus 简介
cavium octeon 处理器启动总线Bootbus 简介: 韩大卫@吉林师范大学 Boot-bus(启动总线)是cavium octeon处理器的一种用于启动系统的硬件. CPU通过boot b ...
随机推荐
- IP报文分片
1. 最大传输单元(Maximum Transmission Unit,MTU). 以太网帧中的数据长度规定最小46 字节,最大1500 字节,MTU 指数据帧中有效载荷的最大长度,不包括帧首部的长度 ...
- nodejs+express-实现文件上传下载管理的网站
Nodejs+Express-实现文件上传下载管理的网站 项目Github地址(对你有帮助记得给星哟):https://github.com/qcer/updo 后端:基于nodejs的express ...
- 完美实现身份证校验 js正则
注意: 1.只针对18为身份证号码进行校验,现在15位的应该很少了, 2.不区分xX大小写, 3.出生年份1900-2099,每月的天数也进行相关验证(考虑的闰月的情况), 4.校验规则详见,这个写的 ...
- Java 线程并发
http://www.yesky.com/9/1899009.shtml http://zhidao.baidu.com/link?url=-xZ9JLo5x4bvCSVyXb2XhO6TODnBcU ...
- 吾八哥学Python(一):搭建Python开发环境(Windows)
学习Python的第一步当然是要配置一下开发环境了,这里记录一下本人在windows 10(64位)下配置Python开发环境的过程,供跟我一样的新手参考一下. 一.下载Python安装包 目前最新的 ...
- ELK简介
什么是ELK ELK是ElasticSearch,LogStash以及Kibana三个产品的首字母缩写.是可以和商业产品 Splunk 相媲美开源项目. 2013 年,Logstash 被 Elast ...
- 【前端】Require.js使用方法总结
一.为什么要使用require.js 首先一个页面如果在加载多个js文件的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长:其次,由于js文件之间存在依赖关系,因此必须严格保证加载 ...
- Redis基本认识和基础学习-基本命令
Redis 基本介绍 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. Redis是一个开源的使用ANS ...
- 利用cookies+requests包登陆微博,使用xpath抓取目标用户的用户信息、微博以及对应评论
本文目的:介绍如何抓取微博内容,利用requests包+cookies实现登陆微博,lxml包的xpath语法解析网页,抓取目标内容. 所需python包:requests.lxml 皆使用pip安装 ...
- 火狐浏览器打开html文件,中文乱码
当html文件为: 解决方法: 将 <meta http-equiv="Content-Type" content="text/html; charset=UT ...