数字逻辑第四次实验——检测1101序列
一.实验内容:
实验十五 摩尔状态机序列检测器(*****)
- 设计“1101”序列检测的状态转换图;
- 设计一个 8 位并转串输出模块 par2ser。该器件有 8 位输入 d[7:0],1 位输出 q,另有一个 clk 端,一个 set 端。set端上升沿将 8位输入锁存到逻辑右移移位寄存器中。
- 调用并转串输出模块,使用Verilog HDL语言的行为描述方式实现一个摩尔状态机,能检测一个8位的二进制数据中是否存在“1101”序列,如果检测到该序列则指定的LED灯亮;
本文将呈现该项目的设计思路,遇到的问题以及对于设计开发的一些帮助(再次感谢学年第一曾神教会我debug)
二.设计思路:
首先思考该项目的输入输出端口和模块数目,输入为八位待测序列,启动信号;输出为一位的结果信号,满足1101序列则为,否则为0。为了项目的完整和严谨,我们再添加一个置位信号作为异步时序控制所有进程的复位。因为setd作为内部的启动信号,需要保证他的稳定性,需要对其进行按键消抖,这是模块一。二是对输入序列的锁存和并转串依次读取;三是以每位作为输入调整状态,起到检验序列的效果;四是一个顶层模块来串接三个模块。
三.设计流程
1.按键消抖模块:
首先对时钟信号进行了100Hz的分频,再在该时钟信号的时钟域下,采用了三个D触发器来对setd进行打三拍操作,即用三个中间变量来承接setd,每一个时钟沿赋值一个中间变量。当三个时钟沿内setd都保持不动时,可以输出desetd表示setd是手动启动而不是按键抖动,以此保证setd的稳定性
代码如下:
module shake(clk,rstn,setd,desetd);
input clk;
input rstn;
input setd;
output desetd;
//parameter T100Hz = 499999;
parameter T100Hz = 4;
reg [31:0]cnt_100Hz;
reg clk_100Hz;
always@(posedge clk,negedge rstn)//100Hz分频模块
begin
if(!rstn)begin
cnt_100Hz <= 32'b0;
clk_100Hz <= 0;
end
else
begin
cnt_100Hz <= cnt_100Hz + 1'b1;
if(cnt_100Hz==T100Hz)
begin
cnt_100Hz <= 32'b0;
clk_100Hz <= ~clk_100Hz;
end
end
end
reg setdr,setdrr,setdrrr;
always@(posedge clk_100Hz,negedge rstn)//按键消抖模块
begin
if(!rstn)
begin
setdr <= 1'b0;
setdrr <= 1'b0;
setdrrr <= 1'b0;
end
else
begin//每次时钟沿赋值一个
setdrrr <= setdrr;
setdrr <= setdr;
setdr <= setd;
end
end
assign desetd = setdr & setdrr & setdrrr;//当三个时钟沿setd都保持稳定的话,就输出信号
endmodule
2.并转串模块
当setd的上升沿到来时,将输入的序列转存到寄存器中,防止在检测期间,序列的改变造成结果的出错,并依次在后八个clk时钟沿,输出序列的各个位数。
这里需要注意的是,需要在setd到来后再进行i的计数,不然会出现setd到来之前i就已经达到了最大值,无法达到并转串的效果。
还有一个细节,为什么我可以在i等于7处保持不变。难道这样不会出现在八个时钟沿之后第九个时钟沿到来,再次输出第0位对最后的结果产生影响吗?答案是不会的,因为1101目标序列的最后两位是互异的,永远不会出现上述情况。下面举个例子说明:输入为10100110序列,当setd的上升沿到来后,在接下来的八个时钟内,依次输出1,0,1,0,0,1,1,0;显然输出并不会截止,这时我的i维持在7,会一直输出序列的第0位0(就是1010011000000000,0不断拼接下去,输出并不会截止)。1101检测中要求最后两位互异,所以不会出现问题,
但如果检测1011序列这种最后的两位是相同的序列,就不可以用了,比如要检测10001101中的1011时,依次输出1,0,0,0,1,1,0,1是不含有1011序列的,但是第九个时钟到来,输出第0位1,这下输出就有了100011011符合条件影响结果。就需要对模块进行限制了,比如持续输出第0位的取反。
而且我们不能对一个序列在不同的always中进行操作,不然在vivido进行实现综合网表的时候会出现问题。我一开始第二个always中采用移位改变relist,不是用i来依次输出,就错了。
然而这个实验并不需要,代码如下:
module exportlist(list,clk,rstn,setd,spot);
input [7:0]list;
input clk;
input setd;
input rstn;
output reg spot;
reg [7:0]relist;
always@(posedge setd,negedge rstn)//以消抖后的setd为时钟信号
begin
if(!rstn) relist <= 8'b0;//重置relist序列
else
begin
relist <= list;//setd上升沿到来,把list锁存给relist
end
end
reg [3:0]i=0;
always@(posedge clk,negedge rstn)
begin
if(!rstn)
begin
spot <= 1'b0;//重置
i <= 0;
end
else
if(setd == 1)begin
begin
spot <= relist[7-i];//spot是relist的最高位
if(i==7) i <= i;
else i <= i+1;
end
end
else
spot <= 1'b0;
end
endmodule
3.检测模块
这个没什么好说的,就是有限状态机状态的不断切换,使能信号是上一个模块的输出。最后要保持result的不变,不然会出现灯只亮一下或者灯不亮的情况。
贴一个状态图
代码如下:
module detector(en,clk,rstn,result);
input clk;
input rstn;
input en;
output reg result=0;
reg [2:0]y;
reg [2:0]Y;
always@(en,y)//0~4依次对应s0~4
begin
case(y)
0:if(en) Y = 1;
else Y = 0;
1:if(en) Y = 2;
else Y = 0;
2:if(en) Y = 2;
else Y = 3;
3:if(en) Y = 4;
else Y = 0;
4:if(en) Y = 2;
else Y = 0;
default: Y = 0;
endcase
end
always@(posedge clk,negedge rstn)
begin
if(!rstn) y <= 0;
else y <= Y;
end
always@(*)
begin
if(!rstn) result <= 0;
else
begin
if(y==4) result <= 1;
end
end
endmodule
4.顶层模块:
就是依次串接各个模块,真的气死我了,串接变量只能是wire类型,找半天查了资料才知道,代码如下:
module top1(list,rstn,clk,setd,result);
input [7:0]list;
input rstn;
input clk;
input setd;
output wire result;
wire desetd;
wire spot;
shake shake_1(
.clk(clk),
.rstn(rstn),
.setd(setd),
.desetd(desetd)
);
exportlist exportlist_1(
.list(list),
.clk(clk),
.rstn(rstn),
.setd(desetd),
.spot(spot)
);
detector detector_1(
.en(spot),
.clk(clk),
.rstn(rstn),
.result(result)
);
endmodule
附上电路图:
四.仿真及实验效果
仿真文件如下:
module detector1101_tb();
reg [7:0] list;
wire result; // 修改为wire类型
reg clk = 0;
reg rstn = 0;
reg setd = 0;
top1 top(.list(list),.clk(clk), .rstn(rstn), .setd(setd), .result(result));
initial begin
rstn = 0;
#9;
list = 8'b10110100;
#10 rstn = 1;
#10 setd = 1;
// 仿真运行一段时间
#100 rstn = 0;
setd = 0;
#10 list = 8'b10101101;
#10 rstn = 1;
#10 setd = 1;
#100
// 在仿真结束后输出结果
$finish;
end
// 时钟翻转
always #1 clk = ~clk;
endmodule
时序图:
可以看到两组测试数据都是符合条件的,在desetd到来后的八个时钟沿后依次读入序列的每个位置,也能正确的做出结果响应。