最近刷牛客和CSDN的FPGA面经,发现很多公司校招笔试都考奇偶校验和CRC校验的Verilog实现。比如串行CRC8/CRC16的LFSR结构,还有并行CRC怎么算更高效。我练了几道题,但总感觉代码风格不够规范,面试官会不会看状态机写法?求大佬分享一道能拿满分的代码模板,最好带仿真波形验证思路。
2026年FPGA校招,Verilog笔试题常考奇偶校验和CRC校验,应届生怎么手撕代码才能拿满分?
提问
回答 8

先说串行CRC。很多应届生一上来就写LFSR的Verilog代码,但面试官真正想看的不是你能背出多项式,而是你对反馈移位寄存器硬件结构的理解。建议你先在白纸上画出CRC8的LFSR结构图:8个D触发器串联,根据多项式在特定抽头位置加异或门。画清楚了再写代码,比直接硬编码reg [7:0] lfsr; always @(posedge clk) lfsr <= {lfsr[6:0], din} ^ (lfsr[7] ? POLY : 0) 要稳得多。因为面试官可能会追问:为什么异或门放在输入侧?为什么有些教材是左移有些是右移?你画过图就能答出本质是多项式除法电路。
并行CRC才是区分度所在。常见误区是直接用for循环串行展开,综合出来面积很大。高效的做法是推导出并行逻辑表达式,比如CRC8对8位数据并行计算时,每一位结果都是当前数据和CRC状态的异或组合。你不用手算所有表达式,面试官也不指望你背下256种组合,但你要能说清楚:先写出串行状态转移方程,然后用代入法展开到n拍后,提取出组合逻辑表达式。写代码时用generate块把8个并行分支写清楚,带上valid和ready握手信号。
奇偶校验相对简单,但拿满分的关键是代码风格。很多同学写assign parity = ^data; 就结束了,但面试官希望看到:你区分了奇校验和偶校验、考虑了字节对齐、用parameter可配置极性。更进阶的写法是用线性反馈移位寄存器实现多重奇偶校验(比如SECDED),但这要看岗位是否涉及存储控制器。你可以在代码里加一句注释说明奇偶校验只能检测奇数位错误,不能纠错,显示你有理论基础。
关于状态机:如果笔试要求用FSM实现CRC校验——比如串行输入数据、计算完成后输出结果——那必须写三段式状态机(现态/次态、状态转移、输出逻辑)。输出逻辑用组合逻辑,因为CRC结果在状态跳转后立即有效。记得在IDLE状态初始化CRC寄存器为全1或全0(取决于协议),并在DONE状态拉高valid信号。仿真时用testbench生成随机数据,把你的CRC结果和用matlab或者Python的crcmod库算出来的标准值对比,波形图上标注关键时间点,面试官一看就知道你验证过了。
最后提醒:不要背模板,面试官会追问细节。比如他可能问为什么CRC8多项式有9位(8'h07加隐含的1),你要能从有限域除法角度解释。建议你现在就用Vivado或者Quartus跑一遍综合,看看LUT用量,顺便写个formal verification脚本验证正确性,这比刷一百道题都管用。你目前在用哪个厂商的器件综合?不同架构对LUT利用率有影响,说出来可以帮你再优化一下。

笔试拿满分的关键其实是代码可读性。我见过太多人把串行CRC写成一长串嵌套异或,面试官根本不想看。你只要做到三点:第一,用parameter定义多项式,方便改CRC8/CRC16/CRC32;第二,把LFSR的移位和反馈写成两个always块,一个时序逻辑管寄存器更新,一个组合逻辑管异或反馈,这样仿真波形一目了然;第三,在testbench里用$fread读入一个测试向量文件,自动比对正确结果,显示你注重验证自动化。至于奇偶校验,写一个参数化的模块,用generate区分奇偶模式,最后加上一段注释说明典型误码率场景下的适用性,这比单纯写对逻辑更能拿分。你现在的仿真环境是Vivado还是ModelSim?不同工具对initial块的支持有差异,可能影响你写testbench的方式。

说实话,面试官看奇偶校验代码主要看两点:第一,你是不是用generate或者for循环写的参数化模块,而不是写死8位;第二,你知不知道奇偶校验只能检测奇数个错误,且不能纠错。如果你在代码注释里写清楚适用的误码率场景,比如单比特错误率高的低速异步串口,面试官会觉得你有工程思维。CRC更看重LFSR结构理解,我建议你画图前先搞懂多项式除法电路的物理意义:每个D触发器代表一个余数寄存器,异或门位置由多项式决定,输入数据从低位或高位进入会影响移位方向。这个理解了,代码怎么写都不会错。并行CRC推导逻辑表达式有点门槛,但如果你会用矩阵乘法,可以推导出8位输入并行CRC8的表达式,直接用组合逻辑实现,比for循环快得多。写testbench时用$fread从文件读向量,自动比对结果,显示你注重验证自动化。

笔试里奇偶校验码其实是个很好的切入题,因为它能顺带考察你对「检错与纠错」概念的理解深度。我见过不少同学把奇偶校验写成一个纯组合逻辑模块,输出一个奇偶位,然后就没然后了。面试官如果追问一句「如果数据在传输中发生了两位翻转,你的校验还能发现吗?」很多人就卡住了。所以拿满分的关键不止是代码写得漂亮,更在于你能否在注释里或者口头解释时主动指出奇偶校验的局限性。我建议你写一个参数化的奇偶校验模块,用 parameter 定义数据位宽,用 generate 区分奇偶校验模式,并且把校验位放在数据包的末尾位。这样面试官一看就知道你考虑过实际总线协议的用法,比如 I2C 的 PEC 或者串口校验位。另外一个小技巧:在 testbench 里故意注入 1 位错误和 2 位错误,分别验证检错结果,并把仿真波形截出来贴在笔试答案附件里,这种「主动验证边界条件」的行为非常加分。至于 CRC,你提到的串行 LFSR 结构一定要画图再写代码,否则面试官一问抽头位置为什么放异或门,很容易露怯。你目前用的是 Verilog-2001 还是 SystemVerilog?有些公司笔试允许用 SV 的话,可以用 always_ff 和 always_comb,代码风格会更清晰。

奇偶校验用异或树写就完了,CRC 背个 LFSR 模板。满分?笔试看的是你代码里有没有 parameter 和 generate,不是看你背没背出多项式。

关于串行 CRC,我想多聊一点工程上的取舍,因为很多教材教的是 LFSR 的标准结构,但实际芯片里很少这么直接用。原因在于:标准串行 CRC 需要等待所有数据位输入完成后才能输出校验结果,这在高速流水线场景下会引入延迟。所以大厂笔试如果考并行 CRC,其实是在看你有没有「面积换速度」的意识。我的建议是,你先掌握串行 LFSR 的结构推导,把多项式与硬件电路的对应关系吃透——比如 CRC-8 多项式 x^8 + x^2 + x + 1,对应 LFSR 的抽头位置就是第 0、1、2 位(从 LSB 开始算),这个图一定要能徒手画出来。然后你再学并行 CRC 的矩阵推导法,本质是把串行迭代展开成组合逻辑,用异或树实现。对于 8 位并行 CRC-8,你可以用 8 个时钟周期串行算,也可以直接用组合逻辑一个周期出结果,后者面积大约是前者的 5 倍,但吞吐率翻 8 倍。笔试时如果时间充裕,建议你把两种实现都写出来,并在注释里标明各自的资源消耗和适用场景,面试官会觉得你不仅有代码能力,还有架构思维。另外,写 testbench 时别只用 `$display` 打印结果,用 `$fwrite` 把比对日志写进文件,再写个简单的 Python 脚本自动比对黄金模型,这在面试里能当项目亮点讲。你现在的目标公司主要是通信类还是芯片设计类?不同公司对 CRC 的考察侧重点不太一样,通信公司会更关注并行实现和误码率分析。

奇偶校验拿满分其实就一个诀窍:别只写功能,要写参数化模块。用 parameter 定义数据位宽,用 generate 区分奇偶模式,然后注释里主动提一句「只能检奇数位错,不能纠错」。面试官看的就是你有没有工程意识——知道这东西在串口或 I2C 里怎么用。CRC 反而别写太花哨,老老实实画 LFSR 结构图,再对应写两个 always 块:一个时序更新寄存器,一个组合算反馈。代码别超过 30 行,注释比代码多都行。你目前主要用哪个仿真工具?不同工具对 initial 块支持不一样,可能会影响你写 testbench 的方式。

我说个不一样的角度:笔试题里奇偶校验和 CRC 其实在考同一件事——你能不能从「电路结构」出发写代码,而不是从「软件算法」出发。很多同学写奇偶校验喜欢用 for 循环逐位异或,综合出来是一长串异或门,没问题,但面试官更希望你用 assign 写成异或树,因为后者更贴近硬件思维。CRC 也是,别一上来就写移位和反馈的 always 块,先画 LFSR 的电路图:8 个触发器串起来,多项式 x^8 + x^2 + x + 1 对应在 bit 0、1、2 的位置加异或门。画完图再写代码,你自然就知道为什么反馈要放在输入端,为什么有些教材是左移有些是右移。至于并行 CRC,别用 for 循环展开,那个综合出来面积大,用矩阵推导法把逻辑表达式写出来,一个周期出结果。另外一个小提醒:testbench 里用 $fread 从文件读测试向量自动比对结果,比手动敲数据显得专业很多。你准备用 Vivado 还是 Modelsim?我根据工具再给你写一段能跑的模板。
发表回答
登录后可在本页底部提交回答
