基于 Verilog 的骰子游戏电路设计与实现(基础版)
一、写在前面
这是骰子游戏的全部代码(除提高)提高部分还暂时没写,后续应该会自己写一个将乐谱转化为verilog的工具并开源(如果顺利的话)。数电实验所有代码均已经开源在我们的GitHub仓库,大家可以免费获取,我们致力于打造自由的作业交流平台,四个实验的代码都是我们小伙伴们自己完成,并自愿开源到GitHub上的(csdn开源的文件还需要强制VIP才能下载,所以我们都托管到了GitHub)如果对你有帮助,希望你可以在我们的仓库为我们点一个star!我们后续会一直维护
这个仓库,上传最新的资料!
二、整体架构概述
整个骰子游戏电路主要由以下几个关键模块组成:
debounce
模块:用于对原始按键输入信号进行消抖处理,确保按键信号的稳定性,避免因机械按键的抖动而产生误触发。random
和random2
模块:这两个模块分别实现了线性反馈移位寄存器(LFSR)算法,用于生成随机数,模拟骰子的点数输出。dot_matrix
模块:负责将骰子的点数以点阵的形式显示出来,通过控制行和列信号来点亮相应的 LED 点阵,直观地展示游戏结果。is_roll
模块:该模块根据时钟信号和外部触发信号,控制骰子的投掷操作,协调随机数生成器的工作,并处理相关的逻辑。debounce_double
模块:针对特定的按键输入进行消抖处理,确保在游戏过程中的按键操作准确无误。segment
模块:用于驱动数码管显示,将玩家的得分以数字形式展示出来,方便玩家实时了解游戏进展。prescaler
模块:对输入时钟进行分频操作,得到满足其他模块工作频率需求的时钟信号,确保整个系统各模块之间的时序协调。score
模块:实现游戏的计分逻辑,根据骰子的点数比较结果更新玩家的得分,并判断游戏是否结束以及最终的胜负情况。standby
模块:在游戏过程中控制 RGB 灯光的显示逻辑,根据游戏状态和得分情况呈现不同的灯光效果,增强游戏的可视化体验。dice
模块:顶层模块,定义了主要用于实例化各个函数。
三、模块详细解析
(一)debounce
模块
module debounce (
input wire clk, // 时钟信号
input wire rst_n, // 异步复位信号,低电平有效
input wire key_in, // 原始按键输入信号
output reg key_pressed, // 按键按下信号
output reg key_released // 按键释放信号
);
// 参数定义:假设消抖时间为 20ms
parameter DEBOUNCE_TIME = 20_000; // 假设时钟频率为 1 MHz(即 1us 一个周期)
reg [19:0] debounce_cnt; // 消抖计数器
reg key_state; // 按键稳定状态
reg key_state_last; // 上一个稳定状态
// 按键状态同步
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
debounce_cnt <= 20'd0;
key_state <= 1'b0; // 默认按键未按下(低电平为未按下)
key_state_last <= 1'b0;
end else begin
if (key_in == key_state) begin
// 输入信号和稳定状态一致,计数器清零
debounce_cnt <= 20'd0;
end else begin
// 输入信号和稳定状态不一致,计数器累加
if (debounce_cnt < DEBOUNCE_TIME) begin
debounce_cnt <= debounce_cnt + 20'd1;
end else begin
// 当计数器满时,更新稳定状态
debounce_cnt <= 20'd0;
key_state <= key_in;
end
end
// 保存上一个稳定状态
key_state_last <= key_state;
end
end
// 按键事件检测
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_pressed <= 1'b0;
key_released <= 1'b0;
end else begin
key_pressed <= (key_state_last == 1'b0) && (key_state == 1'b1); // 按键从未按下到按下
key_released <= (key_state_last == 1'b1) && (key_state == 1'b0); // 按键从按下到未按下
end
end
endmodule
这个模块通过一个计数器来实现消抖功能。当按键输入信号与当前稳定状态不一致时,计数器开始累加。如果在规定的消抖时间内(这里假设为 20ms,对应特定的时钟频率)计数器达到设定值,说明按键状态稳定,此时更新稳定状态并输出相应的按键按下或释放信号。
(二)random
和 random2
模块
module random(
input clk,
input wire rst, // 复位信号
input wire roll, // 掷骰子触发信号
output reg [3:0] dice, // 输出随机数(0-9)
input finish
);
reg [3:0] lfsr; // 4 位线性反馈移位寄存器 (LFSR)
initial begin
lfsr <= 4'b1001; // 初始化 LFSR,不为 0
dice <= 4'b1001;
end
always @(posedge roll or negedge rst or posedge finish) begin
if (!rst) begin
lfsr <= 4'b1001; // 初始化 LFSR,不为 0
dice <= 4'b1001; // 初始骰子值为 0
end else if (finish) begin
dice<=4'b1001;
end
else begin
if(roll)begin
// 更新 LFSR 值
lfsr <= {lfsr[2:0], lfsr[3] ^ lfsr[2]};
dice <= (lfsr % 9)+1; // 映射到 0-9
end
end
end
endmodule
module random2(
input clk,
input wire rst, // 复位信号
input wire roll, // 掷骰子触发信号
output reg [3:0] dice, // 输出随机数(0-9)
input finish
);
reg [3:0] lfsr; // 4 位线性反馈移位寄存器 (LFSR)
initial begin
lfsr <= 4'b0001; // 初始化 LFSR,不为 0
dice <= 4'b1001;
end
always @(posedge roll or negedge rst or posedge finish) begin
if (!rst) begin
lfsr <= 4'b0001; // 初始化 LFSR,不为 0
dice <= 4'b1001; // 初始骰子值为 0
end else if (finish) begin
dice<=4'b1001;
end
else begin
if(roll)begin
// 更新 LFSR 值
lfsr <= {lfsr[2:0], lfsr[3] ^ lfsr[2]};
dice <= (lfsr % 9)+1; // 映射到 0-9
end
end
end
endmodule
这两个模块都基于线性反馈移位寄存器(LFSR)来生成随机数。当 roll
信号有效且系统未处于 finish
状态时,LFSR 的值会根据特定的反馈逻辑进行更新,然后将更新后的 LFSR 值映射到 1 - 9 的范围作为骰子的点数输出。
(三)dot_matrix
模块
module dot_matrix (
input wire clk, // 时钟信号
input wire rst, // 复位信号
input stop,
input wire [3:0] dice1, // 输入骰子数字(1-9)
input wire [3:0] dice2, //第二个骰子
output reg [7:0] row, // 行信号(ROW7 - ROW0)
output reg [7:0] r_col, // 红色列信号(R_COL7 - R_COL0)
output reg [7:0] g_col // 绿色列信号
);
reg [7:0] row_patterns; // 行信号
reg [2:0] red_patterns[2:0]; // 红色列信号
reg [2:0] green_patterns[2:0]; //绿色信号
reg [2:0] scan_index; // 当前扫描行索引(0~7)
reg [1:0] sel; //这是来选择列信号的,每一个时钟周期变化一次
initial //参数初始化
begin
r_col <= 8'b0000_0000;
g_col <= 8'b0000_0000;
end
always@(*)begin //给红色信号的低三位赋值
case(dice1)
4'b0001:begin //1
red_patterns[0] = 3'b000;
red_patterns[1] = 3'b010;
red_patterns[2] = 3'b000;
end
4'b0010:begin //2
red_patterns[0] = 3'b001;
red_patterns[1] = 3'b000;
red_patterns[2] = 3'b100;
end
4'b0011:begin //3
red_patterns[0] = 3'b100;
red_patterns[1] = 3'b010;
red_patterns[2] = 3'b001;
end
4'b0100:begin //4
red_patterns[0] = 3'b101;
red_patterns[1] = 3'b000;
red_patterns[2] = 3'b101;
end
4'b0101:begin //5
red_patterns[0] = 3'b101;
red_patterns[1] = 3'b010;
red_patterns[2] = 3'b101;
end
4'b0110:begin //6
red_patterns[0] = 3'b111;
red_patterns[1] = 3'b000;
red_patterns[2] = 3'b111;
end
4'b0111:begin //7
red_patterns[0] = 3'b111;
red_patterns[1] = 3'b010;
red_patterns[2] = 3'b111;
end
4'b1000:begin //8
red_patterns[0] = 3'b111;
red_patterns[1] = 3'b101;
red_patterns[2] = 3'b111;
end
4'b1001:begin //9
red_patterns[0] = 3'b111;
red_patterns[1] = 3'b111;
red_patterns[2] = 3'b111;
end
default:begin //默认都不亮
red_patterns[0] = 3'b000;
red_patterns[1] = 3'b000;
red_patterns[2] = 3'b000;
end
endcase
case(dice2) //给绿色信号的高三位赋值
4'b0001:begin //1
green_patterns[0] = 3'b000;
green_patterns[1] = 3'b010;
green_patterns[2] = 3'b000;
end
4'b0010:begin //2
green_patterns[0] = 3'b001;
green_patterns[1] = 3'b000;
green_patterns[2] = 3'b100;
end
4'b0011:begin //3
green_patterns[0] = 3'b100;
green_patterns[1] = 3'b010;
green_patterns[2] = 3'b001;
end
4'b0100:begin //4
green_patterns[0] = 3'b101;
green_patterns[1] = 3'b000;
green_patterns[2] = 3'b101;
end
4'b0101:begin //5
green_patterns[0] = 3'b101;
green_patterns[1] = 3'b010;
green_patterns[2] = 3'b101;
end
4'b0110:begin //6
green_patterns[0] = 3'b111;
green_patterns[1] = 3'b000;
green_patterns[2] = 3'b111;
end
4'b0111:begin //7
green_patterns[0] = 3'b111;
green_patterns[1] = 3'b010;
green_patterns[2] = 3'b111;
end
4'b1000:begin //8
green_patterns[0] = 3'b111;
green_patterns[1] = 3'b101;
green_patterns[2] = 3'b111;
end
4'b1001:begin //9
green_patterns[0] = 3'b111;
green_patterns[1] = 3'b111;
green_patterns[2] = 3'b111;
end
default:begin
green_patterns[0] = 3'b000;
green_patterns[1] = 3'b000;
green_patterns[2] = 3'b000;
end
endcase
end
reg [2:0]flag;//计八个数
reg assist;
initial begin
flag<=0;
end
// 显示点阵逻辑
always @(posedge clk or negedge rst ) begin
if (!rst) begin
row <= 8'b1111_1111; // 所有行禁用
r_col <= 8'b0000_0000; // 红色列全灭
g_col <= 8'b0000_0000;
end else begin
if(flag==3'b000)begin
row <= 8'b01111111; // 使用移位来进行赋值
r_col[2:0] <= red_patterns[0];//将红色低三位赋值给列
g_col <= 8'b0000_0000;
flag <=flag +1 ;
end
if(flag==3'b001) begin
row <= 8'b10111111;
r_col[2:0] <= red_patterns[1];//将绿色高三位赋值给列信号
flag <= flag+1;
end
if(flag==3'b010)begin
row <= 8'b1101_1111;
r_col[2:0] <= red_patterns[2];
flag <= flag +1;
end
if(flag == 3'b011)begin
row <= 8'b1110_1111;
flag <= flag +1;
r_col <= 8'b0000_0000;
end
if(flag == 3'b100)begin
row <= 8'b1111_0111;
flag <= flag+1;
end
if(flag ==3'b101)begin
row <= 8'b1111_1011;
g_col[7:5] <= green_patterns[0];
flag <= flag+1;
end
if(flag == 3'b110)begin
row <= 8'b1111_1101;
g_col[7:5] <= green_patterns[1];
flag <= flag+1;
end
if(flag == 3'b111)begin
row <= 8'b1111_1110;
g_col[7:5] <= green_patterns[2];
flag <= 3'b000;
end
end
end
endmodule
(四)is_roll
模块
module is_roll( //测试模块用于根据时钟信号,每0.2秒进行扔骰子操作
input clk, //时钟选择1kHz
input wire rst, // 复位信号
output [3:0] dice1, // 输出随机数(0-9)
output [3:0] dice2,
input start1,
input start2,
input finish
);
reg roll1,roll2; //设置start信号,由时钟控制
reg [9:0] count1,count2; //20计数器,即选择5位
reg cnt;
initial begin
count1 <= 10'b0000_0000;
count2 <= 10'b0000_0000;
roll1 <= 1'b0;
roll2 <= 1'b0;
cnt <= 1'b0;
end
// 实例化第二个随机数生成器
random2 u_random2 (
.clk(clk),
.rst(rst), // 连接复位信号
.roll(roll2), // 连接掷骰子触发信号
.dice(dice2), // 连接第二个骰子的随机数输出
.finish(finish)
);
// 实例化第一个随机数生成器
random u_random1 (
.clk(clk),
.rst(rst), // 连接复位信号
.roll(roll1), // 连接掷骰子触发信号
.dice(dice1), // 连接第一个骰子的随机数输出
.finish(finish)
);
always @(posedge clk or negedge rst) begin
if (!rst) begin
roll1 <= 1'b0;
count1 <= 10'b0;
end
else
if(start1)begin
if (count1 < 10'd100) begin
count1 <= count1 + 1;
roll1 <= 0;
end else begin
count1 <= 10'b0;
roll1 <= 1; // 触发掷骰子
end
end
else begin
count1<=10'd0;
end
end
always @(posedge clk or negedge rst) begin
if (!rst) begin
roll2 <= 1'b0;
count2 <= 10'b0;
end
else
if(start2)begin
if (count2 < 10'd100) begin
count2 <= count2 + 1;
roll2 <= 0;
end else begin
count2 <= 10'b0;
roll2 <= 1; // 触发掷骰子
end
end
else begin
count2<=10'd0;
end
end
endmodule
这个模块主要用于控制骰子的投掷操作时机。它内部包含了计数器 count
,在复位后,随着时钟上升沿,计数器开始累加,当计数器未达到设定值(这里根据代码逻辑与相关时间设定有关)时,不会触发掷骰子操作,即 roll1
和 roll2
保持为 0
。而当计数器达到设定值后,会根据外部输入的 start1
和 start2
信号来触发对应的随机数生成器(random
和 random2
模块)进行骰子点数的生成,以此模拟玩家投掷骰子的动作。同时,还实例化了 debounce
模块用于对相关按键输入(虽然代码中 key_in
等未明确外部连接,但功能上是用于消抖相关按键操作的)进行消抖处理,确保操作的准确性。
(五)debounce_double
模块
module debounce_double (
input stop, //暂停时候不进行任何逻辑
input wire clk, // 时钟信号
input wire btn_in, // 原始按键信号
output reg btn_out // 去抖动后的稳定按键信号
);
reg [16:0] counter = 0; // 用于计数的20位计数器
reg btn_sync_0, btn_sync_1; // 同步化寄存器
reg btn_state = 0; // 记录当前稳定的按键状态
// 使用两个触发器同步按键输入信号,避免亚稳态
always @(posedge clk or posedge stop) begin
btn_sync_0 <= btn_in; // 第一层同步
btn_sync_1 <= btn_sync_0; // 第二层同步
end
// 计时器和去抖动逻辑
always @(posedge clk or posedge stop) begin
if(stop)begin
btn_out<=0;
end
else begin
if (btn_sync_1 != btn_state) begin
// 如果检测到按键状态发生变化,开始计时
counter <= counter + 1;
if (counter == 16'b0100_1110_0010_0000) begin // 经过足够长时间(20位计数器达到最大)
btn_state <= btn_sync_1; // 更新按键状态
btn_out <= btn_sync_1; // 输出去抖动后的按键信号
counter <= 0; // 计数器清零
end
end else begin
counter <= 0; // 如果按键状态没有变化,计数器保持清零
end
end
end
endmodule
该模块实现了按键的消抖功能,尤其适用于游戏中需要稳定按键输入的场景。首先通过两个触发器 btn_sync_0
和 btn_sync_1
对原始按键输入 btn_in
进行同步处理,避免亚稳态问题。然后设置了一个计数器,当检测到同步后的按键状态与当前稳定的按键状态 btn_state
不一致时,计数器开始累加,当计数器累加到特定值(代表经过了足够的消抖时间),就更新按键状态并输出稳定的去抖动后的按键信号 btn_out
。如果按键状态一直没有变化或者系统处于暂停(stop
信号有效)状态,则按照相应逻辑处理计数器和输出信号。
(六)segment
模块
module segment(
input [3:0] score1, // 8位 BCD 数字,低四位为玩家1,高四位为玩家二
input [3:0] score2,
input clk, // 时钟信号
output reg [7:0] cat, // 位选信号,用于选择显示的数码管
output reg [7:0] signal // 7段显示信号
);
// 初始化 cat 为 2'b01,用于初始选择个位
initial begin
cat = 8'b0111_1111;
signal = 8'b00000000; // 给 seg 一个初始值
end
// 在时钟上升沿切换 cat,并根据 cat 显示对应的数码管
always @(posedge clk) begin
// 使用移位操作来交替切换 cat
{cat[7],cat[0]} <= {cat[0], cat[7]}; // 将 cat 的值在 2'b01 和 2'b10 之间切换
// 根据 cat 来选择不同的数码管并输出相应的 BCD 信号
if ({cat[7],cat[0]} == 2'b01) begin
// 显示个位,低四位玩家一
case (score1)
4'b0000: signal = 8'b1111_1100; // 显示 "0"
4'b0001: signal = 8'b0110_0000; // 显示 "1"
4'b0010: signal = 8'b1101_1010; // 显示 "2"
4'b0011: signal = 8'b1111_0010; // 显示 "3"
4'b0100: signal = 8'b0110_0110; // 显示 "4"
4'b0101: signal = 8'b1011_0110; // 显示 "5"
4'b0110: signal = 8'b1011_1110; // 显示 "6"
4'b0111: signal = 8'b1110_0000; // 显示 "7"
4'b1000: signal = 8'b1111_1110; // 显示 "8"
4'b1001: signal = 8'b1111_0110; // 显示 "9"
default: signal = 8'b0000_0000; // 默认全部熄灭
endcase
end else if ({cat[7],cat[0]} == 2'b10) begin
// 显示十位,玩家2
case (score2)
4'b0000: signal = 8'b1111_1100; // 显示 "0"
4'b0001: signal = 8'b0110_0000; // 显示 "1"
4'b0010: signal = 8'b1101_1010; // 显示 "2"
4'b0011: signal = 8'b1111_0010; // 显示 "3"
4'b0100: signal = 8'b0110_0110; // 显示 "4"
4'b0101: signal = 8'b1011_0110; // 显示 "5"
4'b0110: signal = 8'b1011_1110; // 显示 "6"
4'b0111: signal = 8'b1110_0000; // 显示 "7"
4'b1000: signal = 8'b1111_1110; // 显示 "8"
4'b1001: signal = 8'b1111_0110; // 显示 "9"
default: signal = 8'b0000_0000; // 默认全部熄灭
endcase
end
end
endmodule
segment
模块的主要功能是驱动数码管显示玩家的得分情况。模块初始化时设置了 cat
(位选信号)和 signal
(7 段显示信号)的初始值。在每个时钟上升沿,通过移位操作切换 cat
的值,使其在 2'b01
和 2'b10
之间交替,以此来选择不同的数码管(个位或十位对应的数码管)。然后根据 cat
的值以及输入的玩家得分(score1
和 score2
),通过 case
语句来确定 signal
的具体值,从而在数码管上显示出对应的数字,实现了对玩家得分的可视化展示。
(七)prescaler
模块
module prescaler #(
parameter DIVIDE_BY = 1000 // 分频因子,默认分频为2
)(
input wire clk_in, // 输入时钟信号
input wire rst, // 复位信号
output reg clk_out // 输出分频后的时钟信号
);
reg [31:0] counter = 0; // 32位计数器,用于计数输入时钟周期
always @(posedge clk_in or negedge rst) begin
if (!rst) begin
counter <= 0; // 如果复位信号为高电平,清零计数器
clk_out <= 0; // 复位时,将输出时钟清零
end else begin
if (counter == DIVIDE_BY- 1 ) begin
counter <= 0; // 每到达分频周期时,计数器清零
clk_out <= ~clk_out; // 翻转输出时钟
end else begin
counter <= counter + 1; // 否则,计数器自增
end
end
end
endmodule
此模块是一个简单但实用的时钟分频器。它基于一个 32 位的计数器 counter
来实现对输入时钟 clk_in
的分频功能。根据设定的分频因子 DIVIDE_BY
,每当计数器累加到 DIVIDE_BY - 1
时,就将计数器清零,并对输出时钟 clk_out
进行翻转(取反)操作,从而得到一个频率为输入时钟频率除以 DIVIDE_BY
的分频时钟信号,为系统中其他需要不同时钟频率的模块提供合适的时钟源,确保各模块之间的时序配合。
(八)score
模块
module score(
input clk,rst,
input start1,
input start2,
input [3:0] dice1,
input [3:0] dice2,
output reg [3:0] state1,
output reg [3:0] state2,
output reg times,
output reg is_final,
output reg finish
);
reg [27:0] cnt;
reg [3:0] difference,state1_tmp,state2_tmp;
reg start1_before,start2_before,flag1,flag2,sign,start1_sync,start2_sync,switch;
// 初始状态
initial begin
cnt <= 28'd0;
is_final <= 0;
times <= 0;
state1 <= 0;
state2 <= 0;
finish <= 0;
flag1<=0;
flag2<=0;
sign<=0;
difference<=0;
start1_before <= 0;
start2_before <= 0;
start1_sync <= 0;
start1_sync <= 0;
state1_tmp<=0;
state2_tmp<=0;
switch<=0;
end
always @(posedge clk or negedge rst) begin
if (!rst) begin
start1_before <= 0;
start2_before <= 0;
start1_sync <= 0;
start1_sync <= 0;
end else begin
start1_before <= start1_sync;
start2_before <= start2_sync;
start1_sync <= start1;
start2_sync <= start2;
if((start1_before==1)&&(start1==0))begin
flag1<=1;
end
if((start2_before==1)&&(start2==0))begin
flag2<=1;
end
if(flag1&&flag2)begin
sign<=1;
flag1<=0;
flag2<=0;
end
if(sign)begin
if (difference < 2) begin
if(cnt>=28'd3000000)begin
sign<=0;end
end
else begin
if(cnt>=28'd5000000)begin
sign<=0;end
end
end
end
end
// 计数逻辑:由时钟信号驱动
always @(negedge rst or posedge clk) begin
if(!rst)begin
cnt<=0;
finish<=0;
times<=0;
is_final<=0;
end
else begin
if (sign) begin
if (difference < 2) begin
if (cnt >= 28'd3000000) begin // 计时3秒difference < 2
cnt <= 28'd0;
times <= 0;
finish<=0;
is_final <= 0;
switch <=0;
end
else if(cnt>=28'd2999900)begin
finish<=1;
cnt<=cnt+1;
end
else begin
cnt <= cnt + 1;
times <= 1;
end
end
else begin
if (cnt >= 28'd5000000) begin // 计时5秒
cnt <= 28'd0;
times <= 0;
finish<=0;
switch <= 0;
end
else if(cnt>=28'd3000000)begin
switch<=1;
cnt<=cnt+1;
end
else if(cnt >= 28'd4999900)begin
finish<=1;
cnt<=cnt+1;
end
else begin
cnt <= cnt + 1;
times <= 1;
is_final <= 1;
finish<=0;
end
end
end
end
end
// 游戏状态更新逻辑:由 times 的上升沿触发 此时更新的为内在逻辑
always @(negedge rst or posedge finish or posedge times) begin
if(!rst)begin
state1_tmp<=4'b0000;
state2_tmp<=4'b0000;
difference = 4'b0;
end
else begin
if (finish) begin
if(is_final)begin
state1_tmp<=4'b0000;
state2_tmp<=4'b0000;
difference = 4'b0;
end
end
else begin
if(times)begin
if (dice1 < dice2)begin
state2_tmp = state2_tmp + 1;
difference <= (state1_tmp > state2_tmp) ? state1_tmp - state2_tmp : state2_tmp - state1_tmp;
end
else if (dice1 > dice2)begin
state1_tmp = state1_tmp + 1;
difference <= (state1_tmp > state2_tmp) ? state1_tmp - state2_tmp : state2_tmp - state1_tmp;
end
end
end
end
end
//更新显示逻辑
always@(negedge rst or negedge times or posedge switch)begin
if(!rst)begin
state1<=0;
state2<=0;
end
else begin
if(!times)begin
state1<=state1_tmp;
state2<=state2_tmp;
end
else if(switch)begin
if (dice1 < dice2)begin
state1<=state1+1;
end
else begin
state2<=state2+1;
end
end
end
end
endmodule
(九)standby
模块
module standby(clk, times, is_final, rst, score1, score2, rgb_led,is_finish);
input clk;
input times;
input is_final,is_finish;
input rst;
input [3:0] score1, score2;
output reg [15:0] rgb_led;
reg [19:0] cnt;
reg [3:0] score1_reg, score2_reg;
initial begin
cnt<=20'd0;
end
// 同步复位及寄存分数
always @(posedge clk or negedge rst) begin
if (!rst) begin
score1_reg <= 4'b0;
score2_reg <= 4'b0;
end else begin
score1_reg <= score1;
score2_reg <= score2;
end
end
// 计数器逻辑
always @(posedge clk or negedge rst) begin
if (!rst) begin
cnt <= 20'd0;
end else if (!is_final && times) begin //不是最后一把并且times为高电平
if (cnt >= 20'd100001) begin
cnt <= 20'd0;
end else begin
cnt <= cnt + 1;
end
end
end
// RGB灯光逻辑
always @(posedge clk or negedge rst) begin
if (!rst) begin
rgb_led <= 16'b0000_0000_0000_0000;
end
else if (!is_final) begin
if (times) begin
if (rgb_led == 16'd0) begin
rgb_led <= 16'b0000_0000_0000_0001;
end else if (cnt >= 20'd100000) begin
rgb_led <= {rgb_led[0],rgb_led[15:1]};
end
end
else {
rgb_led<=16'b0000_0000_0000_0000;
}
end
else {
if(times)begin
if (score1_reg > score2_reg) begin
rgb_led <= 16'b1111_1111_0000_0000;
end
else {
rgb_led <= 16'b0000_0000_1111_1111;
}
}
else {
rgb_led<=16'b0000_0000_0000_0000;
}
end
end
endmodule
(十)dice
顶层模块
module dice(row,r_col,g_col,rst,clk,start1,start2,clk_div,dice1,dice2,signal,cat,rgb_led,beep);
input rst;
input clk;
input start1,start2;
output [7:0] row; //行控制信号
output [7:0] r_col; //红色列控制信号
output [7:0] g_col; //绿色列控制信号
output clk_div;
output [3:0] dice1,dice2;
output [7:0] signal;
output [7:0] cat;
output [15:0] rgb_led;
output beep;
wire [3:0]score1,score2;
wire btn_out1,btn_out2;
wire divided_clk;
wire times;
wire is_final;
wire finish;
prescaler #(
.DIVIDE_BY(1000)
) u_prescaler (
.clk_in(clk),
.rst(rst),
.clk_out(divided_clk)
);
debounce_double uut_debounce(
.stop(times),
.clk(clk),
.btn_in(start1),
.btn_out(btn_out1)
);
debounce_double uut_debounce2(
.stop(times),
.clk(clk),
.btn_in(start2),
.btn_out(btn_out2)
);
segment u_segment(
.clk(clk),
.score1(score1),
.score2(score2),
.cat(cat),
.signal(signal)
);
is_roll u_roll(
.clk(divided_clk),
.rst(rst),
.dice1(dice1),
.dice2(dice2),
.start1(btn_out1),
.start2(btn_out2),
.finish(finish)
);
// 实例化 dot_matrix 模块
dot_matrix u_dot_matrix (
.clk(clk), // 连接时钟信号
.rst(rst), // 连接复位信号
.dice1(dice1), // 连接第一个骰子的输入
.dice2(dice2), // 连接第二个骰子的输入
.row(row), // 连接行信号输出
.r_col(r_col), // 连接红色列信号输出
.g_col(g_col), // 连接绿色列信号输出
.stop(times)
);
// 实例化standby模块
standby standby_inst (
.clk(clk),
.times(times),
.is_final(is_final),
.rst(rst),
.score1(score1),
.score2(score2),
.rgb_led(rgb_led),
.is_finish(finish)
);
// 实例化socre模块
score socre_inst (
.clk(clk),
.start1(btn_out1),
.start2(btn_out2),
.dice1(dice1),
.dice2(dice2),
.state1(score1),
.state2(score2),
.times(times),
.is_final(is_final),
.finish(finish),
.rst(rst)
);
endmodule
standby
模块在整个骰子游戏系统中承担着控制 RGB 灯光显示效果的重要作用,以此来直观反馈游戏的不同状态。
首先,模块中有一个 cnt
计数器,在复位时被初始化为 20'd0
。通过 always
块实现了对输入分数 score1
和 score2
的同步复位及寄存功能,在时钟上升沿或者复位信号有效时,会相应地更新 score1_reg
和 score2_reg
的值,确保其能准确反映当前玩家的得分情况。
在计数器逻辑部分,当系统未处于 is_final
状态(即游戏未结束)并且 times
信号为高电平(意味着处于相应的有效游戏阶段)时,计数器 cnt
开始工作。如果 cnt
的值达到 20'd100001
,则将其清零;否则,计数器会随每个时钟周期自增,这个计数器主要用于配合后续的灯光显示逻辑来控制灯光切换的时间间隔等。
而 RGB 灯光逻辑是该模块的核心部分。在复位时,rgb_led
被初始化为全 0
,即灯光全灭。当游戏未结束(!is_final
)时,如果 times
信号为高电平,会先判断 rgb_led
的值,若其为 16'd0
,则将其设置为 16'b0000_0000_0000_0001
,表示点亮某一盏灯(具体取决于硬件连接对应的灯光效果),并且当 cnt
达到 20'd100000
时,通过移位操作 {rgb_led[0],rgb_led[15:1]}
来改变灯光的显示状态,实现动态的灯光效果,比如流水灯等形式。当 times
为低电平时,直接将 rgb_led
清零,关闭灯光。
当游戏处于结束状态(is_final
为真)时,如果 times
信号为高电平,会根据寄存的玩家得分 score1_reg
和 score2_reg
来决定最终的灯光显示情况,若 score1_reg
大于 score2_reg
,则 rgb_led
被设置为 16'b1111_1111_0000_0000
(可能表示玩家 1 获胜对应的灯光颜色或样式),反之则设置为 16'b0000_0000_1111_1111
(对应玩家 2 获胜或平局等情况的灯光呈现),若 times
为低电平同样将灯光熄灭,以清晰地通过灯光向玩家展示游戏的最终胜负结果或者游戏过程中的不同阶段状态。
综上所述,通过 standby
模块的这些逻辑,使得整个骰子游戏系统在视觉呈现上更加丰富和直观,增强了游戏的趣味性和交互性。
四、总结
扔色子这个代码最难的地方我觉得在于时序的控制和赋值,尤其是存在五秒和三秒的待机时间,所有的标志位你都需要在一个always块内赋值,所以所有逻辑应该考虑清楚!
五、提高部分
提高部分的python工具我已经写在了这篇博客里面,大家可以跳转阅读,另外也已经更新到GitHub仓库,大家可以进入仓库查看!