资讯动态

Verilog期末题拆解:可综合原则、分频打拍与FPGA上板验证

发布时间:2026/10/26 14:52:17 来源:尧图企业网站定制
简介面向高校数字电路与FPGA课程学习者这是一份Verilog期末测试题及答案文档适合用于考前自测、课堂复习与代码查漏补缺。内容按题型组织系统设计部分覆盖二输入与门、4位全加器、带异步复位的D触发器、乘法函数、四位加法计数器、阶乘任务、偶分频电路及4微秒延时模块等八道典型题并给出可对照的Verilog参考代码后续还包含填空、英文缩写释义与程序设计等题型涉及Quartus工程、Modelsim仿真、negedge、wire、LUT、CPLD/FPGA实现方式等知识点。压缩包共1个docx文件约118KB便于直接查阅和打印。已有3383人学习可作为课程复习提纲和实验前的代码核对材料帮助读者理解always块、复位与置数优先级、分频计数和延时移位等写法并据此整理自己的答题思路。1. 一份只有题和答案的 docx为什么值得反复翻很多人复习 Verilog 的姿势是翻教材目录觉得自己全会结果真坐到电脑前写一个带异步复位的计数器always 块的敏感列表漏了 reset波形跑出来复位压根没生效。这份 Verilog 期末测试题及答案本质上是一套 8 道补全程序题加填空、缩写、简答、程序设计的小型题集知识点从二输入与门一路排到偶分频器和延时打拍旁边还挂着 FPGA 生态的常识问答。它适合两类人期末要过关的学生和刚转 FPGA、想用一套最小题集把语法和建模习惯过一遍的开发者。答案不是终点把每道题跑通、波形对上才算真的会。2. 卷面结构拆解五类题型对应的 Verilog 能力地图这份 docx 不是按章节组织的教材而是一张卷子一、系统设计 8 道补全程序题二、填空题 11 道三、英文缩写 7 个四、程序设计 2 道五、填空题 18 道六、简答题 4 道后面还附了程序设计题的 5 份完整实现。题型看着杂把答案拆开看其实能拼出一条从门电路到分频器的完整上升路径。搞清这条路径比死记答案有用得多因为面试和实际项目里问的永远是「你为什么这么写」不是「答案是什么」。2.1 八道补全题构成的难度台阶这 8 道题不是随机抽的每一道对应一个建模手法难度是递增的题号模块名核心考点最常见的错处1my_and数据流建模 assign端口列表分隔符写错、以为输出必须声明 reg2my_add位宽拼接与进位只写 sum 不拼 c_out进位被截断3my_D异步复位时序模板敏感列表漏 negedge reset、用阻塞赋值4mulfunction 定义在 function 里写延时或时序控制5my_counter复位/置数/计数优先级敏感列表混入 load同步置数变成异步6factorialtask 定义位宽溢出、把 task 当 function 用7fp_even计数器加分频比较值算错、cnt 位宽不够8my_delay移位寄存器打拍用 # 延时代替寄存器链第 1、2 题属于组合逻辑靠 assign 一条语句搞定第 3、5、7、8 题是时序逻辑全部落在 always 块加非阻塞赋值这套模板里第 4、6 题考的是过程建模的两种封装形式。也就是说把这 8 道题写熟Verilog 的可综合子集基本就覆盖了剩下的只是工程经验问题。2.2 填空与缩写题FPGA 生态里必须背下来的那一层填空和缩写看着是纯背诵实际上全是上机时会直接撞上的东西。把两批填空题的考点归拢一下大概是这样几类类别需要记住的点工程与文件工程文件后缀 .qpfVerilog 源文件 .vCPLD 早期工具与主流 IDE 的区别关键字上升沿 posedge下降沿 negedge双向端口 inout数据类型assign 左值必须是 wirereg 默认 1 位宽输出要存值必须显式声明 reg位宽与拼接拼接运算符 {} 的用法X 表示不定值Z 表示高阻器件常识FPGA 主流实现方式分 SRAM、反熔丝、FLASH 三类CPLD 基于乘积项FPGA 基本单元含 4 输入查找表建模习惯时序逻辑优先用非阻塞赋值case 语句一般综合成多路选择器缩写那 7 个EDA、SoPC、FPGA、RTL、HDL、CPLD、ASIC建议连英文全称一起记。RTL 是 Register Transfer Level寄存器传输级这个词在后面看综合报告、看网表的时候天天出现只记住中文翻译没什么用。记忆方法上我一般不建议按卷面顺序背而是按「建工程 → 写代码 → 跑仿真 → 看综合」这条动线串起来记。比如 .qpf 和 .v 的关系你只要亲手建过一次工程就不会忘posedge 和 negedge只要写过一次带复位的触发器就刻进去了。纯背下来的东西一周就忘动过手的能留很久。2.3 简答题里的 14 条可综合原则是全卷最值钱的部分卷子最后一道简答题列了 14 条建立可综合模型的原则这部分比前面所有代码题都值钱因为它决定了你写出来的东西能不能变成电路。挑几条最容易被忽略的展开说。第一条「不使用 initial」。仿真里 initial 块用来给激励、给初值但综合工具对它的处理是直接忽略或者只在部分器件上支持上电初值。你要是靠 initial 给寄存器赋初值仿真跑得通上板之后寄存器是随机态这就是典型的仿真与实物不一致。第二条「不使用 #10 这类延时」。延时语句只描述仿真时间不描述硬件。综合时被无条件丢弃你以为自己写了 10ns 延迟实际电路里什么都没有。要延迟就用时钟打拍这也是第 8 题延时模块的用意。第三条「不使用循环次数不确定的循环」。for 循环在综合时是被展开的次数必须是个常量forever 和 while 展不开综合工具直接报错。想写状态机就一直循环可以用 always别用 forever。第七条「always 描述组合逻辑时敏感信号列表要列全所有输入」。这条是老生常谈但翻车率极高。漏掉一个输入信号仿真时那个信号变化不会触发块执行波形看起来「卡住」综合工具则会据此推断出锁存器latch因为它在某些输入组合下需要保持原值。锁存器在 FPGA 里是能不用就不用的东西时序分析麻烦还容易引入毛刺。第九条「时序逻辑尽量用非阻塞赋值」。这条要配合第十条一起看一个变量不能在一个以上的 always 块里赋值。两个 always 块同时用阻塞赋值改同一个变量仿真器按代码顺序执行不同的仿真器、不同的优化等级可能给出不同结果这种 bug 查起来非常难受。第十四条「避免在 case 分支里用 x 或 z」。x 是不定态z 是高阻它们在仿真里有意义在综合时会被当成 dont care 处理综合出来的电路和你写的分支对不上。写 case 就老老实实写 0 和 1再补一个 default 兜底。这 14 条不用一次背完建议写成一张便签贴在显示器边上每写完一个模块对一遍两三周就成肌肉记忆了。3. 组合与时序基础题与门、全加器、D 触发器逐题拆卷子第一部分是补全程序题干里留了编号的空让你填代码。这一批题的题干本身有几处印刷错误比如把 Verilog 拼成 Venilog、把 assign 拼成 assgin、把 input 拼成 imput、端口列表用点号分隔。真上机的时候照着抄一定编译不过所以下面给的代码都是修正后的版本顺便把每道题的判定逻辑讲清楚。3.1 二输入与门与 4 位全加器位宽拼接才是真考点第 1 题最简单但简单题里藏着两个新手常犯的错。修正后的写法module my_and( input a, input b, output c ); assign c a b; // 数据流建模组合逻辑无时钟 endmodule逻辑说明assign是连续赋值右值任何一个操作数变化左值立刻跟着变等价于一根导线加一个与门。参数说明a、b是 1 位输入c是 1 位输出因为用 assign 驱动c默认就是 wire 类型不需要也不允许再声明成 reg。新手第一个错是把输出声明成output reg c然后接 assign编译直接报「不能对 reg 做连续赋值」。第二个错是端口列表写成my_and(a.b,c)点号是印刷错误实际必须用逗号端口之间用点号在例化时才出现.a(a)这种命名连接。第 2 题的全加器真正考的是位宽module my_add( input [3:0] a, input [3:0] b, input c_in, output [3:0] sum, output c_out ); // 拼接后左值 5 位加法结果的进位不会被丢掉 assign {c_out, sum} a b c_in; endmodule逻辑说明{c_out, sum}是把两个信号拼成一个 5 位向量高位是进位低 4 位是和。左边 5 位、右边三个操作数相加Verilog 会按左值位宽对结果做截断或扩展进位就保住了。参数说明如果写成assign sum a b c_in;左值只有 4 位第 5 位的进位直接被丢弃c_out永远是 0仿真一看就知道不对但很多人第一版就是这么写的。再补一句位宽规则两个 4 位数相加最大是 4b1111 4b1111 1 31需要 5 位才能完整表示。凡是做加法、累加、计数器位宽都要多留一位这是血泪经验溢出问题在波形上表现为「数到某个值突然回到 0」特别容易误判成复位误触发。3.2 异步复位 D 触发器一套要背下来的模板第 3 题要求带异步复位、低电平有效、时钟上升沿触发module my_D( input d, input clk, input reset, output reg q // 在 always 里赋值必须是 reg ); // 时钟上升沿 或 复位下降沿 都会触发 always (posedge clk or negedge reset) begin if (!reset) q 1b0; // 异步复位优先不等待时钟 else q d; // 正常采样 end endmodule逻辑说明敏感列表里同时写了posedge clk和negedge reset意味着复位信号一拉低块立刻执行不等时钟沿这就是「异步」。复位分支必须写在最前面if-else 的优先级从上往下写在后面就被时钟分支挡住了。参数说明q要保存数值必须声明成reg是非阻塞赋值时序逻辑的标准写法。对照一下同步复位的版本差别只在敏感列表// 同步复位敏感列表只有时钟复位要靠时钟沿才能生效 always (posedge clk) begin if (!reset) q 1b0; else q d; end同步复位的好处是复位信号不占用触发器的异步复位端时序分析简单抗毛刺能力强坏处是复位必须比时钟宽时钟停了复位就不起作用。异步复位的好处是时钟没起来也能复位坏处是复位释放的时刻如果不跟着时钟走容易产生亚稳态。工程上的常见做法是异步复位、同步释放用一个两级触发器把复位信号同步到时钟域这一段在卷子里没考但做项目绕不开。3.3 计数器题优先级和敏感列表的矛盾第 5 题要求异步复位高有效、同步置数低有效、时钟上升沿触发题干给的答案有一处硬伤// 题干版敏感列表里混进了 negedge load always (posedge clk or negedge load or posedge clr) begin if (clr) qout 4b0; else if (load 1b0) qout data; else qout qout 1b1; end问题在于题目明说「同步置数」同步的意思是只在时钟沿采样可敏感列表里写了negedge load等于让 load 的下降沿也能直接触发块执行这就变成异步了。更麻烦的是 load 是低电平有效如果它一直保持低块会反复进入置数分支实际行为取决于仿真器的调度波形很难解释。修正版应该只留时钟和复位module my_counter( input clk, input clr, // 异步复位高有效 input load, // 同步置数低有效 input [3:0] data, output reg [3:0] qout ); always (posedge clk or posedge clr) begin if (clr) qout 4b0000; // 优先级最高 else if (load 1b0) qout data; // 次高只在时钟沿生效 else qout qout 1b1; // 4 位自然回绕15 之后回 0 end endmodule逻辑说明clr高有效异步复位所以敏感列表用posedge clr分支判断用if (clr)load低有效但同步所以不进敏感列表判断写成load 1b0。参数说明qout是 4 位加到 15 再 1 会自动回绕到 0不需要额外写清零逻辑这是二进制计数器的天然特性。顺带说一个易错点qout 1b1里如果写qout 11默认是 32 位整数综合工具会推断出 32 位加法器再截断虽然功能对但资源浪费还可能带出位宽警告。养成写1b1的习惯。3.4 3-8 译码器与奇偶校验case 和归约运算程序设计题里的 3-8 译码器和奇偶校验生成电路是卷子里少见的完整实现题module yimaqi3_8( input [2:0] a, output reg [7:0] out // case 在 always 里赋值必须 reg ); always (*) begin // 组合逻辑用 * 自动列全敏感信号 case (a) 3b000: out 8b0000_0001; 3b001: out 8b0000_0010; 3b010: out 8b0000_0100; 3b011: out 8b0000_1000; 3b100: out 8b0001_0000; 3b101: out 8b0010_0000; 3b110: out 8b0100_0000; 3b111: out 8b1000_0000; default: out 8b0000_0000; // 兜底防止综合出锁存器 endcase end endmodule逻辑说明case 语句在综合时一般被映射成多路选择器8 个分支就是 8 选 1。参数说明敏感列表写(*)而不是(a)前者由工具自动推导后者靠人肉维护加一个输入忘了改就会漏。另外 3 位输入理论上只有 8 种取值default 看似多余但它是防止综合出锁存器的保险建议一律写上。奇偶校验那道题一行就够module parity( output even, output odd, input [7:0] bus ); assign even ^bus; // 归约异或8 位里 1 的个数为偶数时结果为 1 assign odd ~(^bus); // 取反即奇校验位 endmodule逻辑说明^bus是归约异或运算符把向量的每一位依次异或起来等价于逐位判断奇偶综合出来是一棵异或树延迟低。参数说明归约运算只能用在向量上^单目形式和双目异或a ^ b是两回事别混。4. function、task 与分频延时过程建模的四道硬题这一批题考的是 Verilog 里两个容易被当成「函数」用的东西function 和 task。它们长得像软件里的函数行为却受硬件建模约束边界搞不清就会写出仿真通过、综合报错、或者干脆不可综合的代码。4.1 function 与 task 的边界能不能带时序先看 function题目要求定义一个 8 位输出的函数把两个 4 位数相乘function [7:0] mul; // 返回值位宽 8 位函数名即返回值 input [3:0] data1; input [3:0] data2; begin mul data1 * data2; // 组合逻辑乘法 end endfunction逻辑说明function 内部是纯组合逻辑不能有#、、wait这类时序控制不能有 output 或 inout 端口至少要有 1 个输入返回值通过函数名本身传出。参数说明两个 4 位数相乘最大 15×152258 位最大 255刚好装得下位宽再小就会溢出。function 可以直接在 assign 里调用也可以写在 always 的组合逻辑里是可综合的。再看 task题目要求算 4 位数的阶乘task factorial; input [3:0] a; output [31:0] c; reg [3:0] i; begin c 1; // 结果从 1 开始连乘 for (i a; i 0; i i - 1) c i * c; end endtask逻辑说明task 可以有多个 input、output、inout没有返回值靠输出端口带结果它允许内部有局部变量这里的i调用位置在 initial 或 always 块里。参数说明原题答案里把c同时声明成 output 和 reg重复了实际写法二选一声明成 output 就行task 内部给 output 赋值会自动当寄存器用。这里有个值得琢磨的细节输入是 4 位最大能到 15但 13! 已经超过 32 位无符号数的上限所以这个 task 实际上是算不准 13 以上的。真要工程化要么把输入改成 3 位最大 77!5040 绰绰有余要么把输出加宽到 64 位。题目没较真你写的时候得知道这件事。一句话总结边界function 只做纯组合能综合task 灵活但一般只用在 testbench 里做激励封装别拿它写综合代码。4.2 偶分频电路比较值和位宽是两个雷第 7 题是偶分频给定参数 N6module fp_even( input clk_in, input rst, // 同步复位低有效 output reg clk_out ); reg [7:0] cnt; parameter N 6; always (posedge clk_in) begin if (!rst) begin cnt 8d0; // 复位时计数器清零 clk_out 1b0; end else if (cnt N/2 - 1) begin cnt 8d0; // 计到一半翻转一次重新开始 clk_out ~clk_out; end else begin cnt cnt 1b1; end end endmodule逻辑说明计数器从 0 计到 N/2-1共 N/2 个时钟周期到点翻转一次输出并清零翻转两次就是一个完整的输出周期所以输出周期是 N 个输入时钟周期N6 时是 6 分频。参数说明cnt位宽 8 位对 N6 是浪费实际按$clog2(N)取就够复位是同步的所以敏感列表里只有posedge clk_in复位判断写在块内第一句。坑一比较值写错。写成cnt N-1再翻转输出周期就变成 2N 个时钟分频比翻倍写成cnt N/2则会多计一个周期输出频率偏高但波形上看不出明显差异只有数周期才发现。坑二位宽不够。N 取得大一点比如要 500 分频reg [7:0] cnt最大 255N/2-1 249还勉强够做到 512 分频时N/2-1 255刚好卡在边界再大一点就永远等不到比较值计数器一路加到 255 回绕输出纹丝不动。这种 bug 仿真跑再久也不报错只是波形不对排查起来很费劲。另外 N 为奇数时N/2会被取整上半个周期和下半个周期不等长占空比就不是 50%。所以这类电路叫偶分频奇数分频要另写一套通常是两个计数器分别对上升沿和下降沿计数再组合。4.3 延时模块用寄存器打拍别用井号第 8 题给定系统时钟 1MHz要求把输入信号延时 4us 输出。1MHz 意味着一个时钟周期正好 1us4us 就是 4 个时钟周期module my_delay( input clk, input inta, output reg out ); reg [2:0] ina_r; // 三级中间寄存器 always (posedge clk) begin ina_r[0] inta; // 第 1 拍 ina_r[1] ina_r[0]; // 第 2 拍 ina_r[2] ina_r[1]; // 第 3 拍 out ina_r[2]; // 第 4 拍输出相对 inta 延迟 4 个时钟 end endmodule逻辑说明每一级寄存器都在时钟上升沿把上一级的值搬到下一级信号从inta走到out正好穿过 4 个触发器延迟 4 个时钟周期也就是 4us。参数说明out声明成 reg 是因为它在 always 里赋值如果改用assign out ina_r[2]那out就只能是 wire延迟变成 3 拍因为 assign 不额外消耗一拍寄存器。这是可综合的标准做法叫延迟链或者打拍。为什么不能用#4因为#描述的是仿真时间综合工具直接忽略你以为延时了实际电路里一根线都没有。这是新手最容易翻车的地方之一仿真波形完美上板之后延迟为零时序全乱。在实际项目里打拍的拍数经常是参数化的// 参数化延迟拍数DELAY 为 1 时退化成单级寄存 module delay_n #(parameter DELAY 4) ( input clk, input din, output reg dout ); reg [DELAY-1:0] pipe; always (posedge clk) pipe {pipe[DELAY-2:0], din}; // 左移一位新数据从低位进 always (posedge clk) dout pipe[DELAY-1]; endmodule这种写法在跨时钟域同步、按键消抖、信号边沿检测里到处都在用记住模板比记住某一道题答案划算。4.4 全加器的门级写法原语实例化的参数顺序卷子最后给了一份门级全加器实现正好用来讲原语实例化的语法module fulladd( output sum, output cout, input a, input b, input cin ); wire s1, s2, c1; xor (s1, a, b); // s1 a ^ b and (c1, a, b); // c1 a b xor (sum, s1, cin); // sum a ^ b ^ cin and (s2, s1, cin); // s2 (a ^ b) cin or (cout, s2, c1); // cout (ab) | ((a^b)cin) endmodule逻辑说明门级原语的实例化格式是「原语名 (输出, 输入1, 输入2, ...)」输出永远排第一个这和模块例化时的位置连接是一个道理。参数说明这份答案里进位那行写成了xor (cout, s2, c1)按全加器的真值表进位应该是s2 | c1用异或会算错a1、b1、cin0 时正确进位是 1异或得到 0。这是一处实打实的错误抄答案的时候必须改过来。门级建模在可综合性上没有毛病但可读性差、改起来麻烦工程上基本只在教学和极少数需要精确控制结构的场合用。日常写 RTLassign {cout, sum} a b cin;一行就够了综合工具会自己选最优的加法器结构。5. 上机验证里的常见问题排查从编译报错到波形不对的 5 条记录题目做过一遍不等于会了真正的分水岭是你把代码丢进工具链从编译、仿真一路走到综合中间会遇到一堆卷子上不会写的问题。下面这几条是新人复现这套题时最常撞上的按「现象 → 原因 → 解决」记。5.1 编译与综合阶段的两类硬伤第一条端口列表报语法错误。现象编译直接停在模块声明那一行提示意外的符号或者端口数量对不上。原因题干里端口用点号分隔my_and(a.b,c)照着抄就错另一种情况是复制答案时把逗号带成了中文全角逗号肉眼看不出区别。解决端口之间一律英文逗号模块声明的推荐写法是用 ANSI 风格把方向和位宽直接写在端口名前面input [3:0] a比老式的「端口列表加方向声明」少一大截出错概率。抄任何代码之前先扫一遍标点。第二条对 wire 赋值的报错。现象cannot assign to wire或者reg is not a valid l-value。原因在 always 块里给一个声明成 wire 的输出赋值或者反过来用 assign 驱动一个声明成 reg 的信号。Verilog 里连续赋值assign只能驱动线网过程赋值always只能驱动寄存器。解决记一句话——用 assign 就是 wire用 always 就是 reg。模块内部要把信号接到 assign 上就把 reg 改成 wire要在 always 里改就老老实实声明 reg 并去掉 assign。新手常犯的是「全声明成 reg」然后在顶层用 assign 接出去编译一报错就懵。5.2 仿真与波形阶段的功能偏差第三条波形跑出来复位不生效。现象复位信号拉低输出还是老样子或者只在时钟沿才变化。原因想做异步复位但敏感列表只写了posedge clk没写negedge reset复位被当成了同步信号处理。解决确认复位类型。异步复位必须把复位边沿写进敏感列表且判断分支放在最前面。用第 3 题的模板对着改别自己凭感觉补。第四条计数器数到一半就不动了。现象仿真跑到某个时刻计数值卡在一个固定值反复回绕输出信号停止翻转。原因分频器里的比较值超过了计数器的表示范围cnt N/2 - 1永远不成立或者位宽少了导致高位被截断。解决先算清楚需要的最大计数值用$clog2(N)声明位宽。仿真时把 N 改小比如改成 6 或 10能快速看出问题跑到 50MHz 的真实时钟下一分钟都翻不了几次波形上根本看不出对错。5.3 上板之后的现象和一份最小验证环境第五条仿真对了板子上 LED 常亮或者完全不亮。现象仿真波形一切正常烧进板子之后没有任何反应。原因通常是约束文件没写或者写错引脚没绑到实际的 LED 和按键上也可能是时钟接到了普通 IO 而不是时钟专用引脚导致全局时钟网络没建立触发器的行为不可预期还有可能是复位极性搞反按下按键反而是把系统锁在复位态。解决先看综合和布局布线的报告确认时钟有没有走全局网络、有没有时序违例再对一遍约束文件里的引脚编号和板子原理图。最简单的一步是点一个 LED 常亮确认工程链路是通的再往下加功能。仿真环境不需要多复杂一个 testbench 就够timescale 1ns / 1ps // 时间单位 1ns精度 1ps module tb_my_delay; reg clk 0; reg inta 0; wire out; // 例化待测模块命名连接比位置连接更不容易错 my_delay u_dut ( .clk (clk), .inta (inta), .out (out) ); // 时钟周期 20ns对应 50MHz always #10 clk ~clk; initial begin inta 0; #55 inta 1; // 制造一个上升沿观察延迟拍数 #200 inta 0; #100 $display(out %b at %0t, out, $time); $finish; // 结束仿真 end endmodule逻辑说明timescale决定仿真时间单位#10表示 10 个时间单位always 块生成自由运行的时钟initial 块给激励。$display在控制台打印配合波形窗口一起看最快。参数说明$finish一定要写否则仿真不会自己停待测模块的端口用命名连接.clk(clk)端口顺序改了也不用动 testbench。6. 二次开发把答案改造成能上板的验证工程把 8 道题各自孤立地跑一遍收获有限。我的做法是把它们串成一个闭环分频器产生慢时钟慢时钟驱动计数器计数器输出接译码器点亮 LED。这样做一次分频、计数、译码三条线全串起来了比单独跑八遍印象深得多。难点在于分频器要参数化。卷子里的fp_even固定 8 位计数器、N6想用 50MHz 分频到 1HzN 要写成 5000 万8 位根本装不下。改造的关键是用$clog2自动算位宽module clk_div #(parameter N 50000000) ( input clk_in, input rst, output reg clk_out ); // $clog2 返回容纳 N 所需的最小位宽综合工具支持 reg [$clog2(N)-1:0] cnt; always (posedge clk_in) begin if (!rst) begin cnt 0; clk_out 1b0; end else if (cnt N/2 - 1) begin cnt 0; clk_out ~clk_out; end else begin cnt cnt 1b1; end end endmodule改完之后仿真时把 N 传成 10几个微秒就能看到一次翻转上板时传 50000000LED 就变成秒闪。同一份代码两个场景复用这是参数化的价值。验证流程我一般走三步先在仿真里把 N 改小数清楚输出翻转之间隔了几个输入时钟再把计数器的输出接到译码器逐一点亮 8 个状态确认 case 分支没有漏最后上板用按键当复位肉眼看 LED 节奏。三步里任何一步对不上都回到公式重新算周期不要凭感觉改代码。改造时最容易忽略的是复位释放的时刻。分数频器用同步复位、计数器用异步复位两个复位源混在一起上电瞬间的状态可能不是你想要的。稳妥的做法是全局只用一个复位信号在顶层做一次异步复位同步释放再分发给各个子模块。还有一个技巧把这批题里的模块名、端口名统一改一遍别留着my_add、my_counter这种教学命名改成adder_pipe、up_counter这类能表达功能的名字。看似小事等模块数涨到二十个你会庆幸当初改了。从那以后我每次拿到一份题集或者一段教学代码都强制自己先改成参数化、补上 testbench、跑通仿真再上板绝不让「答案看起来对」当成「我写对了」。这套笨办法救过我很多次也省下了大量在板子上瞎猜的时间。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