尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FPGA 新人最容易卡住的 10 个问题:从仿真到上板

FPGA 新人最容易卡住的 10 个问题:从仿真到上板 前言很多 FPGA 新人在刚入门时都会遇到同一个问题仿真波形看着没问题综合也没报错bit 文件也能生成但一上板就是不工作。这类问题很常见尤其是对应届生、研二研三学生、刚入行不到一年的 FPGA 新人工程师来说最容易卡住的地方往往不是 Verilog 语法而是没有建立完整的 FPGA 工程思维。FPGA 开发不是“写完 RTL 就结束”而是一条完整链路需求理解 → RTL 设计 → Testbench 仿真 → 综合 → 实现 → 时序分析 → 约束检查 → 上板调试 → 问题定位这篇文章不讲花哨概念只整理 FPGA 新人最容易踩的 10 个坑。每个问题都按下面几个角度展开问题现象 核心概念 排查步骤 常见误区 面试延伸 总结适合三类读者FPGA 应届生研二、研三准备找 FPGA 岗位的同学入行 1 年以内的新人工程师一、把 Verilog 当 C 语言写1. 问题现象很多新人第一次写 Verilog会自然地按照 C 语言思维去写a b; b c; c a;然后以为代码会一行一行顺序执行。实际仿真时就会发现信号变化顺序和自己想的不一样多个always块之间的执行关系搞不清明明代码写在前面结果波形里并不是先发生一个变量在多个地方赋值综合直接报错或者结果异常。2. 核心概念Verilog 描述的是硬件电路不是软件程序。C 语言描述的是“CPU 按顺序执行指令”。Verilog 描述的是“电路在同一时间并行工作”。所以在 FPGA 里下面这些逻辑是同时存在的always (posedge clk) begin cnt cnt 1b1; end always (posedge clk) begin data cnt; end这两个always块不是谁先谁后而是都在同一个时钟沿触发。新人一定要先建立一个意识写 RTL 之前先想电路结构再写代码。3. 排查步骤如果发现仿真结果和预期不一致可以按下面顺序查第一步判断这段逻辑是组合逻辑还是时序逻辑。 第二步组合逻辑使用 assign 或 always (*)。 第三步时序逻辑使用 always (posedge clk)。 第四步检查同一个信号是否被多个 always 块赋值。 第五步看波形时不要只看输出也要看中间寄存器和状态信号。4. 常见误区误区 1以为 always 块按代码顺序执行这是最典型的 C 语言思维。多个always块在硬件上是并行结构不是软件里的多个函数调用。误区 2以为 if-else 就是软件流程控制在 Verilog 里if-else最后会综合成选择器、寄存器使能或者状态跳转逻辑不是 CPU 执行的分支语句。误区 3一个 reg 可以在多个 always 块里赋值比如always (posedge clk) begin a b; end always (posedge clk) begin a c; end这种写法通常是不允许的因为硬件上等于两个逻辑都想驱动同一个寄存器。5. 面试延伸面试官很喜欢问1. Verilog 是并行执行还是串行执行 2. always 块之间有没有执行先后顺序 3. reg 类型是不是一定综合成寄存器 4. 一个信号能不能在多个 always 块里赋值回答时不要只背概念可以这样说Verilog 描述的是硬件结构。多个 always 块本质上对应多块并行电路不存在软件意义上的先后执行顺序。是否综合成寄存器要看赋值所在的过程块和敏感列表而不是只看 reg 关键字。6. 总结FPGA 新人第一关不是语法而是思维转换。不要问“这几行代码怎么执行”而要问这几行代码最后会综合成什么电路二、阻塞赋值和非阻塞赋值乱用1. 问题现象阻塞赋值和非阻塞赋值是新人最容易混淆的地方。典型问题包括仿真结果和预期不一致寄存器级联延迟不对同一段代码换一种写法后波形变化很大时序逻辑中用了大量阻塞赋值后期很难维护。比如下面两段代码仿真行为就不一样。写法一always (posedge clk) begin a b; c a; end写法二always (posedge clk) begin a b; c a; end第二种写法中c拿到的是上一个周期的a这才更符合寄存器同时更新的硬件行为。2. 核心概念基本规则先记住组合逻辑优先使用阻塞赋值 时序逻辑优先使用非阻塞赋值 原因很简单组合逻辑强调当前输入立即决定当前输出时序逻辑强调所有寄存器在同一个时钟沿同时更新。3. 排查步骤检查代码时可以按下面方法第一步搜索所有 always (posedge clk)。 第二步检查里面是否主要使用 。 第三步搜索所有 always (*)。 第四步检查里面是否主要使用 。 第五步如果一个 always 块里混用了 和 重点确认是否有明确目的。 第六步看波形时重点观察寄存器之间是否存在一个时钟周期的延迟。4. 常见误区误区 1觉得和只是写法习惯不是。它们在仿真调度语义上有区别尤其在时序逻辑中影响很大。误区 2时序逻辑大量使用有些代码在简单场景下仿真能过但稍微复杂一点就容易产生理解偏差。误区 3看到别人混用就跟着混用工程代码中确实存在特殊写法但新人阶段不建议一上来就模仿。先把基本规范写扎实// 组合逻辑 always (*) begin y a b; end // 时序逻辑 always (posedge clk or negedge rst_n) begin if (!rst_n) y_reg 1b0; else y_reg y; end5. 面试延伸常见面试问题1. 阻塞赋值和非阻塞赋值有什么区别 2. 为什么时序逻辑建议用非阻塞赋值 3. 两级寄存器用阻塞赋值和非阻塞赋值有什么不同建议回答时结合例子不要只说“阻塞是顺序执行非阻塞是并行执行”。更准确的表达是阻塞赋值会立即更新左值后续语句使用更新后的值非阻塞赋值会在当前时间步末尾统一更新比较符合寄存器在同一时钟沿同时更新的硬件行为。6. 总结新人阶段先记住一句话组合逻辑用 时序逻辑用 三、复位没处理好1. 问题现象复位问题非常常见而且上板后很隐蔽。常见现象有仿真开始时大量信号是X上板后有时候正常有时候异常按下复位键后系统没有回到初始状态状态机跑飞后无法恢复第一次下载后不工作重新下载几次偶尔又好了。2. 核心概念复位的作用是让电路进入确定状态。常见复位类型有同步复位 异步复位 高电平有效复位 低电平有效复位例如低电平异步复位always (posedge clk or negedge rst_n) begin if (!rst_n) cnt 8d0; else cnt cnt 1b1; end低电平同步复位always (posedge clk) begin if (!rst_n) cnt 8d0; else cnt cnt 1b1; end异步复位的特点是复位立即生效不依赖时钟。同步复位的特点是复位只在时钟沿生效。3. 排查步骤复位问题可以按下面顺序排查第一步检查关键寄存器有没有复位值。 第二步确认复位有效电平和代码一致。 第三步确认复位信号是否真的连到了 FPGA 管脚。 第四步检查约束文件里的复位管脚是否正确。 第五步检查异步复位释放时是否同步到对应时钟域。 第六步仿真时先拉住复位几个周期再释放复位。 第七步上板后用 ILA 抓复位信号确认 FPGA 内部看到的复位是否正确。4. 常见误区误区 1以为寄存器上电一定是 0有些 FPGA 支持配置初值但工程里不能随便假设所有寄存器上电后都可靠为 0。关键控制寄存器、状态机、计数器最好明确复位。误区 2复位按钮按下去就一定有效复位按钮可能是低有效也可能是高有效。还可能存在按键抖动。也可能原理图上经过了反相器。不能凭感觉写if (rst)要对照板卡原理图确认。误区 3异步复位释放不做同步异步复位拉低可以异步但释放时最好同步到对应时钟域。常见做法是reg rst_sync_1; reg rst_sync_2; always (posedge clk or negedge rst_n) begin if (!rst_n) begin rst_sync_1 1b0; rst_sync_2 1b0; end else begin rst_sync_1 1b1; rst_sync_2 rst_sync_1; end end wire rst_n_sync rst_sync_2;5. 面试延伸常见面试问题1. 同步复位和异步复位有什么区别 2. 异步复位为什么释放时要同步 3. FPGA 上电后寄存器初值是否可靠 4. 状态机是否必须加复位可以这样回答异步复位的优势是可以不依赖时钟立即复位但释放复位时如果刚好靠近时钟沿可能带来亚稳态或恢复时间问题。因此工程中常用“异步复位、同步释放”的方式。6. 总结复位不是随手加一个rst_n。真正要确认的是复位电平对不对 复位管脚对不对 复位释放是否安全 关键寄存器是否进入确定状态四、时钟没搞清楚1. 问题现象时钟问题是“仿真能跑上板不行”的高频原因。常见现象LED 闪烁频率不对串口波特率不对SPI/I2C 通信失败VGA/HDMI 显示异常逻辑在仿真中正常上板后速度完全不对PLL 输出没锁定后级逻辑一直不工作。2. 核心概念FPGA 是强时钟驱动的器件。只要时钟频率错了下面这些都会错计数器 分频器 定时器 串口波特率 SPI 时钟 I2C 时钟 视频像素时钟 状态机等待周期例如 50 MHz 时钟下产生 1 秒计数localparam CNT_MAX 50_000_000 - 1;3. 排查步骤时钟问题建议这样查第一步确认开发板输入晶振频率。 第二步确认 FPGA 工程中时钟管脚绑定是否正确。 第三步确认 PLL/MMCM 输入频率配置是否正确。 第四步确认 PLL/MMCM 输出频率是否符合设计需求。 第五步检查时钟约束 create_clock 是否正确。 第六步检查复位是否等待 PLL locked 信号。 第七步上板后用 ILA 或外部仪器观察分频信号是否符合预期。一个常见写法是用 PLL 的locked信号参与复位assign sys_rst_n ext_rst_n pll_locked;这样可以避免 PLL 没锁定时后级逻辑提前乱跑。4. 常见误区误区 1以为所有开发板都是 50 MHz不同板卡差别很大。有的是 50 MHz有的是 100 MHz有的是 125 MHz还有的通过差分时钟输入。误区 2改了 PLL但忘记改参数比如串口波特率发生器还按旧时钟计算。localparam BAUD_CNT_MAX CLK_FREQ / BAUD_RATE;如果CLK_FREQ没更新串口就会乱码。误区 3只写 RTL不写时钟约束没有时钟约束工具不知道设计目标频率也就无法正确分析时序。5. 面试延伸常见面试问题1. FPGA 中为什么要做时钟约束 2. PLL/MMCM 的作用是什么 3. 如何产生一个 1 Hz 的 LED 闪烁信号 4. 逻辑时钟和使能信号有什么区别面试中不要只说“PLL 用来分频倍频”可以扩展PLL/MMCM 不仅可以产生不同频率的时钟也可以调整相位、改善时钟分配。工程中还要关注 locked 信号避免时钟未稳定时逻辑提前工作。6. 总结很多 FPGA 问题表面看是逻辑错实际是时钟错。新人调试时可以先问自己我的板卡真实输入时钟是多少 我的 PLL 输出是多少 我的约束文件写的是多少 我的参数计算用的是多少这四个数必须一致。五、跨时钟域处理不对1. 问题现象跨时钟域问题也就是 CDC 问题是 FPGA 新人最容易忽略、也是最难定位的问题之一。典型现象仿真完全正常上板后偶发错误数据偶尔丢状态机偶尔跳错某个信号大多数时候正常偶尔异常降低频率后问题变少但没有彻底消失。CDC 问题最麻烦的地方是它不是稳定复现而是偶发。2. 核心概念不同时钟域之间直接传信号可能导致亚稳态。常见处理方式单 bit 电平信号两级寄存器同步 单 bit 脉冲信号脉冲展宽、脉冲同步或握手 多 bit 数据异步 FIFO 控制命令握手机制 高速数据流异步 FIFO 或专用 CDC IP单 bit 信号两级同步示例reg sig_d1; reg sig_d2; always (posedge clk_b or negedge rst_n) begin if (!rst_n) begin sig_d1 1b0; sig_d2 1b0; end else begin sig_d1 sig_a; sig_d2 sig_d1; end end wire sig_b sig_d2;3. 排查步骤CDC 问题可以按下面方法排查第一步列出工程中所有时钟。 第二步确认每个模块属于哪个时钟域。 第三步查找跨时钟域传输的信号。 第四步单 bit 慢速控制信号使用两级同步。 第五步脉冲信号确认目标时钟域能采到。 第六步多 bit 数据不要简单打两拍。 第七步数据流跨时钟域优先使用异步 FIFO。 第八步检查时序报告中是否存在未正确约束的跨时钟路径。4. 常见误区误区 1仿真没问题就说明 CDC 没问题普通 RTL 仿真通常不会真实反映亚稳态。所以 CDC 问题很可能仿真看不到上板才出现。误区 2多 bit 总线直接打两拍例如data_d1 data_a; data_d2 data_d1;如果data_a是多 bit 总线这样可能导致不同 bit 在不同周期被采到形成错误数据。误区 3异步输入直接进状态机比如外部按键、外部中断、外部数据有效信号直接送进状态机很容易造成异常跳转。5. 面试延伸常见面试问题1. 什么是亚稳态 2. 两级同步器能不能彻底消除亚稳态 3. 多 bit 数据跨时钟域怎么处理 4. 异步 FIFO 为什么能做跨时钟域 5. 格雷码在异步 FIFO 中有什么作用比较稳妥的回答两级同步器不能从理论上彻底消除亚稳态但可以显著降低亚稳态传播到后级逻辑的概率。它适合单 bit 慢速控制信号不适合多 bit 数据总线。多 bit 数据跨时钟域通常使用异步 FIFO 或握手机制。6. 总结CDC 问题不要靠运气。新人写代码时要养成习惯只要看到两个时钟域就先想 CDC。 只要跨时钟域传数据就先想同步方式。 只要是多 bit 数据就不要简单打两拍。六、组合逻辑写出锁存器1. 问题现象综合报告里出现 latch。仿真时某些信号会保持旧值。某些分支下输出没有更新。上板后表现不稳定。比如下面这段代码always (*) begin if (sel) y a; end当sel 0时y没有赋值。工具为了让y保持原值就可能综合出锁存器。2. 核心概念组合逻辑必须在所有条件下都有明确输出。也就是说任何输入组合下输出都必须有确定赋值。正确写法之一always (*) begin if (sel) y a; else y b; end或者先给默认值always (*) begin y b; if (sel) y a; end3. 排查步骤如果综合报告里出现 latch可以这样查第一步搜索综合报告中的 latch 关键字。 第二步定位对应信号。 第三步检查该信号是否在 always (*) 中赋值。 第四步检查 if-else 是否完整。 第五步检查 case 是否有 default。 第六步在组合逻辑开头给默认值。 第七步重新综合确认 latch 消失。4. 常见误区误区 1以为没赋值就是默认 0不是。组合逻辑中某个分支没赋值通常表示保持原值。而保持原值就需要存储结构可能推断出 latch。误区 2case 不写 default例如always (*) begin case (state) IDLE: y 1b0; WORK: y 1b1; endcase end如果state进入其他值y没有明确赋值。建议写always (*) begin y 1b0; case (state) IDLE: y 1b0; WORK: y 1b1; default: y 1b0; endcase end误区 3忽略综合 warning很多新人只看 error不看 warning。但 latch、未约束时钟、时序违例、未使用信号很多一开始都是 warning。5. 面试延伸常见面试问题1. 什么情况下会综合出 latch 2. latch 和 flip-flop 有什么区别 3. 如何避免组合逻辑产生 latch 4. always (*) 中为什么要给默认值可以这样回答在组合逻辑中如果某些条件分支没有对输出赋值综合工具为了保持输出原值可能推断出 latch。避免 latch 的常见方式是覆盖所有 if-else/case 分支或者在组合逻辑开头给输出默认值。6. 总结组合逻辑的底线是所有条件下输出都有明确值。看到 latch 不要急着上板先把原因查清楚。七、状态机写得乱1. 问题现象状态机是 FPGA 设计中最常见的结构也是新人最容易写乱的结构。常见问题包括状态机卡死状态跳转不符合预期状态命名混乱一个状态里塞了太多逻辑没有 default 分支复位后状态不确定上板后偶尔进入异常状态。2. 核心概念状态机本质上就是当前状态 输入条件 → 下一状态 当前状态 输入条件 → 输出常见写法一段式状态机 两段式状态机 三段式状态机新人建议优先掌握两段式或三段式结构更清晰。典型三段式结构// 第一段状态寄存器 always (posedge clk or negedge rst_n) begin if (!rst_n) cur_state IDLE; else cur_state next_state; end // 第二段状态转移逻辑 always (*) begin next_state cur_state; case (cur_state) IDLE: begin if (start) next_state WORK; end WORK: begin if (done) next_state DONE; end DONE: begin next_state IDLE; end default: begin next_state IDLE; end endcase end // 第三段输出逻辑 always (posedge clk or negedge rst_n) begin if (!rst_n) valid 1b0; else begin case (next_state) WORK: valid 1b1; default: valid 1b0; endcase end end3. 排查步骤状态机异常时不要只看输出按下面顺序查第一步确认复位后进入哪个状态。 第二步画出状态转移图。 第三步检查每个状态是否都有退出条件。 第四步检查状态跳转条件是否只持续一个周期。 第五步检查 case 是否有 default。 第六步检查异常状态能否回到 IDLE。 第七步仿真时同时观察 cur_state、next_state、输入条件和输出信号。4. 常见误区误区 1不画状态图直接写代码状态少的时候还能看状态一多就乱。建议先画类似这样的状态图IDLE → CONFIG → WORK → WAIT → DONE → IDLE每条箭头旁边标清楚跳转条件。误区 2状态命名随意不要写S0, S1, S2, S3除非状态很简单。更推荐写IDLE, INIT, READ, WRITE, WAIT_DONE, ERROR状态名本身就是注释。误区 3没有异常恢复如果状态机因为干扰或设计问题进入未知状态没有 default 分支可能一直卡住。5. 面试延伸常见面试问题1. 一段式、两段式、三段式状态机有什么区别 2. Moore 型和 Mealy 型状态机有什么区别 3. 如何避免状态机跑飞 4. 状态机编码方式有哪些可以这样回答Moore 型状态机的输出只和当前状态有关输出更稳定Mealy 型状态机的输出和当前状态、输入都有关响应更快但组合路径可能更复杂。工程中要根据接口时序和稳定性要求选择。6. 总结状态机不要写成一大坨if-else。推荐流程是先画状态图 再定义状态 再写状态跳转 最后写输出逻辑八、仿真只看结果不看过程1. 问题现象很多新人写 Testbench 时只会简单给几个输入然后看最终输出对不对。常见问题输出错了但不知道从哪里开始错Testbench 只测正常流程不测异常流程仿真时间太短模块还没开始工作就结束了波形里信号很多但不知道该看什么没有检查状态机、计数器、valid/ready 等关键信号。2. 核心概念仿真的目的不是证明代码“能跑”而是定位逻辑是否符合预期。一个有效仿真至少要关注时钟是否正确 复位是否正确 输入激励是否正确 状态跳转是否正确 计数器边界是否正确 握手信号是否正确 输出延迟是否符合预期 异常情况是否被覆盖3. 排查步骤建议按照下面顺序看波形第一步看 clk 是否正常翻转。 第二步看 rst_n 是否先复位再释放。 第三步看输入激励是否真的送进 DUT。 第四步看状态机是否按预期跳转。 第五步看计数器是否到达边界。 第六步看 valid、ready、done 等控制信号。 第七步最后再看输出数据。一个简单 Testbench 框架initial begin clk 1b0; forever #10 clk ~clk; // 50MHz end initial begin rst_n 1b0; start 1b0; #100; rst_n 1b1; #100; start 1b1; #20; start 1b0; #5000; $stop; end4. 常见误区误区 1只看最终输出输出错了只能说明前面某个环节错了。真正定位问题要看中间过程。误区 2只测正常输入实际工程中异常输入、边界条件、连续输入、间隔输入都要测。比如 FIFO 至少要测空读 满写 连续写 连续读 边写边读 接近满 接近空 复位后读写误区 3仿真激励不符合真实接口时序比如实际接口要求valid和data同周期有效但 Testbench 中却错开了一个周期。这种仿真通过了上板也可能失败。5. 面试延伸常见面试问题1. 你平时怎么写 Testbench 2. 如何验证一个 FIFO 3. 仿真发现输出不对你怎么定位 4. 什么是自检查 Testbench可以这样回答我一般先保证时钟和复位正确再检查输入激励是否符合接口协议然后观察状态机、计数器和握手信号最后检查输出数据。对于复杂模块会加入参考模型或自检查逻辑而不是只靠人工看波形。6. 总结不会看波形就很难真正调 FPGA。仿真时不要只问输出对不对还要问它是从哪一个周期开始不对的 是哪个中间信号先不对的九、约束文件写错或没写1. 问题现象约束问题是上板失败的重灾区。常见现象bit 文件能生成但板子没反应LED 不亮按键无效串口收不到数据DDR、网口、摄像头等外设不工作IO 电平标准报 warning时钟没有约束管脚复制错了。2. 核心概念约束文件主要告诉工具两件事第一FPGA 的哪个引脚连接外部哪个信号。 第二设计中的时钟和时序要求是什么。以 Xilinx XDC 为例set_property PACKAGE_PIN E3 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] create_clock -period 10.000 -name sys_clk [get_ports clk]这里表示clk 连接到 E3 管脚 IO 电平标准是 LVCMOS33 时钟周期是 10ns也就是 100MHz3. 排查步骤上板不工作时约束建议优先检查第一步确认当前 bit 文件是否是最新生成的。 第二步确认管脚约束是否和原理图一致。 第三步确认 IO 电平标准是否正确。 第四步确认时钟管脚是否约束。 第五步确认 create_clock 周期是否正确。 第六步确认按键、LED、串口 TX/RX 是否方向写反。 第七步检查实现报告中的 warning。 第八步确认没有多个约束文件互相冲突。4. 常见误区误区 1能生成 bit 文件就说明约束没问题不是。很多约束错误不会阻止生成 bit但会导致上板完全不工作。误区 2复制别人的约束文件不核对原理图开发板型号、FPGA 封装、原理图版本不同管脚都可能不同。约束必须和当前硬件一致。误区 3串口 TX/RX 搞反这是新人特别容易犯的错误。要注意FPGA_TX → USB_UART_RX FPGA_RX ← USB_UART_TX从 FPGA 角度和从 USB 转串口芯片角度看名字可能相反。5. 面试延伸常见面试问题1. FPGA 约束文件主要包括哪些内容 2. create_clock 的作用是什么 3. false path 是什么 4. multicycle path 是什么 5. IO 电平标准为什么重要回答时可以说约束文件不仅是管脚绑定还包括时钟约束和时序例外约束。时钟约束决定工具如何进行时序分析。如果时钟约束缺失即使实现通过也不能说明设计在目标频率下可靠工作。6. 总结上板失败不要一开始就大改 RTL。先确认管脚对不对 IO 电平对不对 时钟约束对不对 bit 文件是不是当前工程生成的很多新人卡几天的问题最后就是一个管脚写错。十、时序违例没重视1. 问题现象时序违例是 FPGA 工程里必须严肃对待的问题。常见现象实现后有 timing violation板子有时能跑有时不能跑降频后正常升频后异常同一份代码重新实现一次表现又变了温度、电压变化后系统不稳定逻辑仿真正确但硬件运行错误。2. 核心概念FPGA 不是只要逻辑功能正确就能工作。信号从一个寄存器出发到达下一个寄存器之前必须满足时间要求。核心概念setup time建立时间 hold time保持时间 slack时序裕量 critical path关键路径 timing closure时序收敛简单理解slack 0时序满足 slack 0时序违例3. 排查步骤时序违例建议这样查第一步打开实现后的 timing summary。 第二步关注 WNS、TNS、WHS。 第三步找到最差的 critical path。 第四步查看路径起点寄存器和终点寄存器。 第五步分析中间组合逻辑是否太长。 第六步检查是否存在大位宽加法器、乘法器、比较器、复杂 case。 第七步必要时加流水线。 第八步检查是否有错误的跨时钟域路径。 第九步检查时钟约束是否正确。4. 常见误区误区 1bit 文件能生成就说明可以上板不是。有时序违例的 bit 文件也可能生成但不能保证稳定工作。误区 2时序差一点点没关系如果 WNS 是负数就说明在当前约束下时序不满足。不要靠“也许能跑”来做工程。误区 3只会降低频率不会分析路径降低频率确实可能让问题消失但工程能力体现在能分析是哪条路径慢 为什么慢 能不能切流水 能不能提前计算 能不能减少组合逻辑层级5. 简单优化方法方法 1加流水线原来一拍内完成复杂计算y a b c d;可以拆成两拍sum1 a b; sum2 c d; y sum1 sum2;代价是延迟增加但频率更容易上去。方法 2减少组合逻辑层级不要在一个周期里做太多判断、计算和选择。方法 3使用 FPGA 内部资源比如乘法尽量使用 DSP存储尽量使用 BRAM而不是全部用 LUT 硬堆。方法 4检查约束有些时序违例不是真实逻辑太慢而是约束写错尤其是跨时钟路径。6. 面试延伸常见面试问题1. 什么是 setup violation 2. 什么是 hold violation 3. WNS 和 TNS 分别表示什么 4. 如何优化 FPGA 时序 5. 为什么加流水线能改善时序可以这样回答加流水线的本质是把原来一个时钟周期内完成的长组合路径拆成多个较短路径从而减少单周期内的逻辑延迟提高时序裕量。但代价是增加寄存器资源和数据延迟。7. 总结FPGA 工程里有一句话很重要功能正确只是第一步时序收敛才是上板稳定运行的底线。十一、不会用 ILA/SignalTap 做在线调试1. 问题现象很多新人上板后只会看 LED或者只会用串口打印。一旦板子没反应就不知道下一步怎么办。典型现象外设没输出不知道内部状态状态机是否跳转不知道FIFO 是否写入不知道valid/ready 是否握手不知道只能反复改代码、重新综合、重新下载。2. 核心概念仿真看的是设计在理想环境下的行为。ILA/SignalTap 看的是 FPGA 上真实运行时的内部信号。Xilinx 常用 ILA。Intel/Altera 常用 SignalTap。它们的作用可以理解为在 FPGA 内部接一个逻辑分析仪。3. 排查步骤使用 ILA 时建议抓这些信号时钟域内复位信号 状态机 current_state 关键计数器 输入 valid/ready 输出 valid/ready FIFO empty/full 错误标志 error_flag 外设接口控制信号调试顺序建议第一步先抓复位确认系统真的释放复位。 第二步抓状态机确认是否离开 IDLE。 第三步抓输入握手确认数据是否进来。 第四步抓内部计数器确认流程是否推进。 第五步抓输出握手确认数据是否送出去。 第六步根据异常位置继续细化信号。4. 常见误区误区 1一上来抓几十上百个信号ILA 资源有限抓太多信号会增加实现难度也不利于分析。建议先抓关键路径上的信号。误区 2触发条件设置不清楚如果触发条件设置不合理可能一直抓不到有效数据。比如状态机进入 ERROR 时触发trigger: current_state ERROR或者 valid 拉高时触发trigger: data_valid 1trigger: data_valid 1误区 3抓了波形但不会分析ILA 不是摆设。看波形时也要按链路分析输入有没有 状态走没走 计数到没到 输出有没有 错误从哪里开始5. 面试延伸常见面试问题1. 仿真正确但上板失败你怎么定位 2. ILA 一般抓哪些信号 3. 如果外设没响应你怎么判断问题在 FPGA 内部还是外部可以这样回答我会先确认下载、时钟、复位、约束和时序然后用 ILA 抓关键内部信号。一般先抓复位、状态机、计数器、握手信号和错误标志按数据流方向逐级定位问题。6. 总结上板调试不要靠猜。ILA/SignalTap 是 FPGA 工程师必须掌握的工具。十二、从仿真到上板的推荐排查顺序如果遇到仿真能跑上板不跑不要一上来大面积改 RTL。建议按下面顺序排查。1. 先确认最基础的硬件条件开发板是否正常供电 下载器是否识别 FPGA bit 文件是否下载成功 下载的是不是当前最新工程2. 再确认约束管脚是否正确 IO 电平是否正确 时钟管脚是否正确 复位管脚是否正确 外设方向是否正确4. 再确认复位复位有效电平是否正确 复位是否释放 关键寄存器是否有复位 复位释放是否同步5. 再确认时序WNS 是否为负 TNS 是否为负 hold 是否违例 关键路径在哪里6. 再确认 CDC是否有多个时钟域 跨时钟域信号是否处理 单 bit 是否同步 多 bit 是否用了 FIFO 或握手7. 最后确认 RTL 逻辑状态机是否跑飞 计数器边界是否正确 valid/ready 是否握手 外设协议时序是否满足可以整理成一句工程排查口诀先硬件后逻辑先时钟复位后状态数据先约束时序后怀疑代码先抓内部信号后猜外部现象。十三、给 FPGA 新人的几个建议1. 不要只学语法Verilog 语法并不难。真正难的是理解语法背后的硬件含义。比如if case assign always reg wire这些关键字本身不复杂但它们综合出来的电路差异很大。2. 多看综合结果和时序报告新人不要只盯着仿真波形。建议养成习惯综合后看资源使用 实现后看时序报告 上板前看 warning 出问题看 critical path3. 写代码前先画结构图不要上来就写代码。哪怕只是简单画一下输入接口 缓存模块 控制状态机 数据处理模块 输出接口也比直接写 RTL 清楚很多。4. 模块接口要简单清晰模块之间尽量使用清楚的接口信号valid ready data last start done busy error不要让一个信号承担太多含义。5. 每个模块都要能单独仿真不要等整个工程写完才仿真。建议写完小模块就仿真 小模块对了再集成 集成后再系统仿真 最后再上板6. 不要忽略 warningFPGA 工程里很多严重问题一开始只是 warning。比如latch inferred unconstrained clock multi-driven net timing violation port not connected signal trimmed看到这些 warning不要直接跳过。十四、面试时可以怎么回答“仿真正确但上板失败”这是 FPGA 面试高频问题。可以这样组织回答如果仿真正确但上板失败我不会一开始就改 RTL而是按工程链路排查。 第一步确认 bit 文件是否正确下载开发板供电和下载链路是否正常。 第二步检查约束文件包括管脚绑定、IO 电平标准、时钟约束和复位管脚。 第三步检查时钟和复位确认输入时钟频率、PLL locked 信号、复位有效电平以及复位释放是否正常。 第四步查看时序报告确认 WNS、TNS、hold 是否满足要求。 第五步检查是否存在跨时钟域问题单 bit 信号是否同步多 bit 数据是否使用异步 FIFO 或握手。 第六步使用 ILA 抓内部信号包括复位、状态机、计数器、valid/ready、FIFO 状态和错误标志按数据流方向逐级定位问题。 最后再回到 RTL 逻辑本身检查状态机、计数器边界条件和外设协议时序。十五、结尾FPGA 新人最容易卡住的地方不是某一个语法点而是没有建立完整的工程排查链路。从仿真到上板真正要掌握的是知道代码会综合成什么电路 知道时钟和复位怎么影响系统 知道约束和时序为什么重要 知道跨时钟域为什么不能随便连 知道仿真波形应该看什么 知道上板后怎么用 ILA 定位问题。最后总结一句FPGA 工程能力不是“会写 Verilog”这么简单而是能把 RTL、仿真、约束、时序和上板调试串成一条完整链路。对于应届生、研究生和刚入行的新人工程师来说只要能把这 10 类问题真正理解并在项目中踩实基本就已经从“会写代码”开始向“能做工程”过渡了。
返回列表