前言
之前的第一篇博客讲到了我们这个小游戏的内容和分工,前几个博客也谈到了几个重要的子模块,到现在我们已经实现了所有的底层模块,剩下的还有输出显示模块和顶层模块了。
为什么说这两个模块是顶层模块呢?我们对顶层模块的定义为实现多个模块交互的模块
这可不是简单的调用模块,输入一个时钟信号,得到一个时钟信号(指除颤或者分频),我们需要通过读入使能信号来实现不同的数码管输出方式。
(如果到这里并不清楚数码管的工作逻辑,看这篇博客)
分析
我们在最开始希望构建的输出显示模块就是简单的输8个四位二进制数,然后按照使能端判断主模块的状态来实现输出即可。这里我们也是采用这样的逻辑。
- 既然是4位的数据,我们就需要进行译码,转换成数码管能显示的8位数据。
- 分频是一定要有的模块了,这个不必多说,上面的链接中有完整代码。
- 因为主模块是按照状态来的,所以我们就一个个状态来分析,首先是产生随机数部分,我们的使能端是if_finish,(是在random模块判断读入结束后开始,在按下S1结束的信号)。
这里我们只需要使用5位,第一组数码管的最后一位和第二组数码管的全部,所以前面一组使能端锁定0001,输入数据赋值为out3(从左到右第四个数码管的数据),第二组正常; - 下一个状态是按下S1输入地址,主模块传入的使能端是从按下S1到按下S2之间有效的一个使能端。
我们只需要按照读入地址模块传入的内容进行输出即可,不论是地址有误还是地址正确,模块中都进行了输出数据的赋值,比较良心; - 然后是数据读入的状态,因为有匹配正确和错误两个大状态(这些我们在匹配模块中都进行了使能端的输出来控制)
如果是错误我们还有闪烁的使能端,使能端有效则输出8个0,无效直接将数码管的使能端置零;
正确的情况下,我们只需要七位,所以我们还需要在之前的显示中做一点小处理;另外有一个横的输出,我们创建了变量来进行处理。 - 我们虽然没有复位信号的控制,但是我们因为需要在开机和复位先显示8个常亮的0,所以我将状态外的情况都置为0
上代码
`timescale 1ns / 1ps
//负责亮灯,开机不用管,顶层有初始化
module light(
//输入的内容
input clk,
input s_one,
input s_two,
input flash,
input if_right,
input if_finish,
input [3:0]number1,
input [3:0]number2,
input [3:0]number3,
input [3:0]number4,
input [3:0]number5,
input [3:0]number6,
input [3:0]number7,
input [3:0]number8,
//输出的内容
output reg [7:0]leda,
output reg [7:0]ledb,
output reg [3:0]dn0,
output reg [3:0]dn1
);
reg [7:0]out0; //存放译码结果的寄存器
reg [7:0]out1;
reg [7:0]out2;
reg [7:0]out3;
reg [7:0]out4;
reg [7:0]out5;
reg [7:0]out6;
reg [7:0]out7;
wire clk_o; //分频信号和模块调用
light_divider div1(clk,clk_o);
reg [1:0]count=2'b00; //使能端控制器
reg [7:0]heng = 8'b0000_0010; //没错人家就是那个“横”
//译码部分
always@(number1,number2,number3,number4,number5,number6,number7,number8)
begin
case(number1)
4'b0000: out0 = 8'b1111_1100;
4'b0001: out0 = 8'b0110_0000;
4'b0010: out0 = 8'b1101_1010;
4'b0011: out0 = 8'b1111_0010;
4'b0100: out0 = 8'b0110_0110;
4'b0101: out0 = 8'b1011_0110;
4'b0110: out0 = 8'b1011_1110;
4'b0111: out0 = 8'b1110_0000;
4'b1000: out0 = 8'b1111_1110;
4'b1001: out0 = 8'b1111_0110;
4'b1010: out0 = 8'b1110_1110;
4'b1011: out0 = 8'b0011_1110;
4'b1100: out0 = 8'b0111_1010;
4'b1101: out0 = 8'b0111_1010;
4'b1110: out0 = 8'b1001_1110;
4'b1111: out0 = 8'b1000_1110;
endcase
case(number2)
4'b0000: out1 = 8'b1111_1100;
4'b0001: out1 = 8'b0110_0000;
4'b0010: out1 = 8'b1101_1010;
4'b0011: out1 = 8'b1111_0010;
4'b0100: out1 = 8'b0110_0110;
4'b0101: out1 = 8'b1011_0110;
4'b0110: out1 = 8'b1011_1110;
4'b0111: out1 = 8'b1110_0000;
4'b1000: out1 = 8'b1111_1110;
4'b1001: out1 = 8'b1111_0110;
4'b1010: out1 = 8'b1110_1110;
4'b1011: out1 = 8'b0011_1110;
4'b1100: out1 = 8'b0111_1010;
4'b1101: out1 = 8'b0111_1010;
4'b1110: out1 = 8'b1001_1110;
4'b1111: out1 = 8'b1000_1110;
endcase
case(number3)
4'b0000: out2 = 8'b1111_1100;
4'b0001: out2 = 8'b0110_0000;
4'b0010: out2 = 8'b1101_1010;
4'b0011: out2 = 8'b1111_0010;
4'b0100: out2 = 8'b0110_0110;
4'b0101: out2 = 8'b1011_0110;
4'b0110: out2 = 8'b1011_1110;
4'b0111: out2 = 8'b1110_0000;
4'b1000: out2 = 8'b1111_1110;
4'b1001: out2 = 8'b1111_0110;
4'b1010: out2 = 8'b1110_1110;
4'b1011: out2 = 8'b0011_1110;
4'b1100: out2 = 8'b0111_1010;
4'b1101: out2 = 8'b0111_1010;
4'b1110: out2 = 8'b1001_1110;
4'b1111: out2 = 8'b1000_1110;
endcase
case(number4)
4'b0000: out3 = 8'b1111_1100;
4'b0001: out3 = 8'b0110_0000;
4'b0010: out3 = 8'b1101_1010;
4'b0011: out3 = 8'b1111_0010;
4'b0100: out3 = 8'b0110_0110;
4'b0101: out3 = 8'b1011_0110;
4'b0110: out3 = 8'b1011_1110;
4'b0111: out3 = 8'b1110_0000;
4'b1000: out3 = 8'b1111_1110;
4'b1001: out3 = 8'b1111_0110;
4'b1010: out3 = 8'b1110_1110;
4'b1011: out3 = 8'b0011_1110;
4'b1100: out3 = 8'b0111_1010;
4'b1101: out3 = 8'b0111_1010;
4'b1110: out3 = 8'b1001_1110;
4'b1111: out3 = 8'b1000_1110;
endcase
case(number5)
4'b0000: out4 = 8'b1111_1100;
4'b0001: out4 = 8'b0110_0000;
4'b0010: out4 = 8'b1101_1010;
4'b0011: out4 = 8'b1111_0010;
4'b0100: out4 = 8'b0110_0110;
4'b0101: out4 = 8'b1011_0110;
4'b0110: out4 = 8'b1011_1110;
4'b0111: out4 = 8'b1110_0000;
4'b1000: out4 = 8'b1111_1110;
4'b1001: out4 = 8'b1111_0110;
4'b1010: out4 = 8'b1110_1110;
4'b1011: out4 = 8'b0011_1110;
4'b1100: out4 = 8'b0111_1010;
4'b1101: out4 = 8'b0111_1010;
4'b1110: out4 = 8'b1001_1110;
4'b1111: out4 = 8'b1000_1110;
endcase
case(number6)
4'b0000: out5 = 8'b1111_1100;
4'b0001: out5 = 8'b0110_0000;
4'b0010: out5 = 8'b1101_1010;
4'b0011: out5 = 8'b1111_0010;
4'b0100: out5 = 8'b0110_0110;
4'b0101: out5 = 8'b1011_0110;
4'b0110: out5 = 8'b1011_1110;
4'b0111: out5 = 8'b1110_0000;
4'b1000: out5 = 8'b1111_1110;
4'b1001: out5 = 8'b1111_0110;
4'b1010: out5 = 8'b1110_1110;
4'b1011: out5 = 8'b0011_1110;
4'b1100: out5 = 8'b0111_1010;
4'b1101: out5 = 8'b0111_1010;
4'b1110: out5 = 8'b1001_1110;
4'b1111: out5 = 8'b1000_1110;
endcase
case(number7)
4'b0000: out6 = 8'b1111_1100;
4'b0001: out6 = 8'b0110_0000;
4'b0010: out6 = 8'b1101_1010;
4'b0011: out6 = 8'b1111_0010;
4'b0100: out6 = 8'b0110_0110;
4'b0101: out6 = 8'b1011_0110;
4'b0110: out6 = 8'b1011_1110;
4'b0111: out6 = 8'b1110_0000;
4'b1000: out6 = 8'b1111_1110;
4'b1001: out6 = 8'b1111_0110;
4'b1010: out6 = 8'b1110_1110;
4'b1011: out6 = 8'b0011_1110;
4'b1100: out6 = 8'b0111_1010;
4'b1101: out6 = 8'b0111_1010;
4'b1110: out6 = 8'b1001_1110;
4'b1111: out6 = 8'b1000_1110;
endcase
case(number8)
4'b0000: out7 = 8'b1111_1100;
4'b0001: out7 = 8'b0110_0000;
4'b0010: out7 = 8'b1101_1010;
4'b0011: out7 = 8'b1111_0010;
4'b0100: out7 = 8'b0110_0110;
4'b0101: out7 = 8'b1011_0110;
4'b0110: out7 = 8'b1011_1110;
4'b0111: out7 = 8'b1110_0000;
4'b1000: out7 = 8'b1111_1110;
4'b1001: out7 = 8'b1111_0110;
4'b1010: out7 = 8'b1110_1110;
4'b1011: out7 = 8'b0011_1110;
4'b1100: out7 = 8'b0111_1010;
4'b1101: out7 = 8'b0111_1010;
4'b1110: out7 = 8'b1001_1110;
4'b1111: out7 = 8'b1000_1110;
endcase
end
//按照状态进行数码管内容赋值和使能端赋值
always @(posedge clk_o)
begin
if(if_finish) //随机数生成成功,开始显示
begin
dn0 <= 4'b0001; //左侧锁定
leda <= out3;
if(count == 3)
begin
count <= 1'b0;
ledb <= out7;
dn1 <=4'b0001;
end
else
begin
case(count)
2'b00:
begin
ledb<=out4;
dn1<=4'b1000;
end
2'b01:
begin
ledb<=out5;
dn1<=4'b0100;
end
2'b10:
begin
ledb<=out6;
dn1<=4'b0010;
end
endcase
count <= count + 1'b1;
end
end
else if(s_one) //开始输入地址
begin
if(count == 3)
begin
count = 1'b0;
leda <= out3;
ledb<= out7;
dn0<=4'b0001;
dn1<=4'b0001;
end
else
begin
case(count)
2'b00:
begin
leda<=out0;
ledb<=out4;
dn0<=4'b1000;
dn1<=4'b1000;
end
2'b01:
begin
leda<=out1;
ledb<=out5;
dn0<=4'b0100;
dn1<=4'b0100;
end
2'b10:
begin
leda<=out2;
ledb<=out6;
dn0<=4'b0010;
dn1<=4'b0010;
end
endcase
count = count + 1;
end
end
else if(s_two) //开始匹配
begin
if(if_right) //输入对的
begin
if(count == 3)
begin
count = 1'b0;
leda <= out3;
ledb<= out7;
dn0<=4'b0001;
dn1<=4'b0001;
end
else
begin
case(count)
2'b00:
begin
leda<=out0;
ledb<=out4;
dn0<=4'b0000; //注意这里本来是左侧第一位的使能端控制,不需要所以锁死
dn1<=4'b1000;
end
2'b01:
begin
leda<=out1;
ledb<=out5;
dn0<=4'b0100;
dn1<=4'b0100;
end
2'b10:
begin
leda<=heng; //输出“横”
ledb<=out6;
dn0<=4'b0010;
dn1<=4'b0010;
end
endcase
count = count + 1;
end
end
else //输入不对
begin
leda <= 8'b1111_1100; //数据都为0,直接赋值
ledb <= 8'b1111_1100;
if(!flash) //不亮,使能端锁死
begin
dn0 <= 4'b0000;
dn1 <= 4'b0000;
end
else //亮
begin
if(count == 3)
begin
count = 1'b0;
dn0<=4'b0001;
dn1<=4'b0001;
end
else
begin
case(count)
2'b00:
begin
dn0<=4'b1000;
dn1<=4'b1000;
end
2'b01:
begin
dn0<=4'b0100;
dn1<=4'b0100;
end
2'b10:
begin
dn0<=4'b0010;
dn1<=4'b0010;
end
endcase
count = count + 1;
end
end
end
end
else //其他情况,显示8个0
begin
leda <= 8'b1111_1100;
ledb <= 8'b1111_1100;
if(count == 3)
begin
count = 1'b0;
dn0<=4'b0001;
dn1<=4'b0001;
end
else
begin
case(count)
2'b00:
begin
dn0<=4'b1000;
dn1<=4'b1000;
end
2'b01:
begin
dn0<=4'b0100;
dn1<=4'b0100;
end
2'b10:
begin
dn0<=4'b0010;
dn1<=4'b0010;
end
endcase
count = count + 1;
end
end
end
endmodule
整体的架构想明白了还好,也不是很难,但这小四百行确实有一点离谱,因为有很多部分没有经过化简,
比如译码模块完全可以单拉出来一个;
数码管使能端改变也有能合并的部分。
当时在写这部分的时候,想的不算特别清楚,不少部分是直接CV,没有经过合并,这也算是以后做这种需要多模块交互功能的教训了。