前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Verilog实现--序列检测器、自动饮料售卖机

Verilog实现--序列检测器、自动饮料售卖机

作者头像
FPGA开源工作室
发布2020-03-06 13:44:22
2K0
发布2020-03-06 13:44:22
举报
文章被收录于专栏:FPGA开源工作室FPGA开源工作室

Verilog实现状态机与状态机经典示例

1.状态机原理与三段式状态机

MOORE 与 MEALEY 状态机的特征?

Moore 状态机的输出仅与当前状态值有关, 且只在时钟边沿到来时才会有状态变化。次态=f(现状,输入),输出=f(现状)

Mealy 状态机的输出不仅与当前状态值有关, 而且与当前输入值有关。次态=f(现状,输入),输出=f(现状,输入)

描述同一个事务,mealy的状态更少。

通用mealy三段式状态机(建议实际工程中采用的结构)

代码语言:javascript
复制
//------采用独热码或格雷码或其他方式编码状态
parameter IDLE = ...
parameter S0 = ...
...
reg [x:0] curr_state;
reg [x:0] next_state;
 
//------每个时钟只产生一次状态变化
always @(posedge clk or  posedge asy_rst)
begin
    if(asy_rst)    curr_state <= IDLE ;
    else           curr_state <= next_state;    
end
 
//------产生的下一状态的组合逻辑
always @(*)
begin
    next_state = 'bx;
    case(curr_state)
        IDLE:  if(输入) next_state = S0 ;else...;
        S0:    if(输入) next_state = ...;
        ...
        default:       next_state = ...;
    endcase
end
/************************时序或组合二选一***********************/
//------时序逻辑输出(比组合逻辑延时一个时钟)
always@(posedge clk or posedge asy_rst)
begin
    if(asy_rst)out<= ...;
    else
    case(curr_state)
        sO: if(...) out <= ...;
        sO: if(...) out <= ...;
        default: out <= ...;
    endcase
end
//------组合逻辑输出 采用 assign 或always
always @(*)
begin
    if(asy_rst)out = ...;
    else
    case(curr_state)
        sO: if(...) out  = ...;
        sO: if(...) out  = ...;
        default: out  = ...;
    endcase
end
/***********************************************************/

2.状态机示例

2.1自动饮料售卖机/卖报机

题目:商品是5分钱,只能投3种硬币:1分,2分,5分,考虑找零。

思路1:采用状态机,共分为5个状态,S0-S4代表已经投入的钱,画出状态转换图(略),采用三段式mealy状态机。

思路2:(更简单)不采用状态机,直接对输入的钱进行加法运算,多余5分就输出和找零,同时将内部加法器清零。

另一个版本:用Verilog实现接受0.5元,1元的可乐售卖机,单价2.5元,考虑找零和出货。

代码语言:javascript
复制
module auto_sell_better#(
parameter I_WD = 4 , O_WD = 4
)(
input clk,
input asy_rst,
input [I_WD-1:0] i_dat,
input i_val,
 
output reg [O_WD-1:0] o_money,//找零
output reg o_goods          //商品
    );
 
parameter S0=4'b0000; 
parameter S1=4'b0001; //1分钱
parameter S2=4'b0010; 
parameter S3=4'b0100; 
parameter S4=4'b1000; //4分钱
reg [3:0] curr_s;
reg [3:0] next_s;
//每个时钟只产生一次状态变化
always @(posedge clk or  posedge asy_rst)
begin
    if(asy_rst)
        curr_s <= S0;
    else
        curr_s <= next_s;    
end
//产生的下一状态的组合逻辑
always @(*)
begin
next_s = 'dx;
case(curr_s)
    S0: if(i_dat == 1)          next_s = S1; 
        else if(i_dat == 2)     next_s = S2;
        else if(i_dat == 5)     next_s = S0;
        else                    next_s = S0;
    S1: if(i_dat == 1)          next_s = S2; 
        else if(i_dat == 2)     next_s = S3; 
        else if(i_dat == 5)     next_s = S0; 
        else                    next_s = S1;
    S2: if(i_dat == 1)          next_s = S3; 
        else if(i_dat == 2)     next_s = S4; 
        else if(i_dat == 5)     next_s = S0; 
        else                    next_s = S2; 
    S3: if(i_dat == 1)          next_s = S4; 
        else if(i_dat == 2)     next_s = S0; 
        else if(i_dat == 5)     next_s = S0; 
        else                    next_s = S3; 
    S4: if(i_dat == 1)          next_s = S0; 
        else if(i_dat == 2)     next_s = S0; 
        else if(i_dat == 5)     next_s = S0; 
        else                    next_s = S4;  
    default:    next_s = S0;
