概述
Verilog4——状态机的原理、结构与设计
一、知识讲解:
为什么要使用状态机?
在RTL级的Verilog语言描述当中,通过时序逻辑抽象所得到的有限状态机是其描述的依据。因此,把时序逻辑抽象转换成一个同步有限状态机是设计可综合风格的VerilogHDL模块的关键。(而对于组合逻辑,掌握可综合的组合逻辑设计实例即可。)
有限状态机:
在有限个状态之间按一定规律转换的时序电路。
时钟同步的状态机结构——Mealy&Moore:
- **Mealy:**Mealy 型状态机的输出,不仅与当前状态有关,还取决于当前的输入信号。
Mealy 型状态机的输出是在输入信号变化以后立刻发生变化,且输入变化可能出现在任何状态的时钟周期内。因此,同种逻辑下,Mealy 型状态机输出对输入的响应会比 Moore 型状态机早一个时钟周期。
- **Moore :**Moore 型状态机的输出只与当前状态有关,与当前输入无关。
输出会在一个完整的时钟周期内保持稳定,即使此时输入信号有变化,输出也不会变化。输入对输出的影响要到下一个时钟周期才能(通过输入改变——状态改变——输出改变的流程)反映出来。
这也是 Moore 型状态机的一个重要特点:输入与输出是隔离开来的。
有限状态机设计步骤:
1-逻辑抽象得到状态转移图:把实际问题中的逻辑转换关系(比如交通灯转换、序列检测中的位数变化、自动售货机的投币和输出等等)表示成时序逻辑函数,表达形式可以是状态转换表或状态转换图。
1,分析问题,确定输入输出变量(接口定义)、电路状态数目(状态定义);
2,定义输入输出逻辑状态含意,为状态顺序编号;
3,画出转换图/表;
2-状态化简:合并等价状态,得到最简转换。
3-状态分配编码:选定编码方式(多选用独热码)为各个状态赋编码定义。【独热码每次每个状态中只有一个寄存器置位,可以既保障电路性能又充分发挥 触发器数量多的优势。】
4-选定触发器类型,求状态转移方程和输出方程。
5-描述逻辑转换图:使用VerilogHDL抽象建模语句,always和case等语句以及赋值语句即可实现。
可综合状态机语言指导原则:
1-建议采用独热码为状态进行编码;状态必须明确赋值。通常使用参数parameter或宏定义define语句加上赋值语句来实现
//parameter
parameter IDLE=5'b00000,
START=5'b00001;
current = START;
//define
`define IDLE 5'b00000
`define START 5'b00001
current = `START;
2-建议采用case,casex,casez语句来建立状态机的模型;
3-always块中,
//每个always块中只能有一个事件控制always@(event-expression)
always@(posedge clk or negedge rst_n)
//带有posedge或negedge关键字的事件表达式表示边沿触发的时序逻辑,没有这两个关键字的表示组合逻辑或者电平敏感的触发器。
always@(posedge clk or valid)
//每个表示时序逻辑的always块只能由一个时钟跳变沿触发,置位或复位最好也由这个时钟边沿触发。
//每个always块中的赋值信号都必须定义为reg型或integer型
//每个纯表示组合逻辑的always块中,参与赋值的所有信号都要有明确的值,即赋值表达式右端的信号都必须在always@(敏感电平列表)中列出。(未列出但参与赋值的变量会被放在一个透明锁存器中暂存起来)
always@(a or b or c)begin
e = d & a & b;//d的变化不会立马引起e的变化,要等待ab变化后再执行计算
end
//对一个reg型或integ型变量赋值,只能在一个always块中进行。
典型三段式状态机:
`timescale 1ns/1ns
//**输入输出接口**//
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
//**状态编码、参数定义**//
parameter IDLE=0,ONE=1,TWO = 2,THREE = 3,FOUR= 4,FIVE= 5,SIX= 6,SEVEN= 7,EIGHT= 8;
reg [7:0]cur_state, nxt_state;
//**第一段:时序逻辑,次态赋值给现态**//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cur_state <= IDLE;
end
else begin
cur_state <= nxt_state;
end
end
//**第二段:组合逻辑,描述现态和输入到输出之间的逻辑转换**//
always@(*)begin
case(cur_state)
IDLE: nxt_state = (a==0) ?ONE :IDLE;//必须在第一个要输入一个0,才能启动,不能把复位的0当成初始状态
ONE: nxt_state = (a==0) ?ONE :TWO;
TWO: nxt_state = (a==0) ?ONE :THREE;
THREE: nxt_state = (a==0) ?ONE :FOUR;
FOUR: nxt_state = (a==0) ?FIVE :IDLE;
FIVE: nxt_state = (a==0) ?SIX :TWO;
SIX: nxt_state = (a==0) ?SEVEN :TWO;
SEVEN: nxt_state = (a==0) ?ONE :EIGHT;
EIGHT: nxt_state = (a==0) ?ONE :THREE;
default: nxt_state = IDLE;
endcase
end
//**第三段:同步时序逻辑,描述状态输出**//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
match <= 0;
end
else if (cur_state == EIGHT)begin
match <= 1;
end
else
match <= 0;
end
endmodule
二、实例应用:
状态转移表实现时序电路
题目描述:
思路分析:
要求使用D触发器实现,可通过状态转移表得到输出的逻辑表达式.由题目可以看出,输出Y是与输入A和当前状态Q1nQ0n都有关系。
基础的时序电路设计,可采用列激励方程、输出方程,进而用D触发器和组合逻辑电路实现的方案。
由状态表可得出,电路共4个状态,所以使用2个寄存器来实现状态的寄存。两个寄存器的输出为Q1和Q0。
可以得到次态Q1(n+1)、Q0(n+1)、输出Y的逻辑表达式为:
Q1n+1=D1=A⊕Q1n⊕Q0n
Q0n+1=D0=Q0n
A=Q0n*Q1n
代码实现:
`timescale 1ns/1ns
module seq_circuit(
input A ,
input clk ,
input rst_n,
output wire Y
);
reg q0, q1;
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
q1 <= 0;
end
else begin
q1 <= A ^ q0 ^ q1;
end
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
q0 <= 0;
end
else begin
q0 <= ~q0;
end
end
assign Y = q0 & q1;
endmodule
方法二:状态机实现
//两种方法:
//一是写表达式用D触发器来描述
//二是写状态机描述状态转移过程
`timescale 1ns/1ns
module seq_circuit(
input A ,
input clk ,
input rst_n,
output wire Y
);
reg[1:0]current_state,next_state;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
current_state <= 2'b00;
next_state <= 2'b00;
end
else begin
current_state<= next_state;
end
end
always@(*)begin
case(current_state)
2'b00: begin
next_state = (A == 0)? 2'b01 : 2'b11;
end
2'b01: begin
next_state = (A == 0)? 2'b10 : 2'b00;
end
2'b10: begin
next_state = (A == 0)? 2'b11 : 2'b01;
end
2'b11: begin
next_state = (A == 0)? 2'b00 : 2'b10;
end
default: next_state = next_state;
endcase
end
assign Y = (current_state == 2'b11)? 1: 0;
endmodule
输入序列连续的序列检测
题目描述:
思路分析:
1-有限状态机:输入信号在8个状态之间转移,由状态机描述即可。
2-移位寄存器:设置一个和序列等长的寄存器,每个时钟都将输入移入寄存器的最低位,并判断寄存器中的值是否与序列相同。
代码实现:
//方法一:有限状态机
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
//parameter IDLE=0,ONE='b0,TWO = 'b01,THREE = 'b011,FOUR= 'b0111,FIVE= 'b01110,SIX= 'b011100,SEVEN= 'b0111000,EIGHT= 'b01110001;
parameter IDLE=0,ONE=1,TWO = 2,THREE = 3,FOUR= 4,FIVE= 5,SIX= 6,SEVEN= 7,EIGHT= 8;
//parameter IDLE='b00000000,ONE='b00000000,TWO = 'b00000001,THREE = 'b00000010,FOUR= 'b00000100,FIVE= 'b00001000,SIX= 'b00010000,SEVEN= 'b00100000,EIGHT= 'b01000000;
//不理解这里的编码有什么影响?
reg [7:0]cur_state, nxt_state;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cur_state <= IDLE;
end
else begin
cur_state <= nxt_state;
end
end
always@(*)begin
case(cur_state)
IDLE: nxt_state = (a==0) ?ONE :IDLE;//必须在第一个要输入一个0,才能启动,不能把复位的0当成初始状态
ONE: nxt_state = (a==0) ?ONE :TWO;
TWO: nxt_state = (a==0) ?ONE :THREE;
THREE: nxt_state = (a==0) ?ONE :FOUR;
FOUR: nxt_state = (a==0) ?FIVE :IDLE;
FIVE: nxt_state = (a==0) ?SIX :TWO;
SIX: nxt_state = (a==0) ?SEVEN :TWO;
SEVEN: nxt_state = (a==0) ?ONE :EIGHT;
EIGHT: nxt_state = (a==0) ?ONE :THREE;
default: nxt_state = IDLE;
endcase
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
match <= 0;
end
else if (cur_state == EIGHT)begin
match <= 1;
end
else
match <= 0;
end
endmodule
//方法二:移位寄存器
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
reg[7:0] a_r;
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
a_r<='b0;
end
else begin
a_r<={a_r[6:0],a};
end
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
match <= 1'b0;
end
else begin
match <= a_r==8'b01110001;
end
end
endmodule
含有无关项的序列检测
题目描述
思路分析:
解法有两种:状态机法和序列缓存对比法。
状态机法的过程:在初始状态中,先判断第一位是否符合,若符合则进入下一个状态,判断第二位是否符合;若第一位不符合则保持在初始状态,直到第一位匹配。如前两位匹配,则判断第三位是否符合,若第一位匹配,最新输入的数值和目标序列的第二位不匹配,则根据最新一位是否匹配第一位,进入第一位匹配状态或者初始状态。依次类推。
而在本题中,有空闲状态IDLE和序列状态0,01,011,011x,011xx,011xxx,011xxx1,011xxx11,011xxx110,即S0-S8共10个状态,状态IDLE,S0,S1,S2,的跳转逻辑与前一题目的状态机相同,而中间无关项的状态S3,S4,S5会影响到后续S6,S7,S8的跳转,如若无关项S5时a的序列为011011,则a=0,S5跳往S2,a=1,S5跳往S6,而若S5时a的序列为011010则a=0,S5跳往S0,a=1,S5跳往S6,这里需要判断三个无关项之后的跳转去向。
序列缓存对比法,则是将九个时刻的数据缓存,作为一个数组,每个时刻的输入位于数组的末尾,数组其它元素左移,把最早输入的数据移出。然后截取数组的前三位和目标序列011对比,截取数组的后三位和目标序列110对比,如果两段数组都和目标序列相等,则说明出现目标序列。
代码实现:
`timescale 1ns/1ns
//序列检测法:本题应用序列检测相对更简单
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
reg[8:0]a_reg;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
a_reg <= 9'b0;
else
a_reg <= {a_reg[7:0],a};
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
match <= 0;
end
else begin
match <= (a_reg[2:0] == 'b110) && (a_reg[8:6] == 'b011);
end
end
endmodule
//状态机法
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
parameter IDLE = 4'd0;
parameter S0 = 4'd1;
parameter S1 = 4'd2;
parameter S2 = 4'd3;
parameter S3 = 4'd4;
parameter S4 = 4'd5;
parameter S5 = 4'd6;
parameter S6 = 4'd7;
parameter S7 = 4'd8;
parameter S8 = 4'd9;
reg [3:0] seq_cs;
reg [3:0] seq_ns;
reg [2:0] xxx_cs;
reg [2:0] xxx_ns;
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
seq_cs <= IDLE;
xxx_cs <= 3'b000;
end
else begin
seq_cs <= seq_ns;
xxx_cs <= xxx_ns;
end
end
always@(*) begin
seq_ns = IDLE;
xxx_ns = 3'b000;
case(seq_cs)
IDLE: begin
if(a == 0) seq_ns = S0;
else seq_ns = IDLE;
end
S0: begin
if(a == 0) seq_ns = S0;
else seq_ns = S1;
end
S1: begin
if(a == 0) seq_ns = S0;
else seq_ns = S2;
end
S2: begin
seq_ns = S3;
xxx_ns = {xxx_ns[1:0], a};
end
S3: begin
seq_ns = S4;
xxx_ns = {xxx_ns[1:0], a};
end
S4: begin
seq_ns = S5;
xxx_ns = {xxx_ns[1:0], a};
end
S5: begin
if(a == 0 && xxx_cs == 3'b011) seq_ns = S2;
else if(a == 0) seq_ns = S0;
else seq_ns = S6;
end
S6: begin
if(a == 0) seq_ns = S0;
else seq_ns = S7;
end
S7: begin
if(a == 0) seq_ns = S8;
else seq_ns = IDLE;
end
S8: begin
if(a == 0) seq_ns = S0;
else seq_ns = S1;
end
endcase
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
match <= 1'b0;
end
else begin
match <= (seq_cs == S8);
end
end
endmodule
不重叠序列检测
题目描述
思路分析:
同样有两种方法:状态机法和移位寄存器法
状态机法:题目要求,一旦出现不匹配序列,则这一段的6位数跳过不再检测,且这一批次输入结束后给出不匹配信号,并等待下一批次的六位序列再开始检测,若序列完全符合则6位输入结束后输出匹配信号。那么状态可以设置为闲置状态:
IDLE,S0,S1,S2,S3,S4,S5,SF0,SF1,SF2,SF3,SF5,这种状态设计的转移图为
或者,状态设置为,ZERO,S0,S1,S2,S3,S4,S5,FAIL,将所有不符合的状态设置为FAIL,同时添加时钟计数,满6清零,在FAIL状态中, 只有当计数器计满方可跳入下一状态从而实现序列的批次检测。状态转移如下:
移位寄存器:同样的,寄存器中的数据只能在计数器计满时符合待检测序列才算满足条件
代码实现:
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
output reg match,
output reg not_match
);
parameter IDLE = 0,A=1,B=2,C=3,D=4,E=5,F=6,FAIL=7;
reg[2:0]cur_state,nxt_state,cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= nxt_state;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else
cnt <= (cnt==3'd6)? 1 : cnt+'d1;
end
always@(*)begin //这里要写成组合逻辑形式的always
if(!rst_n)
nxt_state = IDLE;
else
case(cur_state)
IDLE:nxt_state = (data == 0)?A:FAIL;
A:nxt_state = (data == 0)? FAIL: B ;
B:nxt_state = (data == 0)? FAIL: C ;
C:nxt_state = (data == 0)? FAIL: D ;
D:nxt_state = (data == 0)? E: FAIL ;
E:nxt_state = (data == 0)? F: FAIL ;
F:nxt_state = (data == 0)? A: FAIL ;
FAIL:nxt_state = (data==0 && cnt==3'd6)?A:FAIL;
default: nxt_state = IDLE;
endcase
end
//match和not_match都是在检测完一段序列之后才会有判断是否拉高,没检测完整时不做判断
always@(*)begin //这里要写成组合逻辑形式的always
if(!rst_n)begin
match = 0;
not_match = 0;
end
else if(cnt==3'd6) begin
if(cur_state == F)begin
match = 1;
not_match = 0;
end
else if(cur_state == FAIL)begin
match = 0;
not_match = 1;
end
else begin
match = 0;
not_match = 0;
end
end
else begin
match = 0;
not_match = 0;
end
end
endmodule
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
output reg match,
output reg not_match
);
reg [2:0] cnt;
reg [5:0] data_r;
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
cnt <= 0;
else
cnt <= cnt==5? 0: cnt+1;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
data_r <= 6'b0;
else
data_r <= {data_r[4:0], data};
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
match <= 1'b0;
not_match <= 1'b0;
end
else begin
match <= (cnt==5) && ({data_r[4:0], data}==6'b011100);
not_match <= (cnt==5) && ({data_r[4:0], data}!=6'b011100);
end
end
endmodule
输入序列不连续的序列检测:
题目描述:
思路分析:
状态机法:相当于状态机的跳转控制信号变成了两个,输入的序列数据data和使能信号data_valid,状态仍设为IDLE,S0,S1,S2,S3,状态转移图如下:
移位寄存器:寄存器中的序列值只有在使能信号data_valid有效时才可以更新(移位读入)
代码实现:
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
input data_valid,
output reg match
);
parameter IDLE=4'd0,A=1,B=2,C=3,D=4;
reg[2:0]cur_state,nxt_state;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= nxt_state;
end
always@(posedge clk or negedge rst_n)begin
case(cur_state)
IDLE:begin
nxt_state = (data_valid==1 && data==0) ? A : IDLE;
end
A:begin
if(data_valid)begin
if(data)
nxt_state = B;
else
nxt_state = A;
end
else begin
nxt_state = A;
end
end
B:begin
if(data_valid)begin
if(data)
nxt_state = C;
else
nxt_state = A;
end
else begin
nxt_state = B;
end
end
C:begin
if(data_valid)begin
if(data)
nxt_state = IDLE;
else
nxt_state =D;
end
else begin
nxt_state = C;
end
end
D:begin
if(data_valid)begin
if(data)
nxt_state = IDLE;
else
nxt_state = A;
end
else begin
nxt_state = IDLE;
end
end
default: nxt_state =IDLE;
endcase
end
always@( cur_state)//这里同样地,要写成边沿触发的组合逻辑
begin
if(!rst_n)begin
match <= 0;
end
else begin
match <= (cur_state == D)? 1:0;
end
end
endmodule
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
input data_valid,
output reg match
);
reg [3:0] data_r;
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
data_r <= 4'b0;
else
data_r <= data_valid? {data_r[2:0], data}: data_r;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
match <= 0;
else
match <= data_r[2:0]==3'b011 && data==0; // && ~match;
end
endmodule
最后
以上就是活力大船为你收集整理的Verilog4——状态机的原理、结构与设计以及序列检测功能实现的全部内容,希望文章能够帮你解决Verilog4——状态机的原理、结构与设计以及序列检测功能实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复