第一次用安陆FPGA做图像采集项目时,我天真地以为和之前用Altera开发流程差不多。结果刚上手就被现实狠狠教育了——编译一次工程要等40分钟,这期间够我喝完两杯咖啡外加刷半小时短视频。最崩溃的是,明明Modelsim仿真完全通过的逻辑,烧录到板子上就像变了个人似的,数据流动不动就给你表演"失踪魔术"。
这时候才发现板载的ChipWatcher简直就是救命稻草。不过这个逻辑分析仪用起来也够呛:采样深度超过2048点就开始卡顿,最多只能同时抓8根总线信号。有次为了抓一个跨时钟域问题,我不得不像玩俄罗斯方块一样,反复调整信号组合才能凑出完整的调试视图。后来学乖了,关键信号都做成总线打包采样,硬是在这个"八车道收费站"的限制下完成了数据追踪。
当发现安陆的IP核库里竟然没有双口FIFO时,我的表情大概像看到手机突然不支持充电。图像传感器输出的120MHz数据流,需要用80MHz时钟处理,常规做法本该是用双口FIFO做时钟域转换。最后只能祭出"乒乓操作"大法:写了两块RAM模块轮番上阵,用状态机精确控制读写时序。这里有个细节坑——切换RAM的时机必须比数据有效窗口提前两个时钟周期,否则必定会丢帧。
更刺激的是存储器的地址位宽问题。某次调试时,1024深度的FIFO明明写满了却触发不了满标志,后来用ChipWatcher抓取才发现:wrusedw信号居然是11位宽!官方文档里小字标注"实际地址线宽度=ceil(log2(深度))+1",这个设计真是让人防不胜防。同样的问题在RAM访问时又上演了一次——当连续写入1023个数据后突然停止,读侧居然会多冒出一个0xFFFF的数据,查了三天才发现是地址计数器溢出导致的。
图像处理离不开矩阵运算,但安陆的乘法器IP让我学会了耐心。在Altera上直接A*B下一拍就能用的乘法器,在这里必须等够一个时钟周期才能出稳定结果。后来我的代码里到处都是这样的注释:
verilog复制// 安陆特供版乘法器使用规范:
// 当前周期:reg_A <= A, reg_B <= B
// 下一周期:result <= reg_A * reg_B
// 下下周期:才能使用result
最坑的是不同位宽的乘法器延迟还不一样,16x16的比8x8的要多等半个时钟周期,这个特性在时序约束文件里根本找不到说明。
外接W9825G6KH颗粒时遇到的灵异事件,完全可以写进FPGA迷惑行为大赏。相同的控制器代码,换颗IS42S16160就正常,用W9825G6KH就数据错乱。更魔幻的是:当工程里只保留SDRAM模块时调通的相位参数,随着其他模块的逐步添加又会失效。最后发现是电源噪声导致的眼图抖动,解决方法是在PCB上飞了6个去耦电容,同时把SDRAM时钟相位改成动态调整——上电时先用0度相位试读,失败就自动增加45度重试。
Modelsim里完美的时序到了板子上就崩盘,这种情况我遇到了不下十次。最典型的是图像处理流水线中的valid信号:在Altera平台习惯用组合逻辑生成,但在安陆上必须寄存器打拍才能稳定。后来总结出"安陆三原则":
每次综合布线平均45分钟的等待,让我开发出了独特的"时间管理术":把大工程拆分成多个小核验模块,先用快速编译模式验证基础功能;关键路径时序收敛后,立即生成约束文件锁定布局;最后才进行全工程编译。有个彩蛋——安陆的布线器对Verilog的generate语法支持不太友好,改用ifdef分模块反而能节省20%编译时间。
当ChipWatcher的8信号限制遇上图像处理的128位总线时,我发明了"信号轮巡调试法":给每个调试时段分配不同的信号组,像电视台节目表一样排班。比如:
配合自定义的触发条件(比如帧同步信号上升沿),硬是用"管中窥豹"的方式完成了整个图像流水线的调试。后来还发现个诀窍:把关键信号通过GPIO引出,用外接逻辑分析仪捕获,虽然麻烦但能突破8信号的限制。