最近刷牛客和CSDN的FPGA笔试复盘,发现很多2026届同学在Verilog代码题里被I2C主机时序卡住了。序列检测题还能靠模板套,但I2C涉及到起始条件、时钟拉伸、应答位、多字节连续读写,状态机跳转和时序约束一写就乱。面试官说这题能看出你是不是真的做过工程,而不是只会刷题。想问下,这类题到底该怎么准备,才能从「能写」到「写得像项目」?
2026年FPGA校招笔试Verilog代码题,为什么「用状态机实现I2C主机时序」比序列检测更容易暴露工程能力差距?
12 个回答

I2C 主机这题本质是考你有没有真写过带外设的模块:SCL/SDA 什么时候拉低、什么时候释放,ACK 是在第几个 SCL 高电平采样,从机拉伸时钟你怎么等。序列检测是纯组合/移位逻辑,没有双向口和三态,套模板就能过。建议先画一张完整读写的时序图,再照着图写状态机,别一上来就敲代码。你板子上现在有实际的 I2C 从机器件可以对着调吗?

说白了就是序列检测可以靠移位寄存器和模板硬背,I2C主机的SCL/SDA是双向三态口,你得同时想清楚谁在驱动、什么时候释放、ACK在第几个高电平采。没在板子上真调过从机的人,状态机一写就露馅。手边有没有能跑的I2C从机器件?

因为序列检测本质是「模式匹配」,状态机退化成移位寄存器也能过;I2C主机考的是三态口方向切换和跨时钟域的握手。你画过一张完整读写时序图吗?画不出来,代码就是碰运气。

个人感觉,准备这题最划算的一件事不是背状态机模板,而是先把一张完整的单字节读时序图手画出来,标清楚哪一拍谁在驱动SDA。你会发现很多人的代码乱,是因为脑子里根本没有「释放总线」这个概念——写数据的时候SDA是推挽输出,读ACK和读数据那一拍必须切成高阻输入,这个切换点写错了,仿真波形看着像对的,上板就死。画完图再照着图写状态机,每个状态只干一件事,跳转条件就是图上那根竖线,写起来反而比序列检测顺。画图这一步你打算用纸笔还是直接上Visio?

从面试官视角补一个维度:I2C这题真正拉开差距的不是状态机写得多漂亮,而是你有没有考虑异常路径。正常读写谁都能糊出来,但时钟拉伸、从机不响应ACK、总线被别的master占用,这些才是工程里天天遇到的。常见做法是在Testbench里搭一个行为级从机模型,能主动拉低SCL模拟stretch,也能在指定字节返回NACK,然后注入错误看你的状态机是卡死还是能超时退出重发。这一步做了,你面试时就能讲出「我的状态机在NACK后会回到IDLE并置错误标志」,而不是干巴巴说「实现了I2C协议」。反过来,如果时间紧,替代做法是至少把SDA的三态控制写成独立的inout处理模块,别在主状态机里到处assign,这样代码可读性和面试观感都会好一截。风险在于,很多人仿真用的是理想从机,永远及时ACK,结果上板接真实EEPROM才发现第9拍采样点偏了。所以哪怕不写完整TB,也建议手动构造一次SCL被拉长的波形,确认你的计数器是等SCL真的拉高再走,还是自己数拍子。你现在手边有能跑的从机器件或者行为模型吗?

先说一个判断:这题拉开差距的不是状态机写得多花,而是你有没有把「谁在驱动SDA」这件事想透。序列检测没有双向口,SDA永远是一根单方向的线,套模板能过;I2C里SDA既当输出又当输入,读ACK和读数据那两拍必须切成高阻,切早了从机还没拉低你就误判,切晚了总线打架。所以准备顺序我建议反过来:先拿纸把一帧单字节写和单字节读的波形画出来,横轴画SCL的九个脉冲,纵轴标SDA每拍是主机驱动还是从机驱动,画完你再看状态机,会发现每个状态的职责是自动浮现的,根本不用背。画不出来就别急着敲代码,你脑子里没这张图,写出来也是碰运气。你打算先手画还是直接开仿真看波形?

先泼点冷水:很多人准备这题的方向就偏了,一上来就去找「I2C主机状态机模板」,背完了觉得自己会了,结果笔试稍微改一下——比如要求支持时钟拉伸,或者要求连续读两个字节——就崩。原因很简单,模板给你的是状态转移,没给你「为什么这里要跳」。我的建议是换个入口:先拿张纸,横轴画SCL的脉冲,纵轴画SDA,把起始、发送地址、读ACK、读数据、停止这几个片段各画一遍,每个时刻标清楚这根线此刻是主机推挽驱动,还是从机在拉,还是主机释放成高阻。画完你再看那些模板,会发现每个状态的职责是自然浮现的,根本不用背。
另一个容易被忽略的维度是「异常路径」。正常读写谁都能糊出来,但真实工程里从机不响应、时钟被拉伸、总线被别的master抢,才是天天遇到的。如果你时间够,在Testbench里搭一个行为级从机模型,能主动拉低SCL模拟stretch,也能在指定字节返回NACK,注入错误看你的状态机是卡死还是能超时退出。这一步做了,面试时你就能讲出「我的状态机在NACK后会回到IDLE并置错误标志」,而不是干巴巴说「实现了I2C协议」。时间紧的话,至少把SDA的三态控制写成独立模块,别在主状态机里到处assign。
风险在于,很多人仿真用的是理想从机,永远及时ACK,结果上板接真实EEPROM才发现时序对不上。你手边有没有一块能跑的I2C从机器件?

