Verilog陈老师的密码锁作业
Posted _vv123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Verilog陈老师的密码锁作业相关的知识,希望对你有一定的参考价值。
问题定义
-
密码锁共有12个键, 0-9的数字键, *为取消键, #为确定键
-
开锁时,需要输入4位正确密码后,按#号键确定,密码锁可以打开,注意这里只要最后按#键前4位正确即可,密码门打开后30秒回到初始态。
-
如果连续3次输错密码,密码门自动死锁3分钟。
-
密码门有一个六位超级密码230419,输入后可以用户重置并且设置四位开锁密码, 用于设置新的开锁密码时,需要连续输入两次并按#确认,两次必须相同。否则设置失败。
-
无论是开锁还是设置密码可以按*号来取消。
思路推导与代码实现
输入/输出定义如下:
module lock(
input wire clk,
input wire clr,
input wire [3:0] din, //数字键0-9,需要4bit
input wire confirm, //确定键\'#\'
input wire cancel, //取消键\'*\'
output reg unlock_ok, //成功开锁状态时,输出1
output reg reset_ok, //重设密码成功时,输出1
output reg locking //输错密码3次进入锁定状态时,输出1
);
此外,定义一些变量用于保存密码/计时等
//初始密码1234
reg [3:0] passwd0 = 1;
reg [3:0] passwd1 = 2;
reg [3:0] passwd2 = 3;
reg [3:0] passwd3 = 4;
//超级密码230419
reg [3:0] superwd0 = 2;
reg [3:0] superwd1 = 3;
reg [3:0] superwd2 = 0;
reg [3:0] superwd3 = 4;
reg [3:0] superwd4 = 1;
reg [3:0] superwd5 = 9;
//新密码
reg [3:0] newpasswd0 = 0, newpasswd1 = 0, newpasswd2 = 0, newpasswd3 = 0;
//开启状态保持时间
reg [5:0] open_time = 0;
//连续输错密码次数
reg [5:0] wrong_count = 0;
//锁定状态保持时间
reg [5:0] lock_time = 0;
具体功能使用两个状态机实现。
对于"输入密码——判定密码——开锁/死锁"部分功能,划分一个状态机 \\(S\\)。
对于"输出超级密码——重设密码"功能,划分一个状态机 \\(T\\)。
状态机 \\(S\\) 共有 \\(6\\) 个状态:
- S0 - S3: 当前已输入了正确密码的前0/1/2/3位。
- S4: 已输入正确密码,等待确认。
- Open: 已开启,开启状态保持30秒。
- Lock: 已死锁,死锁状态保持3分钟。
状态机S定义部分代码如下:
//状态定义
reg[3:0] present_state_s, next_state_s;//当前状态,下一状态
parameter S0 = 3\'b0, S1 = 3\'b1, S2 = 3\'b10, S3 = 3\'b11;
parameter S4 = 3\'b100; //等待键入确定键\'#\'
parameter Open = 3\'b101; //开启状态
parameter Lock = 3\'b110; //输错3次密码,锁定状态
S0-S3的状态转移为常见的序列检测思路,根据当前输入的数字din是否与保存在passwd0/1/2/3中的正确密码一致,决定下一个状态为 \\(S_i+1\\) 还是 S0。此外,在每个状态中检测确定键和取消键,确定键和取消键都会使状态跳转到S0,而此时按下确定键会使得wrong_count
(记录的连续输错次数)加1。
如果正确输入了四位密码,进入S4,此时按下确认键即可跳转到Open状态,否则回到S0。
在Open状态开始时,输出unlock_ok设为1,将reg [5:0] open_time 赋值为保持开启状态的时钟周期数,这里为方便仿真验证设置为3个周期。每个时钟上升沿open_time递减,直到3个周期后为0,回到S0状态。
如果连续输错次数wrong_count达到3,则跳转至Lock状态,输出locking设为1,将reg [5:0] lock_time 赋值为保持死锁状态的时钟周期数,这里为方便仿真验证设置为3个周期。每个时钟上升沿lock_time递减,直到3个周期后为0,回到S0状态。
状态机 \\(S\\) 的具体实现如下
//状态机S
always @(*) begin
case (present_state_s)
S0:
begin
if (din == passwd0) next_state_s <= S1;
else next_state_s <= S0;
if (confirm == 1)
begin
wrong_count = wrong_count + 1;
if (wrong_count >= 3)
begin
next_state_s <= Lock;
end
else
next_state_s <= S0;
end
if (cancel == 1) next_state_s <= S0;
end
S1:
begin
if (din == passwd1) next_state_s <= S2;
else next_state_s <= S0;
if (confirm == 1)
begin
wrong_count <= wrong_count + 1;
if (wrong_count >= 3)
begin
next_state_s <= Lock;
end
else
next_state_s <= S0;
end
if (cancel == 1) next_state_s <= S0;
end
S2:
begin
if (din == passwd2) next_state_s <= S3;
else next_state_s <= S0;
if (confirm == 1)
begin
wrong_count <= wrong_count + 1;
if (wrong_count >= 3)
begin
next_state_s <= Lock;
end
else
next_state_s <= S0;
end
if (cancel == 1) next_state_s <= S0;
end
S3:
begin
if (din == passwd3) next_state_s <= S4;
else next_state_s <= S0;
if (confirm == 1)
begin
wrong_count <= wrong_count + 1;
if (wrong_count >= 3)
begin
next_state_s <= Lock;
end
else
next_state_s <= S0;
end
if (cancel == 1) next_state_s <= S0;
end
S4:
begin
if (confirm == 1) begin
next_state_s <= Open;
wrong_count <= 0;
end
else
next_state_s <= S0;
end
Open:
begin
if (open_time > 0)
next_state_s <= Open;
else
next_state_s = S0;
end
Lock:
begin
if (lock_time > 0)
begin
next_state_s <= Lock;
end
else next_state_s = S0;
end
endcase
end
//状态切换与输出
always @(posedge clk or posedge clr) begin
if (open_time > 0) begin
if (open_time == 1) unlock_ok <= 0;
open_time <= open_time - 1;
end
if (lock_time > 0) begin
if (lock_time == 1) begin
locking <= 0;
wrong_count <= 0;
end
lock_time <= lock_time - 1;
end
if (clr == 1) begin
present_state_s <= S0;
unlock_ok <= 0;
locking <= 0;
end
else
begin
if (present_state_s != Open && next_state_s == Open) begin
unlock_ok <= 1;
open_time <= 3;
end
if (present_state_s != Lock && next_state_s == Lock) begin
locking <= 1;
lock_time <= 3;
end
present_state_s <= next_state_s;
end
end
状态机 \\(T\\) 共有 \\(22\\) 个状态:
- T0 - T11: 检测12位序列230419230419已经正确输入到哪一位
- T11 - T15: 输入新密码第1/2/3/4位
- T16 - T19: 重复新密码的第1/2/3/4位
- T20: 等待确认。
- OK: 重设密码成功。
T0-T11的状态转移为常见的序列检测思路,T11-T15将输入的数字暂存在reg [3:0] newpasswd0/1/2/3
中,T16-T19分别检测第二次输入的密码是否与寄存器中的密码相同,如果相同,进入T20等待按下确认键。
以上各状态中,超级密码出错/再次输出密码不一致/按下取消键都会回到T0状态。
T20时按下确认键进入OK状态,将newpasswd
中的密码复制到passwd中,并重置连续输错次数wrong_count为0。
状态机 \\(T\\)的具体实现如下:
reg[5:0] present_state_t, next_state_t;
parameter T0 = 5\'b0, T1 = 5\'b1, T2 = 5\'b10, T3 = 5\'b11, T4 = 5\'b100, T5 = 5\'b101,
T6 = 5\'b110, T7 = 5\'b111, T8 = 5\'b1000, T9 = 5\'b1001, T10 = 5\'b1010, T11 = 5\'b1011,
T12 = 5\'b1100, T13 = 5\'b1101, T14 = 5\'b1110, T15 = 5\'b1111,
T16 = 5\'b10000, T17 = 5\'b10001, T18 = 5\'b10010, T19 = 5\'b10011,
T20 = 5\'b10100, //等待键入确定键\'#\'
OK = 5\'b10101;
always @(posedge clk or posedge clr) begin
if (clr == 1)
present_state_t <= T0;
else
present_state_t <= next_state_t;
end
//状态机T
always @(*) begin
case (present_state_t)
T0: if (din == superwd0) next_state_t <= T1;
else next_state_t <= T0;
T1: if (din == superwd1) next_state_t <= T2;
else next_state_t <= T0;
T2: if (din == superwd2) next_state_t <= T3;
else next_state_t <= T0;
T3: if (din == superwd3) next_state_t <= T4;
else next_state_t <= T0;
T4: if (din == superwd4) next_state_t <= T5;
else next_state_t <= T0;
T5: if (din == superwd5) next_state_t <= T6;
else next_state_t <= T0;
T6: if (din == superwd0) next_state_t <= T7;
else next_state_t <= T0;
T7: if (din == superwd1) next_state_t <= T8;
else next_state_t <= T0;
T8: if (din == superwd2) next_state_t <= T9;
else next_state_t <= T0;
T9: if (din == superwd3) next_state_t <= T10;
else next_state_t <= T0;
T10: if (din == superwd4) next_state_t <= T11;
else next_state_t <= T0;
T11: if (din == superwd5) next_state_t <= T12;
else next_state_t <= T0;
T12:
begin
newpasswd0 <= din; next_state_t <= T13;
end
T13:
begin
newpasswd1 <= din; next_state_t <= T14;
end
T14:
begin
newpasswd2 <= din; next_state_t <= T15;
end
T15:
begin
newpasswd3 <= din; next_state_t <= T16;
end
T16:
if (din == newpasswd0) next_state_t <= T17;
else next_state_t <= T0;
T17:
if (din == newpasswd1) next_state_t <= T18;
else next_state_t <= T0;
T18:
if (din == newpasswd2) next_state_t <= T19;
else next_state_t <= T0;
T19:
if (din == newpasswd3) next_state_t <= T20;
else next_state_t <= T0;
T20:
if (confirm == 1) begin
next_state_t <= OK;
end
else next_state_t <= T0;
OK:
begin
passwd0 <= newpasswd0;
passwd1 <= newpasswd1;
passwd2 <= newpasswd2;
passwd3 <= newpasswd3;
next_state_t <= T0;
wrong_count <= 0;//重设密码后,重新计算连续输错的次数
end
default: next_state_t <= T0;
endcase
if (cancel == 1) next_state_t <= T0;
end
always @(posedge clk or posedge clr) begin
if (clr == 1) reset_ok <= 0;
else
if (present_state_t == OK)
reset_ok <= 1;
else
reset_ok <= 0;
end
仿真验证
编写仿真文件进行功能验证。
`timescale 1ns / 1ps
module tb_lock();
reg clk;
reg clr;
reg cancel; //取消键\'*\'
reg [3:0] din; //数字键
reg confirm; //确定键\'#\'
wire unlock_ok; //开锁,输出1
wire reset_ok; //成功重设密码,输出1
wire locking; //输错密码锁定状态,输出1
initial begin
clk <= 0;
confirm <= 0;
din <= 0;
cancel <= 0;
clr <= 1;
#30
clr <= 0;
//依次输入 1 2 3 4 确认,成功开锁
din <= 1;
#20
din <= 2;
#20
din <= 3;
#20
din <= 4;
#20
din <= 0;
confirm <= 1;
#20
confirm <= 0;
#20
#20
#20
#20
//依次输入 1 2 3 5 确认,无法开锁
din <= 1;
#20
din <= 2;
#20
din <= 3;
#20
din <= 5;
#20
din <= 0;
confirm <= 1;
#20
confirm <= 0;
din <= 1;
#20
din <= 0;
#20
#20
#20
#20
//依次输入 230419 230419 6789 6789 确认
//密码从默认的1234 修改为 6789
din <= 2; #20
din <= 3; #20
din <= 0; #20
din <= 4; #20
din <= 1; #20
din <= 9; #20
din <= 2; #20
din <= 3; #20
din <= 0; #20
din <= 4; #20
din <= 1; #20
din <= 9; #20
din <= 6; #20
din <= 7; #20
din <= 8; #20
din <= 9; #20
din <= 6; #20
din <= 7; #20
din <= 8; #20
din <= 9; #20
confirm <= 1; #20
confirm <= 0; din <= 0; #20
//连续输入3次错误密码:
//1234 # 1234 # 1234 #
din <= 1; confirm <= 0; #20
din <= 2; #20
din <= 3; #20
din <= 4; #20
din <= 0; confirm <= 1; #20
din <= 1; confirm <= 0; #20
din <= 2; #20
din <= 3; #20
din <= 4; #20
din <= 0; confirm <= 1; #20
din <= 1; confirm <= 0; #20
din <= 2; #20
din <= 3; #20
din <= 4; #20
din <= 0; confirm <= 1; #20
//连续输错三次后,进入lock状态,无法开锁
din <= 6; confirm <= 0; #20
din <= 7; #20
din <= 8; #20
din <= 9; #20
din <= 0; confirm <= 1; #20
//中途取消test
din <= 6; confirm <= 0; #20
din <= 7; #20
din <= 8; #20
cancel <= 1; din <= 0; #20
din <= 9; cancel <= 0; #20
din <= 0; confirm <= 1; #20
confirm <= 0;
end
always #10 clk = ~clk; //晶振周期20ns
lock test_lock(
.clk(clk),
.clr(clr),
.din(din),
.confirm(confirm),
.cancel(cancel),
.unlock_ok(unlock_ok),
.reset_ok(reset_ok),
.locking(locking)
);
endmodule
在Vivado中进行行为级仿真,结果符合预期
第一部分:依次输入1、2、3、4、Confirm,输出unlock_ok, 保持三个周期。
第二部分:依次输入1、2、3、5、Confirm,回到状态S0, wrong_count = 1
第三部分:依次输入230419 230419 6789 6789、Confirm,密码从1234修改为6789。
此时不关心状态机S的状态。按下确认键后,重设密码成功,则wrong_count会清零。
第四部分:依次输入1234 confirm 1234 confirm 1234 confirm
wrong_count达到3,进入死锁状态,持续三个周期,在此期间输入6789Confirm,也无法开锁。
第五部分:依次按下6 7 8 取消 9 确认。可见按下取消键后回到了S0状态。
个人作业-2016.12.2
时光飞逝,转眼之间一周的上课时间又过去了,眼看马上就要过寒假了,上半学期也就要结束了,陈老师所讲的软件工程这门课我有了自己的认识。
所谓的软件工程,其实就是流程二字,老师所讲给我们的都是一些理论知识,这或许与我所学的其他几门课程有所不同,其他几门课程都是理论中穿插着实践。
就如老师所言,我们软件是一流的课程,我们如果连理论都掌握不动,谈何实践。
我是第一次接触这门课程,刚开始的时候,有点跟不上老师的节奏,老师所说所写在我脑子里都像浆糊一样,说实话,刚开始的时候真的听不懂老师所讲的知识。
但是,到了现在,上半学期已经快要结束了,我也慢慢能跟上老师的节奏了,老师说到哪里自己也能跟得上节奏了。俗话说,万事开头难,而我现在已经慢慢进入老师的
讲法中去,对于我来说,这或许就是一个大的进步。
老师也让我们成立了自己的小组,我是我们小组的组长,我会引领我们小组的成员好好学习的。其实我们小组成员都想学好这门课程,大家心里都不傻,都知道这门课程
非常重要,我们一起讨论,一起学习,目的就是为了能更好的深入学习中去。当然,成立小组学习是个明确的选择,它可以让我们在课下有不懂的地方大家可以互相讨论,这样
可以增进大家的学习,分享自己的学习情况,也可以让小组成员来指出自己的错误之处。这是非常好的。
另外我想如果我们能多一些实践互动,那样会不会更好的促进学习,以小组为单位,能让每一位同学参与其中,扮演自己的角色,从而更好的去学习其中的知识。我相信
大家应该也会非常愿意的,玩中学,学中玩,正是这个理,(●ˇ?ˇ●)。
以上是关于Verilog陈老师的密码锁作业的主要内容,如果未能解决你的问题,请参考以下文章
闽江学院2015-2016学年下学期《软件测试》课程-第五次博客作业
2017-2018-2 20179223《密码与安全新技术》 第三次作业