endcase
end
/**************************时序逻辑的输出*******************************/
always @(posedge clk or  posedge asy_rst)
begin
    if(asy_rst)
    begin
        o_money <= 0;
        o_goods <= 0;
    end     
    else
    case(curr_s)
        S0:
        begin
            o_money <= 0; 
            if(i_dat==5) o_goods <= 1; 
            else         o_goods <= 0; 
        end
        S1: 
        begin                  
            if(i_dat==5) begin  o_money <= 1; o_goods <= 1; end
            else         begin  o_money <= 0; o_goods <= 0; end
        end
        S2:               
        begin             
            if(i_dat==5) begin  o_money <= 2; o_goods <= 1; end
            else         begin  o_money <= 0; o_goods <= 0; end
        end
        S3:               
        begin             
            if(i_dat==5)        begin  o_money <= 3; o_goods <= 1; end
            else if (i_dat==2)  begin  o_money <= 0; o_goods <= 1; end
            else                begin  o_money <= 0; o_goods <= 0; end 
        end
        S4:               
        begin             
            if(i_dat==5)        begin  o_money <= 4; o_goods <= 1; end
            else if (i_dat==2)  begin  o_money <= 1; o_goods <= 1; end
            else if (i_dat==1)  begin  o_money <= 0; o_goods <= 1; end 
        end
        default :
        begin o_money <= 0; o_goods <= 0;end
    endcase
end              
 
/***************************不采用状态机的方法**********************************/
reg [3:0] add_in;//内部计算输入的钱的和
reg [3:0] a_money;//找零
reg a_goods;//商品
always @(posedge clk or  posedge asy_rst)
begin
    if(asy_rst)
    begin
        add_in <= 0;
        a_money <= 0;
        a_goods <= 0;
    end
    else if(i_val)
    begin
        if(add_in+i_dat >=5)
        begin
            add_in <= 0;//清零
            a_money <= add_in + i_dat - 5;//找零
            a_goods <= 1;//输出
        end
        else
        begin
            add_in <= add_in + i_dat;
            a_money <= 0;
            a_goods <= 0;             
        end
    end
    else
    begin
       add_in <= 0;
       a_money <= 0;
       a_goods <= 0;
    end
end
endmodule
代码语言:javascript
复制
/////////////////////////////////////testbench//////////////////////////////////
module auto_sell_sim;
parameter CLK_PERIOD = 10; //仿真周期10ns=100M
parameter RST_CYCLE = 5; //复位周期数
parameter RST_TIME = RST_CYCLE * CLK_PERIOD; 
 
reg sim_clk;
reg sim_asy_rst;
 
initial
begin
    sim_clk = 0;
    sim_asy_rst = 1;
    #RST_TIME sim_asy_rst = 0;
end
   
always #(CLK_PERIOD/2) sim_clk = ~sim_clk;
/******************************************************/
parameter I_WD = 4;
parameter O_WD = 4;
reg [I_WD-1:0]sim_dat;
reg [I_WD-1:0]sim_i_dat;
reg sim_i_val;
always@(posedge sim_clk or posedge sim_asy_rst)
begin
    if(sim_asy_rst)
    begin
        sim_i_dat <= 0; 
        sim_i_val <= 0;
    end
    else
    begin
    sim_dat <= {$random}%6;//产生0-5的随机数
    if( sim_dat== 1 || sim_dat ==2 || sim_dat==5)
    begin
        sim_i_dat <= sim_dat; 
        sim_i_val <= 1;
    end
    else
    begin
        sim_i_dat <= 0; 
        sim_i_val <= 1;
    end
    end
end
 
wire [O_WD-1:0] o_money;
wire o_goods;
auto_sell_better auto_sell_better_u( //auto_sell auto_sell_u (
.clk( sim_clk ),
.asy_rst( sim_asy_rst ),
.i_dat( sim_i_dat ),
.i_val( sim_i_val),
.o_money(o_money),
.o_goods(o_goods)
 );

