实现同样的功能,“10”序列检测器,的Moore机状态图如下:
由于Moore机的输出只与当前状态有关,因此实现同样功能的Moore机状态数往往多于Mealy机。
由于输出仅从当前状态组合产生,而下一状态从当前状态和输入共同组合产生。为了清晰,将这两个组合逻辑分开放在不同的always块中。多always块风格的状态机代码如下:
module test_machine4 (clk, rst_n, i, o);
input clk;
input rst_n;
input i;
output o;
reg o;
reg [1:0] state;
reg [1:0] nstate;
parameter S0=2'b00;
parameter S1=2'b01;
parameter S2=2'b10;
always @(posedge clk or negedge rst_n)
if(~rst_n)
state<=S0;
else
state<=nstate;
always @(state)
if(state==S2)
o<=1'b1;
else
o<=1'b0;
always @(state or i)
case(state)
S0:
begin
if(i)
nstate<=S1;
else
nstate<=S0;
end
S1:
begin
if(i)
nstate<=S1;
else
nstate<=S2;
end
S2:
begin
if(i)
nstate<=S1;
else
nstate<=S0;
end
default:
begin
nstate<=S0;
end
endcase
endmodule
单always块风格的状态机编码如下:
module test_machine3 (clk, rst_n, i, o);
input clk;
input rst_n;
input i;
output o;
reg o;
reg [1:0] state;
parameter S0=2'b00;
parameter S1=2'b01;
parameter S2=2'b10;
always @(posedge clk or negedge rst_n)
if(~rst_n)
begin
state<=S0;
o<=1'b0;
end
else case(state)
S0:
begin
if(i)
begin
state<=S1;
o<=1'b0;
end
else
begin
state<=S0;
o<=1'b0;
end
end
S1: // 注意,当前状态(S1)下可以有不同的输出,因为
begin
if(i)
begin
state<=S1;
o<=1'b0; //输出取决于下一状态,而非当前状态
end
else
begin
state<=S2;
o<=1'b1; //输出取决于下一状态,而非当前状态
end
end
S2:
begin
if(i)
begin
state<=S1;
o<=1'b0;
end
else
begin
state<=S0;
o<=1'b0;
end
end
default:
begin
state<=S0;
o<=1'b0;
end
endcase
endmodule
这两者综合后的参数相同:
Total LUTs:2
Register bits:2
Worst Slack:994.900
Estimated Frequency:196.1MHz
其仿真波形也与前面的Mealy机仿真波形一致。
有趣的是两者的RTL图看起来形式不同,实际上对应的门级网表是一样的:
多always块状态机RTL:
单always块状态机RTL:
下面对两者进行比较。
从输出与当前状态对应关系的描述上说,多always块代码的描述更清晰更接近于状态图,而单always块代码则看起来难以理解。这是因为多 always块状态机是组合输出,当前状态(state)变化立刻引起输出变化,这一点与状态图相符。而单always块是寄存器输出,当前状态(state)变化后,要等到下一次时钟到来检测到此变化才能引起输出的变化,这导致了输出滞后了一个时钟周期。因此,为了将输出提前,在单always 块的Moore机中输出是取决于下一状态而不是当前状态!在例子中,每次状态转移到S2时(state<=S2),必然伴随着输出1(o<= 1'b1)。而转移到其他状态时总是输出0。
多always块状态机往往是组合输出。(在这个例子中,由于输出只有1位,并且刚好等于一个状态位,因此在例子中的多always块状态机的输出刚好是直接从寄存器输出。)为了将该输出同步,需要再增加一级寄存器:
reg no;
将组合always块中对o的赋值都替换成对no进行,然后加入:
always @(posedge clk or negedge rst_n)
if(~rst_n)
o<=1'b0;
else
o<=no;
这样不但导致综合输出的逻辑变大,并且输出滞后了一个时钟,如图:
作为总结,对于Moore机,单always块风格代码与状态图的对应较差,难以理解,但能够产生寄存器输出。多always块风格代码清晰易懂,与状态图的对应较好,但通过组合逻辑输出,若再加寄存器同步则会导致输出滞后一个周期。
另一个有趣的现象:Active HDL 6.2根据状态图自动生成的状态机综合出的门级网表(带输出同步),与多always块综合出的门级网表(带输出同步)对比如下(上面是自动生成,下面是手动代码):
- 器件结构
器件结构对于逻辑实现的影响不言而喻。器件结构不但会影响所采用的综合技术,而且也与状态机的状态编码技术互相作用。
早期的器件由二极管、晶体管和简单的门构成。当时使用“状态编码邻接规则”来指导状态的编码和逻辑的化简。具体来说,是用卡诺图表示下一状态和输出函数,通过合并卡诺图中的“1”使得描述FSM组合逻辑部分的乘机项之和表达式中的乘机项最大,而数量最少。(当年我们作数字逻辑试验就干这个,先用卡诺图化简,然后用74系列搭逻辑)。随后技术发生了变化,而上面的原理依然是各种新算法的基础。
PLA出现时期先后有很多逻辑化简和状态编码方法。例如KISS、MAXAD、NOVA等。这些都是基于符号最小化的方法。它们假定逻辑功能采用两层PLA实现,并在这一假定下试图生成最小势的符号覆盖。
多层逻辑器件的出现,使得上面这些方法难以达到优化的效果。由于增加了搜索空间,同时难以构造评价标准,使得符号最小化在多层逻辑领域难以实现。著名的算法例如MUSTANG,它试图使得输出函数表达式中的公共项的数量和大小最大,通过对选定的状态对进行邻接编码分配来完成。
而查找表器件进一步增加了多层逻辑的挑战。在查找表FPGA中,无法使用项和文字的数量来评估功能实现的成本。例如,一个5输入LUT实现一个5输入与门(1个项,5个文字)与实现一个5输入异或门(16个项,80个文字)需要同要的开销。面向FPGA的综合中的特殊工作主要是对输出函数进行并行分解和顺序分解。并行分解是将一个多输出函数分解为多个模块,每个某块可用给定的逻辑块(如4输入1输出LUT)来实现,使得逻辑块的数量最少,逻辑块之间的互联最少。顺序分解是增加特定的过滤模块,将多输入函数的携带信息较少的部分输入进行预处理,产生精简的输出,作为函数逻辑功能的输入。主要算法有基于信息关系与度量的算法、基于语义分析的算法等。
可见,不同的器件类型需要不同的综合方法与之相适应。即使是同一种类的器件,内部结构不同也有不同的综合过程(看那些第三方的综合工具也真够累的),实现性能也有差别。
最后举一个例子。据说:Altera的FPGA内部没有三态逻辑(有用过Quartus的看它的Avalon总线模型,全是多路器),所有三态逻辑使用多路器来模拟。而Xilinx的FPGA内部有三态逻辑。那么这两种器件实现的三态总线性能上当有差别吧。
你说到点子上了,内部组合逻辑做的复杂与否对于性能的影响非常重要。我看到的好多文章都是把简化组合逻辑作为状态机综合优化的重点。
怎样把组合逻辑做的不复杂,这是很多综合领域的学者在研究的问题,有时候甚至要针对具体的器件结构来想办法。我们做设计的虽然不用太操心这个,但知道一点原理总是没坏处的吧。
状态机的性能只和状态迁移条件有关。我同意,前提是设计者有足够的能力描述这种状态迁移,并且综合工具总是能够找到实现状态迁移的最优方案。但往往在一个状态机的状态迁移条件已经确定的情况下难以寻找最优解,尤其是在输入较多的情况下综合工具需要采用启发式算法来寻找满意解。
国外学者把状态迁移也作为组合逻辑的一部分来考虑。组合逻辑的输入包括当前状态向量(和状态编码有关)和输入,组合逻辑的输出包括下一状态和输出。
我们怎么用HDL描述状态迁移条件,以及综合工具怎么理解它,器件怎么实现它,其间的差别都会影响所实现的状态机的性能。
举一个简单的例子。一个状态机,0输入,3输出(o1,o2,o3),3状态(s1,s2,s3)。它的功能是s1的时候3个输出分别为0,0,1,并转移到s2。s2的时候3个输出分别为0,1,0,并转移到s3。s3的时候3个输出分别为1,0,0,并转移到s1。
上面这个状态机,它的状态迁移条件是确定的。如果我们用Verilog来描述它。使用状态编码:
parameter s1=3'b001;
parameter s2=3'b010;
parameter s3=3'b100;
和另一种状态编码:
parameter s1=2'b00;
parameter s2=2'b01;
parameter s3=2'b11;
其它的代码都完全相同。两者综合出来的状态机性能是不同的。(我说的性能包括使用逻辑资源的数量和能够达到的频率。)前者占用的寄存器多,但能够达到的频率高。后者相反。
嘿嘿,给你出个题,用来说明组合逻辑的简化:
编号 输入(x1~x6) 输出(y1~y4)
0 x1 x2 x3 x4 x5 x6 y1 y2 y3 y4
1 0 - 0 0 0 1 0 1 1 0
2 0 0 0 1 0 - 1 1 - 0
3 1 - 0 0 1 - 1 1 1 0
4 1 1 1 1 1 - 0 - 0 1
5 - 0 1 - 0 0 - 0 - 0
上面是组合逻辑的真值表,把这个组合逻辑用若干个2输入2输出的LUT来实现。要求使用的LUT数量尽量少,LUT之间的连接线尽量少(允许其中某些LUT的输出作为其他LUT的输入)。
这是一篇外国文章上的例子,可惜我拿到的电子版的计算过程好像有点小问题,不知道是不是扫描没扫清楚。dulianwei兄如果有兴趣可以想想答案。其他看到这片帖子的xdjm也欢迎进来讨论。







