这篇文章是留给自己答辩的,根据前面人反馈的信息,答辩基本就问这三个,所以我也按图索骥,根据这几个去复习,第一个便是数电最后一个实验。
想象你有一个自动售货机,这个机器可以根据你投入的钱和选择的商品来决定给你什么商品。我们可以把这种决策过程看作是一个状态机,它根据不同的输入(比如投入的钱和选择的商品)从一种状态转移到另一种状态,并最终给出一个输出(比如发放商品)。
Moore型有限状态机就像是一个非常守规矩的售货员。他只会在你完成所有操作(比如投币和选择商品)之后,在下一次动作(比如按下确认按钮)发生时才会检查你做了什么,然后根据当前的状态(比如你投入了多少钱,选择了哪个商品)来决定给你什么。也就是说,他的行为(输出)完全取决于他现在处于什么状态,而不是你刚才按了什么键或投了多少钱。即使你在等待他反应的时候又按了很多键,他也不会改变主意,直到下一次你做出完整的新操作。这种情况下,售货员的行为(输出)和顾客的操作(输入)是分开的,售货员不会因为顾客中途改变主意而立即做出反应。
Mealy型有限状态机则更像是一个更加灵活的售货员。他会随时注意你的每一个动作,一旦你有新的输入(比如多投了一枚硬币或者更改了商品选择),他可能会立刻改变自己的行为(比如更新显示的余额或者准备不同的商品)。也就是说,他的行为不仅取决于他现在处于什么状态,还直接依赖于你当前的输入是什么。这意味着,如果顾客在操作过程中不断改变主意,售货员会更快地作出响应,但也可能导致一些意外的情况,比如如果顾客不小心碰到了按键,售货员可能会误解顾客的意图。
总结一下,Moore型状态机的输出只依赖于当前状态,而Mealy型状态机的输出既依赖于当前状态也依赖于当前的输入。这意味着Moale型状态机对输入的响应更快,但同时也更敏感,可能会因为输入信号中的噪声而产生错误的输出。
这个时序图也不难理解,就是当w是连续4个0或者连续4个1的时候,把z置为1,只要四个出现,那么就在第四个状态结束时把z抬上去,同样的,只要第四个消失,那么,就在晋江进入第五个到时候把z1放下去,相当于就是——抬上去是向下取整,放下来是向上取整。把下和上看做后和前也行。
这个时序图可以来理解一下,当A为0时,它接下来有两种方式可以走,为1或者为0,倘若为0则往左,为1则往右。当连续出现四次都是沿着某1或者0走的时候,反斜杠右边的数字就变成1即z被抬到了1,反之如果在双方走的过程中,不小心改变了状态,譬如C变1或者D变1,那么一切前功尽弃,直接从F开始计数,注意在变的过程中取得1所以这已经算高电平了,因此后面只需要再保持三个高电平就可以抬z了。OK,这张图可以说是分析完毕。
状态机有九个状态:A/B/C/D/E/F/G/H/I,每个状态代表的情况都不一样,2的三次方是8,表示不了9种,所以需要用4个二进制位即4个状态机。
实验验收内容
咱们就按照实验里的顺序和思路来开始实现,首先,这个实验的验收内容是让咱们是实现三个点,咱们先根据要求进行,我这里把选做内容也实现了,首先我们先把top.nxdc接线进行一波实现,由于这里要将实现七段数码管,所以这里把七个数码管先接好,在top.nxdc中写代码:
seg0 (SEG0A, SEG0B, SEG0C, SEG0D, SEG0E, SEG0F, SEG0G, DEC0P)
seg1 (SEG1A, SEG1B, SEG1C, SEG1D, SEG1E, SEG1F, SEG1G, DEC1P)
seg2 (SEG2A, SEG2B, SEG2C, SEG2D, SEG2E, SEG2F, SEG2G, DEC2P)
seg3 (SEG3A, SEG3B, SEG3C, SEG3D, SEG3E, SEG3F, SEG3G, DEC3P)
seg4 (SEG4A, SEG4B, SEG4C, SEG4D, SEG4E, SEG4F, SEG4G, DEC4P)
seg5 (SEG5A, SEG5B, SEG5C, SEG5D, SEG5E, SEG5F, SEG5G, DEC5P)
seg6 (SEG6A, SEG6B, SEG6C, SEG6D, SEG6E, SEG6F, SEG6G, DEC6P)
seg7 (SEG7A, SEG7B, SEG7C, SEG7D, SEG7E, SEG7F, SEG7G, DEC7P)
我们这里额外添加一个复位信号clrn,作为BTNC,代码如下:
clrn (BTNC)
由于ps2键盘有clk时钟信号和dat信号,所以还需要绑定这两个信号:
ps2_clk (PS2_CLK)
ps2_data (PS2_DAT)
最后就在top.nxdc文件最上方添加top=top的约束就可以了
top=top
接着,我们把第二章的bcd7seg的七段数码管的代码直接拿来用就行,在vsrc文件中新建一个bcd7seg.v文件:
module bcd7seg(input [3:0]b, output reg [7:0]h);
always @(*) begin
case(b)
0:h = 8'b00000011;
1:h = 8'b10011111;
2:h = 8'b00100101;
3:h = 8'b00001101;
4:h = 8'b10011001;
5:h = 8'b01001001;
6:h = 8'b01000001;
7:h = 8'b00011111;
8:h = 8'b00000001;
9:h = 8'b00001001;
10:h = 8'b00010001;
11:h = 8'b11000001;
12:h = 8'b01100011;
13:h = 8'b10000101;
14:h = 8'b01100001;
15:h = 8'b01110001;
endcase
end
endmodule
这里的意思是输入四位的b,输出八位的h,根据b的值来确定输出的h是什么。
接下来是ps2_keyboard_model.v:
module ps2_keyboard(clk,clrn,ps2_clk,ps2_data,data,
ready,nextdata_n,overflow,key_count,shift_key_count,ctrl_key_count,shift_flag,ctrl_flag,CapsLock_key_count,CapsLock_flag);
input clk,clrn,ps2_clk,ps2_data;
input nextdata_n;
output [7:0] data;
output reg ready; // 队列中是否存在元素
output reg overflow; // 队列是否溢出
output reg[7:0]key_count; // 键盘计数
output reg[3:0]shift_key_count;
output reg[3:0]ctrl_key_count;
output reg[3:0] shift_flag;
output reg[3:0] ctrl_flag;
output reg[3:0] CapsLock_key_count;
output reg[3:0] CapsLock_flag;
reg [9:0] buffer; // 输入缓冲区
reg [7:0] fifo[7:0]; // 数据队列
reg [2:0] w_ptr,r_ptr; // 队列读写指针
reg [3:0] count; // 输入位数总数
reg [2:0] ps2_clk_sync; //记录ps2时钟信号
//PS/2时钟同步
/*在每个系统时钟上升沿,更新 ps2_clk_sync 寄存器,使其包含前两个时钟周期的PS/2时钟值。
这样可以检测PS/2时钟的下降沿。*/
always @(posedge clk) begin
ps2_clk_sync <= {ps2_clk_sync[1:0],ps2_clk};
end
/*sampling 是一个组合逻辑信号,当 ps2_clk_sync[2] 为高电平且 ps2_clk_sync[1] 为低电平时,表示检测到PS/2时钟的下降沿。*/
wire sampling = ps2_clk_sync[2] & ~ps2_clk_sync[1];
always @(posedge clk) begin
if (clrn == 1) begin // res