状态机序列检测器vlog.docx
《状态机序列检测器vlog.docx》由会员分享,可在线阅读,更多相关《状态机序列检测器vlog.docx(16页珍藏版)》请在冰豆网上搜索。
状态机序列检测器vlog
一、实验目的
掌握利用有限状态机实现一般时序逻辑分析的方法,了解一般状态机的设计与应用。
二、实验内容
设计一序列检测器并在SmartSOPC实验箱上进行硬件测试。
利用Quartus||软件进行设计、仿真验证,最后进行引脚锁定并完成硬件测试。
用KEY5控制复位,KEY6控制状态机的时钟,KEY1~KEY4控制输入待检预置数和检测预置数(检测密码),并在数码管1\2和4\5上显示。
三、实验原理
(1)序列检测器可用于检测由二进制码组成的脉冲序列信号。
当序列检测器连续收到一组串行二进制码后,如果这组序列码与检测器中预先设置的序列码相同,则输出1,否则输出0.这种检测的关键是必须收到连续的正确码,所以要求检测器必须对前一次接受到的序列码做记忆分析,直到在连续检测中所收到的每一位二进制码都与预置序列码对应相同。
在检测过程中,只要有一位不相等都将回到初始状态重新开始检测。
不考虑重叠的可能。
(2)为了配合硬件测试,本实验提供了一个测试模块(schk_test),该模块主要产生序列检测器所需的时钟、复位、串行输入序列码及预置数等信号。
对莫模块的各端口说明如下:
Clock系统时钟输入(48MHz)
key[5..0]按键输入
disp[3..0]序列检测器检测结果输入(显示于数码管8)
sda串行序列码输出
clkout序列检测器状态机时钟输出
rstout序列检测器复位信号输出
dat[7..0]检测预置数输出
led7..0]LED输出
seg[7..0]数码管段输出
dig[7..0]数码管位输出
四、实验步骤
(1)启动Quartus||建立一个空白工程,然后命名为schk_top.qpf。
(2)新建VerilogHDL源程序文件schk_v,输入程序代码并保存,进行综合编译。
若在编译过程中发现错误,则找出并更正错误,直至编译成功为止。
(3)将光盘中的EDA_Component目录下的schk_test.bsf,schk_test.v拷贝到工程目录。
(原理图形式需此步骤)
(4)新建图形设计文件命名为schk_top.bdf并保存,其模块原理图如下:
(5)选择目标器件并对相应的引脚进行定义锁定,在这里所选择的器件为EPIC6Q240C8芯片,将未使用的引脚设置为三态输入。
(6)将schk_top.bdf设置为顶层实体,对该工程进行全程编译处理,若在编译过程中发现错误,则找出错误并更正错误,直至编译成功为止。
(7)硬件连接、下载程序。
五、实验程序
(一)原理图形式的实验参考程序如下:
模块1为:
moduleschk(sda,clk,rst,dat,disp);//序列检测器模块
inputsda;//串行序列码输入
inputclk;//时钟信号输入
inputrst;//复位信号输入
input[7:
0]dat;//输入待检测预置数
output[3:
0]disp;//检测结果输出
reg[3:
0]disp_r;//检测结果输出寄存器
reg[3:
0]state;//状态机寄存器
parameters0=4'd0,s1=4'd1,//状态机参数
s2=4'd2,s3=4'd3,
s4=4'd4,s5=4'd5,
s6=4'd6,s7=4'd7,
s8=4'd8;
assigndisp=disp_r;//输出检测结果
always@(posedgeclkornegedgerst)
begin
if(~rst)
state<=s0;//复位
else
begin
case(state)
s0:
if(sda==dat[7])state<=s1;elsestate<=s0;//状态s0
s1:
if(sda==dat[6])state<=s2;elsestate<=s0;//状态s1
s2:
if(sda==dat[5])state<=s3;elsestate<=s0;//状态s2
s3:
if(sda==dat[4])state<=s4;elsestate<=s0;//状态s3
s4:
if(sda==dat[3])state<=s5;elsestate<=s0;//状态s4
s5:
if(sda==dat[2])state<=s6;elsestate<=s0;//状态s5
s6:
if(sda==dat[1])state<=s7;elsestate<=s0;//状态s6
s7:
if(sda==dat[0])state<=s8;elsestate<=s0;//状态s7
default:
state<=s0;
endcase
end
end
always@(state)
begin
if(state==s8)
disp_r<=4'hf;//序列码检测正确,输出“F”
else
disp_r<=4'h0;//序列码检测错误,输出“0”
end
endmodule
模块2为:
moduleschk_test(clock,key,sda,clkout,rstout,dat,disp,led,seg,dig);
//外接I/O口
inputclock;//系统时钟
input[5:
0]key;//按键输入
output[7:
0]led;//输出接LED
output[7:
0]seg;//输出接数码管段码
output[7:
0]dig;//输出接数码管位码
//序列码检测模块I/O口
outputsda;//串行序列码输出
outputclkout;//产生时钟信号输出
outputrstout;//产生复位信号输出
output[7:
0]dat;//8位预置数输出
input[3:
0]disp;//输入检测结果
reg[7:
0]dat_r;//输出寄存器
reg[7:
0]led_r;
reg[7:
0]seg_r;
reg[7:
0]dig_r;
reg[16:
0]count;//时钟分频计数器
reg[7:
0]data;//内部寄存器
reg[8:
0]data_shift;
reg[5:
0]dout1,dout2,dout3,buff;//消抖寄存器
reg[2:
0]cnt3;//数码管扫描计数器
reg[3:
0]disp_dat;//数码管扫描显存
regdiv_clk;//分频时钟,用于消抖和扫描
wire[5:
0]key_edge;//按键消抖输出
assigndat=dat_r;
assignled=~led_r;
assignseg=seg_r;
assigndig=dig_r;
//时钟分频部分
always@(posedgeclock)
begin
if(count<17'd120000)
begin
count<=count+1'b1;
div_clk<=1'b0;
end
else
begin
count<=17'd0;
div_clk<=1'b1;
end
end
//按键消抖部分
always@(posedgeclock)
begin
if(div_clk)
begin
dout1<=key;
dout2<=dout1;
dout3<=dout2;
end
end
//按键边沿检测部分
always@(posedgeclock)
begin
buff<=dout1|dout2|dout3;
end
assignkey_edge=~(dout1|dout2|dout3)&buff;
//按键控制处理部分
always@(posedgeclock)//按键1序列码高4位
begin
if(key_edge[0])//下降沿检测
data[7:
4]<=data[7:
4]+1'b1;
end
always@(posedgeclock)//按键2序列码低4位
begin
if(key_edge[1])//下降沿检测
data[3:
0]<=data[3:
0]+1'b1;
end
always@(posedgeclock)//按键3预置数高4位
begin
if(key_edge[2])//下降沿检测
dat_r[7:
4]<=dat_r[7:
4]+1'b1;
end
always@(posedgeclock)//按键4预置数低4位
begin
if(key_edge[3])//下降沿检测
dat_r[3:
0]<=dat_r[3:
0]+1'b1;
end
assignrstout=buff[4];//按键5复位
assignclkout=buff[5];//按键6时钟
always@(posedgeclock)
begin
if(key_edge[4])//按键5复位
begin
data_shift={1'b0,data};//重新装载数据
led_r=8'd0;
end
elseif(key_edge[5])//按键6
begin
data_shift=data_shift<<1;
led_r={data_shift[8],led_r[7:
1]};//LED左移显示
end
end
assignsda=data_shift[8];//串行序列码输出
//数码管扫描显示部分
always@(posedgeclock)//定义上升沿触发进程
begin
if(div_clk)
cnt3<=cnt3+1'b1;
end
always@(posedgeclock)
begin
if(div_clk)
begin
case(cnt3)//选择扫描显示数据
3'd0:
disp_dat=data[7:
4];//第一个数码管
3'd1:
disp_dat=data[3:
0];//第二个数码管
3'd3:
disp_dat=dat[7:
4];//第四个数码管
3'd4:
disp_dat=dat[3:
0];//第五个数码管
3'd7:
disp_dat=disp;//第八个数码管
default:
disp_dat=4'h0;
endcase
case(cnt3)//选择数码管显示位
3'd0:
dig_r=8'b01111111;//选择第一个数码管显示
3'd1:
dig_r=8'b10111111;//选择第二个数码管显示
3'd3:
dig_r=8'b11101111;//选择第四个数码管显示
3'd4:
dig_r=8'b11110111;//选择第五个数码管显示
3'd7:
dig_r=8'b11111110;//选择第八个数码管显示
default:
dig_r=8'b11111111;
endcase
end
end
always@(disp_dat)
begin
case(disp_dat)//七段译码
4'h0:
seg_r=8'hc0;//显示0
4'h1:
seg_r=8'hf9;//显示1
4'h2:
seg_r=8'ha4;//显示2
4'h3:
seg_r=8'hb0;//显示3
4'h4:
seg_r=8'h99;//显示4
4'h5:
seg_r=8'h92;//显示5
4'h6:
seg_r=8'h82;//显示6
4'h7:
seg_r=8'hf8;//显示7
4'h8:
seg_r=8'h80;//显示8
4'h9:
seg_r=8'h90;//显示9
4'ha:
seg_r=8'h88;//显示a
4'hb:
seg_r=8'h83;//显示b
4'hc:
seg_r=8'hc6;//显示c
4'hd:
seg_r=8'ha1;//显示d
4'he:
seg_r=8'h86;//显示e
4'hf:
seg_r=8'h8e;//显示f
endcase
end
endmodule
(二)用程序形式做的程序如下;
moduleschk(clock,key,sda,clkout,dat,disp,led,seg,dig);
//外接I/O口
inputclock;//系统时钟
input[5:
0]key;//按键输入
//inputsda;//串行序列码输入
wirerst;//复位信号输入
//input[3:
0]disp;//输入检测结果
//input[7:
0]dat;//输入待检测预置数
output[7:
0]led;//输出接LED
output[7:
0]seg;//输出接数码管段码
output[7:
0]dig;//输出接数码管位码
outputsda;//串行序列码输出
outputclkout;//产生时钟信号输出
output[7:
0]dat;//8位预置数输出
output[3:
0]disp;//检测结果输出
reg[7:
0]dat_r;//输出寄存器
reg[7:
0]led_r;
reg[7:
0]seg_r;
reg[7:
0]dig_r;
reg[16:
0]count;//时钟分频计数器
reg[7:
0]data;//内部寄存器
reg[8:
0]data_shift;
reg[5:
0]dout1,dout2,dout3,buff;//消抖寄存器
reg[2:
0]cnt3;//数码管扫描计数器
reg[3:
0]disp_dat;//数码管扫描显存
regdiv_clk;//分频时钟,用于消抖和扫描
reg[3:
0]disp_r;//检测结果输出寄存器
reg[3:
0]state;//状态机寄存器
wire[5:
0]key_edge;//按键消抖输出
assigndat=dat_r;
assignled=~led_r;
assignseg=seg_r;
assigndig=dig_r;
parameters0=4'd0,s1=4'd1,//状态机参数
s2=4'd2,s3=4'd3,
s4=4'd4,s5=4'd5,
s6=4'd6,s7=4'd7,
s8=4'd8;
assigndisp=disp_r;//输出检测结果
//时钟分频部分
always@(posedgeclock)
begin
if(count<17'd120000)
begin
count<=count+1'b1;
div_clk<=1'b0;
end
else
begin
count<=17'd0;
div_clk<=1'b1;
end
end
//按键消抖部分
always@(posedgeclock)
begin
if(div_clk)
begin
dout1<=key;
dout2<=dout1;
dout3<=dout2;
end
end
//按键边沿检测部分
always@(posedgeclock)
begin
buff<=dout1|dout2|dout3;
end
assignkey_edge=~(dout1|dout2|dout3)&buff;
//按键控制处理部分
always@(posedgeclock)//按键1序列码高4位
begin
if(key_edge[0])//下降沿检测
data[7:
4]<=data[7:
4]+1'b1;
end
always@(posedgeclock)//按键2序列码低4位
begin
if(key_edge[1])//下降沿检测
data[3:
0]<=data[3:
0]+1'b1;
end
always@(posedgeclock)//按键3预置数高4位
begin
if(key_edge[2])//下降沿检测
dat_r[7:
4]<=dat_r[7:
4]+1'b1;
end
always@(posedgeclock)//按键4预置数低4位
begin
if(key_edge[3])//下降沿检测
dat_r[3:
0]<=dat_r[3:
0]+1'b1;
end
assignrst=buff[4];//按键5复位
assignclkout=buff[5];//按键6时钟
always@(posedgeclock)
begin
if(key_edge[4])//按键5复位
begin
data_shift={1'b0,data};//重新装载数据
led_r=8'd0;
end
elseif(key_edge[5])//按键6
begin
data_shift=data_shift<<1;
led_r={data_shift[8],led_r[7:
1]};//LED左移显示
end
end
assignsda=data_shift[8];//串行序列码输出
//数码管扫描显示部分
always@(posedgeclock)//定义上升沿触发进程
begin
if(div_clk)
cnt3<=cnt3+1'b1;
end
always@(negedgekey_edge[5]ornegedgerst)
begin
if(~rst)
state<=s0;//复位
else
begin
case(state)
s0:
if(sda==dat[7])state<=s1;elsestate<=s0;//状态s0
s1:
if(sda==dat[6])state<=s2;elsestate<=s0;//状态s1
s2:
if(sda==dat[5])state<=s3;elsestate<=s0;//状态s2
s3:
if(sda==dat[4])state<=s4;elsestate<=s0;//状态s3
s4:
if(sda==dat[3])state<=s5;elsestate<=s0;//状态s4
s5:
if(sda==dat[2])state<=s6;elsestate<=s0;//状态s5
s6:
if(sda==dat[1])state<=s7;elsestate<=s0;//状态s6
s7:
if(sda==dat[0])state<=s8;elsestate<=s0;//状态s7
default:
state<=s0;
endcase
end
end
always@(posedgeclock)
begin
if(div_clk)
begin
case(cnt3)//选择扫描显示数据
3'd0:
disp_dat=data[7:
4];//第一个数码管
3'd1:
disp_dat=data[3:
0];//第二个数码管
3'd3:
disp_dat=dat[7:
4];//第四个数码管
3'd4:
disp_dat=dat[3:
0];//第五个数码管
3'd7:
disp_dat=disp;//第八个数码管
default:
disp_dat=4'h0;
endcase
case(cnt3)//选择数码管显示位
3'd0:
dig_r=8'b01111111;//选择第一个数码管显示
3'd1:
dig_r=8'b10111111;//选择第二个数码管显示
3'd3:
dig_r=8'b11101111;//选择第一个数码管显示
3'd4:
dig_r=8'b11110111;//选择第二个数码管显示
3'd7:
dig_r=8'b11111110;//选择第八个数码管显示
default:
dig_r=8'b1111111