先说个反直觉的:这题写不出来,往往不是状态机不会写,而是你根本没搞清楚「这一拍总线归谁管」。我建议准备的时候别急着背模板,先干一件很土的事——拿张纸把单字节写的波形画出来,九个SCL脉冲,每一拍标清楚SDA是主机推挽、还是主机释放成高阻等从机拉。画完你会发现,那些状态转移其实是图上的竖线,不用记。
再往深一层,就是三态口的处理。很多人把inout直接assign到状态机里,读ACK那拍忘了切输入,仿真看着波形对,上板就打架。稳妥点的做法是把SDA的方向控制单独拎成一小块逻辑,主状态机只管「现在要发还是要收」。
但我想额外提醒一个容易被忽略的风险:这题真正拉开差距的其实不是正常读写,而是异常路径。从机不ACK、时钟被拉低拉伸、连续读时最后一个字节要回NACK,这些才是工程里天天碰的。如果你时间只够做一件事,就在Testbench里搭个行为级从机模型,能主动拉低SCL模拟stretch,也能在指定字节返回NACK,然后注入错误看你的状态机是卡死还是能超时退出。
替代做法也有:实在没时间搭模型,至少把「NACK后回IDLE并置错误标志」这条路径写出来,面试时能讲出这句,比干巴巴说「实现了I2C协议」强不少。你现在的仿真里,从机模型是理想应答还是能主动拉低SCL?

先说结论:这题真正想筛的不是「你会不会写状态机」,而是你有没有一个「总线归谁驱动」的心智模型。序列检测里数据线永远是单方向的,你套模板也能跑;I2C的SDA是双向的,写地址那拍主机推挽,读ACK那拍主机必须释放成高阻让从机拉低,切早切晚都出事。仿真里从机理想、永远及时应答,你可能看着波形是对的,一上板接真实EEPROM就死。
所以准备顺序我建议反过来:别先找模板,先拿纸把单字节写和单字节读各画一遍,横轴九个SCL脉冲,纵轴标SDA每一拍是谁在驱动。画完你会发现状态转移其实就是图上的竖线,根本不用背。
一个容易被忽视的风险是异常路径。正常读写谁都能糊出来,但从机不ACK、时钟被拉伸、连续读最后一个字节要回NACK,这些才是工程里天天碰的。如果时间只够做一件事,就在Testbench里搭个行为级从机模型,能主动拉低SCL模拟stretch、能指定字节返回NACK,看你的状态机是卡死还是能超时退出。这一步做了,面试时你能讲出「NACK后回到IDLE并置错误标志」,而不是干巴巴说「实现了I2C协议」。
替代做法也有:实在没时间搭模型,至少把SDA的三态控制单独拎成一小块,别在主状态机里到处assign,可读性和面试观感都能好一截。你现在手边有能跑的从机器件对着调吗?

先给一个可能有点反常识的判断:I2C这题难的不是状态机,而是你在写代码之前有没有在脑子里把总线跑过一遍。序列检测为什么能套模板?因为它的信息流是单向的,数据从左边进来,状态往右边走,你背的是「看到1101跳哪个态」。I2C不一样,SCL和SDA都是双向三态口,同一根SDA线,发地址那几拍是你在推挽,读ACK那一拍你必须松手变成高阻,让从机去拉。这个「松手」的时机,模板不会告诉你,只有你脑子里有那张时序图,才知道该在第几个SCL高电平之前完成方向切换。
所以准备路径我建议是反的:先画图,再写码,最后才看模板。拿张纸,横轴画SCL的脉冲,单字节写大概是起始加九个时钟加停止,纵轴把SDA每一拍标清楚——这一拍是我驱动,这一拍是我释放、从机在拉。画完写和读各画一遍,你会发现状态转移其实就是图上那些竖线,每个状态的职责自然浮现,根本不用背。画不出来就说明你还没理解,这时候敲代码纯属碰运气。
再往下一层是三态口怎么落地。常见的坑是把inout直接assign到主状态机里,读ACK那拍忘了切输入方向,仿真波形看着像对的,上板接真实器件就打架。稳妥做法是把SDA的方向控制单独拎成一小块组合逻辑,主状态机只管「现在要发还是要收」,方向由当前状态译码出来。这样代码可读,面试时你也能讲清楚为什么这么分。
还有一个拉开差距的点是异常路径。正常读写谁都能糊出来,但工程里天天碰的是从机不ACK、时钟被拉伸、连续读最后一个字节要回NACK。如果时间够,在Testbench里搭一个行为级从机模型,能主动拉低SCL模拟stretch,也能在指定字节返回NACK,然后注入错误看你的状态机是卡死还是能超时退出。这一步做了,面试时你能讲出「我的状态机在NACK后回到IDLE并置错误标志」,而不是干巴巴说「实现了I2C协议」。
时间分配上,画图加三态口处理占七成,异常路径占两成,剩下的才是背状态转移。你现在的板子或者仿真环境里,有能跑起来的I2C从机器件吗?
发表回答
登录后可在本页底部提交回答