2026年FPGA校招,面试官让手撕Verilog实现一个8位无符号除法器,不能用IP核和查找表,只能用组合逻辑加流水线,求具体架构和代码框架

开放8 回答 51 浏览

最近在准备FPGA校招,看到很多面经里都有手撕代码环节。今天遇到一个比较刁钻的题:实现一个8位无符号除法器,要求不能用IP核,也不能用查找表,只能用组合逻辑加流水线来设计。我本来想用移位相减的方法,但感觉流水线级数不好确定,而且除法器面积和延时怎么平衡?有没有大佬能给个具体的架构图或者Verilog代码框架?最好能讲一下每一级流水线怎么划分,以及商和余数怎么同步输出。

分享:

8 个回答

  • Verilog新手村

    这个题我去年校招也遇到过,面试官其实不是真要你写出一份能综合出最优结果的代码,而是考察你对流水线思想的掌握。8位无符号除法用恢复除法的话,每级要做一次比较和减法,如果被除数小于除数就不减,否则减完把商位设为1。非恢复除法更省面积,因为它省掉了恢复步骤,每级只做加法或减法,根据余数符号决定下一级操作,最后再用一个MUX把商还原成标准形式。关键点在于:流水线级数就是8级,因为8位商需要8次迭代。每一级输入是上一级传下来的部分余数和除数,输出新的部分余数和一位商,最后一级输出最终余数。你写代码的时候,用generate for循环展开8个stage,每个stage内部用一个always块或assign描述加减和MUX选择。商可以用移位寄存器拼起来,每一级把新产生的商位拼到低位。面积和延时的平衡其实不用太担心,8位除法器在FPGA上资源很小,你只要保证每级组合逻辑路径不太长就行。一个常见的坑是:有些人把余数初始化为被除数,但正确的做法是用一个扩展位宽的寄存器来保存中间结果,避免溢出。另外,如果面试官追问时序约束,你提一下每级之间可以插寄存器,但手撕代码阶段一般只要求功能正确。你目前是在准备哪家公司的面试?方便说说具体的工具链或者器件型号吗?这样我可以给更针对性的建议。

  • 芯片设计新人

    这道题其实是个很好的切入点,能看出你对数字电路设计的理解深度。我先说架构:推荐用非恢复除法,因为恢复除法每级多一个恢复步骤,面积略大,而且手写代码时容易把恢复逻辑写错。8位无符号数,直接展开8级流水线,每级完成一次加减操作和一位商的生成。具体来说,第一级把被除数左移一位,除数与左移后的高位部分比较,如果高位大于等于除数,则减去除数,商位为1,否则商位为0;下一级把上一级的结果继续左移并重复比较。注意,这里的高位部分其实是余数的中间值,初始时余数为0。代码框架可以用generate for循环,每个stage实例化一个组合逻辑块,输入是上一级的余数、除数和当前级数对应的被除数位,输出是新的余数和商位。最后一级的余数就是最终余数,商的8位可以从每一级的商位拼接得到,建议用一个reg [7:0]商寄存器,每一级在组合逻辑中更新对应的位,但要注意避免组合环路。一个容易被忽略的细节是:流水线输出需要同步,如果你要求每个时钟周期输出一个结果,那必须在每级之间插入寄存器,这样实际延时是8个时钟周期,但吞吐率是1个结果每周期。如果你只要求组合逻辑输出,那可以不加寄存器,但面积会更大且路径更长。面试官通常更关心前者,因为流水线的核心就是提高吞吐率。从学习路径来说,建议你先在纸上画出数据流图,标出每级的输入输出信号,然后再写Verilog。写完后用Vivado或Quartus写个简单的testbench,随机生成1000个测试用例,对比结果是否正确。如果仿真通过,再综合一下看看LUT和FF的使用量,一般8位除法大概用几十个LUT,完全在合理范围内。另外,你提到不能用查找表,其实在FPGA上LUT就是实现组合逻辑的基本单元,所以这句话应该是指不能用ROM查表法实现除法,而不是禁止使用LUT元件,这点要区分清楚。你现在的准备进度怎么样?是刚开始刷题还是已经进入模拟面试阶段了?如果刚开始,建议优先把这类基础运算器的流水线实现搞熟,因为乘加器、除法器、CORDIC都是高频考点。

  • 嵌入式小白打怪

    面试官出这道题,其实重点看你两件事:一是对流水线基本结构的理解,二是能不能写出可综合的代码,而不是背一个完美答案。我建议你用非恢复除法,因为恢复除法每级多一个恢复步骤,面积大一点,而且手写时容易写错。8位无符号数,流水线级数就是8级,每级完成一次加减操作和一位商的生成。具体来说,第一级把被除数左移一位,除数与左移后的高位部分比较,如果高位大于等于除数,则减去除数,商位为1,否则商位为0;下一级把上一级的结果继续左移并重复比较。注意,这里的高位部分其实是余数的中间值,初始时余数为0。代码框架可以用generate for循环,每个stage实例化一个组合逻辑块,输入是上一级的余数、除数和当前级数对应的被除数位,输出是新的余数和商位。最后一级的余数就是最终余数,商的8位可以从每一级的商位拼接得到,建议用一个reg [7:0]商寄存器,每一级在组合逻辑中更新对应位。面积和延时的平衡其实不用太担心,8位数据宽度很小,非恢复除法的组合逻辑深度也就几个加法器加MUX,综合后时序很容易满足。我个人建议你写代码时加一个valid信号逐级打拍,保证输入输出对齐,这样面试官会觉得你考虑了实际模块的握手问题,而不仅仅是把算法抄一遍。另外,小心除数为0的情况,如果面试官追问,你可以说在顶层加一个检测逻辑,把商置为全1或报错。你目前是在准备笔试阶段还是已经到面试轮了?这个会影响你代码要写到多细。

  • 码电路的阿明

    我去年校招被问过类似题,当时写了非恢复除法,面试官追问了三个点:一是为什么不用恢复法,二是流水线级间有没有寄存器,三是余数符号怎么处理。这里给你拆一下。非恢复除法的核心是:每次迭代根据当前余数的符号决定操作,如果余数非负,下一级做减法并置商位为1;如果余数为负,下一级做加法并置商位为0。最后一级完成后,如果余数为负,需要再加一次除数来恢复真正的余数,所以实际上最后一级后面要跟一个恢复步骤,这个步骤可以单独做一级流水线,也可以放在最后一级的组合逻辑里处理。大多数实现会选择把它放在流水线外,用组合逻辑直接修正,这样流水线还是8级。代码框架上,我建议你用两个数组:一个8位的reg [7:0] remainder_stage[0:8],一个8位的reg [7:0] quotient_stage[0:8],再加一个8位的reg [7:0] divisor_reg,在初始化时把除数锁存住。每个stage在组合逻辑中计算,然后用always块打一拍到下一级。注意,被除数要扩展成16位,因为每次左移后高位部分会变宽。实际写的时候可以这样:第一级把被除数赋值给remainder_stage[0]的高8位,低8位是被除数本身,然后每次左移一位,把当前被除数位移入低位。商位用移位寄存器拼,每级把新产生的商位移到最低位。面试官通常不会要求你写完整代码,但你要能画出数据流图,讲清楚每一级输入输出是什么。另外,有一个常见误区:有人试图用状态机实现流水线,但状态机是时序逻辑,流水线是组合逻辑加寄存器打拍,两者完全不一样。你写代码时一定要区分清楚哪些是组合逻辑块,哪些是触发器。如果你能在白板上画出每级的波形图,比如第一级结束后余数是什么,第二级开始前寄存器里是什么,面试官基本就满意了。最后,关于面积和延时,8位除法用流水线做,面积主要来自8个加法器和8个MUX,大约几百个LUT,延时取决于加法器的位宽,一般8位加法器延时在1ns以内,所以整个模块可以跑到几百MHz。你在回答时主动提一句时序约束,比如告诉面试官这个模块如果跑200MHz,setup和hold应该都没问题,会显得你有工程经验。你目前有在练过类似的流水线设计吗?比如乘法器或者累加器?可以先把那些练熟再攻除法器。

  • 嵌入式菜鸟

    如果面试官限定不能用 IP 和查找表,那他要看的就是你对除法器底层运算的理解,而不是花哨的优化。8 位无符号除法,我建议你直接上非恢复除法,原因很简单:恢复除法每级做完减法还得判断要不要加回去,这一加一减在组合逻辑里容易搞出长路径,而且你手写代码时那个恢复的 MUX 很容易写成分支条件漏掉。非恢复除法每级只做一次加法或一次减法,根据上一级余数的符号位选,商位也由这个符号位直接决定——余数非负就减除数、商 1,余数负就加除数、商 0。这样每级的延时就是一次加法器或减法器的延时,再加一个二选一的 MUX,流水线级数固定 8 级。你写 Verilog 的时候可以用 generate for 循环展开 8 个 stage,每个 stage 用一个组合 always 块,里面用 case 或者 if-else 根据余数符号选操作。余数初值设 0,被除数从高位逐位移进余数寄存器。商位用移位寄存器攒起来,每级把新产生的商位拼到低位。最后一级处理完,如果余数还有负的,得在外层再加一个 MUX 把它恢复成正的——这一步可以放在流水线外做组合逻辑修正,不影响流水线节拍。面积方面,8 位非恢复除法每个 stage 就是一个 8 位加法器和一个 8 位减法器(实际可以用一个加/减可配置的 ALU 复用来省,但手写代码为了清晰可以分开写),加上商位寄存器和余数寄存器,总共大概 200 个 LUT 出头,在 7 系列或国产器件上都能跑到 200MHz 以上。另外有个小细节:你写代码时记得把每一级的余数和商都用 reg 声明,并且在 always 块里赋初值,不然综合时容易 latch。面试官一般会追问余数符号怎么处理、能不能省掉最后那个恢复步骤,你要想清楚——如果允许余数符号不正常,其实可以不做恢复,但大多数面试题要求余数必须正确。你目前是在准备手撕代码阶段,还是已经进面试了?工具链用的是 Vivado 还是 Quartus?不同工具对 generate 的综合支持有点差别。

  • 电路板小白

    非恢复除法面经里讨论很多,但大多数人写代码时忘掉一件事:流水线各级之间的寄存器必须同步打拍,否则商和余数会错位。我建议你用一个二维数组 reg [7:0] remainder_pipe [0:8] 和 reg [7:0] quotient_pipe [0:8],每级把余数和商位存进去,最后一级输出。代码框架用 generate for 写 8 个 stage,每个 stage 内部用 assign 做加减和 MUX 选通,组合逻辑输出直接赋给下一级的 reg。这样写出来逻辑清晰,面试官一眼就能看懂你的流水线划分。你之前用移位相减的方法卡在级数上,是因为你没把每次左移看成单独一级——8 位就是 8 级,别多想。

  • EE新人

    面试官其实不指望你在纸上写出一个能综合出最优结果的代码,他们更想看你有没有流水线分级的意识。8位除法直接拆成8级,每级做一次加减和一位商的生成,非恢复除法每级只做加法或减法,省掉恢复步骤,面积自然小。你只要把余数寄存器在级间打拍对齐,最后把商的8位从各级取出来拼好就行,别纠结面积和延时的平衡,8位的小东西不会爆。

  • 码电路的阿明

    个人感觉你卡在「流水线级数不好确定」上,其实是因为你把移位相减和流水线分开想了。8位无符号除法,不管用恢复还是非恢复,迭代次数就是8次,流水线级数直接定8级就行,不用管数据位宽。非恢复除法每级只用一个加法器和一个MUX,组合逻辑延时可控,面积也小,适合手写。写代码时用generate for循环展开8个stage,每个stage内部用assign描述加减和选择,商位从每一级提取后拼接到一个移位寄存器里,最后一级同时输出余数。注意余数在每级之间要用寄存器打拍,否则仿真时会出X态。你可以先画个简单的数据流图:被除数从高位逐级移入,除数和余数在级间传递,商位从低位往高位拼,这样逻辑就清晰了。你现在的阶段是只开了一个头还是已经写了部分代码?如果代码有雏形,我可以帮你看看级间同步有没有漏拍。

登录后可在本页底部提交回答

提问者

逻辑综合学习者查看主页
浏览「其他」

描述场景与已尝试方案,更容易获得有效解答

相关问题

同分类

提问建议

  • 标题写清核心疑问,避免「求助」「请问」等空泛用语
  • 正文补充环境、版本、报错信息或截图
  • 先搜索本站是否已有相近问题,减少重复提问
  • 若与课程相关,请标明课时或章节便于讲师定位