对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即为上图中命令有效期的时间区间。在各状态描述的时序逻辑模块中,只是产生了读、写或刷新执行模块的使能信号,即在“写”状态的时候,使能写模块,完成相信的写操作。

  1. always @ (posedge clk or negedge rst_n)
  2. begin
  3. if(rst_n == 1'b0)
  4. begin
  5. current_status <= IDLE;
  6. end
  7. else if(init_ing == 1'b0)
  8. begin
  9. current_status <= next_status;
  10. end
  11. else
  12. begin
  13. current_status <= IDLE;
  14. end
  15. end
  16. 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)
  17. begin
  18. next_status = 5'dx;
  19. case(current_status)
  20. IDLE:
  21. begin
  22. if(ref_req_auto == 1'b1) //收到自动刷新请求
  23. begin
  24. next_status = AUTO_REF;
  25. end
  26. else if(ctrl_valid == 1'b1 && sdram_wrreq == 1'b1)//在读写控制有效区内收到写请求
  27. begin
  28. next_status = WRITE;
  29. end
  30. else if(ctrl_valid == 1'b1 && sdram_rdreq == 1'b1) //在读写控制有效区内收到读请求
  31. begin
  32. next_status = READ;
  33. end
  34. else
  35. begin
  36. next_status = IDLE;
  37. end
  38. end
  39. WRITE:
  40. begin
  41. if(wr_done == 1'b1)
  42. begin
  43. next_status = AUTO_REF;
  44. end
  45. else
  46. begin
  47. next_status = WRITE;
  48. end
  49. end
  50. READ:
  51. begin
  52. if(rd_done == 1'b1)
  53. begin
  54. next_status = AUTO_REF;
  55. end
  56. else
  57. begin
  58. next_status = READ;
  59. end
  60. end
  61. AUTO_REF:
  62. begin
  63. if(ref_done == 1'b1)
  64. begin
  65. next_status = IDLE;
  66. end
  67. else
  68. begin
  69. next_status = AUTO_REF;
  70. end
  71. end
  72. default:
  73. begin
  74. next_status = IDLE;
  75. end
  76. endcase
  77. end
  78. //各个状态下的使能信号,以控制相应的模块执行相应的操作
  79. always @ (posedge clk or negedge rst_n)
  80. begin
  81. if(rst_n == 1'b0)
  82. begin
  83. wr_start <= 1'b0;
  84. rd_start <= 1'b0;
  85. ref_start <= 1'b0;
  86. end
  87. else
  88. begin
  89. case(next_status)
  90. IDLE:
  91. begin
  92. wr_start <= 1'b0;
  93. rd_start <= 1'b0;
  94. ref_start <= 1'b0;
  95. end
  96. WRITE:
  97. begin
  98. wr_start <= 1'b1;
  99. rd_start <= 1'b0;
  100. ref_start <= 1'b0;
  101. end
  102. READ:
  103. begin
  104. wr_start <= 1'b0;
  105. rd_start <= 1'b1;
  106. ref_start <= 1'b0;
  107. end
  108. AUTO_REF:
  109. begin
  110. wr_start <= 1'b0;
  111. rd_start <= 1'b0;
  112. ref_start <= 1'b1;
  113. end
  114. default:
  115. begin
  116. wr_start <= 1'b0;
  117. rd_start <= 1'b0;
  118. ref_start <= 1'b0;
  119. end
  120. endcase
  121. end
  122. end

以下给出写操作模块的部分代码,读操作和刷新同理。中间有些信号是我工程需要,参考一下思路即可。

  1. always @(posedge clk or negedge rst_n)
  2. begin
  3. if(rst_n == 1'b0)
  4. begin
  5. cke_wr <= 1'b0;
  6. cmd_wr <= NOP;
  7. dqm_wr <= DQM_DIS;
  8. bank_addr_wr <= BANK0;
  9. addr_wr <= DONT_CARE_ADDR;
  10. wr_done <= 1'b0;
  11. wr_first_flag_r <= 1'b0;
  12. status_wr <= 4'd0;
  13. end
  14. else if(wr_start == 1'b1)
  15. begin
  16. case(status_wr)
  17. 4'd0:
  18. begin
  19. cke_wr <= 1'b1;
  20. cmd_wr <= NOP;
  21. dqm_wr <= DQM_EN;
  22. bank_addr_wr <= BANK0;
  23. addr_wr <= DONT_CARE_ADDR;
  24. wr_done <= 1'b0;
  25. wr_first_flag_r <= 1'b0;
  26. status_wr <= status_wr + 4'd1;
  27. end
  28. 4'd1:
  29. begin
  30. cke_wr <= 1'b1;
  31. cmd_wr <= ACT;
  32. dqm_wr <= DQM_EN;
  33. bank_addr_wr <= BANK0;
  34. addr_wr <= row_addr; //行地址
  35. wr_done <= 1'b0;
  36. wr_first_flag_r <= 1'b0;
  37. status_wr <= status_wr + 4'd1;
  38. end
  39. 4'd2: //4'd24'd3是为了延时T_RCD,即两个时钟
  40. begin
  41. cke_wr <= 1'b1;
  42. cmd_wr <= NOP;
  43. dqm_wr <= DQM_EN;
  44. bank_addr_wr <= BANK0;
  45. addr_wr <= DONT_CARE_ADDR;
  46. wr_done <= 1'b0;
  47. wr_first_flag_r <= 1'b0;
  48. status_wr <= status_wr + 4'd1;
  49. end
  50. 4'd3:
  51. begin
  52. cke_wr <= 1'b1;
  53. cmd_wr <= NOP;
  54. dqm_wr <= DQM_EN;
  55. bank_addr_wr <= BANK0;
  56. addr_wr <= DONT_CARE_ADDR;
  57. wr_done <= 1'b0;
  58. wr_first_flag_r <= 1'b0;
  59. status_wr <= status_wr + 4'd1;
  60. end
  61. 4'd4:
  62. begin
  63. cke_wr <= 1'b1;
  64. cmd_wr <= NOP;
  65. dqm_wr <= DQM_EN;
  66. bank_addr_wr <= BANK0;
  67. addr_wr <= DONT_CARE_ADDR;
  68. wr_done <= 1'b0;
  69. wr_first_flag_r <= 1'b1; //用于写入第一个数据的时序标记
  70. status_wr <= status_wr + 4'd1;
  71. end
  72. 4'd5:
  73. begin
  74. cke_wr <= 1'b1;
  75. cmd_wr <= WR;
  76. dqm_wr <= DQM_EN;
  77. bank_addr_wr <= BANK0;
  78. addr_wr <= column_addr; //{A12A11,A10,column_address}
  79. wr_done <= 1'b0;
  80. wr_first_flag_r <= 1'b0;
  81. status_wr <= status_wr + 4'd1;
  82. end
  83. 4'd6:
  84. begin
  85. if(sdram_wr_done == 1'b1) //用于增加NOP持续周期
  86. begin
  87. cke_wr <= 1'b1;
  88. cmd_wr <= NOP;
  89. dqm_wr <= DQM_DIS;
  90. bank_addr_wr <= BANK0;
  91. addr_wr <= DONT_CARE_ADDR;
  92. wr_done <= 1'b1;
  93. wr_first_flag_r <= 1'b0;
  94. status_wr <= status_wr + 4'd1;
  95. end
  96. else
  97. begin
  98. cke_wr <= 1'b1;
  99. cmd_wr <= NOP;
  100. dqm_wr <= DQM_EN;
  101. bank_addr_wr <= BANK0;
  102. addr_wr <= DONT_CARE_ADDR;
  103. wr_done <= 1'b0;
  104. wr_first_flag_r <= 1'b0;
  105. status_wr <= status_wr;
  106. end
  107. end
  108. 4'd7:
  109. begin
  110. cke_wr <= 1'b1;
  111. cmd_wr <= NOP;
  112. dqm_wr <= DQM_DIS;
  113. bank_addr_wr <= BANK0;
  114. addr_wr <= DONT_CARE_ADDR;
  115. wr_done <= 1'b0;
  116. wr_first_flag_r <= 1'b0;
  117. status_wr <= 4'd0;
  118. end
  119. default:
  120. begin
  121. cke_wr <= 1'b1;
  122. cmd_wr <= NOP;
  123. dqm_wr <= DQM_EN;
  124. bank_addr_wr <= BANK0;
  125. addr_wr <= DONT_CARE_ADDR;
  126. wr_done <= 1'b0;
  127. wr_first_flag_r <= 1'b0;
  128. status_wr <= 4'd0;
  129. end
  130. endcase
  131. end
  132. else
  133. begin
  134. cke_wr <= 1'b1;
  135. cmd_wr <= NOP;
  136. dqm_wr <= DQM_EN;
  137. bank_addr_wr <= BANK0;
  138. addr_wr <= DONT_CARE_ADDR;
  139. wr_done <= 1'b0;
  140. wr_first_flag_r <= 1'b0;
  141. status_wr <= 4'd0;
  142. end
  143. end

参考文献

SDRAM驱动篇之简易SDRAM控制器的verilog代码实现

SDRAM操作(FPGA实现)的更多相关文章

  1. 用ModelSim仿真SDRAM操作

    之前写了两篇关于Modelsim仿真的blog,其中模块管脚的命名可能让人觉得有些奇怪,其实不然,之前的两篇内容都是为了仿真SDRAM操作做铺垫的. 由于SDRAM的仿真过程相对比较复杂,也比较繁琐. ...

  2. [FPGA] 1、开发板使用和引脚连接

    目录 1.注意事项 2.设备简介 3.引脚分配 注意事项: ① 插拔下载线时必须断电! ② Quartus II 软件和 NIOS 软件的版本必须一致,并安装在同一个目录下面,安装目录不要有中文和空格 ...

  3. 通过HPS控制FPGA端的GPIO

    该笔记主要记录HPS端如何通过AXI Bridge控制FPGA端口的GPIO,主要是如何操作FPGA侧的Led 1.AXI Bridge         AXIB主要包括H2FB.F2HB.LWH2F ...

  4. HPS端如何通过AXI Bridge控制FPGA端口的GPIO

    该笔记主要记录HPS端如何通过AXI Bridge控制FPGA端口的GPIO,主要是如何操作FPGA侧的Led 1.AXI Bridge         AXIB主要包括H2FB.F2HB.LWH2F ...

  5. 转载 基于NicheStack协议栈的TCP/IP实现

    一.摘要 Altera软件NIOS II高版本(7.2版本以上,本例程中使用的是9.0版本)中实现TCP/IP所用的协议栈为NicheStack,常用的例程有2个,web_server和simple_ ...

  6. 【DSP开发】6455EMIF

     外部设备连接接口包括外部存储器连接接口(EMIF).主机接口(HPI)等.外部存储器接口主要用来同并行存储器连接,这些存储器包括SDRAM.SBSRAM.Flash.SRAM存储器等,外部存储器接口 ...

  7. [转]DDR3基础知识介绍

    本文转自:(4条消息) xilinx ddr3 MIG ip核使用详解_admiraion123的博客-CSDN博客 1,DDR3基本内容介绍1.1,DDR3简介DDR3全称double-data-r ...

  8. 【图像处理】【SEED-VPM】6.文件目录结构

    ———————————————————————————————————————————————————————————————————————— seed-vpm6467 \ Hardware Tes ...

  9. cavium octeon 处理器启动总线Bootbus 简介

    cavium octeon 处理器启动总线Bootbus 简介: 韩大卫@吉林师范大学 Boot-bus(启动总线)是cavium octeon处理器的一种用于启动系统的硬件. CPU通过boot b ...

随机推荐

  1. IP报文分片

    1. 最大传输单元(Maximum Transmission Unit,MTU). 以太网帧中的数据长度规定最小46 字节,最大1500 字节,MTU 指数据帧中有效载荷的最大长度,不包括帧首部的长度 ...

  2. nodejs+express-实现文件上传下载管理的网站

    Nodejs+Express-实现文件上传下载管理的网站 项目Github地址(对你有帮助记得给星哟):https://github.com/qcer/updo 后端:基于nodejs的express ...

  3. 完美实现身份证校验 js正则

    注意: 1.只针对18为身份证号码进行校验,现在15位的应该很少了, 2.不区分xX大小写, 3.出生年份1900-2099,每月的天数也进行相关验证(考虑的闰月的情况), 4.校验规则详见,这个写的 ...

  4. Java 线程并发

    http://www.yesky.com/9/1899009.shtml http://zhidao.baidu.com/link?url=-xZ9JLo5x4bvCSVyXb2XhO6TODnBcU ...

  5. 吾八哥学Python(一):搭建Python开发环境(Windows)

    学习Python的第一步当然是要配置一下开发环境了,这里记录一下本人在windows 10(64位)下配置Python开发环境的过程,供跟我一样的新手参考一下. 一.下载Python安装包 目前最新的 ...

  6. ELK简介

    什么是ELK ELK是ElasticSearch,LogStash以及Kibana三个产品的首字母缩写.是可以和商业产品 Splunk 相媲美开源项目. 2013 年,Logstash 被 Elast ...

  7. 【前端】Require.js使用方法总结

    一.为什么要使用require.js 首先一个页面如果在加载多个js文件的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长:其次,由于js文件之间存在依赖关系,因此必须严格保证加载 ...

  8. Redis基本认识和基础学习-基本命令

    Redis 基本介绍 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. Redis是一个开源的使用ANS ...

  9. 利用cookies+requests包登陆微博,使用xpath抓取目标用户的用户信息、微博以及对应评论

    本文目的:介绍如何抓取微博内容,利用requests包+cookies实现登陆微博,lxml包的xpath语法解析网页,抓取目标内容. 所需python包:requests.lxml 皆使用pip安装 ...

  10. 火狐浏览器打开html文件,中文乱码

    当html文件为: 解决方法: 将   <meta http-equiv="Content-Type" content="text/html; charset=UT ...