最近刷到好几套FPGA笔试题都要求用Verilog写SDRAM刷新控制器,我每次都在刷新周期计数和读写请求仲裁那里卡住。明明状态图画对了,代码也能综合,但仿真波形一看刷新请求总是被读写抢占,或者计数器溢出后状态跳错。想问问有经验的前辈,这类题的评分点到底在哪?是状态机编码方式还是仲裁逻辑的时序?40分钟里怎么分配才能把核心功能写完整?
2026年FPGA校招笔试Verilog代码题,为什么「用Moore型状态机实现SDRAM刷新控制器」总在刷新计数器和仲裁优先级上丢分?
8 个回答

先说结论:这类题真正考的不是状态机画得漂不漂亮,而是你有没有把它当成一个「带实时约束的调度系统」来写。我当年也卡在同一个地方,后来复盘发现,状态图只是骨架,丢分全在骨架之间的缝里。
先说刷新计数器。很多人写个 cnt 到某个值就置请求,仿真看着没问题,但评分老师会盯两件事:位宽够不够、溢出后行为对不对。SDRAM 刷新周期一般是 7.8us 量级,你系统时钟如果是 50M 或 100M,算一下需要多少拍,位宽少一位就会提前绕回,波形上表现为刷新请求忽早忽晚。更隐蔽的是复位那一刻:如果计数器没给初值或没和状态机对齐,第一个刷新周期会偏,边界测试一测就露。稳妥写法是让计数器从复位后固定初值开始,溢出时既产生请求又同步重载,别用「先清零再判断」那种顺序含糊的写法。
再讲仲裁。题目说「读写请求可能同时来」,本质是问你懂不懂优先级和防饿死。常见丢分是:读写随便给了高优先级,刷新一直排队,波形上刷新请求举了几十个周期都没被响应。正确思路是刷新优先级最高、但读写也不能无限等,可以用「刷新请求有效时锁定仲裁结果」或者给读写一个超时窗口。关键是仲裁逻辑要和状态机解耦:仲裁只输出「这一拍该给谁」,状态机负责按这个结果走命令时序。
状态机本身,三段式是加分项但不是必须,至少组合逻辑和时序逻辑要分开,别把 next_state 和输出搅在一起。刷新期间屏蔽读写命令这点很多人漏,仿真里读写刚好插进刷新窗口就出错。
40 分钟怎么分:前 10 分钟写计数器和请求生成,中间 15 分钟写仲裁加状态机主流程,剩下 10 分钟补屏蔽逻辑和复位,最后 5 分钟专门查边界——计数器满值、读写同时请求、复位中途来请求。顺序别倒过来,先搭时序骨架再补细节,不然时间不够。
你平时仿真用的是自己写的 testbench 还是现成激励?这个会影响你发现溢出问题的效率。

个人感觉这题最容易被忽略的是「刷新请求是个持续信号,不是脉冲」。很多人计数器一到就发一拍请求,结果状态机刚好在读写周期里,请求就丢了,波形看着就是被抢占。改成请求保持到被响应为止,问题基本消失一半。仲裁那边同理,别在组合逻辑里做太复杂的优先级链,容易出毛刺和时序问题。

先说个我自己的观察:这类题丢分的人,往往不是不会画状态机,而是把它当成了「先画图再填代码」的作业,没当成一个带实时约束的调度器来设计。所以我想沿一条主线讲:刷新计数器和仲裁其实是同一件事的两面,核心矛盾是「谁有权打断谁」。
先看刷新计数器为什么容易错。SDRAM 的刷新周期通常按 7.8us 量级算,你系统时钟 50M 就是约 390 拍、100M 约 780 拍。很多人写个 cnt 到某个值就置请求,仿真看着没问题,但评分会盯两件事:位宽够不够、溢出后行为对不对。位宽少一位就提前绕回,波形上表现为刷新请求忽早忽晚。更隐蔽的是复位那一刻,计数器没给初值或没和状态机对齐,第一个刷新周期就偏了,边界测试一测就露。稳妥写法是复位后从固定初值开始,溢出时既产生请求又同步重载,别用「先清零再判断」这种顺序含糊的写法。
再讲仲裁,这才是真正的分水岭。题目说读写请求可能同时来,本质是问你懂不懂优先级和防饿死。常见错误是把刷新请求做成脉冲,一拍就撤,结果状态机刚好在读写周期里,请求直接丢了,波形看着就是被抢占。正确做法是请求保持到被响应为止,也就是握手语义。仲裁逻辑本身尽量用时序或至少干净的组合,别在组合链里堆太深的优先级判断,容易出毛刺也难收敛。
顺着这条线往下想,其实还藏着一个取舍:刷新优先级该多高。刷新给最高优先级最省事,但读写也不能无限等,所以常见做法是刷新请求一旦有效,在当前命令周期结束后立刻插入,而不是硬插在周期中间。这要求你把状态机的命令发射点和仲裁结果对齐,三段式在这里的价值就出来了——组合段算 next state 和仲裁结果,时序段只做寄存,输出段干净。
40 分钟怎么分?我的建议是先花 5 分钟把刷新计时和请求生成写死,再 10 分钟搭仲裁,剩下时间接状态机,最后一定留 5 分钟专门检查复位和边界。因为评分点里「位宽、溢出、防饿死、刷新期间屏蔽读写」这几条,全是靠检查而不是靠灵感拿到的。你现在是习惯先写状态机再补计数器,还是反过来?这个顺序其实挺影响你丢分的位置。

