SDRAM控制器

1.SDRAM简介

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

  • 老规矩,先查阅手册,找到时序图,可惜没有我想要的完整的,那分开吧,
  1. 激活命令,激活对应的row地址和bank
  2. 延时两个周期
  3. 写命令,指定对应的col行地址和数据(A10最好为0,不然给一个命令后,又得给激活命令,重复1,2步骤)
  4. 如果想继续写,写数据达到突发长度后,然后继续再给一个写命令和新的col地址
  5. 如果不想写了,写数据达到突发长度后,给一个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)
begin
	if(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;
			else
				next_read_ptr <= next_read_ptr + 1'b1;
		else
			next_read_ptr <= next_read_ptr;
	else
		next_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)
begin
	if(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;
	else
		data_count <= curr_write_ptr - curr_read_ptr;
end

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

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FPGA之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值