SDRAM那些事儿第一季教程 —— 小结

时间:2024-04-07 20:16:34

                       前言
  肺炎当道,被堵在家回不去学校,莫得器材,莫得设备,干不了活,只有手边一块放假顺手捎回来的FPGA开发板。在家颓过了整个春节, 手机电脑都快玩吐了 也不能一直浪费时间,那就搞搞这块板子呗,为假期结束后要做的东西提前准备好改造素材也好。然后就瞄上了Kevin大佬的SDRAM教程。12号晚开工,中间翘了两天,直到18号下午正式调通。东西虽然简单,但是整体的梳理还是要做一下的,故有此一文。(顺便提高以后可能存在的复习效率)

一、项目整体介绍
  要做的事情很简单,PC端利用串口助手通过UART向FPGA板子发送数据包,FPGA接收后提取出对SDRAM操作命令及操作数据,即将数据写入SDRAM和将数据读出SDRAM。数据读出SDRAM,将读出的数据通过串口回馈到PC端。项目的系统设计框图如下所示(下图中Cmd_endcode有误,应为Cmd_decode):
SDRAM那些事儿第一季教程 —— 小结
二、系统各模块设计结构梳理
1.串口收发模块(UART_RX及UART_TX)
(1)时序设计部分
SDRAM那些事儿第一季教程 —— 小结
  串口接收波形设计如上图。下面说明设计思路:

  • rx 为FPGA的串口接收端,传递PC端过来的数据。空闲时为高电平,PC端要发送数据过来时会将该线拉低表示起始位,接着依次发送8bit数据(最低位优先)。无校验位。

  • rx_t ~ rx_ttt 是对 rx 信号的延拍。打两拍作用是跨时钟域处理,打三拍作用是捕获下降沿(起始位)。这里附上其他人关于跨时钟域处理的学习总结: FPGA基础学习(3) – 跨时钟域处理方法

  • rx_flag 是串口接收端识别出起始位后,拉高表示接收模块开始工作的标志信号。在接收8bit数据完毕后重新拉低。

  • baud_cnt 为波特率计数器,FPGA使用时钟为50MHz,所以串口发送1bit的数据占用的时钟周期计算方式为:
    (1 / 9600)* 10^9 / 20 = 5208 ,这里9600为波特率,20为20ns。也可记为 50 * 10^6 / 9600,50 * 10^6 即50MHz。
    计满自动清零。

  • bit_flagbaud_cnt 的中间值,用于作为数据抓取标志。

  • bit_cnt 用于统计一次通信累计接收的数据位数,计数到8表示当前数据接收完毕后清零。

  • rx_data 通过移位拼接实现串转并。

  • po_flag 为串口数据接收完成,数据可用的标识
    SDRAM那些事儿第一季教程 —— 小结
      串口发送波形设计与接收大致相同,其工作条件同样需要一个触发信号( tx_trig )。
    (2)代码细节部分
      串口收发能有个????的细节
    (3)Modelsim波形仿真实录(50MHz系统时钟)
    SDRAM那些事儿第一季教程 —— 小结
    SDRAM那些事儿第一季教程 —— 小结
    2.命令解析模块(Cmd_decode)
    (1)时序设计部分
    SDRAM那些事儿第一季教程 —— 小结

  • uart_flag 为UART_RX模块给出的 po_flag 信号输入,uart_data 为UART_RX模块给出的 rx_data

  • rec_num 为接收数据计数器,计满4个清零(该项目默认一帧数据一次写入4个)。

  • cmd_reg 为命令寄存器,默认一帧数据中的第一个数据为SDRAM控制器的操作命令。—— 8‘h55为写,8’haa为读。

  • wr_trig 为给SDRAM的写触发信号。等4个数据全部写入wfifo后,再让SDRAM控制器到wfifo这里读出来。

  • rd_trig 为给SDRAM的读触发信号,当解析出8’haa读命令时给出触发脉冲。

  • wfifo_wr_en 为wfifo的写使能信号。命令解析模块解析出8’h55写命令后,将接下来的4个数据判断为要写入wfifo的数据。
    (2)代码细节部分

  • rec_num 应注意,不对8‘haa读命令进行计入。

  • wr_trigrd_trigwfifo_wr_en这三者实际上都可看作是不同条件下对uart_flag 的筛分。
    (3)Modelsim波形仿真实录(50MHz系统时钟)
    SDRAM那些事儿第一季教程 —— 小结

