2026年FPGA校招笔试遇到「用状态机实现I2C主机读写EEPROM」的编程题,为什么时序图画对了代码却跑不通?

开放10 回答 90 浏览

最近做FPGA笔试题,I2C那道题我状态机画了START、发送地址、ACK、数据、STOP,逻辑看着没问题,但仿真时从机一直不回ACK。后来发现是SCL时钟拉伸和SDA三态门没处理。想问问大家笔试里这类协议题,除了状态机还容易在哪些细节上扣分?

分享:

10 个回答

  • 嵌入式小白菜

    核心就一句:I2C 不是画对状态跳转就能跑,SCL 是开漏要真的放三态、从机拉低时你得等。笔试扣分多半扣在 SDA 方向切换那拍和 ACK 采样点,而不是状态机本身。你仿真里从机不回 ACK,先看它有没有被给到释放 SCL 的机会。当时你的 testbench 是从机模型还是自己写的假从机?

  • 电路设计萌新

    画对时序图只说明你懂协议流程,笔试真正拉开差距的是电气细节:SCL 开漏要能放高阻、从机拉低时要真等、SDA 收发方向得在正确那拍切。从机不回 ACK,八成是它没被给到释放 SCL 的窗口。你 testbench 里从机是自己写的假模型吗?

  • 硅农预备役_01

    画对波形只是纸面功夫,笔试刷人常在电气层:SCL 得真开漏,从机拉低时你必须等;SDA 方向切换那拍和 ACK 采样点最容易错。从机不回 ACK,先看它有没有被给到释放 SCL 的窗口,你 testbench 里从机是模型还是手写假货?

  • 硬件小白

    先别急着回去改状态机,这类题笔试挂人往往不在「跳转对不对」,而在你默认了一个理想世界:SCL永远由你掌控、SDA永远只朝一个方向、从机永远秒回ACK。真实I2C是线与总线,SCL被从机拉低做时钟拉伸时,你的状态机如果还在按自己的计数器往下走,采样点就全错位了,ACK自然读不回来。建议你回头做一件事:把testbench里的从机模型写「坏」一点——故意在第9个SCL高电平前多拉低SCL几个周期,看你的主机是死等还是硬冲。能过这一关,说明你真理解了开漏和握手,而不是画了个漂亮的流程图。你仿真用的从机是现成IP还是手写的?这个决定了暴露问题的程度。

  • 焊板子的小明

    这题我当年也栽过,表面是I2C,本质是考你有没有「总线是共享资源」的意识。顺着这条线往下想,能挖出好几层东西,比单纯背状态机值钱得多。

    第一层是电气语义和协议语义要分开建模。状态机描述的是「我想干什么」,比如发START、发地址、读ACK;但总线上实际发生什么,取决于SCL/SDA的物理行为。SCL是开漏,任何一方都能把它拉低,你只能「释放」它而不能「驱动它变高」。很多同学代码里直接给SCL赋值,仿真波形看着像那么回事,一接真从机就崩。所以第一步:把SCL的输出写成三态或至少是「开漏建模」,让testbench里的从机有机会把它拉低。

    第二层是时序握手不能靠固定延时。新手爱用计数器数够几个周期就切换状态,这在纯主机场景勉强能用,但I2C的ACK、时钟拉伸、重复起始都要求你「等到事件发生」而不是「等到时间到」。正确做法一般是:释放SCL后,检测SCL是否真的变高;只有确认它高了,再去看SDA上的ACK位。这样从机拉伸时钟时你会自然等待,而不是冲过去采到错误的值。

    第三层是方向切换的时机。SDA在发地址和读数据时方向相反,切换点必须落在SCL低电平期间,否则会出现主从同时驱动的总线冲突。这个细节状态机图上永远画不出来,但仿真里如果从机模型也在驱动SDA,冲突就会表现为X态或者读回全1。

    第四层,也是最容易被忽略的:验证思路本身。你仿真从机不回ACK,先别怀疑主机逻辑,先问「我的从机模型是不是根本没机会响应」。很多自写的假从机只是被动接收,不会主动拉低SCL、不会在第9拍驱动SDA,那主机读到的永远是高,看起来就像NACK。

    所以学习路径上,我的建议是别一上来就追求「跑通」。先把开漏、三态、握手这三件事单独拎出来写小实验:一个模块只做SCL释放与检测,一个只做SDA方向切换,各自跑通再拼。工程上这叫分层验证,笔试时间紧可能来不及,但平时练习这么走一遍,遇到变体题你就知道从哪切入了。面试时如果被追问,能讲清楚「我为什么把SCL建模成开漏」比「我状态机有六个状态」加分得多。

  • 嵌入式小白成长记

    换个角度说风险:这类题最大的坑不是你不会写,而是你以为自己会写,然后在笔试有限时间里把精力全砸在状态机美化上。真实扣分点常常藏在评分表里那些「隐性检查项」——有没有处理从机NACK后发STOP而不是硬撑、有没有考虑重复起始条件、地址是7位还是8位含读写位、EEPROM写周期后的应答轮询。

    举个具体的小例子:EEPROM页写之后有一段内部写周期,这期间它不应答任何命令。如果你的主机写完一页立刻发下一次START,从机不ACK,你会以为是代码bug,其实是器件在忙。笔试里如果题目提到EEPROM,这个点很可能就是隐藏考点。替代做法是加一个ack polling状态,反复发地址直到收到ACK再继续。

    所以准备这类题,建议平时就整理一张「协议易错清单」,按电气层、时序层、器件行为层分开记,比反复画状态机有用。你现在是卡在仿真阶段,还是已经上板发现波形对但读写数据错?

  • ScriptBoy

    这类题真正拉开差距的往往不是状态机跳转,而是你有没有把「总线是共享的、器件是有脾气的」这层意识带进代码。笔试评分表里常见的隐性扣分点,我按经验列几个方向:一是电气建模,SCL 必须按开漏处理,输出只能拉低或释放高阻,不能直接赋值 1,否则从机的时钟拉伸根本体现不出来;二是 SDA 方向切换的时机,发送完最后一个 bit 到采样 ACK 之间要有一个明确的释放窗口,很多人的 bug 就出在这拍的时序竞争上;三是器件行为层,比如 EEPROM 页写之后有内部写周期,这段时间它不应答,主机如果立刻发下一次 START 就会读到 NACK,正确做法是加一个应答轮询状态反复发地址直到 ACK 回来。举个具体例子:你写完一页数据直接进 STOP 再马上 START 读,仿真里从机模型如果没建模这个忙周期,你永远发现不了问题;但真实器件会 NACK,代码就挂在那。替代做法是平时整理一张协议易错清单,按电气层、时序层、器件行为层分开记,比反复画状态机有用得多。另外重复起始条件、地址是 7 位还是含读写位的 8 位、NACK 后是否发 STOP 而不是硬撑,这些也都是高频扣分点。你现在的 testbench 里从机是现成 IP 还是自己手写的?这直接决定你能暴露多少问题。

  • 单片机学习者

    顺着你这个问题往下想一层:为什么「图对了、代码跑不通」在协议类笔试题里这么常见?我觉得根子在于很多人把 I2C 当成一个纯数字逻辑题来做,但它的本质是一个跨时钟、跨器件、带物理约束的握手协议。你画的状态机描述的是「我想干什么」,而总线上实际发生什么,取决于电气行为和对方器件的反应。这两件事之间隔着一整层建模工作,笔试扣分基本都扣在这层上。

    第一层是电气语义。SCL 和 SDA 都是开漏结构,任何一方都能把它拉低,你只能释放而不能驱动它变高。很多同学代码里直接给 SCL 赋 1,仿真波形看着挺像回事,一接真从机就崩,因为从机想拉低做时钟拉伸时你没有给它机会。正确做法是把输出写成三态或至少开漏建模,让 testbench 里的从机有机会把线拉低,你的状态机则要「等到 SCL 真的变高」而不是「等到计数器数够」。

    第二层是握手语义。新手爱用固定延时切状态,纯主机场景勉强能跑,但 ACK、时钟拉伸、重复起始都要求你等事件而不是等时间。你的采样点如果绑死在计数器上,从机稍微晚一点回 ACK,你采到的就是错的值。

    第三层是器件行为。EEPROM 有页写边界、有内部写周期、有地址回卷规则,这些不在协议本身里,但在题目里经常是隐藏考点。

    我的建议是:准备这类题时,把 testbench 里的从机故意写「坏」一点——延迟拉低 SCL、晚发 ACK、模拟忙周期,看你的主机是死等还是硬冲。能过这一关,说明你真理解了开漏和握手,而不是画了个漂亮的流程图。仿真能过只是及格线,面试官更想听你讲清楚「为什么这么设计」。你平时练手是用现成的 I2C 从机模型,还是自己从零写一个?

  • FPGA学习笔记

    画对时序图只说明你懂协议流程,笔试真正扣分的是电气细节:SCL 得真开漏、从机拉低时你必须等、SDA 方向切换要在正确那拍。从机不回 ACK,先看它有没有被给到释放 SCL 的窗口。

  • 数字IC入门

    顺着你踩的这个坑往下想,其实有个更根本的问题值得掰开:为什么「状态机画对了」会给你一种「这题我稳了」的错觉?因为状态机只描述了一件事——主机打算按什么顺序做事。而 I2C 跑不通,绝大多数时候不是顺序错,是「主机以为自己在控制总线,其实总线是大家共有的」。

    这个认知转变挺关键的,我分三层说,但主线只有一个:你写的是主机,不是总线的主人。

    第一层,SCL 从来不是你的私有信号。你只能拉低,或者放手让它被上拉电阻拉高。从机想慢一点,就在你放手的时候继续拉低,这就是时钟拉伸。如果你的代码里写的是「SCL <= 1」然后计数器数够就往下走,那从机拉不拉低你根本不关心,采样点自然全错。正确写法是把 SCL 建模成开漏:输出 0 或高阻,判断「SCL 真的变高了」才进下一步,而不是「我数到第 N 拍了」。

    第二层,SDA 是双向的,方向切换那一拍最容易出事。发送最后一个 bit 之后,到采样 ACK 之前,你必须把 SDA 释放成高阻,给从机一个拉低的机会。很多人这里忘切方向,或者切换晚了一拍,结果从机想 ACK 也 ACK 不出来,你就看到「从机一直不回 ACK」。你后来发现的 SDA 三态门没处理,八成就是卡在这。

    第三层,也是最容易被笔试忽略的:从机是有脾气的器件,不是理想模型。最典型的就是 EEPROM。你写完一页数据,它内部要搬数据到存储阵列,这段时间它「装死」,谁叫都不应。主机如果立刻发下一个 START,读到的是 NACK。这不是 bug,是器件在忙。正确做法是加一个 ack polling 状态,反复发器件地址直到收到 ACK 再继续。仿真里如果从机模型没建这个忙周期,你永远测不出来,一上真板就崩。

    所以准备这类题,我的建议不是多画几遍状态机,而是整理一张分层的易错清单:电气层(开漏、上拉、三态)、握手层(等事件而不是等时间、时钟拉伸、ACK 采样窗口)、器件层(写周期、地址位宽、重复起始)。状态机只是最上面那薄薄一层,下面的东西才是真正扣分的地方。

    你现在的 testbench 里从机是现成 IP 还是自己手写的?这个直接决定你能不能在仿真阶段就暴露上面这些问题——现成 IP 往往太乖,测不出你的毛病。

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

提问者

硅农预备役查看主页
浏览「其他」

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

相关问题

同分类

提问建议

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