首先依次回答上篇提出的几个问题:
第一个问题:如何避免状态机产生lacth 示例如下,通过在always(*)语句块中,添加默认赋值,ns_state = cs_state;
always@(*)
ns_state = cs_state;
case(cs_state)
idle :
if(start)
ns_state =op1_state;
op0_state :
if(op0_over)
ns_state =op1_state;
op1_state :
if(op1_over)
ns_state =op2_state;
op2_state :
if(op2_over)
ns_state =op3_state;
op3_state :
if(op3_over)
ns_state =op4_state;
op4_state :
if(op4_over)
ns_state =op4_state;
default ns_state = idle;
endcase
这样,分支没有赋值的语句全部会赋值为ns_state = cs_state ;以IDLE状态为例,当前cs_state为idle。因此实际上 ns_state=idle。这条语句的作用,即在没有分支赋值的情况下,默认赋值当前状态。
第二个问题:更直观的独热码的状态机实现方式。示例如下
//状态定义
parameter idle == 0,
op0_state == 1,
op1_state == 2,
op2_state == 3,
op3_state == 4,
op4_state == 5;
//(1)当前状态
always@(posedge sys_clk or negedge rst_n)
if(!rst_n)
cs_state <=6'b000001;
else
cs_state <= ns_state;
//(2)下一状态的赋值
always@(*)
ns_state = 0;
case(1)
cs_state[idle] :
if(start)
ns_state[op0_state] =1'b1;
else
ns_state[idle] = 1'b1;
cs_state[op0_state] :
if(op0_over)
ns_state[op1_state] = 1'b1;
else
ns_state[op0_state] = 1'b1;
cs_state[op1_state] :
if(op1_over)
ns_state[op2_state] = 1'b1;
else
ns_state[op1_state] = 1'b1;
cs_state[op2_state] :
if(op2_over)
ns_state[op3_state] = 1'b1;
else
ns_state[op2_state] = 1'b1;
cs_state[op3_state]:
if(op3_over)
ns_state[op4_state] = 1'b1;
else
ns_state[op3_state] = 1'b1;
cs_state[op4_state] :
if(op4_over)
ns_state[idle]= 1'b1;
else
ns_state[op4_state] = 1'b1;
default ns_state[idle]= 1'b1;
endcase
//(3)输出状态
assign out1 = cs_state [op1_state];
always@(posedge sys_clk or negedge rst_n)
if(!rst_n)
out2_reg <= 1'b0;
else if (cs_state[op2_state])
out2_reg <= 1'b1;
else
out2_reg <= 1'b0;
上例中,定义状态机是,同样定义为0,1,2,3,4,5,6的值而不是独热码,只不过使用时,这些值用于赋值的为状态机的某一bit。值得注意的是,在ns_state 通过组合逻辑赋值时,首先需要将ns_state赋值为零,也就数说,除了需要赋值为1的状态,其他都需要赋值为0。但此种编码方式下,就需要谨慎对待分支赋值不全的情况,因此此时,ns_state会赋值为0。产生非想要的后果。
通过第三段的输出赋值可以看出,其输出分别是cs_state [op1_state]的直接输出,cs_state[op2_state]的寄存后一拍再输出。其产生的效果与前文(控制-上)中介绍的产生的效果是一致的。因此可根据习惯,选择一种的实现即可。
最后一个问题:状态机使用可以直观的通过定义的状态来控制各个信号的输出和控制,独热码本质上还是将状态机转变成一组某一时刻只有一个起效的寄存器,换个角度可以看做加强版的移位寄存器。其他需要注意问题有,
(1)如果状态机定义而没有使用,综合工具将综合掉此状态,因此综合后的状态会和工程师所定义的状态不同。通过检查综合文件,就能得知其对应关系,避免通过嵌入式逻辑分析仪抓信号时,信号与实际不一致现象。
(2)如通过单周期信号启动状态机,要注意,如单周期信号起效时,状态机未跳转回有效状态,会导致出错,应该将单周期信号转换成电平信号,等启动有效后再将电平信号拉低。
(3)状态如出现未定义的状态(如独热码出现全零状态),latch是其中一个主要的原因,上电后未有效复位也会导致此种可能。
(4)状态机结合移位寄存器可以有效减少状态的数目。例如某个状态中,每个周期要进行多个操作,不需要再分解成多个小状态,通过移位寄存器来控制这些状态的操作能够简化设计。
总之:状态机是FPGA设计的一项基本设计,而“独热码”和“三段式”的设计能够使设计达到事半功倍的效果。也是事实上行业内的FPGA设计的标准写法。而标准化能使难以理解的FPGA的代码能够有更好的移植和IP化的基础。