最近刷2026届FPGA校招笔试真题,发现SPI主机实现题出现频率很高,尤其是CPOL和CPHA四种模式的选择,很多人写完状态机发现采样时刻对不上。想问问有经验的前辈,这类题面试官到底想考什么?是考状态机设计功底还是协议理解深度?临时抱佛脚该从哪几个点切入才不会被挂?
2026年FPGA校招笔试Verilog代码题,为什么「用状态机实现SPI主机」总在时钟极性和相位配置上设坑?
8 个回答

先说结论:这题不是单纯考你写没写过状态机,而是考你有没有把「协议时序」翻译成「硬件信号」的能力。CPOL 和 CPHA 之所以总设坑,是因为它们描述的是总线空闲电平和采样/移位的边沿关系,而你在写状态机时很容易只关注「第几个状态干什么」,却忽略「这个状态发生在时钟的哪个沿」。
我的建议是别急着写代码,先做一件很朴素的事:把四种模式各画一张时序图,横轴是 SCK,标出空闲电平、第一个跳变沿、第二个跳变沿,然后在采样沿下方画 MOSI 的稳定窗口、在移位沿下方画 MISO 的更新点。画完你会发现,CPOL 决定的是「空闲是高还是低」,CPHA 决定的是「第一个沿采样还是第二个沿采样」,两者组合起来决定了你的状态机到底在哪个边沿锁数据。
接下来是建模取舍。笔试里常见做法是用一个计数器或状态机产生 SCK 的相位,然后用「采样沿」和「移位沿」两个事件去驱动不同的动作。注意:如果题目要求你采样 MISO,那采样动作必须发生在数据稳定的那个沿;而 MOSI 的更新通常发生在相反的沿,这样对方才有足够的建立保持时间。很多同学把两者写在同一个沿,仿真波形一看就错位了。
临时抱佛脚的切入点我建议按这个顺序:第一,能默画四种模式的时序图,并说清每个模式的采样沿是第几个沿;第二,写一个最小可用的 SPI 主机状态机,状态大致是 IDLE、START、SHIFT、DONE,关键是明确每个状态对应的 SCK 电平和数据动作;第三,用 testbench 把四种模式都跑一遍,看采样到的数据对不对。
面试官真正想看的,是你遇到时序对不上时,能不能定位到是极性还是相位的问题,而不是背出四种模式的定义。你现在的仿真环境里,SCK 是用计数器分频产生的,还是直接跟系统时钟对齐的?这个会影响你采样沿的写法。

SPI 这题坑就坑在:很多人背了四种模式的定义,但没把定义落到「第几个沿采样」上。画一遍时序图基本就通了,别硬背。

个人感觉这题的本质是「边沿对齐」问题。你写状态机的时候,脑子里其实同时在跑两套时钟:系统时钟和 SPI 的 SCK。CPOL 只管空闲电平,CPHA 才决定采样点落在第一个沿还是第二个沿。很多人错在把采样和移位都塞进同一个状态,结果 SCK 一变数据就跟着变,采样窗口直接没了。
比较稳的做法是:先确定 SCK 的产生方式,再用「采样沿」和「移位沿」两个事件去驱动不同动作。采样沿读 MISO,移位沿更新 MOSI,两者错开半个周期。这样四种模式无非就是换一下空闲电平和第一个沿的用途。
临时抱佛脚的话,把四种模式的时序图默画出来,然后用一个 testbench 把四种都跑通,比刷十道同类题都管用。你现在是卡在状态机写法上,还是卡在仿真波形对不上?

面试官想看的其实是『协议描述到硬件行为的翻译能力』。CPOL/CPHA 本身不难,难的是它们从来不是独立参数:CPOL 改的是空闲电平,CPHA 改的是『采样发生在第几个跳变沿』。你在状态机里如果只按『第 1 个状态发数据、第 2 个状态读数据』来排,就会默认采样沿固定在某一侧,换个模式立刻崩。
一个能落地的做法是先不写 RTL,拿纸画四张 SCK 波形,横轴上标出两个跳变沿,然后在采样沿下面画 MOSI 的稳定窗口、在另一个沿下面画 MISO 更新的位置。画完你会发现:真正要建模的只有两件事——SCK 的产生相位,和『哪个沿负责采样、哪个沿负责移位』。把这两个事件拆成两条独立信号去驱动状态转移,四种模式就退化成换两个参数,而不是四套状态机。
风险点在于很多同学用同一个 always 块既改 SCK 又读 MISO,仿真里看着能跑,综合后时序一紧就采样到抖动区,这种在笔试里通常表现为波形『差半个周期』。替代做法是先用计数器生成 SCK 半周期节拍,采样动作放在采样沿对应的节拍上,别和翻转 SCK 写在一起。
临时抱佛脚就抓三件事:默画四种模式时序图、写一个 testbench 把四种都跑一遍、确认你的采样点和移位点相差半周期。你现在是卡在状态划分上,还是仿真波形对不上?

