Verilog实现--序列检测器、自动饮料售卖机
扫描二维码
随时随地手机看文章
Verilog实现状态机与状态机经典示例
1.状态机原理与三段式状态机
MOORE 与 MEALEY 状态机的特征?
Moore 状态机的输出仅与当前状态值有关, 且只在时钟边沿到来时才会有状态变化。次态=f(现状,输入),输出=f(现状)
Mealy 状态机的输出不仅与当前状态值有关, 而且与当前输入值有关。次态=f(现状,输入),输出=f(现状,输入)
描述同一个事务,mealy的状态更少。
通用mealy三段式状态机(建议实际工程中采用的结构)
//------采用独热码或格雷码或其他方式编码状态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 = ...; endcaseend/************************时序或组合二选一***********************///------时序逻辑输出(比组合逻辑延时一个时钟)always@(posedge clk or posedge asy_rst)begin if(asy_rst)out<= ...; else case(curr_state) sO: if(...) out <= ...; sO: if(...) out <= ...; default: out <= ...; endcaseend//------组合逻辑输出 采用 assign 或alwaysalways @(*)begin if(asy_rst)out = ...; else case(curr_state) sO: if(...) out = ...; sO: if(...) out = ...; default: out = ...; endcaseend/***********************************************************/
2.状态机示例
2.1自动饮料售卖机/卖报机
题目:商品是5分钱,只能投3种硬币:1分,2分,5分,考虑找零。
思路1:采用状态机,共分为5个状态,S0-S4代表已经投入的钱,画出状态转换图(略),采用三段式mealy状态机。
思路2:(更简单)不采用状态机,直接对输入的钱进行加法运算,多余5分就输出和找零,同时将内部加法器清零。
另一个版本:用Verilog实现接受0.5元,1元的可乐售卖机,单价2.5元,考虑找零和出货。
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 @(*)beginnext_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;endcaseend/**************************时序逻辑的输出*******************************/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 endcaseend /***************************不采用状态机的方法**********************************/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; endendendmodule
/////////////////////////////////////testbench//////////////////////////////////module auto_sell_sim;parameter CLK_PERIOD = 10; //仿真周期10ns=100Mparameter RST_CYCLE = 5; //复位周期数parameter RST_TIME = RST_CYCLE * CLK_PERIOD; reg sim_clk;reg sim_asy_rst; initialbegin 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;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的随机数 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 endend wire [O_WD-1:0] o_money;wire o_goods;auto_sell_better auto_sell_better_u( //auto_sell auto_sell_u (sim_clk ), sim_asy_rst ), sim_i_dat ), sim_i_val), .o_money(o_money),.o_goods(o_goods) );
2.2序列检测器
笔试题目:如果序列长度为8,需要8个状态,最少(3)个寄存器进行状态转换(mealy)。
题目:用状态机实现 101101 的序列检测。
思路:
画出mealy状态转换图,并进行化简[1],灰色表示合并为一个:
“101101”序列检测状态机输出表
果采用moore状态机,其状态就多了,且输出比mealy延时一个时钟参考[1]:
//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; //101101reg [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 @(*)beginnext_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;endcaseend/**************************时序逻辑的输出*******************************/ always @(posedge clk or posedge asy_rst)begin if(asy_rst) begin o_detect <= 0; end else case(curr_s) : begin o_detect <= 0; end S5: begin =1 ) o_detect <= 1; = end default : begin o_detect <= 0; end endcaseend endmodule
/////////////////////////////////////testbench/////////////////////////////////////module seq_detect_sim;parameter CLK_PERIOD = 10; //仿真周期10ns=100Mparameter RST_CYCLE = 5; //复位周期数parameter RST_TIME = RST_CYCLE * CLK_PERIOD; reg sim_clk;reg sim_asy_rst; initialbegin 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; endend wire [O_WD-1:0] o_detect;seq_detect seq_detect_u (), ), ), .i_val(sim_i_val),.o_detect(o_detect) );endmodule