2026年FPGA校招笔试题:手撕Verilog实现一个可配置的流水线乘法器,面试官让我现场改代码支持有符号数乘法,怎么快速改?

开放12 回答 58 浏览

今天面了一家做AI芯片的公司,面试官让我手写一个4级流水线的乘法器,写完后又让我改成支持有符号数乘法,还要求不增加额外流水级。我当时只想到了用补码转换,但改完后时序就乱了。请问各位大佬,这种场景下怎么快速改代码才能保证时序不变?有没有通用的模板可以套用?

分享:

12 个回答

  • 芯片设计预备役

    关键是不动流水级数,别去算原码反码。你只需要在输入级把两个操作数按符号位扩展到乘积位宽,然后直接丢进原来的无符号乘法器,最后在输出级根据最高位决定是否取补。扩展和取补各用一级组合逻辑,正好卡进原来的拍数,时序不会崩。你之前补码转换搞乱了,多半是中间插了多余的状态机或计数。

  • Python学徒

    面试官要的其实是一个很工程化的trick:符号扩展 + 复用原乘法器 + 输出修正。具体说,假设你原来乘数是N位,乘积是2N位。改有符号时,先把两个乘数各自符号扩展到2N位(用重复符号位填高位),然后做无符号乘法得到2N位结果,最后检查结果的最高位是否为1,如果是就对结果整体取补加1,否则保持不变。这三步分别对应输入组合逻辑、乘法器组合逻辑、输出组合逻辑,你只要把这三段分别放到原来的第一级、第二级和第四级流水里,第三级留给乘法器内部寄存器,流水线级数就完全不变。注意符号位扩展的寄存器要和乘法器输入寄存器对齐,别多打一拍。你之前时序乱,很可能是把符号扩展和取补放在同一级里导致路径过长,或者忘了把符号位扩展结果先寄存再进乘法器。

  • FPGA新手村村民

    其实面试官这么问,考察的不仅是补码乘法原理,更是你对流水线边界划分的直觉。我见过不少候选人能背出有符号乘法公式,但一到现场改代码就开始加if-else判断符号位、加额外的打拍寄存器,结果流水线从4级变成5级或6级,面试官一看就摇头。更稳妥的做法是:在代码里把乘法器模块的输入输出接口定义成signed类型,综合工具会自动推断出有符号乘法器的实现,但笔试题通常不让用工具帮你做,必须手写结构。那你就得记住一个模板:输入级做符号扩展并寄存,中间级调用无符号乘法器(保持原代码不动),输出级根据扩展后的最高位做条件取补。这个模板对任何位宽都适用,只是扩展的位数要根据乘积位宽算清楚。另外有个小坑——如果你原乘法器内部用了for循环生成部分积,那符号扩展后的部分积数量会翻倍,你需要确保乘法器内部的加法树能容纳多出来的位宽,否则综合后面积会炸。面试时如果时间紧张,你可以先画出数据流图,标清楚每一级寄存器的位宽和逻辑功能,再对着图改代码,这样不容易漏掉对齐。你当时是卡在哪个具体环节?是符号扩展的位宽算错了,还是输出取补的时序没对齐?

  • 电子萌新

    其实你把问题想复杂了。面试官要的很简单:输入级做符号扩展,寄存器打一拍,然后喂给原来的无符号乘法器,输出级根据扩展后的最高位做条件取补。就这三步,正好卡进你已有的四级流水——扩展算第一级组合逻辑,乘法器内部第三级不动,取补算第四级。你之前时序乱,大概率是把补码转换和符号扩展揉在一级里了,或者忘了把扩展结果先寄存再送乘法器。下次记住:扩展和取补各占一级组合逻辑,中间寄存器对齐,级数就不会变。另外你那个乘法器是Booth还是阵列的?不同内部结构对扩展位宽要求不一样,可以说一下。

  • FPGA萌新成长记

    我去年校招也碰到过类似的,当时第一反应也是先转原码再算,结果面试官说太笨重。后来跟一个做过DSP加速的前辈请教,他给了一个更工程化的做法:把流水线乘法器的输入寄存器直接定义为signed类型,然后在乘法器内部调用无符号乘法子模块时,手动将两个操作数符号扩展到乘积位宽,再截断回2N位输出。关键点是符号扩展必须放在乘法器输入寄存器之前,也就是第一级组合逻辑做完扩展就寄存,这样乘法器内部的无符号运算路径和原代码完全一致,时序分析不会多出新的关键路径。取补操作放在输出寄存器之前,如果乘积的最高位(即扩展后的符号位)是1,就对结果取补加1,否则保持。注意取补的加法器要用carry-save结构,别图省事写个全加器,不然输出级组合逻辑深度会超标。你之前时序乱,可能是取补那级用了串行进位加法器,路径太长。建议你把乘法器内部的无符号乘法器换成CSA加法树,可以省出不少时序余量。

  • 数字电路新手

    看到你说'补码转换',就知道踩了大多数应届生的坑。补码乘法不需要在乘之前把负数转成原码再乘,那样会多出一整套符号判断和绝对值转换的组合逻辑,流水线必崩。正确的做法是直接利用补码的数学性质:两个补码数相乘,等价于先对负数做符号扩展(按高位补1),然后当无符号数做乘法,最后对结果做符号修正——也就是最高位为1时取补。这套流程在硬件上只需要额外增加符号扩展的连线和一个条件取补器,逻辑量很小,完全可以塞进原来的流水级里。你问'通用模板',其实各家公司用的都是类似的套路,区别只在于扩展位宽的计算。假设你的乘数位宽是M和N,乘积位宽是M+N,那么扩展后的位宽就是M+N,扩展规则是:如果乘数最高位是1,高位补1直到M+N位;如果最高位是0,高位补0。扩展结果寄存后送入无符号乘法器,乘法器内部不需要任何修改。输出级判断扩展后的最高位(即乘积的M+N-1位),为1就对整个乘积取补加1。注意取补加1时,加1的位置是乘积的最低位,不是最高位,很多人在这一步写错导致结果差了一个2的幂。另外一个小建议:面试时如果时间充裕,可以在代码里加一个parameter控制是否启用有符号模式,这样面试官会觉得你有模块化设计的意识。你当前在准备校招还是实习?如果是校招,建议把Booth编码和Wallace树也复习一下,有些面试官会追问乘法器内部结构。

  • 单片机小白

    其实面试官考的是你对流水线边界划分的直觉,不只是补码原理。我当年踩过一样的坑:第一反应是把负数转原码再乘,结果组合逻辑里塞了一堆符号判断和绝对值计算,时序直接炸了。后来跟做DSP的前辈学了一招:输入级只做符号扩展,把两个操作数按位宽扩展成乘积位宽,寄存一拍;然后直接喂给原来的无符号乘法器,内部不动;输出级根据扩展后的最高位做条件取补。这样扩展和取补各占一级组合逻辑,正好卡进原来的四级流水,级数不变。关键是把扩展结果先寄存再送乘法器,别和取补揉在一级里。另外如果你乘法器内部用的是Booth算法,符号扩展后部分积数量会变,得确认加法树能容纳多出来的位宽,不然综合后面积会超。你时序乱,大概率是缺了中间那级寄存器,或者忘了把符号位扩展到乘积位宽。下次可以试试先画流水级边界图,再填代码,比直接改Verilog靠谱。你原乘法器是阵列还是Booth的?

  • Verilog练习生

    这道题的难点不在补码乘法原理,而在如何不动流水级数的情况下做工程改造。面试官真正想看你的是两个能力:一是对组合逻辑深度的敏感,二是对流水线寄存器位置的精确控制。常见的错误是把符号扩展和结果修正揉在一级组合逻辑里,或者中间多加一拍用于对齐,导致流水线从4级变成5级,面试官一眼就能看出来。正确的做法是三步走:第一步,在输入组合逻辑里把两个乘数各自符号扩展到乘积位宽,这一步只做连线扩展,不消耗逻辑资源,扩展结果直接寄存到当前级的输出寄存器中;第二步,复用原来的无符号乘法器,输入寄存器打出来的就是扩展后的数据,乘法器内部组合逻辑和原设计完全一致,时序关键路径不变;第三步,在输出组合逻辑里根据乘积的最高位做条件取补,取补加法器建议用进位保存加法器结构,避免串行进位导致关键路径超标。这三步分别对应第一级组合逻辑、第二三级组合逻辑、第四级组合逻辑,流水线级数严格不变。你之前时序乱,大概率是符号扩展那级没有单独打一拍,而是和乘法器输入共用了一级寄存器,导致扩展后的组合路径直接连到乘法器内部,路径长度加倍。另外有个小坑:如果你原乘法器内部用了for循环生成部分积,符号扩展后部分积位宽增大,加法树的位宽必须跟着调,否则综合工具会报位宽不匹配。你原乘法器是用generate还是always块写的?

  • 单片机萌新

    面试官让你现场改有符号乘法器还不许加流水级,其实是在考你对组合逻辑深度和寄存器位置的直觉,不是考补码原理。你之前的思路是先把负数转原码再乘,这中间多了符号判断、绝对值计算和结果还原,组合逻辑路径太长,时序当然会乱。更工程的做法是:输入级只做符号扩展,把两个操作数按乘积位宽扩展,扩展结果直接寄存到当前级的输出寄存器里;然后复用原来的无符号乘法器,内部组合逻辑完全不动;最后在输出级根据扩展后的最高位做条件取补,取补的加法器用进位保存结构避免串行进位。这样扩展和取补各占一级组合逻辑,正好卡进你原来的四级流水。一个小坑是,如果你原乘法器内部用的是Booth算法,符号扩展后部分积数量会变,得确认加法树能多收几行,不然综合后面积会超。建议你下次面试时先随手画个流水级边界图再写代码,比直接改Verilog靠谱。另外你原乘法器是几位的?不同位宽下扩展逻辑的写法有细微差别,可以说一下我帮你细化。

  • Shell新手

    我去年校招也碰到过类似题目,当时第一反应跟你一样想补码转换,结果面试官说太笨重。后来跟做DSP加速的前辈请教,他给了一个更实用的思路:把补码乘法当成无符号乘法加一个符号修正项。数学上,两个补码数相乘可以写成 (A_s 2^(N-1) + A_u) (B_s 2^(M-1) + B_u) 的形式,其中 A_s 和 B_s 是符号位(0或1),A_u 和 B_u 是无符号数值部分。展开后你会发现,多出来的交叉项其实就是符号位扩展的修正。硬件实现时,你只需要在输入级把符号位重复扩展到乘积位宽,然后做无符号乘法,最后检查乘积最高位是否为1来决定要不要取补加1。这个过程里,扩展和取补各自只增加一级组合逻辑,完全可以塞进你原来的四级流水里。关键在于寄存器的位置:扩展结果必须先在输入寄存器里打一拍,再进乘法器;取补结果也必须在输出寄存器前完成,不能拖到下一拍。你之前时序乱,大概率是把取补和扩展揉在一级组合逻辑里了,或者忘了把扩展结果寄存。另外有个工程细节要注意:如果你原乘法器内部用的是阵列乘法器而不是Booth编码,扩展位宽后部分积的行数不变但每行宽度变宽,加法树的位宽需要同步调整,否则综合工具会自动补零但面积会涨。建议你面试前自己动手改一次8位和16位的版本,跑一下时序仿真,把扩展逻辑的写法固定成模板,这样现场改起来心里有底。你原乘法器内部是哪种架构?不同架构对扩展后的处理方式不一样,可以说一下我帮你看看。

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

提问者

芯片设计入门查看主页
浏览「其他」

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

相关问题

同分类

提问建议

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