所有通常写状态机时也按照这三个模块将状态机分成三部分来写,如下面就是一种良好的状态机设计方法:
/*-----------------------------------------------------
This is FSM demo program
Design Name : arbiter
File Name : arbiter2.v
-----------------------------------------------------*/
module arbiter2 (
clock , // clock
reset , // Active high, syn reset
req_0 , // Request 0
req_1 , // Request 1
gnt_0 ,
gnt_1
);
//-------------Input Ports-----------------------------
input clock ;
input reset ;
input req_0 ;
input req_1 ;
//-------------Output Ports----------------------------
output gnt_0 ;
output gnt_1 ;
//-------------Input ports Data Type-------------------
wire clock ;
wire reset ;
wire req_0 ;
wire req_1 ;
//-------------Output Ports Data Type------------------
reg gnt_0 ;
reg gnt_1 ;
//-------------Internal Constants--------------------------
parameter SIZE = 3 ;
parameter IDLE = 3'b001 ,
GNT0 = 3'b010 ,
GNT1 = 3'b100 ;
//-------------Internal Variables---------------------------
reg [SIZE-1:0] state ; // Seq part of the FSM
wire [SIZE-1:0] next_state ; // combo part of FSM
//----------Code startes Here------------------------
assign next_state = fsm_function(req_0, req_1);
//------------fsm_function--------------//
function [SIZE-1:0] fsm_function;
input req_0; //parameter
input req_1; //parameter
begin
case(state)
IDLE :
if (req_0 == 1'b1)
fsm_function = GNT0;
else if (req_1 == 1'b1)
fsm_function = GNT1;
else
fsm_function = IDLE;
GNT0 :
if (req_0 == 1'b1)
fsm_function = GNT0;
else
fsm_function = IDLE;
GNT1 :
if (req_1 == 1'b1)
fsm_function = GNT1;
else
fsm_function =IDLE;
default : fsm_function = IDLE;
endcase
end
endfunction
always@(posedge clock)
begin
if (reset == 1'b1)
state <= IDLE;
else
state <= next_state;
end
//----------Output Logic-----------------------------
always @ (posedge clock)
begin
if (reset == 1'b1)
begin
gnt_0 <= #1 1'b0;
gnt_1 <= #1 1'b0;
end
else
begin
case(state)
IDLE :
begin
gnt_0 <= #1 1'b0;
gnt_1 <= #1 1'b0;
end
GNT0 :
begin
gnt_0 <= #1 1'b1;
gnt_1 <= #1 1'b0;
end
GNT1 :
begin
gnt_0 <= #1 1'b0;
gnt_1 <= #1 1'b1;
end
default :
begin
gnt_0 <= #1 1'b0;
gnt_1 <= #1 1'b0;
end
endcase
end
end // End Of Block OUTPUT_
endmodule
状态机通常要写成3段式,从而避免出现过大的组合逻辑。
上面说的都是可以通过流水的方式切割组合逻辑的情况,但是有些情况下我们是很难去切割组合逻辑的,在这些情况下我们又该怎么做呢?
状态机就是这么一个例子,我们不能通过往状态译码组合逻辑中加入流水。如果我们的设计中有一个几十个状态的状态机,它的状态译码逻辑将非常
之巨大,毫无疑问,这极有可能是设计中的关键路径。那我们该怎么做呢?还是老思路,减少组合逻辑。我们可以对状
态的输出进行分析,对它们进行重新分类,并根据这个重新定义成一组组小状态机,通过对输入进行选择(case语句)并去触发相应的小状态机,从而实现了将大的状态机切割成小的状态机。在ATA6的规范中(硬盘的标准),输入的命令大概有20十种,每一个命令又对应很多种状态,如果用一个大的状态机(状态套状态)去做那是不可想象的,我们可以通过case语句去对命令进行译码,并触发相应的状态机,这样做下来 这一个模块的频率就可以跑得比较高了。( 嵌套 )
总结:提高工作频率的本质就是要减少寄存器到寄存器的时延,最有效的方法就是 避免出现大的组合逻辑,也就是要尽量去满足四输入的条件,减少LUT级联的数量。我们 可以通过加约束、流水、切割状态的方法提高工作频率。
在FPGA中进行时钟设计时也要注意一下几点:
1. 一个模块尽量只用一个时钟,这里的一个模块是指一个module或者是一个entity。在多时钟域的设计中涉及到跨时钟域的设计中最好有专门一个模块做时钟域的隔离。这样做可以让综合器综合出更优的结果。
2. 除非是低功耗设计,不然不要用门控时钟(不用FPGA内部的全局时钟资源BUFG来控制触发器的时钟沿输入端而是采用组合逻辑和其它时序逻辑(如分频器)产生的信号作为触发器的时钟沿输入端http://www.cnblogs.com/crazybingo/archive/2010/12/08/1900388.html#)--这会增加设计的不稳定性,在要用到门控时钟的地方,也要将门控信号用时钟的下降沿 打一拍再输出与时钟相与。
3. 禁止用计数器分频后的信号做其它模块的时钟,而要用改成时钟使能的方式,否则这种时钟满天飞的方式对设计
的可靠性极为不利,也大大增加了静态时序分析的复杂性。
1.4 不同时钟域之间的同步
当一个设计中的两个模块分别用的是两个工作时钟,那么在它们的接口处就工作在异步模式,这时为了保证数据能正确的处理那么就要对两个模块进行同步。
这里的不同的时钟域通常是以下的两种情况:(分立的时钟源)
1、 两个时钟的频率不同;
2、 虽然两个时钟的频率相同,但是它们是两个独立的时钟,其相位没有任何关系。
分别如下两个图所示:

图10 两个时钟的频率完全不同

图11两个时钟的频率相同,但相位不相关 |