我换个角度说吧。这题真正想看的不是状态机编码,而是你有没有意识到「刷新请求是个持续信号,不是脉冲」。计数器一到就发一拍,状态机刚好在读写周期里,请求就没了,波形一看就像被抢占。改成请求保持到被响应为止,问题基本消失一半。仲裁那边同理,别在组合逻辑里做太复杂的优先级链。
所以我的做法是:先把刷新计时和请求生成写死,再写一个简单的优先级仲裁——刷新最高,但读写之间也要有个说法,不然会饿死。最后才接状态机,而且尽量三段式,组合段只算 next state,时序段只寄存。这样刷新期间屏蔽读写命令就很自然了,因为仲裁结果本来就在命令发射前定好。
留五分钟检查复位和计数器溢出,比多写两行功能值钱。你系统时钟大概是多少,这个直接决定计数器位宽怎么取。

我个人偏向从面试官视角反推。如果我是出题人,40 分钟的题不可能指望你写出完整可综合的 SDRAM 控制器,所以评分点一定是那几个「一眼能看出你有没有工程意识」的地方:刷新计数器位宽和溢出、仲裁有没有优先级且不饿死刷新、状态机有没有清晰分离组合和时序、刷新期间有没有屏蔽读写命令。
这四条里,前两条是必查项,后两条是加分项。很多人栽在前两条,是因为写代码时脑子里只有「功能对不对」,没有「边界对不对」。计数器位宽少一位,仿真跑几万个周期才露馅,但评分老师可能直接看你的参数定义就扣分了。仲裁不饿死刷新,说白了就是刷新请求不能被读写请求无限压后,你得有个机制保证它最终会被响应。
至于 40 分钟分配,我的建议是先写刷新计时和请求生成,再写仲裁,最后接状态机。顺序反了容易返工,因为状态机的命令发射点依赖仲裁结果。最后留 5 分钟专门检查复位和边界,别省。
你现在是卡在不知道怎么算计数器初值,还是卡在仲裁写出来仿真不对?这两个的补法不太一样。

先说个容易被忽略的坑:很多人把「刷新计数器」当成一个纯粹的定时器写,结果它和状态机之间没有握手,仿真里就出现你说的被抢占。其实刷新请求应该是一个「电平型」的请求信号,计数器溢出时置位,被仲裁器响应后才清掉,而不是溢出那一拍发个脉冲就完事。脉冲型请求在读写周期正好覆盖时就是直接丢失,波形看着像刷新被抢,本质是请求根本没被记住。
另一个常见风险是计数器位宽。SDRAM 刷新周期一般按 7.8us 量级算,如果你系统时钟 50M,大概 390 拍左右,位宽取 9 位刚好够但没留余量,取 8 位就提前绕回,波形上表现为刷新忽早忽晚,评审一眼就看出来。稳妥做法是按最大可能时钟估一个上限,多留一两位,溢出时既产生请求又同步重载,别写成「先清零再判断」这种顺序含糊的代码,综合出来有时候和你脑补的不一样。
替代做法上,也可以把刷新计时和请求生成先单独写成一个模块或一段 always,仿真确认请求节奏对了,再接仲裁和状态机。这样返工成本低很多。你现在的系统时钟大概是多少,这个直接决定计数器的取值和位宽,方便的话说一下。
最后提一句,别在 40 分钟里追求状态机编码多优雅,评审真正盯的是边界和握手,先把这两块写稳比什么都值。

个人感觉,这类题丢分最集中的地方其实不是状态机本身,而是「刷新请求的生命周期」没想清楚。很多人写计数器一到就发一拍脉冲,结果状态机正好卡在读写周期里,这一拍就被吞了,波形上看就像刷新被抢占。正确做法是让请求保持住,直到仲裁器真正响应它才清掉,这样刷新只是被延迟,不会被丢掉。
仲裁那边同理,别在组合逻辑里堆太复杂的优先级链,刷新给最高优先级,但读写之间也要有个说法,不然会饿死一边。写代码顺序上我建议先写刷新计时和请求生成,再写仲裁,最后接状态机,因为命令发射点依赖仲裁结果,顺序反了容易返工。留五分钟专门查复位和计数器溢出,比多写两行功能值钱。你平时仿真会跑到复位后的第一个刷新周期吗?

先说一个很多人没意识到的角度:这题其实在考「你写的到底是硬件还是软件」。刷新计数器丢分,表面看是位宽和溢出,根子上是不少人把它当 C 语言的定时器在写——到点置个标志就完事。硬件里这个标志必须是一个能保持住的电平信号,被仲裁器真正响应之后才清掉。如果你写成溢出那一拍发脉冲,而状态机恰好在读写周期里,这一拍就没了,波形上看就是刷新被抢占,其实是请求根本没被记住。
顺着这个思路,仲裁也一样。别在组合逻辑里堆一长串优先级判断,容易出毛刺,综合出来时序也难看。常见做法是刷新给最高优先级,但读写之间也要有个说法,比如轮转或者固定优先级加一个「连续服务上限」,否则一边会被饿死。评分老师看的就是你有没有这个意识,而不是你写得有多花。
还有个更隐蔽的风险:复位后计数器的初值。如果复位时没给固定初值,或者和状态机的复位没对齐,第一个刷新周期就是偏的,边界测试一测就露馅。稳妥写法是复位后从固定初值开始,溢出时既产生请求又同步重载,别写「先清零再判断」这种顺序含糊的代码,综合结果可能和你脑补的不一样。
替代做法上,如果你时间紧,可以先用一个简单的分频计数器加一个保持型请求寄存器把刷新这条链路跑通,再回头优化仲裁,别一上来就追求完美调度。你现在的仿真里,有没有专门跑过复位后的第一个刷新周期?
发表回答
登录后可在本页底部提交回答