「博文精选」iMXRT1050 从外部QSPI Nor Flash启动
晕,不看不知道一看吓一跳,都快两年没有更新我的博客了,哎,惭愧的同时也是感叹时间过的真快啊,之前写博客还高喊着自己快奔三的人了,结果现在都已经迈过去了。。。不继续说了,都暴露年龄了,咳咳。不过虽然时光易逝,但是还好激情尚在,这两年尽是韬光养晦了,虽然波折蛮多,经验和收获也还好,这不,趁着我们NB的号称跨界处理器的i.MXRT系列的推出一睹为快吧(想起当年也是Kinetis刚出来,我在学校正好开始写它的那一系列技术文档,这是啥,缘分呐)。其实在发表到AET之前,我先把此文发到阿莫论坛上了试了试水,收到的一句话挺让我心情激荡的,就是“FSL又活了”,my answer is “灵魂依在。。。”,哎,啥也不说了,下面开整吧。
在此文正式成型之前,历经了3个版本的修改和优化,最终fixed掉了已知的bug和稳定性才敢拿出来献丑,希望让广大AET的博友们继续能有所收获有所体会,也算为了RT1050的未来发展贡献点力量吧,貌似不少人在观望RT系列,也有很多第三方设计公司和开源项目在蠢蠢欲动,所以也希望我抛的这块砖能在RT的广泛市场激起一点涟漪多引出几块好玉来。。。
2018新年伊始,I.MXRT105x已经推向市场有一段儿时间了,以其极高的性价比引起了不少工业和消费领域客户的兴趣,毕竟相比于目前市场上已有的多数“高价低配”的高端ARM Cortex-M7产品,RT105x最高600MHz主频、极丰富的外设和3美金起步的定位着实算是业界良心了(还有马上要面世的LQFP封装I.MXRT1020更是让人期待),只是当然这种“低价高配”是有代价的,其内部不自带Flash需要外挂串行SPI Nand/Nor Flash或者并行的Nand/Nor Flash(貌似一下子给不少心动的人泼了凉水,呵呵),不过这一消息倒是有人欢喜有人忧,对欢喜的客户来说,他们的应用代码本身内部Flash就装不下需要外扩,这一下反倒好了,省的内心纠结了再也不用担心老板整天催自己优化代码精简应用看看能不能只用内部Flash搞定了,哈哈,而且动则几Mb或者几十Mb的外部SPI Flash价格便宜以后升级也是绰绰有余,而对忧的客户来说,外部Flash启动方式带来的启动配置复杂、代码安全性问题和对系统性能的影响等都是潜在的棘手问题。
关于外部Flash的代码安全问题和代码在外部执行对整个系统性能的影响,RT105x的加密启动(HAB)功能和32KB的L1 ICache/DCache是可以解决的,只是本文先从RT105x的启动配置问题着手,毕竟系统如果都Boot不起来,其他的问题都是空谈了,呵呵。另外,如上面所说,RT105x支持的启动方式有不少,不过个人觉着外部串行SPI Flash启动会是大多数人的折中选择。由于RT105x的官方EVK板上是带有两块串行SPI Nor Flash(一个是Cypress高性能8线高速的Hyper Flash S26KS512SDPBHI02,一个是ISSI物美价廉的4线QSPI Flash IS25WP064AJBLE),所以咱就手上有啥来啥,以板载的Hyper Flash和QSPI Flash为例详细说明下串行SPI Nor Flash的启动流程和具体使用方法。
串行SPI Nor Flash启动流程
1
当RT105x EVK板载的Boot模式选择开关SW7设定为如下Table1-1所示的前两者时(其他BOOT_CFG 管脚默认被下拉到地,即OFF状态),系统会通过片上的FlexSPI接口执行外部串行SPI Nor Flash的启动,其启动地址为Table1-2所示的0x6000_0000(如果是从并行的Nor Flash启动则启动地址为0x8000_0000),关于其他Boot pin对启动模式的配置影响,见RT105x的RM手册8.6章节,这里就不细说了。
Table1-1 典型的启动模式设定
SW7-1
SW7-2
SW7-3
SW7-4
启动模式
OFF
ON
ON
OFF
Hyper Flash启动
OFF
OFF
ON
OFF
QSPI Flash启动
ON
OFF
ON
OFF
SD卡启动
Table1-2 启动相关地址
Start Address
End Address
Size
Description
0x80000000
0xDFFFFFFF
1.5GB
SEMC 外部存储器接口 (SDRAM, 并行NOR Flash, PSRAM,
并行NAND Flash)
0x60000000
0x7F7FFFFF
504MB
FlexSPI
0x20200000
0x2027FFFF
512KB
OCRAM
0x20000000
0x2007FFFF
512KB
DTCM
0x00000000
0x0007FFFF
512KB
ITCM
如图1所示为完整的SPI Nor Flash启动流程图,当RT105x的片上ROM在检测到启动模式为FlexSPI接口后,会根据Boot Pin的配置信息配置该模式下需要使用的FlexSPI接口管脚的复用模式并将FlexSPI的时钟配置成默认的低速30MHz,然后会以0x6000_0000为首地址读取前512个字节作为外部Flash的配置信息(即Flash Configuration Parameters,包括几线制的SPI Flash,SPI的时钟频率,LUT查找表,DDR/SDR模式以及片选CS管脚的Hold/Setup Time等信息,见RM8.6.3章节)并以此来配置FlexSPI模块以满足外部Flash的特性,待配置完毕后,RT105x CPU以后即会以AHB总线读取数据和指令的方式(虽然从外部来看仍然是FlexSPI接口,但是由于LUT查找表读取数据的指令已经配置好,CPU只是通过AHB总线发送读取指令即可触发LUT以操作外部Flash,也就是说在内部对CPU来讲已经屏蔽掉了FlexSPI的底层)来读取接下来的跟用户Image相关的几个关键信息,即IVT(Image Vector Table),Boot Data和DCD(Device configuration data)如图2,其中IVT需要放在外部Flash的固定偏移地址(如图3,对Nor Flash来说其需要存储在基地址 + 4KB的偏移地址,比如0x60000000+4*1024)供ROM读取以便让系统知道用户Image的第一条可执行指令放在了哪里以及BootData和DCD的存放地址,BootData则保存了完整Image的首地址和整个image所占的空间大小,而DCD则包含了一些配置命令以便在跳到用户程序入口之前供ROM调用配置内部外设以更好的匹配外部IC,一般如果系统外挂了SDRAM的情况则需要配置好DCD(因为有时需要主程序在跑起来之前,外部SDRAM就得处在ready状态,以供CPU把data或者code copy到SDRAM里时不会出错)。
图1 SPI Nor Flash启动流程
图2 完整Image组成元素
图3 IVT针对不同存储介质的存放地址
综上所述,我们实际上就可以得出结论来,如果要让系统能正常Boot起来,则烧写到外部串行SPI Flash里面的完整image必须要包括五个重要元素,即Flash Configuration Parameters,IVT,Boot Data,DCD和用户image,而其中Flash Configuration Parameters和IVT是存放在固定的地址的,而后三者则由IVT的内容来决定,无论是直接编译生成或者通过辅助工具手动添加,只要我们最后形成的image里面包含了这几个元素,CPU就会认可这个image并执行(先不谈安全加密的事)。所以我们只要搞清楚这个基本原理了,无论是什么方法,最后都是殊途同归,下面就可以放心大胆地去解决RT105x的FlesSPI启动问题了。
SPI Nor Flash启动配置方法
2
前面提到如果想要RT105x能正常从外部串行SPI Nor Flash启动的话,待烧写的image是必须要集成五个元素的(Flash Configuration Parameters, IVT, Boot Data, DCD and User Image, 重要事情要多说几遍 呵呵),那具体的实现方法我目前想到的无非两种,一种是在编译链接过程中直接把这几个元素和应用代码链接到一块,并将这几个元素指定好链接地址,最后通过IDE的Flashloader下载到SPI Flash里,第二种则是通过辅助工具在编译链接好的裸应用代码前面手动的添加一个信息头(头里面包含这几个元素),然后通过单独的下载工具或者MCU内部的ROM Bootloader ISP下载进去。这两种各有优劣势,前者比较适合在前期调试的时候使用,这样在IDE里面编辑修改应用代码后做印证测试的时候可以直接在线download和debug(比如单步,断点,查看寄存器和Memory等等),我相信这也是目前大家比较迫切需要的,但是缺点的话则是前期配置过程稍微复杂些且需要对Boot过程和外部SPI Flash的Spec有一定了解(当然这些配置是一劳永逸的,配置一次即可),而后者则是客户不需要关心其他几个元素的配置只专注用户应用的开发,待开发完毕后通过辅助工具生成最终的Image用于小批测试或者量产,缺点是前期调试的话会比较麻烦,每次修改完重新编译生成用户image还需要用辅助工具手动添加信息头然后再通过Bootloader下载进去验证(我想想都有点累的慌。。。这种情况workaround只能是前期先在RAM里调试,待成熟了之后再走这个流程)。
第二种使用辅助工具的方法,官方已经提供了一整套工具链(可以从RT105x官方主页的”Flashloader i.MX-RT1050”软件包里找到)且有了相应的AN应用笔记介绍其具体操作步骤,这里就不再赘述了。本次我们重点介绍第一种方法,即在IDE环境里通过对工程的配置达到生成并下载调试完整image的目的,说到这里我再啰嗦几句,实际上这种方法做下来不只方便了在线download和debug,好处也不少,一是所有的这几个元素配置信息都以C语言的常量形式体现在应用工程文件里面,会加深我们对Boot的理解程度不说,这样的话如果更换外部SPI Flash也可以很方便的更改适配信息,第二呢则是这种方式形成的image文件会是通用的image格式(比如.bin, .hex和.S19),也会兼容市面上大多数可以直接烧写SPI Flash的量产工具的批量烧写。好了,不再多说了,再说就有点话痨了,呵呵,因为板载有Hyper Flash和QSPI Flash两种,下面就分别详细说明下这两种Flash在IDE环境下的配置方法(我使用的IAR,Keil的可以参考第四章节自行配置,需要添加的几个文件是IAR,Keil和GCC三个环境兼容的)。
开发测试环境:
Hardware Platform: MIMRT1050-EVK (SCH-29538 REV A1)
Software Package: SDK_2.3.0_EVK-MIMXRT1050(mcuxpresso.nxp.com)
IDE: IAR_v8.20.1
Debugger: On-Board CMSIS-DAP
2.1 Cypress 1.8v Hyper Flash 启动
RT105x的EVK板子默认是使用Hyper Flash启动的,所以硬件不需要改动,只需要将SW7启动模式修改成Table1-1第一行配置使能Hyper Flash启动即可,如下图4,然后我们以SDK2.3中的hello world为例介绍具体配置方法。实际上在最新的SDK2.3里面Keil和MCUXpresso版本已经有针对Hyper Flash启动的hello_world_xip的样例了,只是IAR反倒是没有,不过这下正好我们来走一遍完整的配置过程,这样也可以为下一小节的QSPI启动打下基础(官方例程里是没有QSPI XIP例程的),毕竟大多数客户估计还是会倾向于选择QSPI的。
图4 Hyper Flash启动模式
(1)打开SDK2.3的hello world例程\boards\evkmimxrt1050\demo_apps\hello_world\iar,在Workspace下可以看到默认是有8种配置的,包括在SDRAM调试,OCRAM调试和spi nor Flash调试,原始的flexspi_nor_debug配置是没有其他几种元素的,所以这种配置下当把代码下进去外部Hyper Flash上的时候可以在线debug但是当重新上电或者外部复位后系统是Boot不起来的,因为没有其他元素信息RT1050启动的时候识别不了它的。接下来我们在此配置基础上新建一个配置出来然后在新的工程配置上添加文件和修改配置,点击IAR菜单栏Project->Edit Configurations,然后在打开的窗口下选择New,在新的工程配置下起一个新的名字“HyperFlash_bootok”,Base on Configuration则选择原有的flexspi_nor_debug以最大限度的保留原有的配置,改好之后点击Ok即当前工程会进入新添加的HyperFlash_bootok配置状态;
(2)在此工程配置下,新添加一个Group(右键工程->Add->Add Group)并命名为“xip”,然后右键该Group->Add->Add Files,找到根目录\devices\MIMXRT1052\xip路径下的四个文件,将该四个文件都添加到当前工程上来,如下所示,我们可以打开这两个.c文件即可看到除了User Image之外的其他四个元素信息都以常量的方式被定义在指定的段地址内,以便在编译链接之后将这几个元素配置信息分配到指定的地址上以保存在最终的Image上,且这四个文件是可以兼容IAR,Keil和GCC三大编译器的所以可以随意Porting,当然不要忘了在工程配置中头文件搜索路径上添加那两个头文件的路径,另外如果没有XIP_EXTERNAL_FLASH这个宏的话也要加上;
(3)由于四个元素被分别定义到指定的段内,但是默认的原始工程里面的链接文件是没有这几个段的定义的(Keil和MCUXpresso由于有xip的样例,它们是定义好的了),所以还需要在原始的链接文件基础上添加这几个段的定义并分配好地址,我们将原来的链接文件MIMXRT1052xxxxx_flexspi_nor.icf复制一份重命名为MIMXRT1052xxxxx_hyperflash_nor_bootok.icf(名字倒是无所谓),然后打开该文件,添加这几个段的定义和地址分配如下(修改好的源文件见随本文档附带的压缩包),然后在工程配置Options->Linker里选择新修改的链接文件,最后点击确认;
(4)不要以为这样就结束了,呵呵,我们这两步说到几个重要的元素信息是以常量的形式通过链接文件保存在指定的地址段内,不过我们的应用工程却没有调用它们,在编译的时候编译器会默认把他们又给优化掉了搞的最后没有体现在Image里面,前面的工作白搭了,所以还需要额外一步告诉编译器把这几个常量给Keep住,具体配置如下图,然后点击确认;
(5)这样工程配置就结束了,我们重新编译整个工程,然后在生成的.map文件里即可看到如下这四个元素信息都已经正确的分配到指定地址了,这样就万事俱备,就差IDE IAR的Flashloader这个“东风”了;
(6)在IAR v8.20.1以上的版本已经有HyperFlash的Flashloader了,我们在Options->Debugger选项下选择CMSIS-DAP,然后接下来需要注意的是在Options->CMSIS-DAP选项下,Reset类型需要选择Core Reset(CMSIS-DAP默认的Reset方式会导致调试的时候打不了断点,J-link倒是没有这个问题),然后点击Ok保持配置,最后开始Debug即可完美的把带有几个重要元素信息的配置数据都下载到外部Hyper Flash里面并进入Debug模式,正常单步或者断点,并且按键复位或者重新给板子上电都可以让RT105x正常Boot起来,最后有图有真相,发个串口打印的结果“hello world”, Enjoy it…
2.2 ISSI 1.8v QSPI 启动
上面说完Hyper Flash的启动方法,接下来继续说说QSPI启动。由于板子上的Hyper Flash和QSPI时钟、片选和部分数据线是共用的,所以在测试QSPI启动的时候需要把Hyper Flash焊掉(跟板子硬件设计有关系,不焊掉会影响时序,稍微有点心疼,毕竟Hyper Flash比较贵而且又是BGA的焊下来就焊不上去了,当然,如果是客户自己设计的板子只会有一种SPI Nor Flash,那就没这个问题了),然后将下图中DNP的0欧姆电阻焊上,最后特别注意的是需要在QSPI Flash的第7脚和3脚即Reset和WP管脚上拉4.7k的电阻到电源Flash_VCC。因为我发现有个别型号的QSPI Flash,比如板子用的ISSI这个,在系统启动的时候Reset和WP这两个脚是悬空态,而QSPI Flash默认上电都是单线方式,这样的话Reset和WP如果是低电平则QSPI一直处于复位和写保护状态,从而造成系统RT1050读取QSPI信息失败进而导致启动失败,所以需要在这两个脚上加个上拉电阻给它有效的高电平,后来我测试的QD和Winbond的QSPI这两个脚里面是有上拉电阻的,系统启动后是固定的高电平就没有问题了,不过我的建议是最好外部再加一个保证可靠,也不会影响后续的4线通信。另外,别忘了SW7启动模式需要修改成Table1-1第二行配置使能QSPI Flash启动,如下图5:
图5 QSPI Flash启动模式
(1)我们仍然以上面的hello world工程为例,有前面章节打基础,本章说起来就轻松不少了,在当前工程下,点击IAR菜单栏Project->Edit Configurations继续新建一个工程配置并以HyperFlash_bootok为蓝本将新的配置命名为“Qspi_nor_bootok”如下图,然后点击Ok进入该配置模式下;
(2)此时xip的Group文件组仍然有效,不过由于Boot的Flash类型发生了变化,所以需要修改外部SPI Flash的前512字节的Flash Configuration Parameters元素信息(其他几个元素无需修改)。打开fsl_flexspi_nor_flash.c文件,然后在hyperflash_config常量前面添加如下图Qspiflash_config的常量(修改后的源文件见随本文档附带的压缩包),实际上只是修改了外部SPI Flash的Pads类型、SPI时钟频率、Flash的大小(包括总大小以及page和sector的大小)和最重要的LUT查找表指令,这里我使用了外部QSPI Flash的四线Quad I/O Read模式(即0xEB),此模式下SPI的时钟频率可以跑到133MHz,总带宽可以达到532Mbps即66.5MByte/s(实际上很多MCU片内的Flash时钟也差不多这个频率甚至比这个低,不过内部的Flash线宽会大些),这个速度虽然相比于CPU 600MHz的主频慢很多,但是实际上RT1050的一级缓存ICache和DCache各有32KB且在程序里默认是打开的,实测下来对大部分代码来说外部SPI Flash的带宽几乎不会拖累CPU性能,针对特定应用的复杂代码留待大家自行测试(实测Opus音频编解码,跑在外部QSPI Flash上的性能大概为跑在内部SRAM上的70%~80%);
(3)上一步通过预编译QSPI_BOOT宏来使能QspiFlash的配置信息主要是为方便跟前面HyperFlash启动兼容,只需在当前工程配置下添加QSPI_BOOT宏即可,如下图所示,这样即使再跳回HyperFlash_bootok工程时也不必麻烦地来回修改Flash Configuration Parameters元素信息了,同时该工程配置下的链接文件由于各个段地址没有变化则不用修改,只是需要Keep的常量记得替换成Qspiflash_config,然后点击确认保存;
(4)此时整个工程的配置工作就完成了,点击Build完成对整个工程的编译和链接,查看.map文件也可以看到其他4个主要元素配置信息也都链接到整个image里了。不过接下来需要解决的是QSPI Flash的Flashloader问题了。在IAR For ARM v8.20.2以上的版本的Flashloader里已经加入了对QSPI Flash的支持了,但是该Flashloader只支持ISSI的QSPI Flash,我在此基础上做了修改和优化加入了对GD和Winbond QSPI Flash的支持,该新的Flashloader见随本文档附带的压缩包文件,将压缩包路径Firmware\IAR_8.20_Flashloader下的四个文件copy到IAR安装目录下C:\Program Files (x86)\IAR Systems\Embedded Workbench 8.0\arm\config\flashloader\NXP并覆盖源文件。此外,该修改后的Flashloader同时支持HyperFlash和QSPI Flash的download和debug(通过查询外部Boot模式来切换这两种烧写算法);
(5)一切准备就绪,在点击debug之前记得确认options->debugger下选择好CMSIS-DAP并且确保Reset类型必须要选择Core Reset,然后点击确认保存,最后debug将代码下载到外部QSPI Flash里并调试或者重新上电测试,hello world仍然让人激动不已,不容易啊不容易。
上述两种外部SPI Nor Flash的启动配置方式仅仅是以hello world工程为例了,实际上大家可以参照如上几个步骤任意porting到SDK的其他工程实例里面去,而且由于SDK_2.3.0_EVK-MIMXRT1050\devices\MIMXRT1052\xip目录是共享的文件(包括Flashloader也是共享的),所以添加的那四个文件内容不需要再修改了,只需要手动添加文件到工程和配置IAR相关选项即可,操作起来还是比较快的。
代码跑在SPI Flash数据跑在SDRAM的使用方法
3
前面比较详细的讲了HyperFlash和QSPI Flash的启动方式,趁着兴致尚在我们再加点餐。我相信在很多客户开发RT105x过程中,将code跑在外部SPI Flash里,而数据存放在外部SDRAM里面会是比较常用的一种选择(比如一些会用到高分辨率的GUI显示方面,内部RAM不够用了)。虽然前面提到的xip那四个文件(两个.c和两个.h)里面的fsl_flexspi_nor_boot.c文件有定义dcd_sdram这个常量,不过很快就会发现这个常量数组里面的数据很大且看不出什么意义来(看不出意义就意味着不知道怎么修改),这是因为这个常量数组里面是编译后的可执行命令,它是根据不同的SDRAM编译后的结果,也就意味着不同的SDRAM这个数组是不一样的,这就尴尬了,客户如果不是使用EVK板载的SDRAM的话该怎么搞呢?哈哈,这个倒是不用担心,官方很快会推出一个辅助工具来生成不同SDRAM的DCD常量数组,不过在此之前我们也不能干等着吧,所以本章节的“加餐”就是提出一种workaround先提前解决下这个问题。
我们要知道,在系统上电前无论代码还是数据都是存在SPI Flash这些非易失存储介质里面的,待上电后程序会有一个Copy过程将数据或者想要在RAM里执行的代码copy到RAM里面去,对内部RAM来讲无所谓(因为一上电内部的SRAM就已经初始化完成了)而对外部SDRAM来说是需要事先初始化RT105x的SDRAM控制器才能往该SDRAM可寻址的空间copy内容的。幸运的是在Copy之前我们是有段缓冲时间的,恰恰就是这段缓冲时间给了我们有可以不使用DCD配置的机会(实际上DCD也是ROM在跳到应用代码之前读取其内容对SDRAM进行初始化)。以IAR的启动代码为例,打开startup_MIMXRT1052.s文件找到CPU的起始入口Reset_Handler如下图,执行的Copy过程实际上是在__iar_program_start这个函数里面实现的(具体内容使用的是IAR自己的库, Keil是在__main里实现的),而在该函数之前(即在SystemInit函数里面)我们只需要将SDRAM初始化好让其Ready了就不会影响后续数据的copy和使用,所以下面我就简单介绍下具体操作方法。
(1)仍然是在当前hello world工程下,点击IAR菜单栏Project->Edit Configurations继续新建一个工程配置并以上面的HyperFlash_bootok为蓝本将新的配置命名为“flexspi_code_sdram_data”如下图,然后点击Ok进入该配置模式下;
(2)打开system_MIMXRT1052.c系统配置文件,也就是SystemInit函数所在的文件里面添加如下图所示SDRAM初始化相关的函数并在SystemInit的最后调用就可以在使用SDRAM之前将其初始化(具体修改后的源文件见随文档附带的压缩包),而且初始化函数均是以C语言形式读写配置SDRAM相关寄存器,这样的话即使SDRAM更换了也可以随时修改初始化寄存器配置以适配不同的SDRAM了,俗话说未知是最可怕的,而一旦都是开放的话就没那么难了。至于SDRAM这块的初始化是怎么找到的呢,实际上就是将SDRAM的预处理文件(当前目录下的evkmimxrt1050_sdram_init.mac)里面的命令用C语言实现了而已;
(3)由于仍然是为了兼容性问题,上面使用了预编译命令判断HYBERFLASH_SDRAM宏来决定是否初始化SDRAM,所以在当前工程配置下需要添加HYBERFLASH_SDRAM宏声明以使在当前工程配置下SDRAM初始化生效;
(4)然后需要修改下链接文件,将数据都分配到SDRAM地址范围内,将MIMXRT1052xxxxx_hyperflash_nor_bootok.icf复制后重命名为MIMXRT1052xxxxx_flexspi_code_sdram_data.icf,打开该文件,添加data3相关信息之后保存该链接文件,并在Options->Linker文件里选择新的链接文件(源文件见随文档附带的压缩包),点击确定保存配置;
(5)一切都修改完毕之后,点击编译链接,再打开.map文件即可看到所有的数据变量已经被分配到外部SDRAM的寻址空间里面了,最后再点击debug将代码下载到外部SPI Flash里面(注意SW7启动模式记得修改成HyperFlash启动,因为本例子演示的是code跑在HyperFlash里,数据跑在SDRAM里),这样就完美实现代码在SPI Flash里执行而数据跑在SDRAM里。当然,hello world本身没多少数据,大家可以把该功能照本宣科的poring到复杂一点的应用里面或者在当前工程里面自己memory alloc一段足够大的数据区,然后读修改写的去操作以测试该方法的可靠性,我这里就不多说了。
GD和Winbond 3.3v QSPI Flash启动
4
在第2.2章节里我已经介绍了RT1050 EVK板载1.8v ISSI的QSPI Flash的启动方法,不过经过一段时间客户的反馈,实际上市面上常用的QSPI Flash目前以GD和Winbond的3.3v产品居多,物美价廉且采购渠道丰富,所以我特意增加了第4章用来专门介绍下GD和Winbond 3.3v QSPI Flash启动方法并修改优化了Keil下对这两家QSPI Flash的烧写算法供客户在keil下擦写和debug。
硬件上仍然是以我们官方RT1050的EVK板子为调试平台,在2.2章节的硬件改动基础上焊掉ISSI的QSPI Flash,换成GD的GD25Q32CSIG 3.3v QSPI Flash(3脚和7脚仍然上拉电阻到VDD),然后将下图中EVK板子R49的0欧姆电阻去掉,R301用0欧姆电阻短接,即将Flash电源由之前的1.8v改成3.3v,其他部分硬件与2.2章节保持一致,接下来我介绍Keil环境下的配置方法(前面的示例用的IAR,这次用keil让大家对IAR和keil下的开发配置都熟悉一遍):
(1)首先打开Keil下SDK开发包的hello_world_xip工程(路径demo_apps\hello_world_xip\mdk,Keil自带xip的工程),默认只有hello_world_xip Flexspi_nor_debug和hello_world_xip Flexspi_nor_release两个跑在板载HyperFlash的工程配置,点击Keil菜单栏Project->Manage->Project Items,我们新添加一个工程配置hello_world_xip Qspi_nor_debug如下图,然后选择该工程配置为当前工程,最后点击OK确认:
(2)在新建的工程配置里,右键工程Options->C/C++,添加QSPI_BOOT和XIP_EXTERNAL_FLASH这两个宏如下图1,然后打开工程目录下xip->fsl_flexspi_nor_flash.c文件,参考2.2章节中(2)点,手动添加QSPI的Flash Configuration Parameters元素信息如下图2,有一点不同的是我们官方板子的ISSI QSPI Flash的最高时钟可以到133MHz,而Winbond的QSPI最高到80MHz,GD QSPI最高到120MHz,我板子上自己焊了一块GD的GD32Q32CSIG QSPI Flash,所以将serialClkFreq设定为kFlexSpiSerialClk_100MHz,然后sflashA1Size改成4M Bytes;
(3)由于keil下自带了xip的工程,所以我们就直接复用其现成的.scf链接文件即可不用做修改,如下图Options->Linker。而且keil下也已经添加了Keep变量以防止xip这几个元素信息因为程序里没有调用而自动被编译器优化掉(注意:下图显示窗口有限,实际上要keep的信息段包括.boot_hdr.ivt,.boot_hdr.boot_data,.boot_hdr.dcd_data和.boot_hdr.conf,格式参考下图);
(4)至此我们编译整个工程,最后打开生成的.map文件如下图可以看到我们想要的几个QSPI启动必须的元素信息头都已经正确的分配到image文件里了;
(5)支持QSPI启动的完整image文件通过以上几步我们已经解决了,接下来要解决keil下download、Erase和debug需要的QSPI Flash烧写算法问题,本文档附带的压缩包文件里路径Firmware\Keil_Algorithm下MIMXRT_QSPIFLASH.FLM文件为我已经优化修改好的可以支持ISSI,GD和Winbond烧写的Flash算法,将其copy到keil安装路径下C:\Keil_v5\ARM\Flash即可,然后右键工程options->Debug-> CMSIS-DAP debugger,进入settings->Flash Download,点击Add找到如下图所示的MIMXRT_QSPIFLASH(4KB Sec),另外建议给Flash算法预留的RAM空间大一些否则可能会导致算法运行失败,因为这个flash算法比较占空间,我这里修改成了0x4000大小是没有问题的;
(6)设置完毕之后,我们再次重新编译整个工程,然后点击F8或者keil菜单栏Flash->Download即可完成对外部QSPI的擦除和写入,待烧写完毕之后我们点击debug即可正常调试(建议:如果程序做了修改,在编译之后进入debug之前先Download一次,再点debug),重新断电之后程序也可以。
无论是阿莫论坛还是AET,由于图片有点多,排版都比较麻烦而且整体视觉效果不太好,所以我也把PDF的文档加到随本文附带的压缩包里,另外本文提到的代码和源文件均见如下压缩包(阅读原文可见压缩包下载链接)。
「干货」FPGA设计中大位宽、高时钟频率时序问题调试经验总结
今天跟大家分享的内容很重要,也是我们调试FPGA经验的总结。随着FPGA对时序和性能的要求越来越高,高频率、大位宽的设计越来越多。在调试这些FPGA样机时,需要从写代码时就要小心谨慎,否则写出来的代码可能无法满足时序要求。另外,最近跟网友聊天时,有谈到公众号寿命的问题,我觉得网络交换FPGA公众号应该在这方面有优势,作为高校里从事FPGA开发的老师,又有兴趣做公众号的维护,不会遇到其他公众号做着做着就停更的问题,我们的公众号也许将陪伴你十年甚至几十年。同时,也请大家放心,我们今后每篇文章都将用心的去总结归纳,确保篇篇都是干货!新的一年开始了,希望在新的一年里,继续给大家分享平时遇到的FPGA调试中遇到的问题及经验,一起进步,一起努力,加油!
跨时钟域信号的约束写法
问题一: 没有对设计进行全面的约束导致综合结果异常,比如没有设置异步时钟分组,综合器对异步时钟路径进行静态时序分析导致误报时序违例。
约束文件包括三类,建议用户应该将这三类约束文件分开写在三个xdc/sdc文件中。
第一类是物理约束,它主要对设计顶层的输入输出引脚的分配约束、电平标准的约束,
如下图所示:在quartus环境下,对pcie_rstn和pcie_refclk的电平标准和管脚进行了约束。
如下图所示:在vivado环境下,对rst_n和sys_clk_PCIe_p的电平标准和管教进行了约束。
第二类是调试约束,用户在使用ila调试时,Vivado会自动生成相关ila的调试约束。
如下图所示,这是Vivado自动生成的相关ila的调试约束。
第三类是时序约束,这类约束的种类最多,它包括时钟周期约束、输入输出延迟约束、跨时钟域路径约束、多周期路径约束、伪路径约束等。
时钟周期约束:用户需要将设计中的所有时钟进行约束后,综合器才能进行合理的静态时序分析。一个设计中的时钟主要分为两类:主时钟和生成时钟。主时钟包括由全局时钟引脚接入的时钟、高速收发器的输出时钟。生成时钟包括由MMCM/PLL产生的时钟、用户逻辑分频产生的时钟,建议用户不要使用后者,因为它通常是由组合逻辑或触发器生成的时钟,这种时钟的歪斜、抖动、驱动能力都很差。对时钟进行约束时,主要针对时钟的频率、占空比、抖动、不确定性等参数进行约束。
全局时钟引脚接入的时钟约束举例:
如下图所示,在quatus环境下,对全局时钟引脚接入的时钟pcie_refclk进行了约束,因为占空比是50%,抖动和不确定性也采用默认值,所以图中只对频率进行了约束。
如下图所示,在vivado环境下,对全局时钟引脚接入的时钟sys_clk_PCIe_p进行了约束,因为占空比是50%,抖动和不确定性也采用默认值,所以图中只对频率进行了约束。
高速收发器的输出时钟约束举例:由于高速收发器通常是例化IP核来使用的,所以这种约束通常是IP核自带的。
如下图所示,在vivado环境下,PCIE IP核中对高速收发器的输出时钟进行约束。
MMCM/PLL生成时钟约束举例:
如下图所示,在quatus环境下,在sdc中加入以下命令,quatus会对所有PLL产生的时钟进行了约束。因为用户只要对PLL的输入时钟(通常情况下是主时钟)进行了约束,在sdc中加入以下命令后,quatus能够根据输入时钟和输出时钟的关系自动推断出PLL的输出时钟的时钟周期、占空比、相位关系等。
如下图所示,在vivado环境下,用户对PCIE IP核中的MMCM的输出时钟进行重命名,用户只要确保对MMCM的输入时钟(通常情况下是主时钟)进行了约束,Vivado会自动能够根据输入时钟和输出时钟的关系自动推断出PLL的输出时钟的时钟周期、占空比、相位关系等。
跨时钟域约束:在介绍跨时钟域之前,先介绍两个概念:同步时钟和异步时钟。同步时钟:当两个时钟间的相位是固定的,则可以称这两个时钟为同步时钟。一般同源,如由同一个MMCM/PLL产生的两个时钟可以称为同步时钟。异步时钟:无法判定两个时钟间相位时,则可以称这两个时钟为异步时钟。两个来自不同晶振的时钟,一定是异步时钟。通常情况下设计中不同的主时钟肯定是异步时钟。由不同的MMCM/PLL产生的两个输出时钟即使频率相同,但是由于相位关系不确定,所以也属于异步时钟。
用户想要进行跨时钟域的约束,首先需要对设计中的所有时钟进行异步时钟分组。若用户没有设置异步时钟分组,综合器在综合时会认为所有的时钟都是相关的,从而对某些源时钟与目的时钟属于异步时钟关系的路径进行了静态时序分析,由于源时钟与目的时钟的相位关系不确定,所以该路径的建立时间或保持时间必定是存在违例的。若用户设置了异步时钟分组,Vivado在时序分析时,当源时钟和目的时钟属于同一个时钟组时,才会分析此时序路径;而源时钟和目的时钟属于不同时钟组时,则会略过此时序路径的分析。
那么如何进行时钟的异步分组呢?首先要按照上面提到同步时钟和异步时钟的概念对设计中的所有时钟进行分类,属于异步时钟关系的时钟必定要划分在不同的分组中,属于同步时钟关系的时钟可以分在同一个分组也可以分在不同的分组中,如何划分要看具体情况而定。
如下图所示,在quatus环境下,对时钟进行异步时钟分组的划分,图中主要有3类时钟,
由全局时钟引脚输入的pcie_refclk,PCIE IP核中的MMCM输出的时钟(图中用通配符*表示)
Flash_pll输出的两个不同频率的时钟outclk0和outclk1。首先要确定全局时钟引脚输入的时钟和PCIE IP核中的MMCM输出的时钟以及Flash_pll输出的两个不同频率的时钟属于异步时钟关系,它们必须要划分在不同的分组中。但是,Flash_pll输出的两个不同频率的时钟outclk0和outclk1如何进行划分呢?首先,考虑一下设计中,outclk0和outclk1之间有没有进行数据交互,如果没有数据交互,那么就将这两个时钟划分在同一个时钟分组即可,图中就属于这种情况。如果有数据交互,可以分为以下三种方案进行操作:
1.将这两个时钟划分在不同的时钟分组,用户在逻辑中进行了跨时钟域处理。(推荐使用)
2.将这个时钟划分在相同的时钟分组,用户在逻辑中进行了跨时钟域处理,在xdc/sdc中添加set_flase_path(伪路径约束)禁止综合器对跨时钟域路径(通常是双触发器同步的跨时钟路径)进行静态时序分析。
3. 将这个时钟划分在相同的时钟分组,用户逻辑中不需要进行跨时钟域处理,由后端保证时序。
伪路径约束:伪路径约束主要用于以下情况:1.上文提到的对通过双触发器同步的跨时钟域路径设置伪路径。2.异步复位路径
在vivado环境下,通过以下指令将异步复位路径sys_rst_n 设置为伪路径
set_false_path-from [get_ports sys_rst_n]
在quatus环境下,通过以下指令将异步复位路径pcie_rstn 设置为伪路径
set_false_path-from [get_ports pcie_rstn] -to *
判断条件过长问题
问题二:一个always块的判断条件中的部分变量或赋值语句中的部分被赋值变量是直接由组合逻辑产生的。当组合逻辑不是特别长时或FPGA的资源利用率比较低时,这种时序问题很可能会被综合器优化处理掉。但是当组合逻辑链路过长时,尤其是大位宽的寄存器进行逻辑运算生成了较长的组合逻辑链路时,这种代码风格导致的时序问题就会比较明显,最终导致always块的输入信号的延时变大,建立时间违例。
有时序问题的代码如下图所示,req_start_addr_reg信号和PC_LAST_ADDR信号属于位宽较大的寄存器,图中有三个典型问题需要注意。
第一个需要注意的问题:PC_LAST_ADDR信号是一个组合逻辑链路的输出信号,该组合逻辑链路是由逻辑运算导致的。错误的地方在于PC_LAST_ADDR信号直接被当做always块的输入信号使用,导致always块的输入信号的延时变大,建立时间违例。正确的处理方式应该是将PC_LAST_ADDR信号打一拍后,再将打一拍后的输出信号作为always块的输入信号使用。
第二个需要注意的问题:在always块的第三个和第四个的判断条件中都是先进行了逻辑运算后再进行逻辑判断的。这个问题的本质和第一个问题相同,都是将组合逻辑的输出信号直接作为always块的输入信号使用,导致always块的输入信号的延时变大,建立时间违例。正确的处理方式应该是将组合逻辑单独拎出来,然后将组合逻辑的输出结果打一拍后,再将打一拍后的输出信号作为always块的输入信号使用。而且在always块的第三个和第四个的判断条件中使用了三段式状态机的n_state作为了判断条件,原因同上。正确的处理方式应该尽量使用c_state作为判断条件。
第三个需要注意的问题:在always块的第四个赋值语句中,将两个信号进行逻辑运算后进行赋值操作。这个问题的本质和第一个问题相同,都是将组合逻辑的输出信号直接作为always块的输入信号使用,导致always块的输入信号的延时变大,建立时间违例。正确的处理方式应该是将组合逻辑单独拎出来,然后将组合逻辑的输出结果打一拍后,再将打一拍后的输出信号作为always块的输入信号使用。
修改之后的代码如下图所示:
总结: 在编写代码时,应该注意以下三点:
1.应该尽量保证每一个always块的判断条件简洁(判断条件中尽量只进行逻辑判断,尽量避免逻辑运算)
2.应该尽量保证每一个always块的判断条件中的每一个变量都是直接来源于某个always块输出信号,尽量避免将组合逻辑的输出直接作为某个always块的判断条件的一部分,这样就可以保证每一个always块的输入信号的延时比较固定,有利于时序收敛。
3.应该尽量保证每一个always块的赋值语句中的被赋值变量都是直接来源于某个always块输出信号,尽量避免将组合逻辑的输出直接作为某个always块的赋值语句中的一部分,这样就可以保证每一个always块的输入信号的延时比较固定,有利于时序收敛。
if else嵌套层数过多
问题三 :always块中的if…else…嵌套层数过多导致综合问题
有时序问题的代码如下图所示,always块中嵌套了三层if…else…,正确的处理方式应该尽量减少always块中if…else…的嵌套层数。
修改之后的代码如下图所示,
总结: 在编写代码时,应该尽量减少always块中的if…else…嵌套层数.
逻辑信号扇出过大
问题四: 部分用户逻辑信号扇出过大,导致驱动能力不足。
有时序问题的代码如下图所示,图中三个always块产生了一组RAM的写信号,这组信号作为55个always块的输入信号,也就是说该信号驱动了55个always块,普通的信号的扇出能力一般在15-20左右。这组信号作为普通的用户逻辑,如果采用加BUFG的方式来增加驱动能力,这样不仅浪费BUFG的资源,而且BUFG会给信号加入极大的延时,这种延时对于有些高速设计来说也许是不可接受的,一般只有全局复位信号才会使用BUFG增加驱动能力。所以对于这种用户逻辑信号,推荐采用复制寄存器的方式解决驱动能力不够的问题。
修改之后的代码如下图所示:将原来的一组信号,复制了两份,总共三组信号,且三组信号的逻辑相同,每组信号驱动的always块的个数变成了原来的1/3。但是同时需要注意的问题是,这种复制的寄存器有极大的可能会被综合器当作等效寄存器优化掉,为了防止综合器多管闲事,在quatus下需要在声明被复制的寄存器前面加上(* noprune *)告知综合器此寄存器不需要优化,在vivado下需要在声明被复制的寄存器前面加上(* keep =“true”*)告知综合器此寄存器不需要优化。
总结: 在编写代码时,需要注意信号驱动能力与扇出的问题,对扇出大的用户逻辑信号进行寄存器复制解决驱动能力不足的问题。对于全局复位信号,使用加BUFG解决驱动能力不足的问题。
数据选择器过大
问题五: 用户需要使用大型的数据选择器(8选1以上的选择器)时,如果直接使用组合逻辑的case语句实现大型数据选择器,可能会导致以下问题:综合器综合速度变慢,逻辑资源占用率变大,数据选择器的相关信号时序违例会很大,数据选择器的输出结果也会极不稳定。
有时序问题的部分代码如下图所示:用户需要一个128选1的数据选择器,图中直接使用了一个组合逻辑的case语句实现了128选1。正确的处理方式应该是将一个128选1的选择器,分为3级,第一级使用了16个8选1,第二级使用了2个8选1,第三级使用了1个2选1,总共消耗3个时钟,选出最终的输出结果,注意每一级的输出结果都需要打一拍后再输入到下一级进行选择。
修改之后的部分代码如下图所示,
总结: 大型数据选择器不能直接使用组合逻辑的case语句实现,在对选择器的延时要求不是很高的情况下,最好将大型数据选择器进行分级选择的处理。
大位宽RAM数据总线约束
问题六 :高速设计中,RAM的输出或寄存器的位宽太宽时(64bit以上),可能会出现在某个时钟的上升沿时,寄存器的某些bit由于布线导致路径时延不一致,不能与其他bit的数据在同一个时钟上升沿到达,导致用户采样到数据出现错误或者采样到亚稳态。所以,用户要使用某个数据位宽很宽的寄存器时,无论这个寄存器数据来源于FIFO还是来源于用户逻辑,建议先将该寄存器打拍,然后使用打拍后的数据,这样更有利于时序收敛。
但如果从RAM里面输出的大位宽的数据总线经过打拍后仍然不稳定又该怎么办呢? 笔者在实际调试过程中发现,采用对使用大位宽总线RAM的时钟信号进行约束的方法非常有效。具体实现跟FPGA外围管脚时钟信号约束的方法一样,比如下图中在vivado工具中可以对设计中内部某个用到大位宽的RAM的时钟进行创建即可。
总结: 大位宽的数据总线需要保持数据传输过程中时延的一致性,尽可能的多采用时序逻辑,同时对于大位宽RAM的时钟要进行约束。
结束语
顺便提一下,在刚刚过去的2019年公众号分析统计的数据中,本公众号经常使用的词汇前十名排行榜如下图,从图中使用频度较高的词可以看出,本公众号关注的内容非常切合公众号的名字,同时,从中也能看出,时钟对于FPGA调试的确起着举足轻重的作用。
全文完。
相关问答
flash 帧 频率 的概念?视频就是一幅一副图片在更换,这个更换速度就是帧频率,也就是每秒播放图片的张数,flash时间轴上的每一格代表一帧,FPS越小,这个时间轴走的越慢。帧频在flas...
dell vostro bios设置对照?VostroBIOS中英文对照表System系统BIOSVersionBIOS版本Servicetag服务编号SystemInfoAssettag资产编号Ownership...
1080ti怎么超频?NVIDIA的GTX1080TI是一款性能强劲的显卡,可以通过超频进一步提高运行速度。具体操作方法如下:1.下载并安装显卡超频工具,比如MSIAfterburner、EVGAPrecis.....
为什么我的FLAShplayer总是提示我更新? - 152****9182 的回...1、当flashplayer版本低时,又有新版本发布,它就会提示更新。成功更新它后,在一段时间内就不会再提示更新了。2、这个软件更新所用的时间不会太长,...
reg52和regx52的区别?REG52和REGX52都是基于Intel8051架构的单片机,但它们之间有一些区别:1.芯片制造商不同:REG52是由STC(深圳市思特奇微电子有限公司)生产的,而REGX52是由...
ak281芯片参数?芯片详细参数如下:CPU:四核ARMCortex-A7,主频达1.3GHzGPU:ARMMali-400MP2GPU,支持OpenGLES1.1/2.0图像处理:内嵌高性能2D加速...
STC89C51单片机简述?1.STC89C51RC是采用8051核的ISP(InSystemProgramming)在系统可编程芯片,最高工作时钟频率为80MHz,片内含4KBytes的可反复擦写1000次的Flash...
arm外设特点?自带廉价的程序存储器(FLASH)和非易失的数据存储器(EEPROM)。这些存储器可多次电擦写,使程序开发实验更加方便,工作更可靠。(2)高速度,...(5)程序下载方便...
t25s16是什么芯片?25P16是ST(Numonyx)公司生产的一款存储器16M(2Mx8)SPI串行芯片。存储器容量:16Mbit内存配置:2Mx8接口类型:串行、SPI时钟频率:75MHz...
FJ是什么意思?将...FJ指的是平头结合。这是一个建筑学概念,它是将大体积的混凝土在不同的连接点上进行平和,防止出现无规则的裂缝,裂缝这种方式被称为平头。平头接合指的...