verilog 独热码状态机-FPGA常见问题社区-FPGA CPLD-ChipDebug

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 以确保综合出来的逻辑与设计意图吻合。

请登录后发表评论