前言在全国大学生电子设计竞赛的赛场上一直流传着一条鄙视链纯软件的怕做控制的做控制的怕调模电的而调模电的最怕那些带着 FPGA 来的。当仪器仪表类、信号类题目出现高速数据采集如 20Msps 以上的 ADC、任意波形发生器DDS或等精度测频时传统单片机MCU那“一行行排队执行”的串行思维直接宣告死刑。只有拥有“真·并行计算”能力的 FPGA 能接下这招。但是很多同学初学 FPGA 时由于无法摆脱 C 语言思维写出的代码不仅消耗了海量逻辑资源还充满了毛刺和死锁。本文将带你重塑硬件思维、掌握 FPGA 三板斧、打通 MCUFPGA 联合架构让你在电赛中用 FPGA 轻松实现降维打击TOC一、 认知颠覆把 Verilog 当 C 语言写是万恶之源Verilog HDL 不是编程语言它是“硬件描述语言”你写下的每一行代码综合工具最后都会把它变成真实的导线、触发器DFF和逻辑门。 致命错误 1if-else 的滥用锁存器陷阱在 C 语言中if (A) do_B; 非常正常条件不满足就不执行。但在组合逻辑的 Verilog 中如果你写了 if 却没有写完整的 else或者 case 语句没有 default综合器会认为“条件不满足时你要保持原来的状态”。于是它会在底层的硅片上给你强行搭一个锁存器Latch后果锁存器会引入无法预测的毛刺Glitch和时序违例Timing Violation你的波形看起像被狗啃了一样。对策在 always (*) 组合逻辑中所有的 if 必须配对 else所有的变量必须赋初始值 致命错误 2阻塞与 非阻塞的混用组合逻辑没有时钟沿触发的如 always (*)必须使用 。它像 C 语言一样写在前面的先执行立刻生效。时序逻辑由时钟沿触发的如 always (posedge clk)绝对、必须使用 它代表“同时发生”。所有使用 赋值的变量都在时钟上升沿到来的那一瞬间并行更新二、 赛场实战电赛 FPGA 的“三大杀器”在电赛的四天三夜里你不需要精通极其复杂的系统级芯片SoC设计只需要熟练掌握以下三个“祖传轮子”。1. 任意波形发生器DDS 核心利用相位累加器查 ROM 表。优势在 100MHz 的时钟下你可以轻松输出频率精度达到 0.001Hz、相位完全可调的正弦波单片机的 DAC 根本望尘莫及。2. 高速 ADC/DAC 控制器电赛经常要求使用独立的高速转换芯片如 8 位 100Msps 的 AD9280 / AD9708。优势单片机读取这种并口芯片极易错位。FPGA 直接用时钟去锁存数据做到 100% 同步采进来的高频数据再存进内部 FIFO慢速交给单片机处理。3. 等精度测频模块注原理在前几期《模电与仪器仪表指南》中已讲。优势FPGA 内部极其丰富的计数器资源可以轻松同时跑两个 32 位、甚至 64 位的计数器频率测量无上限误差永远卡在 1 个时钟周期内。三、 跨时钟域CDC处理解决 99% 灵异事件的核心现象FPGA 接收按键输入或者接收外部芯片给的异步信号时状态机经常莫名其妙卡死或者跑飞原因亚稳态Metastability。当外部异步信号的变化边缘刚好撞上 FPGA 内部时钟的采样边缘时触发器无法判断它是 0 还是 1输出端会产生一个不稳定的半高电平并且在整个系统中像病毒一样传播。 黄金对策单bit信号的“两级触发器同步法”打两拍这是 FPGA 工程师刻在骨子里的代码赛场上遇到任何外部按键、单片机给的 GPIO 信号进 FPGA 的第一件事就是打两拍Verilog 标准源码片段codeVerilogmodule synchronizer ( input wire clk, // FPGA内部高速时钟 (如 50MHz) input wire rst_n, // 复位信号 input wire async_in, // 外部进来的异步信号 (比如单片机给的控制引脚) output reg sync_out // 同步后的安全信号 ); reg sync_d1; // 第一拍缓存 always (posedge clk or negedge rst_n) begin if (!rst_n) begin sync_d1 1b0; sync_out 1b0; end else begin // 异步信号先经过第一级触发器 sync_d1 async_in; // 第一级的结果再送给第二级99.99% 消除亚稳态 sync_out sync_d1; end end endmodule四、 状态机的艺术彻底告别“一段式”烂代码写状态机FSM是 FPGA 开发的核心。很多新手喜欢用一个 always 块把状态转移、判断条件、输出控制全写在一起一段式一旦修改牵一发而动全身。电赛满分规范标准“三段式”状态机Moore / MealycodeVerilog// 1. 定义状态 (强烈建议使用独热码 one-hot 编码虽然费寄存器但速度极快) parameter IDLE 3b001; parameter WORK 3b010; parameter DONE 3b100; reg [2:0] current_state, next_state; // --- 第一段时序逻辑更新当前状态 --- always (posedge clk or negedge rst_n) begin if (!rst_n) current_state IDLE; else current_state next_state; end // --- 第二段组合逻辑决定下一个状态 --- always (*) begin next_state current_state; // 默认保持当前状态防止产生锁存器 case (current_state) IDLE: if (start_flag) next_state WORK; WORK: if (work_done) next_state DONE; DONE: if (ack_flag) next_state IDLE; default: next_state IDLE; endcase end // --- 第三段时序逻辑根据状态控制输出 --- always (posedge clk or negedge rst_n) begin if (!rst_n) begin // 输出复位 adc_en 1b0; end else begin case (next_state) // 注意这里用 next_state 能让输出比状态提前一拍准备好或者用 current_state IDLE: adc_en 1b0; WORK: adc_en 1b1; DONE: adc_en 1b0; default: adc_en 1b0; endcase end end五、 王者联动MCU FPGA 黄金通信架构FPGA 算得快但不聪明STM32 算得慢但逻辑控制极强。在电赛中最完美的系统架构是FPGA 负责底层的高速 ADC/DAC 和数字信号处理如 FIR 滤波STM32 负责读取 FPGA 处理好的结果进行 OLED 显示、键盘交互和逻辑调度。它们俩怎么通信最稳1. 方案一高速 SPI 通信推荐中小数据量STM32 做硬件 SPI 主机FPGA 自己写一个 SPI 从机逻辑。避坑FPGA 的 SPI 从机一定要用高速时钟比如 50MHz去过采样OversamplingSTM32 发来的 SCK 时钟线不要直接把 SCK 当作 FPGA 的时钟沿否则极易发生时序违例2. 方案二FSMC 并口通信终极外挂大数据量极推如果你用的是 STM32F407 这种带 FSMC灵活静态存储器控制器的芯片你可以把 FPGA 伪装成一块外部的 SRAM 内存芯片做法FPGA 暴露出 16 根数据线数根地址线以及 CS片选、WR写、RD读信号线。震撼效果STM32 只要向某个指定的内存地址如 0x60000000写数据这段数据就瞬间并行传到了 FPGA 内部通信速度可以飙升到几十兆字节每秒完美解决高速 ADC 采完数据送给单片机做 FFT 时的带宽瓶颈结语Verilog 学习曲线极其陡峭但熬过了思维转换的阵痛期你会看到一片全新的天地。当你的 STM32 还在因为几个高频中断而气喘吁吁时你的 FPGA 早已在 100MHz 的时钟下风轻云淡地并行处理着成千上万的数据流。这就是硬件的魅力。赛前把 DDS、跨时钟域处理、SPI/FSMC 接口这几个轮子写好封装成 .v 文件赛场上你就是最冷酷的分数收割机预祝各位挑战 FPGA 的电赛勇士综合一秒过时序不违例板级一通电波形美如画如果这篇硬核文章对你有启发别忘了点赞 ⭐收藏写不出逻辑的时候翻出来复习一下“三段式”你在初学 FPGA 时写出过什么“奇葩”的玄学 Bug欢迎在评论区分享吐槽博主在线陪聊