一、序列检测器
可以说是最经典的题目了,数电课本上的题目,涉及到状态机的设计,出现频率非常高,这里分别采用两种方式实现:
1.状态机
2.移位寄存器
1.1 题目描述与分析
使用verilog代码,设计电路,检测序列为1001,检测到的时候输出1,没检测到的时候输出0
分析:检测四位,那么就会有五个状态。分别为 0 1 10 100 1001。显然是一个摩尔型状态机。当到达第五个状态时,输出1.对五个状态进行状态编码,IDLE = 3'b000 , s1 = 3'b001 , s2 = 3'b011 ,s3 = 3'b010, s4 = 3'b110;可以使用格雷码编码,并画出状态转移图。
至此,我们可以根据这个状态转移图去写两段式状态机了。
两段式要点:parameter 状态声明 ; 第一段 当前状态转移成下一状态 ;第二段 组合逻辑 根据输入,对当前状态进行case变换;第三段 组合逻辑 检测是否到最后一个输出状态。?:
1.2 状态机实现序列检测
module moduleName1 (
input clk,
input rst_n,
input seq,
output wire detect
);
parameter IDLE = 3'b000 , s1 = 3'b001 , s2 = 3'b011 ,s3 = 3'b010, s4 = 3'b110;
reg [2:0] cstate,nstate;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cstate <= IDLE;
else
cstate <= nstate;
end
always @(*) begin
case (cstate)
IDLE : nstate = seq ? s1 : IDLE;
s1 : nstate = seq ? s1 : s2 ;
s2 : nstate = seq ? s1 : s3 ;
s3 : nstate = seq ? s4 : IDLE;
s4 : nstate = seq ? s1 : IDLE;
default: nstate = IDLE;
endcase
end
assign detect = nstate == s4 ? 1 : 0 ;
endmodule
1.3 移位寄存器实现序列检测
用移位寄存器的方法比状态机的方法代码要轻简很多,先说一下原理:
①先定义一个4位的变量用来移位——shift = 4’b0000;
②然后每输入一位x,移位放至shift中——shift <= {shift[2:0], x};
③检测shift是否等于1001.
至于为什么移位是这样的表达,大家可以看下面的这个图。
这样代码就简单了不少,如下所示:
module moduleName2 (
input clk,
input rst_n,
input seq,
output wire detect
);
reg [3:0] shift;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
shift <= 4'b0000;
else
shift <= {shift[2:0],seq};
end
assign detect = shift == 4'b1001 ? 1 : 0;
endmodule
二、 序列发生器
2.1 题目描述与分析
编写一个模块,实现循环输出序列001011。
分析:这里需要循环输出序列001011,显然定义一个reg[5:0] 去存储001011,然后按照时钟一拍一拍的把他的最高位读出即可。由于这里需要循环输出,所以当读到100000的时候,需要重新写入reg的值。
2.1 移位实现序列发生器
module moduleName (
input clk,
input rst_n,
output reg data
);
reg [5:0]data_r;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
data_r <= 6'b001011;
else if (data_r == 6'b100000) // 加括号
data_r <= 6'b001011;
else
data_r <= (data_r << 1 ); // 加括号
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
data <= 1'b0;
else
data <= data_r[5];
end
endmodule
2.2 用状态机实现序列发生器
用状态机的思路也比较简单,可以设计 s0 = 0 , s1 = 00 ,s2 = 001, s3= 0010,s4 =00101,s5= 001011 这六个状态,并进行状态编码,每个状态都可以对应一个输出,而且状态机的转换与输入没有关系,case当前是s1,那么无条件跳到s2,并且可以对应data输出,s0对应输出0,s1对应输出0,s2对应输出1,s3对应0,等等。
module moduleName2 (
input wire clk;
input wire rst_n;
output reg data
);
reg [2:0] cstate,nstate;
parameter s0 = 3'b000, s1 = 3'b001 , s2 = 3'b011 ,s3 = 3'b010, s4 = 3'b110, s5= 3'b111;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cstate <= s0;
else
cstate <= nstate;
end
always @(*) begin
case (cstate)
s0 : begin nstate = s1; data <= 1'b0;end
s1 : begin nstate = s2; data <= 1'b0;end
s2 : begin nstate = s3; data <= 1'b1;end
s3 : begin nstate = s4; data <= 1'b0;end
s4 : begin nstate = s5; data <= 1'b1;end
s5 : begin nstate = s0; data <= 1'b1;end
default: begin nstate = s0; data <= 1'b0;end
endcase
end
endmodule
三、模三检测器
3.1 题目描述与分析
使用verilog代码,设计电路,判断输入序列能否被三整除,能的时候输出1,不能的时候输出0。
思路:首先,一个数去除以3,其余数只有0 1 2 三种状态,我们再加一个IDLE状态,对这四个状态进行编码。IDLE = 2‘b00,s0 = 2‘b01,s1 = 2‘b11,s2= 2'b10。分别对应余0,余1,余2三种状态。
这个题目是判断输入序列能不能被三整除,所以我们要推断出,输入与状态转换之间的关系。每输入一位,就相当于现在的序列左移一位,在加上输入的数。
所以,当输入为0时,序列值变成前一刻的值x的二倍 2x 。当输入序列为1时,序列值变成 2x + 1。这样我们可以推断出余数的状态转移图。
对于s0,可以看做是3x。
来一个1时,变成2*(3x)+ 1,则余1,转到s1态。
来一个0时,变成2*(3x) ,则余0,转到s0态。
对于s1,可以看做是3x+1。
来一个1时,变成2*(3x+1)+ 1,则余0,转到s0态。
来一个0时,变成2*(3x+1) ,则余2,转到s2态。
对于s2,可以看做是3x+2。
来一个1时,变成2*(3x+2)+ 1,则余2,转到s2态。
来一个0时,变成2*(3x+2) ,则余1,转到s1态。
最后判断是否为s0态,输出检测信号高电平。
3.2 状态机实现模三检测器
module moduleName (
input clk,
input rst_n,
input data,
output wire mod0
);
parameter IDLE = 2'b00,s0 = 2'b01,s1 = 2'b11,s2 = 2'b10;
reg[1:0] cstate,nstate;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cstate <= IDLE;
else
cstate <= nstate;
end
always @(*) begin
case (cstate)
IDLE : nstate = data ? s1 : s0 ;
s0 : nstate = data ? s1 : s0 ;
s1 : nstate = data ? s0 : s2 ;
s2 : nstate = data ? s2 : s1 ;
default: nstate = IDLE;
endcase
end
assign mod0 = (cstate == s0) ? 1 : 0;
endmodule
四、自动售卖机(一)
4.1 题目描述与分析
使用Verilog设计电路,完成以下功能:每瓶饮料1.5元,一次只能投入一个硬币,可投入0.5与1.0两种硬币,具有找零功能。
分析:首先就要进行 状态的划分。状态数值为售卖机里面的钱,一共有IDLE,0.5,1,1.5,2五个状态。
具体描述一下:(只会找零0.5元)
IDLE:复位状态,表示饮料机中的余额为0元
s1:饮料机的余额为0.5元
s2:饮料机中的余额为1元
s3:饮料机中的余额为1.5元(输出饮料,不找零)
s4:饮料集中的余额为2元(输出饮料,找零0.5元)
这个设计不会找零1元
这个题目的不同点在于,有两个输入信号,决定状态机的转换,所以第二段的组合逻辑,状态转换时需要先考虑第一个输入,再考虑第二个输入。才决定状态转换。
那么对于这个状态机的输入和输出来说
输入:两位input代表投入的硬币,input[1]拉高意味着投入1元,input[0]拉高意味着投入0.5 元,默认一次只能投入一枚硬币。
输出:drink代表输出饮料,coin代表输出硬币。
先判断是否有input[1],再去判断input[2].
画出状态转移图,红色代表input[1],蓝色代表input[0],
s3,s4 收到0的时候,返回IDLE。
4.2 状态机实现自助售卖机
module moduleName (
input clk,
input rst_n,
input [1:0] money,
output wire drink,
output wire coin
);
parameter IDLE = 3'b000,s1 = 3'b001,s2 =3'b010,s3 = 3'b011,s4 = 3'b100;
reg[2:0] cstate,nstate;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cstate <= IDLE;
else
cstate <= nstate;
end
always @(*) begin
case (cstate)
IDLE : nstate = money[1] ? s2 : (money[0] ? s1 : IDLE) ;
s1 : nstate = money[1] ? s3 : (money[0] ? s2 : s1 ) ;
s2 : nstate = money[1] ? s4 : (money[0] ? s3 : s2 ) ;
s3 : nstate = money[1] ? s2 : (money[0] ? s1 : IDLE) ;
s4 : nstate = money[1] ? s2 : (money[0] ? s1 : IDLE) ;
default: nstate = IDLE ;
endcase
end
assign drink = (cstate == s3) || (cstate == s4) ;
assign cion = (cstate == s4) ? 1 : 0 ;
endmodule
五、自动售卖机(二)
5.1 题目描述与分析
题目描述:
设计一个自动贩售机,输入货币有两种,为0.5/1元,饮料价格是1.5/2.5元,要求进行找零,找零只会支付0.5元。
ps:
1、投入的货币会自动经过边沿检测并输出一个在时钟上升沿到1,在下降沿到0的脉冲信号
2、此题忽略出饮料后才能切换饮料的问题
注意rst为低电平复位
信号示意图:
d1 0.5 d2 1 sel 选择饮料 out1 饮料1 out2 饮料2 out3 零钱
题目分析:
与上题不同的是。多了一个选择信号,这个选择信号可以选择购买不同的饮料。
首先,我们确定状态个数。
IDLE : 贩卖机里累计0元。
s1 : 贩卖机里累计0.5元。
s2 : 贩卖机里累计1元。
s3 : 贩卖机里累计1.5元。
s4 : 贩卖机里累计2元。
s5 : 贩卖机里累计2.5元。
s6 : 贩卖机里累计3元。
第一种思路: 当贩售机内金额大于1.5时,也就是S3
及以后的状态,还应根据饮料种类做判断。如果选择的是饮料1,sel=0
,则返回S0
状态;如果选择的是饮料2,sel=1
,则根据投入的货币继续向下转移状态。即s3以后的状态,三目运算符会多一点点。
第二种思路:分别两个状态机,由sel信号一开始就决定进入哪个状态机。
5.2 状态机实现自动贩卖机(二)
第一种思路:
module moduleName (
input wire clk ,
input wire rst_n ,
input wire d1 ,
input wire d2 ,
input wire sel ,
output reg out1,
output reg out2,
output reg out3
);
parameter IDLE = 3'b000, s0_5 = 3'b001,s1 =3'b010,s1_5 = 3'b011,s_2 = 3'b100;
parameter s2_5 = 3'b101, s3 = 3'b110;
reg[2:0] cstate,nstate;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cstate <= IDLE;
else
cstate <= nstate;
end
always @(*) begin
case (cstate)
IDLE : nstate = d1 ? s0_5 : (d2 ? s1 : IDLE) ;
s0_5 : nstate = d1 ? s1 : (d2 ? s1_5 : s1) ;
s1 : nstate = d1 ? s1_5 : (d2 ? s_2 : s1) ;
s1_5 : nstate = !sel ? IDLE : d1 ? s_2 : (d2 ? s2_5 : s1_5);
s_2 : nstate = !sel ? IDLE : d1 ? s2_5 : (d2 ? s3 : s_2);
s2_5 : nstate = IDLE ;
s3 : nstate = IDLE ;
default: nstate = IDLE ;
endcase
end
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
out1 <= 1'b0;
out2 <= 1'b0;
out3 <= 1'b0;
end
else begin
if(!sel)begin
case (nstate)
s1_5: begin out1 <= 1'b1;out2 <= 1'b0;out3 <= 1'b0;end
s_2: begin out1 <= 1'b1;out2 <= 1'b0;out3 <= 1'b1;end
default:begin out1 <= 1'b0;out2 <= 1'b0;out3 <= 1'b0;end
endcase
end
else begin
case (nstate)
s1_5: begin out1 <= 1'b0;out2 <= 1'b1;out3 <= 1'b0;end
s_2: begin out1 <= 1'b0;out2 <= 1'b1;out3 <= 1'b1;end
default:begin out1 <= 1'b0;out2 <= 1'b0;out3 <= 1'b0;end
endcase
end
end
end
endmodule
第二种思路:
module seller2(
input wire clk ,
input wire rst ,
input wire d1 ,
input wire d2 ,
input wire sel ,
output reg out1,
output reg out2,
output reg out3
);
parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 , S4 = 'd4, S5 = 'd5, S6 = 'd6;
reg [2:0] current_state;
reg [2:0] next_state;
wire [1:0] input_state;
assign input_state = {d1,d2};
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end
always@(*)begin
if (!sel) begin
case(current_state)
S0:begin
case(input_state)
2'b10 :next_state = S1 ;
2'b01 :next_state = S2 ;
default:next_state = next_state;
endcase
end
S1:begin
case(input_state)
2'b10 :next_state = S2 ;
2'b01 :next_state = S3 ;
default:next_state = next_state;
endcase
end
S2:begin
case(input_state)
2'b10 :next_state = S3 ;
2'b01 :next_state = S4 ;
default:next_state = next_state;
endcase
end
default: next_state = S0;
endcase
end
else begin
case(current_state)
S0:begin
case(input_state)
2'b10 :next_state = S1 ;
2'b01 :next_state = S2 ;
default:next_state = next_state;
endcase
end
S1:begin
case(input_state)
2'b10 :next_state = S2 ;
2'b01 :next_state = S3 ;
default:next_state = next_state;
endcase
end
S2:begin
case(input_state)
2'b10 :next_state = S3 ;
2'b01 :next_state = S4 ;
default:next_state = next_state;
endcase
end
S3:begin
case(input_state)
2'b10 :next_state = S4 ;
2'b01 :next_state = S5 ;
default:next_state = next_state;
endcase
end
S4:begin
case(input_state)
2'b10 :next_state = S5 ;
2'b01 :next_state = S6 ;
default:next_state = next_state;
endcase
end
default: next_state = S0;
endcase
end
end
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
out1 <= 1'b0;
out2 <= 1'b0;
out3 <= 1'b0;
end
else begin
if(!sel)begin
case (next_state)
S3: begin out1 <= 1'b1;out2 <= 1'b0;out3 <= 1'b0;end
S4: begin out1 <= 1'b1;out2 <= 1'b0;out3 <= 1'b1;end
default:begin out1 <= 1'b0;out2 <= 1'b0;out3 <= 1'b0;end
endcase
end
else begin
case (next_state)
S5: begin out1 <= 1'b0;out2 <= 1'b1;out3 <= 1'b0;end
S6: begin out1 <= 1'b0;out2 <= 1'b1;out3 <= 1'b1;end
default:begin out1 <= 1'b0;out2 <= 1'b0;out3 <= 1'b0;end
endcase
end
end
end
endmodule