1、 状态机的Verilog分类、写法、编码方式
状态机:简单说是通过不同的状态迁移来完成一些特定的顺序逻辑。
状态机的分类
(1)Moore型:状态机的输出仅和当前状态有关。
(2)Mealy型:状态机的输出不仅与当前的状态有关,还取决于当前的输入条件。
.
当两者实现同一种功能的时候,Moore型状态机需要比Mealy型状态机多定义一个状态,且输出也比米利型延后一个时钟周期。这里之所以多一个状态,是因为Moore型的输出只取决于当前状态,因此需要有一个中间状态来产生一个输出。
用代码来区别两种状态机:
// Moore型
assign out = state == B ;//表示,输出只和状态B有关,当状态为B的时候,不管输入是多少,都输出1。
// Mealy型
assign out = ((state == B) && (in == 1)) ; //表示输出和状态以及输入有关,当状态为B且输入等于1的时候输出1.
用状态图来区别两种状态机:
比如用用两种方式,来实现同一功能的状态机。
Mealy型如下:
Moore型如下:(多一个状态)
状态机的写法
状态机一般包括三种写法:一段式、两段式和三段式。
(1)一段式:整个状态机写到一个always块里面,在该块中既描述状态寄存器,又描述状态转移和输出;
(2)二段式:用两个always块来描述状态机,一个always块采用同步时序描述状态转移;另一个块采用组合逻辑判断状态转移条件和状态输出;
(3)三段式:使用三个always块,一个always块采用同步时序描述状态转移,一个always采用组合逻辑判断状态转移条件,描述状态转移规律,另一个always块描述状态输出(可以用组合电路输出,也可以时序电路输出)。
状态机编码方式
独热码、格雷码。
独热码永远只有一位为1,缺点:用独热码后会有多余的状态;优点:可减少组合逻辑资源的消耗,比如在描述输出的时候,当采用组合逻辑进行比较的时候,可以只用其中的一位。assign state = S1[0] ;
格雷码相邻两个数只有一位不同;优点:状态少,而且可以避免毛刺出现;缺点:消耗组合逻辑资源多。assign state = S1;
注意:写case语句时,需要要加上default的情况,不然的话,会生成锁存器(跟电平有关),但在时序逻辑中,不加default生成的不是锁存器,是触发器。其中default分支可以用默认项表示,也可以用确定向来表示,以确保能最初状态。
2、根据状态转移图写状态机
小例子:
假如有三个状态,IDLE,S1,S2,在IDLE状态的时候,若en=1,则进入到S1状态,否则保持原状态不变。在S1状态的时候,若en=1,则进入到S2状态,否则保持原状态不变。在S2状态的时候,若en=1,则进入到IDLE状态,同时输出vld=1,否则保持原状态不变。
module state(clk,rst_n,en,vld);
input clk,rst_n;
input en;
output reg vld;
reg [2:0] state;
localparam IDLE= 3'b001; //parameter声明全局常量,可用在整个工程中,localparam仅在当前module中有效
localparam S1= 3'b010;
localparam S2= 3'b100;
reg [2:0] state_n;
reg [2:0] state_c;
always @ (posedge clk or negedge rst_n)begin
if(!rst_n)
state_c <= IDLE;
else
state_c <= state_n;
end
always @(*) begin
case(state_c)
IDLE:begin
if(en)
state_n = S1;
else
state_n = IDLE;
end
S1:begin
if(en)
state_n = S2;
else
state_n = S1;
end
S2:begin
if(en)
state_n = IDLE;
else
state_n = S2;
end
endcase
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
vld <= 0;
else if (state_c == S2 && en==1)
vld<=1;
else
vld <= 0;
end
endmodule
testbench
`timescale 1ns/1ps //时间精度
`define Clock 20 //时间周期
module state_tb;
reg clk,rst_n;
reg en;
wire vld;
state state1(
.clk(clk),
.rst_n(rst_n),
.en(en),
.vld(vld)
);
//-- 状态机名称查看器
localparam S0 = 4'b0001 ;
localparam S1 = 4'b0010 ;
localparam S2 = 4'b0100 ;
//2字符16位 这里是用ASICC表示,其中的S0就是IDLE
reg [15:0] state_name ;
always@(*)begin
case(state1.state_c)
S0: state_name = "S0";
S1: state_name = "S1";
S2: state_name = "S2";
default:state_name = "S0";
endcase
end
initial
clk = 0;
always #(`Clock/2) clk= ~clk;
initial begin
rst_n=0; #(`Clock*3);
rst_n=1;
end
initial begin
en=0;
#(`Clock);en=1;#(`Clock);en=0;#(`Clock);en=0;
#(`Clock*2); en=1;#(`Clock*2);en=1;#(`Clock*2);en=0;
#(`Clock*2);en=1;#(`Clock*2);en=1;#(`Clock*2);en=1;
#(`Clock*2);en=0;#(`Clock*2);en=1;#(`Clock*2);en=1;
#(`Clock*2);en=0;#(`Clock*2);en=0;#(`Clock*2);en=1;
#(`Clock*2); en=0;#(`Clock*2);en=1;#(`Clock*2);en=0;
#(`Clock*50);
$stop;
end
endmodule
3、售货机的状态机设计
举例子:设计一个关于售货机的状态机,要求只能投入一元和两元钱,脉动饮料的价格是四元,若超出则找零,否则不找,当达到四元时,给出脉动饮料,否则不给。
IDLE:初始状态,未投币或者已取出饮料。
S0 :投币一元的状态。
S1:投币两元的状态。
S2:投币3元的状态。
S3:投币4元的状态。(对应输出饮料)
S4:投币5元的状态。(对应找零)
三段式的写法进行verilog编写:
module Sys_Rst(
input clk,
input rst_n,
input [1:0] data,
output flag_0, //找零
output flag_1 //出饮料
);
parameter IDLE = 0,
S0 = 1,
S1 = 2,
S2 = 3,
S3 = 4,
S4 = 5;
reg [3:0] cur_state,next_state;
//第一段
always @(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
cur_state <= IDLE;
else
cur_state <= next_state;
//第二段
always @(*)
case(cur_state)
IDLE : begin
if(data == 2'b01 )
next_state <= S0;
else if(data == 2'b10)
next_state <= S1;
else
next_state <= IDLE;
end
S0 : begin
if(data == 2'b01 )
next_state <= S1;
else if(data == 2'b10)
next_state <= S2;
else
next_state <= S0;
end
S1 : begin
if(data == 2'b01 )
next_state <= S2;
else if(data == 2'b10)
next_state <= S3;
else
next_state <= S1;
end
S2 : begin
if(data == 2'b01 )
next_state <= S3;
else if(data == 2'b10)
next_state <= S4;
else
next_state <= S2;
end
S3 : next_state <= IDLE;
S4 : next_state <= IDLE;
default: next_state <= IDLE;
endcase
//输出
assign flag_0 = (cur_state == S4)? 1'b1: 1'b0; //找零
assign flag_1 = (cur_state == S3)? 1'b1: 1'b0; //出饮料
endmodule
testbench编写:
`timescale 1ns/1ns
module Sys_Rst_tb;
reg clk;
reg rst_n;
reg[1:0] d; //序列输入
wire flag_0 ;
wire flag_1 ;
Sys_Rst u1 (
.clk(clk),
.rst_n(rst_n),
.data(d), //序列输入
.flag_0(flag_0),
.flag_1(flag_1)
);
parameter IDLE = 0,
S0 = 1,
S1 = 2,
S2 = 3,
S3 = 4,
S4 = 5;
//4字符16位
reg [31:0] state_name ;
always@(*)begin
case(u1.cur_state)
IDLE: state_name = "IDLE";
S0: state_name = "S0";
S1: state_name = "S1";
S2: state_name = "S2";
S3: state_name = "S3";
S4: state_name = "S4";
default: state_name = "IDLE";
endcase
end
//
initial clk = 0;
always #1 clk = ~clk;
//
initial begin
rst_n = 0;
#1;
rst_n = 1;
end
//
initial begin
d = 2'b01; //1元
#2;
d = 2'b10; //2元
#2;
d = 2'b01; //1元
#2;
d = 2'b01; //1元
#2;
d = 2'b01; //1元
#2;
d = 2'b01; //1元
#2;
d = 2'b10; //2元
#2;
d = 2'b01; //1元
#3;
d= 2'b10; //2元
#20;
$stop;
end
endmodule
波形如下:
其中S3表示投入共四元,因此出饮料,因此flag_1拉高。
S3表示投入超过了四元,因此找零,因此flag_0拉高。
售货机如果有错误请指正~