3.读写FIFO(wfifo、rfifo)
(1)时序设计部分
SDRAM那些事儿第一季教程 —— 小结
  上半部分对接命令解析模块,对接SDRAM的主要是下半部分。这也是尤为需要注意的地方:在命令解析模块向SDRAM给出写触发脉冲( wr_trig )后,SDRAM给出wfifo的读使能信号,从wfifo里读出要写入SDRAM的数据。所以,wfifo的读使能信号在wfifo数据未被全部读出时必须保持高电平,全部读出后才能拉低。
SDRAM那些事儿第一季教程 —— 小结
SDRAM那些事儿第一季教程 —— 小结
  图中的 rfifo_empty 信号为rfifo的空信号,实际应该取反,即为空时为高电平,内部有数据时该信号拉低。当命令解析模块解析出8’haa读命令时,给SDRAM控制器一个读触发脉冲。SDRAM收到后,将rfifo的写使能信号拉高,并把读出来的数据写入rfifo(上图中的红框部分)。由此,rfifo内部不再为空, rfifo_empty 信号拉低,UART_TX模块给出rfifo的读使能,开始将从rfifo读出的数据做并转串发送。


以下的部分为SDRAM控制器设计,这就需要根据自身的所用芯片的不同,参考芯片的数据手册进行修改了。Kevin大佬在****中所用的是地址位为12位的SDRAM,而我板子上的SDRAM是13位的地址,就不能直接用,得自己看着改。
SDRAM那些事儿第一季教程 —— 小结
SDRAM那些事儿第一季教程 —— 小结
SDRAM那些事儿第一季教程 —— 小结
再贴一下Kevin大佬的模式设置:
SDRAM那些事儿第一季教程 —— 小结
4.SDRAM控制器 —— init部分(初始化)
  时间已经从手册上查出并标注在了时序图的对应位置。
SDRAM那些事儿第一季教程 —— 小结
  初始化操作完成后,对外输出一个高电平表示初始化操作已完成。
5.SDRAM控制器 —— aref部分(刷新)
SDRAM那些事儿第一季教程 —— 小结
  自动刷新的频率根据手册要求来决定。如博主使用的SDRAM芯片要求在64ms内刷新完8192行,这就要求
64 * 1000 / 8192 = 7.8us 要有一次刷新。
  同时,在这里引入仲裁机制。顾名思义,在判断出SDRAM已完成初始化的情况下,为确保刷新、读、写的操作循环有序进行,引入仲裁机制。这里对刷新模块引出刷新请求信号,即在刷新模块内部进行7.8us的周期计时,计满拉高刷新请求信号,传递至仲裁状态机给出刷新使能后,再由刷新模块对SDRAM执行刷新操作。(这里先不考虑读、写模块)刷新操作执行完成后,拉高刷新完成信号,反馈给仲裁状态机,确保仲裁状态机退出刷新标记状态,以便进行下一次仲裁。

6.SDRAM控制器 —— write部分(写)
SDRAM那些事儿第一季教程 —— 小结
  接收到写触发信号后,写模块对外拉高写请求信号,经仲裁状态机判断后给出写使能(默认处理优先级顺序为刷新、写、读),写模块执行写操作。写操作完成后,同样拉高写完成信号,反馈到仲裁状态机。由于写模块整个操作流程涉及到的命令较多,故使用状态机进行设计。
  在这里还有两点细节需要注意:

  • 可能存在写满了一行,但仍未写完的情况,此时需要Precharge关闭现有行,然后回跳到行**状态,**新行后继续执行写操作。
  • 可能存在正在执行写操作时刷新模块发出刷新请求的情况,此时继续保持写入直到此次突发写完再关闭现有行,退出写状态,并重新发送写请求等待仲裁状态机响应。

Modelsim仿真实录(50MHz)
SDRAM那些事儿第一季教程 —— 小结
SDRAM那些事儿第一季教程 —— 小结
7.SDRAM控制器 —— read部分(读)
SDRAM那些事儿第一季教程 —— 小结
  读与写设计思路大体相同,最需要注意的部分就在时序图上:读模块的读命令、Precharge命令的给出与数据读出的时序关系。

Modelsim仿真实录(50MHz)
SDRAM那些事儿第一季教程 —— 小结
SDRAM那些事儿第一季教程 —— 小结