最近一个星期,用EP1S20这个FPGA嵌入NIOS2,研究了一下FLASH和SDRAM的驱动,最后终于是好用了,但中间遇到了不少曲折,现在就把我的一些经验分享给大家,希望大家以后在这方面不要走弯路。
首先,先提一个名词--SOPC.任何一本关于FPGA嵌入式的书籍都会有的一个名词,翻译过来是“片上可编程系统”。也就是说我们在用FPGA作嵌入式 的时候其实相当于自己根据自己的需要作一个处理器,这个处理器的内核就是NIOS2处理器,而外围可以根据自己的需要添加我们需要的任何外设,包括定时 器,UART,IO,SPI接口等等。这些都是我们在用单片机和ARM时经常接触和使用的东西,所以以前用单片机和ARM的时候在使用SOPC的时候会觉 得非常的熟悉。它的灵活性在于可以根据我们的需要任意搭配一个最节省的CPU,而且使用AVALON总线连接你的外设,不需要自己去搭建结构,但缺点在于 它是一种软核,是用语言实现的一种硬件算法,所以比较占资源。具体的细节大家可以上网上去搜索。
搭建好了这个SOPC之后就可以用NIOS来实现我们想要的功能了。下面转入正题,用NIOS内部的SDRAM控制器,在SOPC里添加了这个项目之后, 我们就可以狠方便的实现SDRAM的驱动了。因为控制器已经为你引出了SDRAM需要的连线,我们所作的工作只是需要把这些线连起来就可以了,下面简单的 介绍一下这些引脚的功能。
CLK--时钟输入
CKE--内部刷新时钟控制端
CS--片选
BA1,0--BANK的选择
A0--A11--地址线,包括行地址与列地址
RAS--行地址选通
CAS--列地址选通
WE--写使能
LDQM,UDQM--字节与字控制端
DQ0--DQ15--数据端
看了这些控制线,大家应该感到狠痛苦了,确实SDRAM的控制确实狠复杂。但是NIOS里的控制器帮我们解决了这些问题,当我们正确的连好了这些线之后,我们就不需要去管它了,NIOS会帮我们处理好这些时序,也就是说我们要作的工作是:1 在SOPC里设计一个根我们用的SDRAM时序一样的控制器,这需要参考SDRAM的资料。2 正确地连接好NIOS与外部SDRAM的连线。3 也是最麻烦的一点,CLK的时序问题。下面着重介绍一下CLK的时序设置。
我们先看一下原理,FPGA里的NIOS2需要一个时钟,SDRAM需要一个时钟,那这两个时钟之间是什么关系呢?答案是很复杂。首先来讲这两个时钟都是 PLL的输出,PLL里根据不同的FPGA有不同的选项,一般来讲,PLL的输出有C1,C0,E1,E0........,C就是说PLL经过内部的复 杂逻辑的输出,所以它根原始的时钟输入之间是相位差的,E就是说直接时钟输出,它的相位差几乎位0,但是它需要特定的引脚。我们在设计的时候最好用E端作 SDRAM的时钟输入,具体的原因看后面的解释。
SDRAM的特殊性决定了它的驱动的复杂性,想要完全明白它的所有时序的意义需要很长的时间积累,但是应用的话不需要那么多,所以下面简单介绍一下最关键的几个。
1 tsu--输入建立时间,SDRAM里的参数
2 ton--输出保持时间,SDRAM里的参数
3 tinsu--FPGA从引脚输入到内部逻辑的建立时间
4 toutco--FPGA内部逻辑到输出的建立时间
这是我们计算SDRAM的CLK所需要的几个最关键的参数,tsu意义是说当NIOS的地址线和数据线到达SDRAM时,需要保持这么长的时间,toh是 说SDRAM在读取的时候,需要这么长的时间才能把输出值输出到总线上,设CLK为时钟信号,CLK1为SDRAM的时钟,CLK2为FPGA外部引脚的 时钟,可以看出来,CLK2-CLK=toutco,关于CLK2与CLK1我们可以仿真得到他们之间的相差,所以我们要做到的是,1 CLK2-CLK1=tsu 2 CLK1-CLK2=(tinsu+ton) (以上的计算是相差的计算),相差是正的话说明被减数在减数之后。 所以通过上边的公式可以确定CLK1的范围,所以在PLL里设置一下就可以让我们的SDRAM满足系统的要求了。关于为什么要用E0来作SDRAM的时钟 的问题,首先我们每次编译的结果的时序都不一样,虽然变化部大,但在高频的时候部可忽视,而E0的相位与编译无关,其次,它的抖动比C端要小。现在我的系 统跑在75M上,还可以,但感觉这种方法在高频中会有潜在的问题,以后我会进行相应的测试,然后告诉大家结果。
上边算是对SDRAM的一个小结。
下面对FLASH作一下解释,跟SDRAM一样,FLASH只需要连好相应线就可以。但在实际的应用中有一个小问题,就是说假如FLASH的总线是22位 的,既21..0,NIOS会给你22..0,所以我们在连接的时候需要错一位,也就是说NIOS的1接FLASH的0,依次类推下去。但有的时候我们需 要SDRAM和FLASH公用总线,这就存在一个问题,FLASH需要从1开始接,而ADRAM是从0开始,所以设计公用总线的时候,原理图上SDRAM 的A1要接FLASH的A0,这点一定要注意,但发现ALTERA部推荐这么去设计,因为SDRAM需要自己的一套时序,共用的话会大大降低SDRAM的 性能,在实际中也发现了这点,所以在资源部紧张的情况下还是部要复用。还有NIOS对AMD的FLASH支持的很好,其他的象SST的FLASH ,对以前的200,400,800系列支持的好,但之后的160,320,640就支持部好了,所以用NIOS的时候最好用AMD的FLASH.
以上算是这几天的一点小收获,分享给大家,希望对大家有帮助,之后我会对UART,SPI。。。。。其他的一些外设作测试,然后更加具体的了解一下NIOS的编程风格与规范,整理好会跟大家分享。
SDRAM使用注意事项:
1. 首先要在sopc里加入sdram控制器,根据器件手册设置好数据宽度,行列地址宽度等;
2. 加入PLL移相。因为SDRAM为动态的存储器,对时序要求比较高.因为有延迟,所以加入PLL来调整相位,一般的经典值为-63deg,要是不行,可以 每5度增加或者减少;用cylone器件时,最好用e0输出SDRAM的时钟,同时用专用的时钟输出引脚输出;cycloneⅡ用c2;
3. 数据线设置成双向,同时因为管脚比较多,务必要保证管脚锁定正确;
附SDRAM测试程序:
#include "system.h"
#include "altera_avalon_pio_regs.h"
#include "alt_types.h"
int main (void) __attribute__ ((weak, alias ("alt_main")));
int alt_main (void)
{unsigned char sdrw_d[60],sdrr_d[60],i;
for(i=0;i<50;i++)
{
sdrw_d = 0x0;
}
for(i=0;i<50;i++)
{
sdrw_d = i;
IOWR(SDRAM_BASE, i, sdrw_d);
}
for(i=0;i<50;i++)
{
sdrr_d = 0x0;
}
for(i=0;i<50;i++)
{
sdrr_d = IORD(SDRAM_BASE, i);
if(sdrr_d != sdrw_d)
while(1){IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, 0xff);};
}
}
在sopc里加入SDRAM控制器,命名为SDRAM,让程序在片上RAM执行,
本例中,当读写的数据不一致时,输出信号来点亮LED,也可以根据实际情况另做判断。比如在循环那里加断点,总之,是要判断程序是否进入死循环,从而判断读写数据是否一致。