0

verilog 独热码状态机

前言

在查看ARM7TDMI源码的时候,我注意到他的内核ALU中的乘法器使用了一种不寻常的状态机,经过一番搜索终于确定了它就是独热码状态机。其实关于独热码状态机(one-hot state machine)念书的时候就有所了解,但是当时的了解仅限于一种状态机的编码方式,就跟格雷码或者二进制码一样,没有什么区别,直到看了大量英文资料才了解到这样一种状态机的优势所在——速度超快。

1. 什么是独热码?

其实独热码一点也不神秘,4’b0001,4’b0010,4’b0100,4’b1000这四个数值就组成了一组独热码。独热码的特点就是,每一个编码中只有1位是1,其他位都是0,这也是独热(one-hot)这个名字的来由。

2. 为什么使用独热码

首先,很明显,这样的一种编码方式是极其浪费硬件资源的,我们通过一组对比就可以很快知道这一点。如果一个状态机的设计状态是16个,使用二进制码的话,一共需要4个比特表示,也就是对应着4个寄存器资源,如果使用格雷码也是同理。而如果使用独热码的话,则一共需要16个寄存器资源。由此可见,当状态数量上升时,使用二进制码或格雷码可以有效节省寄存器资源,那为什么还要用独热码呢?

首先我们来想一想,为什么使用二进制码可以节省那么多寄存器资源?这是因为在4个寄存器的输入端我们设计了非常复杂的组合电路,用来对不同状态之间复杂的跳转关系进行译码。套用前面的举例来说,在16个状态的状态机中,任意比特为1(需要对应位的寄存器输出1)的状态都有8个,所以对任意位寄存器进行译码的组合逻辑都必须考虑到8种情况。因此,当状态数量大幅增加的时候,这些组合电路的规模增长也是非常可观的。寄存器之间的组合电路复杂度越高,系统能支持的最高频率就会下降。而在独热码状态机中,由于每一个寄存器只对应一种状态,译码电路就会非常简单,速度就会快很多。说起来,独热码状态机就是一种典型的以面积和功耗换取速度的设计方法。

3. 如何正确地设计独热码状态机

了解了独热码状态机的优势和工作原理,下面让我们来看看如何正确的设计一个独热码状态机。是不是把普通的二进制状态机编码方式改成独热码就可以了呢?当然不是!这个错误我在很多公司的设计中都看到过。设计错误的独热码状态机性能不仅不如普通的二进制状态机,浪费的寄存器资源更是后者的指数倍,因此学习正确的设计方法非常重要。下面我们来看一个标准的独热码状态机Verilog源代码。

localparam IDLE = 0;
localparam RUN  = 1;
localparam DONE = 2;
reg [2:0]  state;

always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= 3'd1;
end
else begin
state <= 3'd0;
case (1'd1) // synthesis parallel_case full_case
state[IDLE]: begin
if (go)  state[RUN]  <= 1'd1;
else     state[IDLE] <= 1'd1;
end
state[RUN]: begin
if (finished) state[DONE] <= 1'd1;
else          state[RUN]  <= 1'd1;
end
state[DONE]: begin
state[IDLE] <= 1'd1;
end
default: begin
state[IDLE] <= 1'd1;
end
endcase
end
end

首先是state <= 3’d0语句,实际上在一个设计正确的独热码状态机中,3’d0是一种绝对不可以出现的非法状态(甚至建议在代码中内置assertion对该状态进行监测以确保验证过程中不会出现与之有关的错误),但在这里作为首句写上是为了让后面的语句更简洁明晰。然后是case(1)分支块,在这个块中会依次对state的3个比特位(寄存器值)进行判断,如果某个比特位的值是1,则根据输入信号的情况,在下一个时钟沿到来时发生对应的状态跳转。这里值得注意的是,由于硬件设计的并行性,我们并不希望这几种状态之间出现竞争关系,因此需要加上// synthesis parallel_case 以确保综合出来的逻辑与设计意图吻合。

编辑 重设标签(回车键确认) 标为违禁 关闭 合并 删除

提问于 2019-05-03 09:07:11 +0800

这个帖子被标记为一个社区wiki

这个帖子是一个wiki(维基). 任何一个积分 >500的人都可以完善它

想向站长提问,微信扫码立刻加入! shawn的FPGA圈.png
1
  答案登陆可见 做站不容易,小伙伴支持一下我们吧!
编辑 标为违禁 删除 链接 更多选项...
Jackle910 头像

评论

这个跟一段式还是两段式关系不大,只要always块里用的是<=赋值,就可以保证状态跳转之后,前一个状态的索引位被清零。else begin下面的第一句state <= 3'd0是用来完成清零的,而所有的位的下一个状态都会在下一个时钟沿同时生效,不管是从0到1还是从1到0。

Altera_wiki 头像Altera_wiki ( 2019-05-03 09:14:15 +0800 )编辑
1

第十二行的case(1’d1)是什么意思?

编辑 标为违禁 删除 链接 更多选项...
yang9527 头像

评论

普通状态机的典型写法是 case(signal_name) 1’d0: begin end 1’d1: begin end 表示,当signal_name = 1’d0时怎样,signal_name = 1’d1时怎样 而独热码是反过来的 case(1’d1) signal_name[0]: begin end signal_name[1]: begin end 表示,当为1’d1的信号是signal_name[0]时怎样,当为1’d1的信号是signal_name[1]时怎样。由于独热码的编码结构决定了signal_name[0]和[1]不可能同时为1,因此他是可以正常工作的。

Altera_wiki 头像Altera_wiki ( 2019-05-03 09:17:10 +0800 )编辑
登录/注册后进行回答

提问工具

1 follower

统计

已提问: 2019-05-03 09:07:11 +0800

已查看: 51 次

最后更新: May 03 '19