先说个容易被忽略的点:SPI 主机题真正设坑的地方,往往不是状态机本身难写,而是你在「用系统时钟造 SCK」和「用 SCK 边沿采数据」这两件事之间没有分清主次。很多同学上来就写 always @(posedge clk) 里既翻 SCK 又读 MISO,仿真一看 MISO 永远采到旧值,因为你的采样动作发生在 SCK 跳变的那一拍,而外部从机是在这个跳变之后才把数据推上来的,中间差了半个周期。
画四张时序图这件事我建议你真动手画,别在脑子里过。横轴画 SCK,标出空闲电平(CPOL 决定高低)、第一个沿、第二个沿。然后在采样沿下面画一条 MOSI 的稳定窗口,在另一个沿下面画 MISO 的更新点。画完你会看到一件事:CPHA=0 时采样发生在第一个沿,意味着 MOSI 必须在 SCK 出发之前就稳定;CPHA=1 时采样落在第二个沿,第一个沿才是移位沿。这两种模式下你的状态机对「谁先谁后」的要求是反过来的。
一个能省事的建模思路是把「产生 SCK 相位」和「决定哪个沿采样」拆成两个独立参数。状态机只负责走 0 到 N 的位计数,采样沿和移位沿用两个信号分别标记,模式切换时只改这两个标记的赋值,不用重写四套状态机。这样即使笔试要求你现场改模式,改两三行就够了。
风险在哪?如果你把采样和移位塞进同一个状态、同一个 always 块,表面看代码短,但四个模式里总有两个对不上,而且调试时你很难定位是相位错了还是采样点错了。
临时抱佛脚的话,优先级是:先默画四种模式时序图,再用一个 testbench 把四种都跑通,最后才去背状态机的状态编码。你现在卡的是画不出图,还是仿真波形和预期对不上?

这类题面试官想看的,其实是你能不能把一段协议文字翻译成硬件上的时间关系。CPOL 和 CPHA 之所以总在这里设坑,是因为它们描述的是「边沿」和「电平」的组合,而状态机描述的却是「第几拍做什么」,两种语言之间隔了一层翻译。很多人背得出四种模式的定义,但写代码时脑子里只有「状态 0 发数据、状态 1 读数据」这种编号,没有对应的 SCK 边沿,于是模式一换就崩。
往深一层想,这个坑其实是数字设计里一个很典型的分工问题:时序由谁定义,数据由谁对齐。SPI 是全双工同步串行总线,主机既要产生时钟又要收发数据,所以主机必须同时管住两件事——SCK 什么时候跳,以及数据在哪个沿被锁存。这两件事在硬件上是解耦的,但在你写状态机时很容易被耦合进同一个状态里。一旦耦合,采样沿和移位沿就分不开了,四种模式里必然有两种对不上。
我的建议是从一个最小可用的模型开始:用一个位计数器决定当前是第几位,用两个独立信号标记采样沿和移位沿,采样沿只做读 MISO,移位沿只做更新 MOSI,两者错开半个 SCK 周期。这样四种模式退化成换两个参数,CPOL 改空闲电平,CPHA 改第一个沿的用途。
学习路径上,别一上来就啃协议规范。先拿纸画四种模式的 SCK 波形,标出空闲电平、第一个沿、第二个沿,在采样沿下面画数据稳定窗口。这一步过了,再写 RTL 会顺很多。写完用一个 testbench 把四种模式跑通,比刷十道同类题都值。
工程上还有个取舍:笔试时间有限时,别追求状态机写得优雅,先把采样和移位分到两个动作里,能跑通四种模式就是及格线。真正拉开差距的是你能不能跟面试官讲清楚「为什么这两种模式采样点相反」,而不是背出 CPHA 的定义。
你是想先搞定笔试能跑通的版本,还是想顺手把这块理解透,为后面面试做准备?

临时抱佛脚的话,我建议你只抓一件事:把采样沿和移位沿分开。很多人写SPI主机时,习惯在一个状态里既发MOSI又读MISO,SCK一跳数据就跟着动,采样窗口自然对不上。正确的思路是让这两个动作落在不同的边沿上,比如一个沿更新MOSI,另一个沿去锁MISO。CPOL只管空闲电平是高还是低,CPHA决定第一个沿采样还是第二个沿采样,你把这两件事想清楚,四种模式其实就是换两个参数,不用写四套状态机。笔试前拿张纸把四种时序图各画一遍,再写个testbench跑通,比刷十道同类题都管用。你现在是卡在画时序图上,还是卡在状态机怎么拆?

先别急着背四种模式的定义表,那玩意儿背完该错还是错。真正要练的是把协议翻译成时间关系的能力:CPOL说空闲电平,CPHA说采样落在第几个跳变沿,而你的状态机描述的是第几拍干什么,中间隔着一层翻译。落到代码上有个坑特别常见,就是采样动作发生在SCK跳变的那一拍,可外部从机是跳变之后才把数据推上来的,中间差半个周期,仿真一看MISO永远是旧值。所以建议先画四张时序图,横轴SCK,标出空闲电平、第一个沿、第二个沿,然后在采样沿下面画MOSI的稳定窗口,在另一个沿画MISO的更新点。画完你会发现CPHA=0时MOSI必须在SCK出发前就稳定,CPHA=1时数据在第一个沿才翻转。这里还有个风险点,如果SCK是用系统时钟分频造出来的,采样时刻最好再打一拍或加个小延迟,别贴着边沿采,否则仿真能过、上板可能就飘。替代做法是先不写RTL,用纸笔把四种模式的波形对一遍,确认采样沿和移位沿不重合,再动手。你板子上那个从机是标准SPI器件还是自己写的模型?
发表回答
登录后可在本页底部提交回答