SDRAM控制器

发布时间:2025-12-09 21:13:49 浏览次数:4

1.SDRAM简介

  • 简介:SDRAM为同步动态随机存储内存,同步指的是时钟与外部输入的时钟保持一致,也就是与外部共用一个时钟;动态指的是每个时间段内,都要进行一次刷新操作,否则里面的数据会丢失,这也是由存储特性决定的;随机指的是数据存储地址可由我们自己任意指定。
  • bank与地址: 在SDRAM中,有多个bank,每个bank都有自己的行列地址,相互独立,在读写的时候,我们需要指定那个bank的那行,那列进行读写,其中行列地址线是共用的,下面会具体的说明
  • 命令:我认为SDRAM是响应式的,对任何SDRAM的操作都要给出对应的命令,否则是无法得到想要的结果的。
  • 数据: SDRAM输入输出的数据线是共用的

  • 本文章使用的SDRAM是黑金AX4010上的


    2. SDRAM的操作流程

    • 上电延时,然后初始化
    • 进入仲裁状态机,优先执行刷新请求,读写请求的优先级可自行决定
      ps:是不是感觉挺简单的٩(๑>◡<๑)۶

    3. SDRAM 初始化

    • 找到说明文档,查阅初始化时序图

    • CKE为SDRAM使能线,在整个运行过程中,一直为高即可

    • COMMAND,command是由4根线共同组成的,分别是:CS,RAS,CAS,WE,在手册的可以方便的找到不同的命令,其4根线上高低电平分别是怎样的

    • DQM为掩码,设置dqm的值,在读写sdram时,可以只对数据的高八位或者低八位或者十六位都感兴趣,一般一直设为00,即十六位都感兴趣

    • A 为行列地址线,row地址时,A全部有效,col地址时,其第九位有效

    • BA 为bank,指定对那个bank进行操作

    • DQ: 为sdram的数据线

    • 从时序图可以看到,上电后,我们需要延时最少100us,然后给个NOP命令,一般为了稳定,都会延时200us;在NOP命令后,然后再给PRECHARGE(预充电)命令,此时需要指定bank,如果A10为高的话,就是对所有的bank,如果A10为低的话,就按照BA指定的来,一般设置A10为高,后面的一些延时和对应的命令按照时序图来即可

    • 直接来到LOAD Mode Register这个命令,此时需要指定A的值,不同的A值,在后面对SDMRA的操作会不同,阅读手册可知

    • A0,A1,A2:控制突发长度,给一个读写命令,我可以连续读写多少个数据,我设置的是000,突发长度为1,我认为这样便于书写程序

    • A3: 读写是连续的,还是随机的,仅对突发长度不为1时有效;读写的需要给定地址,也就是说写入的第一个数据的地址是以给定的,如果突发长度不为1的话,后面的数据的地址是在给定地址的基础上依次加一,还是说是随机的。

    • A4,A5,A6:仅对读数据有效,给了读命令后,在接下来的几个周期内,数据才会出来,俗称潜伏期

    • 其他位就不作介绍了,都为0即可,

    • 至此SDRAM的初始化就完成了


    4. SDRAM的写

    • 老规矩,先查阅手册,找到时序图,可惜没有我想要的完整的,那分开吧,
  • 激活命令,激活对应的row地址和bank
  • 延时两个周期
  • 写命令,指定对应的col行地址和数据(A10最好为0,不然给一个命令后,又得给激活命令,重复1,2步骤)
  • 如果想继续写,写数据达到突发长度后,然后继续再给一个写命令和新的col地址
  • 如果不想写了,写数据达到突发长度后,给一个PRECHARGE命令,然后就写完成了。
    ps : 此步骤只是写SDRAM的一种,我认为是最简单,也是比较高效的一种
    可以参考下面的写时序,与我说的,只是A10给定的值不同,可以看出,如果A10给1了的话,它会自动进行一个PRECHARGE操作,而为0的话,则需要我们自己给定,以结束这次的写操作。

  • 5. SDRAM读和刷新

    SDRAM的读与SDRAM的写基本上一样,同样刷新操作更简单,大家可以按照读时序图自己琢磨,下面给出对应的时序图

    6. 注意事项

    1. 读写的时候,A10的值是非常值得关注的。2. 读时,何时接收数据;写时,何时获取数据。3. 仲裁模块的书写,确保读写数据后,没有超过刷新周期,这里可以用读写速度换取稳定性

    7. 异步FIFO

    最后来介绍一下异步FIFO的实现,我们都知道FIFO是一种先进先出的一种结构,可以说是很简单的,但是设计起来还是有点难度的,难点如下

    1. 由于使用的是同一块内存,如何做到读与写如何不冲突2. 如何判断是否写满了,以及是否为空3. 如何计算FIFO里面读写的数据量

    接下来,我还是结合代码来说明吧

    1. 读写

    curr表示当前读写的地址,next表示下一次的读写地址只需要合理地对next地址进行更新即可做到合理性

    reg[8:0]curr_read_ptr,next_read_ptr;//读指针reg[8:0]curr_write_ptr,next_write_ptr;//写指针

    对读地址进行更新,可以看到,只要不为空的时候,我们就可以一直更新next读地址的值,同样,只要没有写满的时候,就可以一直更新next写地址的值。

    always@(posedge rclk or negedge rst)beginif(rst == 1'b0)next_read_ptr <= 'd1;else if(r_en == 1'b1)if(r_empty == 1'b0)if(next_read_ptr == 'd511)next_read_ptr <= 'd0;elsenext_read_ptr <= next_read_ptr + 1'b1;elsenext_read_ptr <= next_read_ptr;elsenext_read_ptr <= next_read_ptr;end

    2. 判断是否为空和满

    满和空其实很好判断,只要当下一个写的地址等于当前读的地址,即可判断为满,
    空也是如此,当下一个读的地址等于当前写的地址,即为空,还要考虑初始情况,next_read等于next_write的时候也是为空的

    assign w_full = (next_write_ptr == curr_read_ptr ) ? 1'b1 : 1'b0; assign r_empty = ( next_read_ptr == curr_write_ptr || (next_read_ptr == next_write_ptr)) ? 1'b1 : 1'b0;

    3. 计算FIFO里面的数据量

    FIFO里面的数据量其实读写地址的差值,但是当写的地址在读的地址后面的时候,又不仅仅是差值了,而是数据总长度减去这个差值了。

    always@(posedge rclk or negedge rst)beginif(rst == 1'b0)data_count <= 'd0;else if(curr_read_ptr >= curr_write_ptr && next_read_ptr != next_write_ptr)data_count <= 'd511 - curr_read_ptr + curr_write_ptr;elsedata_count <= curr_write_ptr - curr_read_ptr;end

    最后给出我自己实现的SDRAM_FIFO控制器,其中包括sdram_pluse仿真模块,以及testbench;FIFO没有调用IP核,利用SDRAM和FIFO将sdram封装成一个可以方便实用的异步sdram_fifo.
    欢迎大家下载学习,写的很通俗易懂。下载地址.

    需要做网站?需要网络推广?欢迎咨询客户经理 13272073477