
前言
Verilog编程技巧系列文章将聚焦于介绍Verilog的各种编程范式或者说技巧,编程技巧和编程规范有部分重合,但并非完全一样。规范更注重编码的格式,像变量命名、缩进、注释风格等,而编程技巧则更偏重更直观易读、更便于维护、综合效果更好的Verilog/SV代码写法,像:如何编写状态机、如何进行参数化设计、如何进行流水线设计等。可以说编程技巧就是一些编程套路,熟练掌握这些技巧可帮助我们更高效的完成FPGA开发工作。
本文是Verilog编程技巧系列的第一篇文章,介绍了如何编写三段式状态机。
在FPGA设计中,三段式状态机因其结构清晰、可靠性高等特点,成为实现状态机的最佳方式。本文主要说明了以下问题:
-
如何评价一个状态机写的好不好; -
为什么三段式状态机更好; -
为什么更推荐独热码; -
第三段输出逻辑应该怎么写。
最后,本文分享了Verilog与SV的三段式状态机模板。通过阅读本文,你将能够更深入的理解三段式状态机的写法及其背后的原理。
本文主要参考了Clifford E. Cummings的文章,他是数字电路设计领域的技术先驱,享有一定国际声誉。他的文章可从此网站(界面如下图)下载:http://www.sunburst-design.com/papers/。他的一些关于状态机的文章给本文的一些结论提供了理论和实验支撑。
一. 什么是状态机
状态机是描述系统行为的数学模型。包含状态
(系统某时刻状况)、转移
(状态间切换)、事件
(触发转移因素)和动作
(转移时执行的操作),用于分析和设计系统逻辑,在多领域广泛应用。
依据状态数量,状态机可分为两类:
特征 | 有限状态机(FSM) | 无限状态机(ISM) |
---|---|---|
全称与简称 | Finite State Machine,简称FSM | Infinite State Machine,简称ISM |
状态数量 | 有限 | 无限 |
转移方式 | 离散、明确条件 | 可能连续(如基于变量变化) |
典型应用 | 离散逻辑控制 | 连续系统或复杂动态系统 |
在HDL编码时,我们构建的总是有限状态机,所以这里我们说的状态机默认是指有限状态机
。
依据输出与当前状态的关系,状态机也可分为两类:
状态机类型 | 输出决定因素 | 特点描述 |
---|---|---|
Moore型(摩尔型) | 仅取决于当前状态 | 任意时刻,只要状态确定,输出就确定,输出与输入无关 |
Mealy型(米利型) | 取决于当前状态和输入信号 | 输出不仅和当前状态有关,还受当前输入的影响 |
这两种状态机的命名均来源于人名,看着比较奇怪。
实际应用中,无需刻意去区分状态机属于哪种类型
,没有什么意义。
在Verilog/SV编码中,状态机应用极其广泛,它几乎可以描述所有逻辑,也就是说如果你想,所有的模块的内部逻辑都可以写成状态机。所以,研究如何编写更好的状态机很有必要。
二. 一段、二段、三段与四段式状态机
在Verilog/SV中,状态机按功能可以分为三个部分:
-
状态转移
:负责在不同的状态之间进行切换,它决定了状态机如何从当前状态迁移到下一个状态。 -
状态判断
:根据当前状态和输入信号来确定下一个状态是什么。 -
输出逻辑
:根据当前状态机的状态来产生相应的输出信号。
根据这三部分在代码中用几个always块来描述,可将状态机分为以下几种:
2.1 一段式状态机
状态转移、状态判断与输出逻辑全都写在同一个always块中,称为一段式状态机
。示例如下:
// 状态定义
localparam S0 = 1'b0, S1 = 1'b1;
reg state;
// 状态转移、判断与输出
always @(posedge clk) begin
if (~rstn) {state, out} <= {S0, 1'b0};
else
case (state)
S0: if (in) state <= S1; else state <= S0; out <= 1'b0;
S1: if (in) {state, out} <= {S0, 1'b1}; else state <= S0;
default: {state, out} <= {S0, 1'b0};
endcase
end
出于排版的考虑,去掉了非必要的begin-end,尽量缩短了代码行数,下同。
2.2 二段式状态机
状态转移写在一个时序always块中
,状态判断与输出写在一个组合always块中
,称为二段式状态机。示例如下:
// 状态定义
localparam S0 = 1'b0, S1 = 1'b1;
reg state, next;
// 第一段:时序逻辑,状态转移
always @(posedge clk) begin
if (~rstn) state <= S0;
else state <= next;
end
// 第二段:组合逻辑,次态和输出逻辑
always @(*) begin
case (state)
S0: if (in) {next, out} = {S1, 1'b0}; else {next, out} = {S0, 1'b0};
S1: if (in) {next, out} = {S0, 1'b1}; else {next, out} = {S0, 1'b0};
default: {next, out} = {S0, 1'b0};
endcase
end
2.3 三段式状态机
状态转移写在一个时序always块中
,状态判断写在一个组合always块中
,输出写在一个时序或组合always块中
,称为三段式状态机。示例如下:
// 状态定义
localparam S0 = 1'b0, S1 = 1'b1;
reg state, next;
// 第一段:时序逻辑,状态转移
always @(posedge clk) begin
if (~rstn) state <= S0;
else state <= next;
end
// 第二段:组合逻辑,计算次态
always @(*) begin
case (state)
S0: next = in ? S1 : S0;
S1: next = in ? S0 : S0;
default: next = S0;
endcase
end
// 第三段:时序逻辑,输出逻辑
always @(posedge clk) begin
if (~rstn) out_a <= 1'b0;
else out_a <= (state == S1 && in) ? 1'b1 : 1'b0;
end
// 第三段:组合逻辑,输出逻辑
always @(*) begin
out_b = (state == S1 && in) ? 1'b1 : 1'b0;
end
对于第三段,因为时序逻辑的输出相对组合逻辑会慢一个时钟周期
,如果对输出延时有极高的要求,则可以考虑用组合逻辑。但通常还是建议用时序逻辑
,时序逻辑的输出更加稳定(无竞争和冒险),也更便于整体的时序分析和约束。
2.4 四段式状态机
四段式状态机是将三段式状态机的输出写成两个always块,一个固定为组合always块,写输出使能逻辑;另一个固定为时序always块,写输出逻辑。示例如下:
// 前面与三段式相同,仅第三段输出逻辑并分为两段
// 组合逻辑,输出使能
always @(*) begin
out_en = (state == S1 && in);
end
// 时序逻辑,输出逻辑
always @(posedge clk) begin
if (~rstn)
out <= 1'b0;
else if (out_en)
out <= 1'b1;
else
out <= 1'b0;
end
从实现的功能上来说,三段式和四段式完全相同,但在设计理念
、灵活性
和可维护性
等方面,两者存在明显区别:
对比维度 | 三段式状态机 | 四段式状态机 |
---|---|---|
核心思想 | 输出与状态/输入直接耦合,无中间控制信号。 | 引入输出使能信号(out_en ),分离输出条件与赋值逻辑。 |
代码结构 | 输出逻辑与状态转移混合在时序逻辑中。 | 输出使能(组合逻辑)与输出赋值(时序逻辑)分离,模块化更清晰。 |
输出条件修改 | 需直接修改时序逻辑中的条件判断,可能影响其他逻辑。 | 只需修改组合逻辑中的out_en ,输出赋值逻辑保持不变。 |
扩展性 | 新增输出条件需修改时序逻辑,可能导致冗余。 | 新增条件只需扩展out_en 逻辑,不影响输出赋值部分。 |
适用场景 | 简单场景(如固定序列检测、单一输出控制)。 | 复杂场景(如多条件输出、动态使能控制)。 |
总的来说,四段式状态机在负责场景有一些代码维护上的优势。
根据2019 CummingsSNUG2019SV_FSM 状态机设计.pdf
中的内容,四段式状态机在复杂逻辑的综合中会有一定优势,但三段式代码更加简洁,更符合直观逻辑,所以在FPGA开发中,作者推荐总是优先使用三段式
,仅在复杂场景开发的最后,将三段式改为四段式,以提升综合效果。
综合效果
:指的是综合工具对不同代码写法的优化支持程度。现代综合工具对四段式会有更好的优化效果,使得四段式在资源效率和时序性能方面通常优于三段式,但具体效果还需实测。
综上,针对FPGA开发,本文仅讨论三段式状态机,如需针对复杂场景进行优化,各位同学可根据需要将三段式改为四段式
,并实际比较综合后的资源效率和时序性能确定最终方案。
三. 什么样的状态机是好的状态机?
换句话说,评价一个状态机好坏的标准是什么
?一般来说,可以从以下4个维度进行评价:
-
可维护性:代码易于修改、扩展,适应需求变更。
-
可读性:代码结构清晰、逻辑紧凑,避免冗余,便于阅读理解。
-
可调试性:支持高效的仿真验证,便于在仿真或实测时快速定位问题。
-
综合效果:对综合工具友好,代码在转化为硬件时的面积、速度与功耗等性能指标优异。
上述四种状态机从这四个维度进行评价,表格如下:
指标 | 一段式 | 二段式 | 三段式 | 四段式 |
---|---|---|---|---|
可维护性 | ★ | ★★★ | ★★★ | ★★★ |
可读性 | ★ | ★★★ | ★★★ | ★★ |
可调试性 | ★ | ★★ | ★★★ | ★★★ |
综合效果 | ★★★ | ★ | ★★★ | ★★★ |
说明:
-
一颗星(★)表示“差”,两颗星(★★)表示“良”,三颗星(★★★)表示“优” 。 -
四段式的综合效果在非常复杂场景可能略好于三段式,但绝大多数场景下,三段式的综合效果同样优异,两者区别不大,所以两者都标注为三颗星。
对于一个功能需求来说,最重要的两件事是开发效率
和模块性能
。可维护性、可读性和可调试性影响开发效率,综合效果则影响模块性能。
综上,我们可以得出结论:
-
永远不应该用一段式状态机
:一段式状态机逻辑不清,导致代码难以维护、调试和扩展,极易成为”屎山代码”的一部分。其唯一优势(综合效果高)完全无法抵消后期维护的灾难性代价。即使在非常简单的场景中,也不应该使用。 -
不推荐用二段式状态机
:二段式虽然分离了部分逻辑,但输出仍依赖组合逻辑,存在输出毛刺风险和时序收敛困难,且可调试性和综合效果低于三段式,没有独特的应用场景优势。 -
不推荐直接用四段式状态机
:四段式虽然逻辑分层更彻底,但会导致代码冗余和开发效率下降。它仅适用于极少数复杂场景(例如多路独立输出需严格时序控制),对大多数设计属于”过度设计”。 -
仅推荐使用三段式状态机
:三段式是经过验证的最佳实践,没有短板。
四. 状态编码的类型和优缺点
状态编码指的是状态机的不同状态用怎样的二进制去表示,状态编码一般有以下几种类型:
-
二进制码(Binary):用二进制自然序列表示状态,N个状态需满足 2^N ≥ S
(S为状态总数)。8个状态编码为000, 001, 010, 011, 100, 101, 110, 111
。 -
格雷码(Gray Code):相邻状态仅有一位不同,形成循环单步跳变。3位格雷码为 000, 001, 011, 010, 110, 111, 101, 100
。 -
独热码(One-Hot):每个状态独占一个比特位,总位数等于状态数(N = S)。3个状态编码为 100, 010, 001
。 -
零空闲独热码(Zero-Idle One-Hot):独热码基础上引入全零( 000...
)作为空闲状态,其他状态为单一高位。5个状态编码为0000(空闲态), 1000, 0100, 0010, 0001
。 -
独冷码(One-Cold):与独热码相反,每个状态由唯一一个低电平位(0)表示,其他位为1。3个状态编码为 011, 101, 110
。 -
约翰逊码(Johnson Code):也称为扭环码(Twisted Ring Code),是一种特殊的环形移位码,相邻状态通过左移或右移,然后补移出位的反码生成,最终形成一个环。3位右移约翰逊码为 000, 100, 110, 111, 011, 001
(循环)。 -
混合编码(Hybrid Encoding):高位独热码 + 低位二进制码。状态数>20时,平衡资源与性能。
在FPGA设计中,零空闲独热码、独冷码与独热码的资源消耗和实现逻辑高度相似,而约翰逊码和混合编码的实际应用场景较为局限。因此,工程中的核心选择通常集中在以下三种编码:二进制码、格雷码和独热码。这三种编码方式的优缺点对比如下表:
指标 | 二进制码(Binary) | 格雷码(Gray Code) | 独热码(One-Hot) |
---|---|---|---|
时序逻辑资源消耗 (触发器,FF) |
★★★★☆ 极低:仅需 ⌈log2(N)⌉ 个触发器(N为状态数) |
★★★☆☆ 低:触发器数与二进制相同,但需额外触发器存储转换码(可选) |
★★☆☆☆ 高:需 N 个触发器,但FPGA中触发器资源充足 |
组合逻辑资源消耗 (LUT) |
★★☆☆☆ 中高:状态跳变需复杂组合逻辑(如状态译码器) |
★★★☆☆ 中:需异或门实现码值转换 |
★★★★☆ 极低:状态跳变仅需单比特操作 |
时序性能 | ★★☆☆☆ 中:多比特翻转限制频率 |
★★★☆☆ 中高:单比特跳变,但转换逻辑增加延迟 |
★★★★★ 最优:单比特跳变,路径最短,适合高频 |
功耗 | ★★☆☆☆ 高:多比特翻转动态功耗大 |
★★★★☆ 低:单比特翻转降低功耗 |
★★★☆☆ 中:静态功耗略高(触发器多),动态功耗低 |
容错性 | ★☆☆☆☆ 差:非法状态多,需额外检测 |
★★★☆☆ 中:非法状态较少 |
★★★★☆ 优:非法状态极易检测(非单比特为1) |
可读性 | ★★★☆☆ 中:自然顺序易理解,跳变逻辑复杂 |
★★☆☆☆ 中低:编码非自然排列,需查表辅助 |
★★★★★ 极优:每个状态对应唯一比特,仿真直观 |
推荐优先级 | ★★☆☆☆ – 资源敏感型设计 – 简单状态机 |
★★★☆☆ – 低功耗/跨时钟域 – 异步FIFO |
★★★★★ – FPGA高频设计 – 状态数≤16 |
星级
说明:
-
★★★★★:强烈推荐(显著优势,无替代方案) -
★★★★☆:推荐(优势明显,适用场景明确) -
★★★☆☆:一般推荐(需权衡利弊) -
★★☆☆☆:不推荐(仅限特定场景) -
★☆☆☆☆:避免使用(缺陷明显)
由此可以得出结论
:
-
二进制码(★★☆☆☆): -
仅限极简设计(如ASIC或低成本FPGA),需承担时序和功耗风险。 -
添加输出寄存器过滤毛刺。
-
-
格雷码(★★★☆☆): -
低功耗/跨时钟域专用,如异步FIFO指针同步,但需手动添加转换逻辑。 -
避免在复杂状态机中滥用。
-
-
独热码(★★★★★): -
FPGA设计首选,因LUT资源占用低、时序性能最优、容错性高,完美适配FPGA架构。 -
仅需注意状态数≤16(超过时可混合编码)。
-
所以,在FPGA中进行状态机设计时,总是应该使用独热码
。
五. 第三段输出应该用组合逻辑还是时序逻辑?
指标 | 组合逻辑输出 | 时序逻辑输出 |
---|---|---|
优点 | ✅ 零延时响应:输出随状态或输入立即变化,无时钟周期延迟。 ✅ 节省触发器资源:无需额外触发器。 |
✅ 无毛刺:输出在时钟边沿更新,规避组合逻辑竞争风险。 ✅ 时序友好:路径延迟要求宽松,易于高频设计。 |
缺点 | ❌ 毛刺风险:组合逻辑路径延迟差异可能导致下游电路误动作。 ❌ 时序收敛难:需满足组合逻辑路径时序约束。 |
❌ 延迟1周期:输出比状态变化晚一个时钟周期。 ❌ 触发器资源略增:需额外触发器(FPGA中影响可忽略)。 |
应用建议 | 🔴 总体不推荐! 仅用于对延迟极度敏感的信号(如实时中断响应)。 |
🟢 推荐默认方案! 适用于所有关键信号(如使能、数据总线),确保稳定性和可靠性。 |
结论:默认时序输出,非关键信号可组合输出
。
六. 第三段输出应该用case语句还是if-else语句?
case示例:
always @(posedge clk) begin
case (state)
S_IDLE: output <= 8'h00; // 空闲状态输出
S_START: output <= 8'h01; // 启动信号
S_WORK: output <= 8'hFF; // 工作状态全使能
default: output <= 8'h00; // 默认处理非法状态
endcase
end
if-else示例:
always @(posedge clk) begin
if (state == WORK)
if (counter > 100) output <= 8'h80; // 工作状态下根据计数调整输出
else output <= 8'h40;
else
output <= 8'h00;
end
case与if-else指标对比:
指标 | case语句 | if-else语句 |
---|---|---|
可读性 | ✅ 优:明确对应状态机所有状态,分支清晰易维护。 | ❌ 中低:嵌套层次多时逻辑混乱,尤其状态数>5时维护困难。 |
综合效果 | ✅ 高:综合工具自动识别为多路选择器(MUX),逻辑层级少,时序性能好。 | ❌ 中低:可能生成优先级编码逻辑,增加路径延迟(尤其未优化嵌套时)。 |
完整性检查 | ✅ 强制完整:需显式处理default 分支,避免遗漏状态,减少锁存器风险。 |
❌ 易遗漏:需手动覆盖所有条件,否则生成锁存器(组合逻辑中)。 |
灵活性 | ❌ 局限:仅基于单一状态变量分支,复杂条件需结合if-else 。 |
✅ 高:支持多条件组合(如if (state==S1 && input_valid) ,适合条件优先级场景。 |
仿真行为 | ✅ 确定性强:并行匹配所有状态,无优先级歧义。 | ❌ 优先级依赖:条件顺序影响行为,可能引入隐式逻辑错误。 |
应用建议 | 🟢 推荐默认方案! 可在 case 分支内嵌套if-else ,结合二者优势。 |
🔴 总体不推荐! 仅在需要条件优先级或同一状态内多条件分支时使用,并严格验证完整性。 |
结论:默认使用case语句,可在case分支内嵌套if-else,结合二者优势
。
七. 什么是反向case语句?有必要使用它吗?
-
传统case语句:根据一个多比特信号的不同取值选择分支,例如:
case (state)
3'b001: ... // 状态1
3'b010: ... // 状态2
3'b100: ... // 状态3
endcase -
反向case语句:reverse case,将
case
表达式固定为1'b1
,通过检测每个条件分支的布尔值是否为真来选择分支。这种写法仅适配独热码
,对于其它编码方式,这种写法是错误的。示例:case (1'b1) / synthesis parallel_case
state[0]: ... // 状态1
state[1]: ... // 状态2
state[2]: ... // 状态3
endcase
case语句和反向case语句的优缺点对比
:
对比维度 | 反向case | 传统case |
---|---|---|
资源消耗 | ✅ 独热码下更优(单bit匹配,逻辑简单) | ❌ 状态译码逻辑相对更加复杂 |
时序性能 | ✅ 潜在更优(逻辑层级少) | ⚠️ 依赖综合器优化能力(可能需额外约束) |
代码可读性 | ❌ 较差(需理解反向匹配模式) | ✅ 直观(直接匹配状态值) |
可维护性 | ❌ 复杂条件逻辑可能导致维护困难 | ✅ 结构清晰,易于扩展和调试 |
综合指令依赖 | ❌ 一般需添加 // synthesis parallel_case ,提示综合工具所有分支互斥,避免优先级逻辑, 错误使用可能导致逻辑错误 |
✅ 无需综合指令 |
适用编码 | ❌ 仅适配独热码 | ✅ 支持任意编码(二进制、格雷码、独热码等) |
分析:
-
综合工具优化能力在进步,反向case的综合效果优势已越来越小。
-
追求那一点微弱的性能优势,而牺牲代码可读性和可维护性是不划算的。
-
反向case通常需要搭配// synthesis parallel_case,易引起功能或仿真问题。
-
如果后期在维护时要修改编码(如改为混合编码),反向case将无法工作,修改工作巨大,这也是风险点之一。
综上,目前FPGA开发,建议总是使用传统case语句
,不使用反向case。
我注意到,一般只有很老(2010年以前)的书籍或文章还在推荐反向case,近几年的资料都不再推荐反向case,因为现在一般认为开发速度和可维护性比微小的性能优势更重要。
另外,建议在写case语句时,总是添加上default语句
,这样可避免生成锁存器,也在提醒我们考虑变量的所有可能取值。
八. 第三段输出应该用case(next)还是case(state)
state表示当前状态,next表示即将转换的下一个状态。
-
case(state)代码示例:
always @(posedge clk) begin
case (state) // 使用当前状态
S_IDLE: out <= 1'b0;
S_RUN: out <= (input_valid) ? 1'b1 : 1'b0;
default:out <= 1'b0;
endcase
end -
case(next)代码示例:
always @(posedge clk) begin
case (next) // 使用下一状态
S_IDLE: out <= 1'b0;
S_RUN: out <= (input_valid) ? 1'b1 : 1'b0;
default:out <= 1'b0;
endcase
end
case(state)与case(next)的优缺点对比:
对比维度 | case(state) | case(next) |
---|---|---|
输出生效时刻 | 当前状态对应的输出延迟一个时钟周期生效 | 下一状态对应的输出提前一个时钟周期生效, 与state的跳转对齐, 这是**case(next)**的最大优势 |
时序安全性 | ✅ 输出与当前状态严格同步,无逻辑冲突风险 | ⚠️ 需确保后续逻辑能正确处理提前生效的信号 |
逻辑可预测性 | ✅ 行为直观,符合常规设计模式 | ❗ 需额外注释说明“输出基于下一状态”,否则易误解 |
功能验证复杂度 | ✅ 仿真和调试简单(输出与状态严格对齐) | ❌ 需覆盖状态跳变边界的测试用例,验证复杂度高 |
适用场景 | 通用设计(如控制信号生成、状态指示) | 特殊需求(如预取数据、提前握手、低延迟响应) |
结论:
-
case(state)
:默认选择,安全、直观、易维护,适用于绝大多数场景。 -
case(next)
:特殊优化,需权衡延迟需求与验证成本,适用于高性能设计。 -
两者可配合使用,某些输出用case(state),另外一些用case(next), 但不推荐
,代码会比较乱,降低了可读性和可维护性。
九. Verilog三段式状态机模板(强烈推荐)
推荐总是用state和next作为状态变量的名称
,简单直观,不要使用类似c_state, n_state这种变量名,多此一举。
现代编码规范中,一个module内部只允许一个状态机,所以,也不用担心多状态机同名的情况。
//++ 三段式状态机-状态定义与转移 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//~ 状态定义
localparam IDLE = 3'd1 << 0; // 空闲态, 'h1
localparam WORK = 3'd1 << 1; // 工作态, 'h2
localparam DONE = 3'd1 << 2; // 结束态, 'h4
reg [2:0] state, next;
//~ 初始态与状态转移
always @(posedge clk) begin
if (~rstn)
state <= IDLE;
else
state <= next;
end
//-- 三段式状态机-状态定义与转移 ------------------------------------------------------------
//++ 三段式状态机-状态判断 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wire work_begin;
wire work_end;
reg done_end;
//~ 判断跳转到下一个状态的条件
always @(*) begin
next = state;
case (state)
IDLE: if (work_begin)
next = WORK;
WORK: if (work_end)
next = DONE;
DONE: if (done_end)
next = IDLE;
default: next = IDLE;
endcase
end
//-- 三段式状态机-状态转移 ------------------------------------------------------------
//++ 三段式状态机-输出逻辑 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
always @(posedge clk) begin
done_end <= done_end;
case (state)
IDLE: done_end <= 0;
WORK: ;
DONE: done_end <= 1;
default: done_end <= 1;
endcase
end
//-- 三段式状态机-输出逻辑 ------------------------------------------------------------
十. SystemVerilog三段式状态机模块
SV新增的语法特性在实现状态机时有一些优势,主要是枚举类型enum和unique case语句。
SV引入的这些特性统一了设计和仿真,也不再需要添加任何的综合指令。更多SV特性细节,见文末参考。
//++ 三段式状态机-状态定义与转移 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//~ 状态定义
enum logic [2:0] {
IDLE = 3'd1 << 0, // 空闲态, 'h1
WORK = 3'd1 << 1, // 工作态, 'h2
DONE = 3'd1 << 2 // 结束态, 'h4
} state, next;
//~ 初始态与状态转移
always_ff @(posedge clk) begin
if (~rstn)
state <= IDLE;
else
state <= next;
end
//-- 三段式状态机-状态定义与转移 ------------------------------------------------------------
//++ 三段式状态机-状态判断 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wire work_begin;
wire work_end;
logic done_end;
//~ 判断跳转到下一个状态的条件
always_comb begin
next = state;
unique case (state)
IDLE: if (work_begin)
next = WORK;
WORK: if (work_end)
next = DONE;
DONE: if (done_end)
next = IDLE;
default: next = IDLE;
endcase
end
//-- 三段式状态机-状态转移 ------------------------------------------------------------
//++ 三段式状态机-输出逻辑 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
always_ff @(posedge clk) begin
unique case (state)
IDLE: done_end <= 0;
WORK: ;
DONE: done_end <= 1;
default: done_end <= 1;
endcase
end
//-- 三段式状态机-输出逻辑 ------------------------------------------------------------
十一. 参考资料与Verilog编程技巧开源仓库分享
为更方便的分享最新的状态机模板,本人创建了开源仓库,未来此仓库将会新增更多关于Verilog/SV编程技巧的内容。两处仓库同步:
Gitee:https://gitee.com/xuxiaokang/Verilog-Coding-Techniques
Github:https://github.com/zhengzhideakang/Verilog-Coding-Techniques
目前仓库文件:
-
Verilog三段式状态机模板.v -
SyetemVerilog三段式状态机模板.sv
本文参考资料(如下图)通过网盘分享。
欢迎大家关注我的微信公众号:徐晓康的博客,回复以下6位数字获取网盘链接。
984102
建议复制过去不会码错字!
如果本文对你有所帮助,欢迎点赞、转发、收藏、评论让更多人看到,赞赏支持就更好了。
如果对文章内容有疑问,请务必清楚描述问题,留言评论或私信告知我,我看到会回复。

徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。