2.2序列检测器

笔试题目:如果序列长度为8,需要8个状态,最少(3)个寄存器进行状态转换(mealy)。

题目:用状态机实现 101101 的序列检测。

思路:

画出mealy状态转换图,并进行化简[1],灰色表示合并为一个:

“101101”序列检测状态机输出表

果采用moore状态机,其状态就多了,且输出比mealy延时一个时钟参考[1]:

代码语言:javascript
复制
//mealy状态机
module seq_detect#(
parameter I_WD = 1 , O_WD = 1
)(
input clk,
input asy_rst,
input [I_WD-1:0] i_dat,
input i_val,
output reg [O_WD-1:0] o_detect         
    );
 
parameter S0=6'b000001; 
parameter S1=6'b000010; 
parameter S2=6'b000100; 
parameter S3=6'b001000; 
parameter S4=6'b010000; 
parameter S5=6'b100000; 
 
//101101
reg [5:0] curr_s;
reg [5:0] next_s;
//每个时钟只产生一次状态变化
always @(posedge clk or  posedge asy_rst)
begin
    if(asy_rst)
        curr_s <= S0;
    else
        curr_s <= next_s;    
end
 
//产生的下一状态的组合逻辑
always @(*)
begin
next_s = 'dx;
case(curr_s)
    S0: if(i_dat == 1)          next_s = S1;        
        else                    next_s = S0;
    S1: if(i_dat == 0)          next_s = S2;    
        else                    next_s = S1;
    S2: if(i_dat == 1)          next_s = S3; 
        else                    next_s = S0; 
    S3: if(i_dat == 1)          next_s = S4; 
        else                    next_s = S2;////// 1010---提取10
    S4: if(i_dat == 0)          next_s = S5;     
        else                    next_s = S1; 
    S5: if(i_dat == 1)          next_s = S0;    
        else                    next_s = S0; 
    default:    next_s = S0;
endcase
end
/**************************时序逻辑的输出*******************************/
 
always @(posedge clk or  posedge asy_rst)
begin
    if(asy_rst)
    begin
        o_detect <= 0; 
    end     
    else
    case(curr_s)
        S0,S1,S2,S3,S4:
        begin
            o_detect <= 0;     
        end     
        S5:               
        begin             
            if(i_dat ==1 )  o_detect <= 1; 
        end
        default :
        begin o_detect <= 0;     end
    endcase
end              
endmodule
代码语言:javascript
复制
/////////////////////////////////////testbench/////////////////////////////////////
module seq_detect_sim;
parameter CLK_PERIOD = 10; //仿真周期10ns=100M
parameter RST_CYCLE = 5; //复位周期数
parameter RST_TIME = RST_CYCLE * CLK_PERIOD; 
 
reg sim_clk;
reg sim_asy_rst;
 
initial
begin
    sim_clk = 0;
    sim_asy_rst = 1;
    #RST_TIME sim_asy_rst = 0;
end
   
always #(CLK_PERIOD/2) sim_clk = ~sim_clk;
/******************************************************/
parameter I_WD = 1;
parameter O_WD = 1;
parameter IN_SEQ = 32'b00101101_01011000_10110111_01010101;
 
reg [4:0]cnt;
reg sim_i_val;
always @(posedge sim_clk or  posedge sim_asy_rst)
begin
    if(sim_asy_rst)
    begin
        cnt <= 0;
        sim_i_val <= 0;
    end
    else
    begin
        cnt <= cnt +1;
        sim_i_val <= 1;
    end
end
 
wire [O_WD-1:0] o_detect;
seq_detect seq_detect_u (
.clk(sim_clk ),
.asy_rst(sim_asy_rst ),
.i_dat(IN_SEQ[cnt] ),
.i_val(sim_i_val),
.o_detect(o_detect)
 );
endmodule

参考资料 [1]“101101”序列检测器Verilog设计实例与VCS仿真(mealy型和moore型)

作者:king阿金

文章链接:

https://blog.csdn.net/qq_34070723/article/details/100737225

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FPGA开源工作室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Verilog实现状态机与状态机经典示例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档