Verilog有限状态机(6)
前言
今天继续更新状态机小节的习题。
题库
Fsm hdlc
同步帧检测涉及对数据的连续位流进行解码,以寻找指示帧(数据包)开始和结束的位模式。 6个连续的1(即01111110)是表示帧边界的“标志”。 为了避免数据流意外包含“标志”,发送方必须在接收方必须检测并丢弃的每5个连续的1秒后插入一个零。 如果连续7个或更多1,我们还需要发出错误信号。
可以通过状态机来识别下面三种序列:
0111110:表示5个1后面的0bit需被忽略;
01111110:表示一帧的开始或结束;
01111111…:错误
当状态机被复位时,它应当表现为之前的输入为0;
下面是三种波形示例:
官方提供的状态机设计提示:
Solution:
//----------------way1----------------------
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err);
parameter NONE = 4'd0,ONE = 4'd1,TWO = 4'd2;
parameter THREE = 4'd3,FOUR = 4'd4,FIVE = 4'd5;
parameter SIX = 4'd6,ERROR = 4'd7;
parameter DISC = 4'd8,FLAG = 4'd9;
reg [3:0] current_state,next_state;
always @(*) begin
case(current_state)
NONE:begin
next_state = in ? ONE : NONE;
end
ONE:begin
next_state = in ? TWO : NONE;
end
TWO:begin
next_state = in ? THREE : NONE;
end
THREE:begin
next_state = in ? FOUR : NONE;
end
FOUR:begin
next_state = in ? FIVE : NONE;
end
FIVE:begin
next_state = in ? SIX : DISC;
end
SIX:begin
next_state = in ? ERROR : FLAG;
end
DISC:begin
next_state = in ? ONE : NONE;
end
FLAG:begin
next_state = in ? ONE : NONE;
end
ERROR:begin
next_state = in ? ERROR : NONE;
end
endcase
end
always @(posedge clk) begin
if(reset)begin
current_state <= NONE;
end
else begin
current_state <= next_state;
end
end
always @(posedge clk) begin
if(reset)begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
end
else begin
case(next_state)
DISC:begin
disc <= 1'd1;
flag <= 1'd0;
err <= 1'd0;
end
FLAG:begin
disc <= 1'd0;
flag <= 1'd1;
err <= 1'd0;
end
ERROR:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd1;
end
default:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
end
endcase
end
end
endmodule
//----------------way2----------------------
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err);
parameter NONE = 3'd0,DATA = 3'd1;
parameter DISC = 3'd2,FLAG = 3'd3,ERROR = 3'd4;
reg [2:0] current_state,next_state;
reg [2:0] counter;
always @(*) begin
case(current_state)
NONE:begin
next_state = in ? DATA : NONE;
end
DATA:begin
case(counter)
3'd5: next_state = in ? DATA : DISC;
3'd6: next_state = in ? ERROR : FLAG;
default:next_state = in ? DATA : NONE;
endcase
end
DISC:begin
next_state = in ? DATA : NONE;
end
FLAG:begin
next_state = in ? DATA : NONE;
end
ERROR:begin
next_state = in ? ERROR : NONE;
end
endcase
end
always @(posedge clk) begin
if(reset)begin
current_state <= NONE;
end
else begin
current_state <= next_state;
end
end
always @(posedge clk) begin
if(reset)begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
counter <= 3'd0;
end
else begin
case(next_state)
DATA:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
counter <= counter + 1'd1;
end
DISC:begin
disc <= 1'd1;
flag <= 1'd0;
err <= 1'd0;
counter <= 3'd0;
end
FLAG:begin
disc <= 1'd0;
flag <= 1'd1;
err <= 1'd0;
counter <= 3'd0;
end
ERROR:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd1;
counter <= 3'd0;
end
default:begin
disc <= 1'd0;
flag <= 1'd0;
err <= 1'd0;
counter <= 3'd0;
end
endcase
end
end
endmodule
其中way1与题目提示的思路相同,其中第三段状态机一开始忘记加reset的那种情况,果然还是长时间没接触verilog手生了,以后继续练习;
way2使用counter去掉中间状态,靠输入和counter的值来决定状态转移,是一种米利状态机的思想;状态转移图如下(手绘有点丑,见谅):
米利型状态机
检测输入的X中”101“是否出现,出现的话输出Z为1,否则为0。复位为异步低电平复位;只允许出现3种状态;允许交叠检测:即输入若为10101时,Z应该在时刻3和时刻5各输出一次1;
代码中主要妙在两处:一是如何用3种状态表示,需在第三种状态中将输出与输入关联起来;二是如何进行交叠检测,状态的转移有妙处;
Solution:
module top_module (
input clk,
input aresetn, // Asynchronous active-low reset
input x,
output z );
parameter S0 = 2'd0,S1 = 2'd1,S2 = 2'd2;
reg [1:0] current_state,next_state;
always @(*) begin
case(current_state)
S0: next_state = x ? S1 : S0;
S1: next_state = x ? S1 : S2;
S2: next_state = x ? S1 : S0;
endcase
end
always @(posedge clk or negedge aresetn) begin
if(~aresetn)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end
always @(*) begin
z = (current_state == S2) ? x : 1'b0;
end
endmodule
Q5a:Serial two’s complementer(Moore FSM)
作者这里假设我们这边输入的都是负数,不用管符号位的问题;即补码全部都是取反加1求得。
以上图为例,输入的左边为低位数据,右边为高位数据;即输入为00110100,则取反加1后得输出为11001100;
取反操作好进行,主要麻烦在加一的操作上,不知道进位到哪一位为止,此时我们用状态机来解决;若最前面的输入都是0的话,取反均为1,低位加1的时候一直进位,则输出都是0,直到输入有个1为止(取反加1不进位),这一阶段我们用一个状态S0来表示;后面阶段就将输入取反进行输出即可,因为进位链在S0状态已结束;
因为是摩尔型状态机,输出的结果与输入无关,仅与状态有关。所以我们这里用到3个状态,代码如下所示:
Solution:
module top_module (
input clk,
input areset,
input x,
output z
);
parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2;
reg [1:0] current_state, next_state;
always @(*) begin
case(current_state)
S0: next_state = x ? S1 : S0;
S1: next_state = x ? S2 : S1;
S2: next_state = x ? S2 : S1;
endcase
end
always @(posedge clk or posedge areset) begin
if(areset)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end
assign z = (current_state == S1);
endmodule
Q5b:Serial two’s complementer(Mealy FSM)
题意与上题相同,此时用米利型状态机实现。下面是官方提供的状态转移提示:
module top_module (
input clk,
input areset,
input x,
output z
);
parameter S0 = 1'b0, S1 = 1'b1;
reg current_state,next_state;
always @(*) begin
case(current_state)
S0: next_state = x ? S1 : S0;
S1: next_state = S1;
endcase
end
always @(posedge clk or posedge areset) begin
if(areset)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end
assign z = ((current_state == S0) && x) || ((current_state == S1) && ~x);
endmodule
结语
今天先更新这几题吧,大家如果对转补码的题目还有什么疑问欢迎评论交流,代码有不足之处还望指正。