ZYNQ入门日记
所谓成长,大概也是跨越曾经无法挑战的困难吧…
所以在大学失败一次,毕业后失败一次,失败了n次之后,再一次尝试学习fpga来啦…
我得说真的是丢人到爆了(
虽然很不想碰,但是还是避不开的玩意啊这东西
其实我一直还挺喜欢zynq这玩意的设计理念的,集成度高,性能强,还贼便宜(在国内环境下)
你妹小黄鱼的7010已经到了4块拆机带板的程度了…翻新好的也就七块
我买个G431还要10块呢(摔
所以重新来挑战一下吧…
屎山无边,回头是岸,努力攀登吧
参考:
- 教程系列http://www.hellofpga.com/index.php/2023/02/17/ebaz4205_source/
- 正点原子领航者zynq教程文档
- 其他网络资料、群友们和deepseek的帮助
感谢相关教程
day1 找硬件,矿板改造,装开发环境
改板子
首先我们需要一个开发板对吧
在之前某次尝试的时候凑热闹买了个咸鱼爆火的矿板,就这玩意(那时候zynq还没降价到现在这样,四五十的7010核心板已经很便宜的夸张了),一直没怎么玩,先拿来入门下吧
没去用正经的开发板也是想更多的体验下对硬件的探索,熟悉这玩意的外围电路,毕竟是想要拿来做系统的软硬件都得学习吧
另外就是群友的大作jlc开源核心板,非常的强大。这东西应该也已经很火了吧
已经在准备啦,感谢立创的免费打板,正在采购备料中…打算等熟悉一点之后拿来替换这个矿板
这里是链接:https://oshwhub.com/z_star/zynq7020-core-board-and-various-rf-modules
关于矿板,在网上找到了很不错的资源汇总教程,我也姑且先按照这个来探索一下
http://www.hellofpga.com/index.php/2023/02/17/ebaz4205_source/
先是过一遍电路图,pcb,熟悉下布局(这玩意竟然是protel的…用ad可以导入就是)
参考文章做一下改造:引出电源接口,焊jtag排针,串口排针,sd卡槽,pl端晶振
http://www.hellofpga.com/index.php/2024/02/01/ebaz4205_re/
改完长这样,调试器是某宝买的烂大街兼容品(拆开看下里面没有意外是一颗ft232,标准方案,做这么大实在是属于撑的…)
装软件
IDE的话在群友的建议下装了vivido 2018.3,比较经典的版本,bug少,轻便,占空间小(新版vivado一下就一两百G尼玛…比小而美还吓人)
网上教程一堆,不多说
然后推荐一下偶然发现的vscode插件,Digital IDE。感觉功能挺丰富的,用vsc编辑代码感觉就现代多了(
还能提供原语补全啊语法检查之类一坨东西,对vivado支持也不错
这里是官网,推荐一下:https://nc-ai.cn/
以及感谢群友的blog文章和提供的帮助
https://badboy2002.xyz/2025/03/06/note/fpga-kai-fa-gong-ju-lian/
day2 hello world and ?
LED闪灯
这里主要是熟悉下新建工程,怎么用vivado
这IDE其实还挺像人的…就选芯片型号然后加.v文件就行了
代码没啥可说的,就计数器
这里的综合和实现(是叫这个吗?)有啥区别,没搞懂
只知道必须先综合然后跑实现
让我们问下大d老师
综合完了就能看原理图了
闪个灯这么一坨东西?
然后做管脚约束,原理图里有两个LED,接一个(W13)
时钟输入接晶振(N18)
因为所有的IO都是3.3V供电,所以这里都选LVCMOS33。改一下约束然后点保存
其实约束文件长这样,也可以手写(大概
理论上还应该有时序约束。但是这个工程太简单先懒得管了
然后再综合,实现,生成bit流(好慢。。这么点工程这么慢。。)
自动连接然后右键program device
这里遇到了问题,给我报错Launch Error: Unable to launch local hw_server executable.
本来以为是下载器固件或者驱动有问题,折腾了半天
最后其实是win的防火墙把vivado的调试器服务拦了,重启电脑然后重新开vivado,允许访问网络就ok了
ok,闪灯了
就这样
看起来只用fpga其实也不用配arm那边的东西就是(但是这样没法固化引导,掉电就没了,据我所知如果要引导还算必须依赖ps那边的)
总之算是熟悉了一遍工作流吧。软件界面,创建工程,写代码,约束引脚,综合,下载。
先这样。
todo:
仿真,在线调试,固化程序。
ps:发现矿板上的美光内存可以用官网的这个工具从丝印查到型号
https://www.micron.com/sales-support/design-tools/fbga-parts-decoder
我这个D9PTK似乎是MT41K128M16JT-125这玩意 也就是256M的16bit DDR3内存
day3 PS端 AXI GPIO闪灯
然后熟悉下ps是吧
其实正常来说是应该用mio的控制吧。。这教程咋上来就用axi总线的gpio ip核了
算了试试也行
http://www.hellofpga.com/index.php/2021/08/02/zynq_ps_to_pl_led_test/
复杂一点 照着做也就好了
也算是熟悉一下开发流程
有几个点吧…
这个vivado sdk(ps的开发工具,应该是个eclipse)里面按Ctrl+s保存代码会自动重新编译(
然后就是这个例程
他这个write函数是 直接写入IO的状态寄存器,也就是你写对应位是0就是输出低,对应位是1是输出高。
并不是按照mask给对应位置高,0的位不操作…
他这个例程也是有点误导性,下面是个clear,我还以为一个是置位一个是复位
实际上clear确实是对mask标志的位进行复位(写0)
但是write不是只对mask对应位写1啊….他会把其他位也写进去啊…
结果发现另一个led也被操作了
折腾半天没明白为啥会这样(我的bit0和bit1分别是一个led)
所以你要想只操作bit0,就要手动把bit1的状态也写上保证不会被误操作(因为我led是低点亮。。。)
好吧怪我没读api手册照着例程断章取义咯(
好吧大d老师nb。。。竟然有专用的函数(
ps:关于address editior怎么开
在block design里找window菜单里面有
day4 PS GPIO方法2(EMIO) 程序固化
MIO或者EMIO其实是比较正常的GPIO用法吧…
MIO是固定的PS IO
EMIO就是用PL做IO的mux,随便定义IO接在哪个pin
所以MIO的IO号是0~53,EMIO的号是54~117
这里是AXI的GPIO和EMIO的区别
这篇文章不错:https://adaptivesupport.amd.com/s/article/1012484?language=zh_CN
程序固化
zynq的程序固化必须依赖ps(从ps的fsbl也就是主boot loader引导再加载bit文件)
所以必须建一个fsbl工程
然后就是实测必须改nand的时序参数(可能是矿板这块8位nand的特殊吧)不然能烧录但是没法引导起来
ps:改完vivado的配置之后,需要重新综合,实现,生成bit流,导出硬件描述文件,开sdk,重新编译。。
第一次开sdk好像还可能挂,还得重开一遍
总之是相当的麻烦
难道ps和pl联调时候pl改一点都得这么鼓捣一遍吗(干
还有就是下载的话必须先跳线到jtag模式,烧录,然后再关机,跳线到nand模式,再开机…不进jtag模式还没法烧录。难绷。。。
pss:小寄巧
这个向导好像也可以用来创建管脚约束(另一个方法是综合完开sch然后点那些io,在下面的边栏选按结构显示,然后一个个改)
这个办法可以更快自动生成boot(不用手动去加三个文件)
todo:
sd卡启动,网卡
day5 sd卡启动 uart 工程结构和添加verilog文件
先说下sd卡启动
其实没啥可说的
我折腾了一晚上纯粹是因为sd卡不兼容 两个看着一样的卡,就是有一个不能用…
鼓捣了半天boot模式,外设配置啥的
另外必须用MIO40~45这一组SD接口去启动,格式化成fat32
串口,也没啥可说的,默认的波特率是115200。默认用哪个串口print可以在这里直接改(如果你有两个串口)
他几个print xil_print和printf,标准的printf是支持的功能最全的。
如果你用printf,其实可以不用写初始化函数啥的,自动都给你做好了
但是如果你要调用他的底层函数,配中断啥的,那还得初始化一遍就是了。
然后我就突然想看下这vivado生成出来的是个什么东西
深坑似海啊…
首先他自动生成的顶层模块,这里面的暴露的接口就是在约束文件里写的接口
连接到下一层(也就是block design那一层)
然后这个design1就是block design生成的verilog文件
里面把上一层的模块连接到ps核里
这个就是ps核的例化
再下层,里面都是ps核里的axi什么乱七八糟的描述,看不懂,不看了
总之大概是这么个层次吧。
那么我们知道,zynq是没法单独启动pl(fpga部分)的,必须用ps引导pl
所以我直接开个.v文件,写点自己的逻辑,然后综合进去,像day2那样,是可以跑,但是没法固化,只能用编译器下载到内存里(只能调试用)
如果想要启动,就必须加一个ps核,然后用ps的启动镜像(包含fsbl)来引导加载pl的bitstream文件
于是我就想,如果我ps不做事,只用来引导pl,我的pl怎么做呢?
首先必须用block design建立一个ps核,然后配好ddr和nand(或者sd卡),这还是跑不掉,不然fsbl引导不了,下面的都走不下去。
然后直接把.v添加到工程里,再给拖进block design里…然后手动给这个模块的输入输出端口右键make external创建连接,然后综合,添加约束…这样竟然是ok的((((
于是他就变成了这样,ps和pl没什么关系,但是是可以这么跑的…
原来zynq是这种奇妙的结构吗…真是绝了
然后我们发现vivado自动生成了一大堆东西,把这个加进去的模块给放在了整个顶层设计里,并且引出端口…
真奇妙啊,zynq
todo:
- 串口中断接收
- 网口测试
day6 串口接收 网络测试
小寄巧:
- layout——io planning可以看到器件的IO封装和IO分配 方便改
- 在sdk里给project-build automatically关了就不会自动编译了
- 记得给编码改成utf-8 不然很容易中文乱码
关于串口
接收还是用中断实现比较好
zynq的中断真的一大坨…照着bsp的串口中断例程一顿抄,鼓捣一下午,算是能用了
就说几个点吧
- 初始化中断时候有两个设置中断handler的地方,一个xscugic的connect,一个uartps的sethandler。这俩玩意一个是默认连接到库自带的底层接收中断处理判断,一个是调用自己实现的应用中断callback(前者默认的函数里会自动判断并调用后面set的用户callback函数)
- 接收就用自带的XUartPs_Recv函数就可以,这个玩意会自动处理中断判断啥的,并且在接受满buffer或者超时之后调用户回调。这个是一直运行的,一次调用之后会一直往buffer里读(除非把中断关了)
- 如果想手动清空buffer,用memset之后要手动重新调用一遍上面的recv函数,不然不会重置uart库内部的接收buffer的指针,buffer会乱掉
- 必须设置中断异常处理并enable,否则中断不生效
关于网口:
用ps自带的lwip库的例程做了个测试
这玩意是可以自动dhcp的。但是矿板默认的那个phy似乎只能自动协商到10M 很奇怪。不太搞得懂
pl那边默认的eth phy是8位(千兆)的,如果连矿板的百兆口,需要手动给8位裁剪到4位,网上教程有用Concat ip核的也有自己写.v文件做assign的,反正都能用(
我用的concat比较简单
win的telnet真难用(
跑去wsl用linux的好用多了\\\\\
day7 自定义ip核 仿真
今天有点懒
没学啥东西
学着建了个最简单的axi4 ip核(1个寄存器,控制一个led灯)
本身很简单 难度似乎全都在vivado的操作上大概。。
然后基本的仿真,也没啥难度
看了几篇zynq不用ddr的教程,感觉什么xip啊,在l2上跑程序,还是很难
但是最基础的,用256k的ocm,把fsbl跑在前192k,用户程序用后64k,这个很简单
问题就是内存太小了,试试吧,如果能用的话。。至少能当个fpga用不是吗
至少这东西实在是太便宜了(
day8 失败了
鼓捣no ddr启动半天失败了
拼尽全力无法战胜
试了各种方法吧
最后去读fsbl代码和调试输出信息
都已经识别启动设备,并且加载镜像了,在加载pl的bitstream的时候挂掉了
看起来用nand flash和sd卡好像都不行(代码里有向ddr缓存数据,挂掉了)
可能必须按照教程用qspi的flash吧。手头没硬件,暂时放弃了
实际来说,意义也比较小就是了,256k的内置ram,fsbl要占据192k,剩下的64k还要加载程序代码进去,可用空间太小了,能跑的东西很有限
做xip或者裁剪省空间啥的,难度有点高,我又搞不定。可能实际意义就是当纯pl fpga用吧…或许
挫折 去鼓捣焊板子去了,明天再搞
day9 pl-ps 简单的频率计
感觉也学了点东西了,试着实现一个小玩意
之前用stm32做等精度频率计,实际上写出来因为中断触发代码执行之类的没那么快,不是特别稳
这东西还是适合用fpga写,又比较简单,于是拿来测试下。
初学verilog,写的非常的蛋疼,照着大d老师写三段式状态机,做axi ip核给ps写寄存器,封装ip,仿真。。
vivado还没法在ip里仿真,必须塞到工程里仿真(得手动给顶层模块改成tb文件,不然好像仿不出来,不知道是不是我的问题)
仿真的差不多,上板子。。我日出来的都是0
这下瞎了
两眼一抹黑
开始迷茫:是我硬件连接有问题,信号没进去,还是逻辑部分没跑起来,还是ps读axi的部分有问题?
按理说,这里应该用ila,但是我还没学ila…查吧
网上给的ila用法基本都是在block里加ila的ip或者自动添加啥的,但是这样加没法看到ip核里面的逻辑信号
比如这样,因为看不到ip核里面
也先试了下,至少证明信号输入进去了…但是里面有没有正确运转呢。。不知道
于是试着用群友提供的另一个方法 参考https://badboy2002.xyz/2025/03/06/note/fpga-kai-fa-gong-ju-lian/
在ip核代码里加调试信息,然后更新ip核,重新综合
综合完了在这里加ila
这样就能看ip核内部的信号了(但是这种ila不会在block里显示)如果要删除,需要手动还是选上面这个然后disconnect
重新跑一遍实现生成啥的,再导出硬件,进sdk然后重新编译下载(真的慢。。)
跑起来之后进vivado点自动连接然后就自动跳出ila了
用ila看了下,行。输出寄存器都是0
逻辑没跑起来
为啥呢,试着加触发看状态转移,抓不到,折腾半天,还以为是ila有问题
最后发现,确实是状态一直没跳转,自然抓不到,抓计数器的跳转瞬间能看到,应该已经执行了跳转的代码,但是实际上状态没跳。为啥呢?百思不得解
无奈只能求助万能的群友了…然后…
果然是我代码写的太屎了
多驱动了,然后综合器又没报错,状态锁死了
因为偷懒 状态机写的不对,多个块里给next state赋值,没有给状态转移专门用组合逻辑实现,所以炸了
fpga真神奇啊…verilog真难啊(哭
明天再改 待续
day10 重构
学习了正确的状态机写法,重构中…
因为if没有写else又炸了好几次,谷歌查报错才明白…
组合逻辑不写else就会在生成bitstream阶段报LUT cells form a combinatorial loop. 然后炸掉
改来改去总算能过了,跑一下
通一个1k信号进去,噢噢噢噢,跑起来啦!
频率也挺稳的,试了下从50到20M都问题不大(我的参考频率是50M)
开心
歇 感谢群友们在线支持 感谢相关资料参考
verilog博大精深,任重道远啊。
day ?11? 一波三折的核心板 ddr测试 qspi启动
这几天没怎么搞zynq 去调别的东西了
那么还记得最开始提的那个核心板吗…料也差不多了,焊一下
立创eda自带的ibom功能倒是也不错
为了防止电源焊错boom 于是先焊了外围元件没焊zynq本体(但是其实可以通过不焊穿心电容来隔离电源…我没仔细看电路图) 这也导致后面焊zynq的时候底面有元件,用加热台很难正确的给板子预热
不过确实第一次焊电源出了点问题,fb电阻焊错导致有一路输出不正确。。倒是也不算多虑吧
然后就干bga,焊zynq和ddr,ddr是立创买的全新,7010来自淘宝7.5的拆机翻新植球
因为预热不充分,焊的有点折磨,然后接上电。。果不其然就下载不进去
用万用表戳了下jtag接口的二极管值,发现tck是开路
得了,焊接有问题
重新加热吹一下没改善,无奈只能拆了,清理焊盘,换了另一片芯片上去
尝试改进一下焊接手法,提高了温度,多加助焊剂,这次jtag二极管值正常了,也能连接识别了
尝试烧个led进去测试下,发现这个核心板pl时钟接错了 接到了差分时钟n上(=-= 正常约束引脚走不过去实现,不过通过强制覆盖规则的方法倒是也能通过就是了(或者还是用ps提供时钟吧也行。。
嗯,总之pl ok了(那根绿线,这个板子jtag没引出vref,下载器不接vref会下载失败)
然后ps 也是emio 点灯
烧进去没反应,尝试调试,发现一直卡住(进错误处理了?
怀疑ddr有问题,那就跑下ddr测试吧
很简单,开ddr和串口,然后在sdk里创建一个dram test的模板工程就行了
直接编译下载
用的ddr是256M 16bit也就是512M的,选511M的测试,也就是输入5然后跑一下
日 全是误码,果然炸了
问了下群友基本上来说肯定是ddr焊接有问题了
把ddr拆了(这玩意自带高温锡,本身就贼难焊…)
完蛋,这锡珠很明显是连锡短路了
然后这ddr就折磨了我两三个小时,来回来去的拆和装,新芯片用完了只能重新植球,板子上开关和led都吹变色了
植球中…
最后放一个焊好的吧
然后用r和i指令可以测试读写眼图
这个眼宽还ok吧
重新烧录用户程序,可以正常跑起来了…真是一波三折啊
实际上来说,这玩意难度基本就在焊接bga上了,这种大的bga多层板还是需要良好的预热,加大量助焊剂,充分吹透,然后不要去下压芯片,会连锡…
然后洗洗板子,试一下qspi flash启动
没啥难度,就烧录flash然后切对启动模式就ok了 很简单,顺利
day12 点屏 lvgl移植
断断续续的学习吧
上周去了个慕展,很开心,回来继续赤石
给核心板顺手画了个转接板,把io都引出成pmod格式,jlc打了一下,还挺合适的
试试用ps刷个屏,最经典的320*240的st7789 来自中景园
参考教程 没啥难度
http://www.hellofpga.com/index.php/2022/04/08/zynq_ps_spi_lcd_blackboard/
改了下硬件spi的分频系数,166 div4大概是42M的SPI时钟速率,用示波器实测是对应的,跑的也没问题
但是这个阻塞的一个一个像素依次刷新实在效率有点低,用示波器看大概只有25%时间在传输,剩下时间都在跑代码,实际速率很低,而且阻塞其他程序运行。测下来320*240的屏完整刷新一次要120ms左右,有点慢的难以接受了
于是寻找连续传输的方法,最好的是用dma,但是查了下没找到spi dma的资料,似乎并不好实现…
当然另外的方案是用ps pl共享ddr buffer,使用vdma传输到pl,在pl端实现spi流式刷新…但是我懒,鼓捣这一套看起来很复杂
所以还是用另一种吧,读了下ps的spi库,是支持中断非阻塞传输的, 类似之前调过的串口一样的做法,中断服务程序会自动处理长buffer的传输,并且填充fifo来提高传输效率
按照sdk提供的例程稍微改了下,变成了这样
1 | int SpiSetupIntrSystem(XScuGic *IntcInstancePtr, |
然后开一块320x240x2(16bit颜色)的buffer,填满之后扔进去刷,改成这样
这样就不阻塞了,中断服务程序会自动在后台处理完传输,实测速度大概在30M左右(当然还是会有一部分空闲时间。。但是已经优化很多了)
完整刷一帧的时间大概是38ms左右 可以接受很多了
另外要注意的就是7789的字节序似乎是大端的。。也就是16bit颜色的高8位要放在低内存地址先传输(spi传输是按照内存地址从低往高) 不然颜色会乱掉
然后尝试移植了下lvgl,用的8.3.10(因为nxp的gui工具推荐这个版本,最新版是9.2.2)
http://www.hellofpga.com/index.php/2025/01/01/lvgl_demo_01/
这个教程写的很棒,基本上按照他来改就行了
只说下遇到的几个问题吧
首先还是端序的问题,lvgl里比较好处理,直接开反转的宏定义就行了
然后就是tick,因为lvgl依赖一个1ms的系统tick,所以要开一个定时器,我这里调了好久调试发现定时器进不去,最后问题定位是发现,如果你初始化两个中断,初始化时候必须传入同一个中断控制器实例变量,不然就会互相覆盖,导致只有一个中断起效….
buffer这块,因为zynq内存多的是,就直接开了完整的双缓冲,再加上spi的传输buffer,相当于有三个buffer(可能可以优化,但是我懒得想了)
需要自定义的刷屏代码,很简单
这个color_t实际上就是uint16的联合体,2个字节,所以直接memcpy扔到uint8的buffer里是没有问题的
最后效果就是这样了
因为帧率限制是30,所以就这样。cpu占用很低
我这个屏没有触摸,也就没有做相关的接口,所以没法交互了,看看得了(
实际上这里还有个坑我很久的点是。。不好意思说出来
lvgl里定义的屏宽和高,千万别反了(横屏和竖屏)
不然就全变乱码了…我最开始就是写反了(摔