前言
这是作者第一次做eda有关的课设,选题为“幸运老虎机的设计”。功能逻辑为本人以及一位小伙伴原创,上机显示部分参考了老师给的代码。整体编写流程就是先写源代码,然后通过仿真调试逻辑,最终基本逻辑无误后再下载到板子上运行调试。
在仿真阶段,这篇文章写的很好,我就是按照这个步骤来的~
最后希望能对大家的编写提供一定的借鉴,也欢迎大家提问或者给出建议~~~
问题简介
主要功能为:设计一个游戏机,有三位数码管显示0—7之间的数码,按下按钮,三个数码管循环显示,抬起按钮,显示停止,当显示内容相同时,为赢。 要求:
1、三个数码管循环显示的速度不同。
2、停止时的延迟时间也要不同。
3、游戏胜利是,发光二极管闪烁提示。
4、有“作弊”功能,按下该功能按键后,每次都能赢。
基本实现思路
1、利用时钟分频来使得数字增加的速度不同。
2、四个随机数r1、r2、r3、rand跟随时间变化。
(1)正常情况下,最终显示的三个数字,是stop时刻三个不同的随机数r1、r2、r3。
(2)作弊(cheat为1)情况下,最终显示的三个数字都是cheat信号为1时刻的随机数rand。
3、发光二极管用不断取反实现闪烁,注意stop信号下次开始的时候会停止闪烁。
注意事项以及说明
1、为了保证最后停止的时间不同,我们采用了不同的随机数作为最后的数字的方法。
2、为了最后停止的时候不会遇到这个随机数就停下,我们选择不管stop的时刻什么情况,都需要至少再跑一轮才能停下。
3、随机函数random在仿真阶段可以写到testbench里面,但是在模块里面是不支持的。
这里我们采用了不断乘以限定的数字,需要的时候对8取余传入即可得到一种随机数。
对于这种方法,我有几点说明:
(1)因为是根据时间来随机,这可能在某种程度上比随机算法更优。
(2)需要判断数字溢出的情况以及可能取到负数的情况,应加以规避。
(3)随机数传入的时刻需要注意,不能一直在赋值,一旦信号确定,传入后随机数字记录的数字就不能再发生变化。
4、最终数字显示到数码管上的时候需要进行对应的译码。
环境:
源代码编辑环境:
Quartus (Quartus Prime 18.1) Lite Edition
仿真环境:
ModelSim - Intel FPGA Starter Edition 10.5b (Quartus Prime 18.1)
板子型号
上机驱动
控制信号
输入信号:
(1)stop信号控制循环的开始与停止。
(2)cheat信号控制作弊功能。
输出信号:
(1)out1、out2、out3对应三个循环输出的数字。
(2)tip信号作为最终三个数字相同时候的闪烁提示。
仿真阶段源代码(.v文件)
module lhj(in,cheat,stop,r1,r2,r3,out1,out2,out3,tip,rand);
input in,cheat,stop;
input [2:0] r1,r2,r3,rand;
output[2:0] out1,out2,out3;
output tip;
reg tip;
reg[2:0] out1,out2,out3;
integer flag1=0,flag2=0,flag3=0;
integer num1,num2,num3;
integer t1,t2,t3;
integer n1=0,n2=0,n3=0;
initial
begin
out1=3'b000;
out2=3'b000;
out3=3'b000;
t1=-1;
t2=-2;
t3=-3;
tip=1'b0;
end
always @(in)
begin
n1=n1+1;
if(stop==0 && n1%2==0)
begin
out1=out1+1;
if(cheat==0)
num1=r1;
else if(cheat==1)
num1=rand;
end
if(stop==1 && n1%2==0)
begin
if(flag1==0)
begin
if(out1==num1)
begin
flag1=1;
out1=out1+1;
end
else
out1=out1+1;
end
else if(flag1==1)
begin
if(out1!=num1)
out1=out1+1;
else
flag1=2;
end
else if(flag1==2)
begin
out1=out1;
t1=out1;
end
end
end
always @(in)
begin
n2=n2+1;
if(stop==0 && n2%3==0)
begin
out2=out2+1;
if(cheat==0)
num2=r2;
else if(cheat==1)
num2=rand;
end
if(stop==1 && n2%3==0)
begin
if(flag2==0)
begin
if(out2==num2)
begin
flag2=1;
out2=out2+1;
end
else
out2=out2+1;
end
else if(flag2==1)
begin
if(out2!=num2)
out2=out2+1;
else
flag2=2;
end
else if(flag2==2)
begin
out2=out2;
t2=out2;
end
end
end
always @(in)
begin
n3=n3+1;
if(stop==0 && n3%4==0)
begin
out3=out3+1;
if(cheat==0)
num3=r3;
else if(cheat==1)
num3=rand;
end
if(stop==1 && n3%4==0)
begin
if(flag3==0)
begin
if(out3==num3)
begin
flag3=1;
out3=out3+1;
end
else
out3=out3+1;
end
else if(flag3==1)
begin
if(out3!=num3)
out3=out3+1;
else
flag3=2;
end
else if(flag3==2)
begin
out3=out3;
t3=out3;
end
end
end
always @(posedge in)
begin
if(t1==t2 && t2==t3)
begin
tip=~tip;
end
end
endmodule
仿真阶段testbench文件(.vt文件)
// Copyright (C) 2018 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions
// and other software and tools, and its AMPP partner logic
// functions, and any output files from any of the foregoing
// (including device programming or simulation files), and any
// associated documentation or information are expressly subject
// to the terms and conditions of the Intel Program License
// Subscription Agreement, the Intel Quartus Prime License Agreement,
// the Intel FPGA IP License Agreement, or other applicable license
// agreement, including, without limitation, that your use is for
// the sole purpose of programming logic devices manufactured by
// Intel and sold by Intel or its authorized distributors. Please
// refer to the applicable agreement for further details.
// *****************************************************************************
// This file contains a Verilog test bench template that is freely editable to
// suit user's needs .Comments are provided in each section to help the user
// fill out necessary details.
// *****************************************************************************
// Generated on "06/14/2019 08:40:20"
// Verilog Test Bench template for design : lhj
//
// Simulation tool : ModelSim (Verilog)
//
`timescale 1 ps/ 1 ps
module lhj_vlg_tst();
// constants
// general purpose registers
reg eachvec;
// test vector input registers
reg cheat;
reg in;
reg stop;
reg [2:0] r1;
reg [2:0] r2;
reg [2:0] r3;
reg [2:0] rand;
integer seed;
// wires
wire [2:0] out1;
wire [2:0] out2;
wire [2:0] out3;
wire tip;
// assign statements (if any)
lhj i1 (
// port map - connection between master ports and signals/registers
.cheat(cheat),
.in(in),
.r1(r1),
.r2(r2),
.r3(r3),
.out1(out1),
.out2(out2),
.out3(out3),
.stop(stop),
.tip(tip),
.rand(rand)
);
initial
begin
// code that executes only once
// insert code here --> begin
in=1'b0;
stop=0;
seed=1;
cheat=0;
forever #20 in=~in;
// --> end
$display("Running testbench");
end
always
// optional sensitivity list
// @(event1 or event2 or .... eventn)
begin
// code executes for every event on sensitivity list
// insert code here --> begin
#53 cheat=1;
#156 stop=1;
// --> end
end
always @(posedge in)
begin
r1<={$random(seed)}%8;
r2<={$random(seed)}%8;
r3<={$random(seed)}%8;
rand<={$random(seed)}%8;
end
endmodule
上机源文件(.v文件,内含注释)
module lhj (
//input
input cheat, //作弊信号
input stop, //停止信号和开始信号
input sys_clk ,//系统时钟
input sys_rst_n ,//复位时钟
//output
output wire seg_c1 ,//数码管位选信号
output wire seg_c2 ,
output wire seg_c3 ,
output wire seg_c4 ,
output reg tip, //闪烁提示灯
output reg seg_a ,//数码管段选信号
output reg seg_b ,
output reg seg_c ,
output reg seg_e ,
output reg seg_d ,
output reg seg_f ,
output reg seg_g ,
output reg seg_h
);
//parameter define
parameter WIDTH2 = 26;
parameter WIDTH = 5;
parameter SIZE = 8;
reg[2:0] out1,out2,out3;
reg[2:0] a1,a2,a3;
//reg define
reg [3:0] counter ;
reg [WIDTH2-1:0] count ;
reg [ 3:0] disp_data ;
reg [SIZE-1:0] dat ;
reg disp_clk ;
reg [25:0] clk_cnt ;
reg [25:0] clk_cnt1 ;
reg [25:0] clk_cnt2 ;
reg [15:0] scan_cnt ;
reg [ 3:0] segled_bit_sel ;
reg segled_a ;
reg segled_b ;
reg segled_c ;
reg segled_e ;
reg segled_d ;
reg segled_f ;
reg segled_g ;
reg segled_h ;
integer flag1=0,flag2=0,flag3=0;//标志,判断当前数字是否与随机数相等,并且使当前数字在经过一轮变化
integer num1,num2,num3;//最后停在的数字
integer t1,t2,t3;//记录最后停在的数字
integer n1=0,n2=0,n3=0;
integer r1,r2,r3;//随机数
reg[18:0] c;//记录停止前的时间
reg[18:0] rand;//作弊信号的记录时间
reg seed;
reg seed1,seed2,seed3;
parameter half_sec = 25'd25_000_000;
reg [24:0] countt;
initial //将所有值初始化
begin
out1=0;
out2=0;
out3=0;
c=0;
t1=-1;//由于要判断tip,先将三个输出置为不同值
t2=-2;
t3=-3;
tip=1'b1;
seed=1;
seed1=0;
seed2=0;
seed3=0;
end
//wire define
/*******************************************************************************************************
** Main Program
** 主要功能为:设计一个游戏机,有三位数码管显示0—7之间的数码,按下按钮,三个数码管循环显示,抬起按钮,显示停止,当显示内容相同时,为赢。 要求:
(1)三个数码管循环显示的速度不同
(2)停止时的延迟时间也要不同
(3)游戏胜利是,发光二极管闪烁提示
(4)有“作弊”功能,按下该功能按键后,每次都能赢
********************************************************************************************************/
// clk cnt for DISP data increase
always @(posedge sys_clk or negedge sys_rst_n) begin //在复位时钟为1时使scan_cnt+1
if (sys_rst_n ==1'b0)
scan_cnt <= 16'b0;
else
scan_cnt <= scan_cnt + 16'b1;
end
// gen segled bit sel by cnt high 2 bit
always @(posedge sys_clk or negedge sys_rst_n) begin //通过scan_cnt来选择数码管的位选
if (sys_rst_n ==1'b0)
segled_bit_sel <= 4'b0001;
else if ( scan_cnt[15:14] == 2'b00 )
segled_bit_sel <= 4'b0001;
else if ( scan_cnt[15:14] == 2'b01 )
segled_bit_sel <= 4'b0010;
else if ( scan_cnt[15:14] == 2'b10 )
segled_bit_sel <= 4'b0100;
else if ( scan_cnt[15:14] == 2'b11 )
segled_bit_sel <= 4'b1000;
else ;
end
// clk cnt for DISP data increase
always @(posedge sys_clk or negedge sys_rst_n) begin //将时钟分频,目的是使三个数码管的速度不同
if (sys_rst_n ==1'b0)
clk_cnt <= 26'b0;
else if ( clk_cnt == 26'd10000000 )
clk_cnt <= 26'b0;
else
clk_cnt <= clk_cnt + 26'b1;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n ==1'b0)
clk_cnt1 <= 26'b0;
else if ( clk_cnt1 == 26'd20000000 )
clk_cnt1 <= 26'b0;
else
clk_cnt1 <= clk_cnt1 + 26'b1;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n ==1'b0)
clk_cnt2 <= 26'b0;
else if ( clk_cnt2 == 26'd30000000 )
clk_cnt2 <= 26'b0;
else
clk_cnt2 <= clk_cnt2 + 26'b1;
end
always @(posedge sys_clk or negedge sys_rst_n) begin //记录开始时到stop为1时的时间,作为随机数的种子
if (sys_rst_n ==1'b0)
c <= 4'd0;
else if ( clk_cnt == 26'd10000000 ) begin
c<=c+1;
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin //记录开始时到cheat为1时的时间,作为作弊随机数的种子
if (sys_rst_n ==1'b0)
rand <= 4'd0;
else if ( clk_cnt == 26'd10000000 ) begin
if(cheat==0)
rand<=rand+1;
else
rand<=rand;
end
end
/*以下三个always为控制数码管,在stop为0时使数字一直加1,当stop为1时,flag为0表示未到达随机数位置继续加1,
flag为1表示当前数字已达到随机数但是还需要继续循环显示一轮,以延迟停止,flag为2表示已达到延迟显示的要求,之后的输出不再变化*/
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n ==1'b0)
out1 <= 4'd0;
else if ( clk_cnt == 26'd10000000 ) begin
if(stop==0)
begin
t1=-1;
flag1=0;
out1 <= out1+4'b1;
if(cheat==0)
begin
r1=(c*123)%8;//使用时间作为种子,将该数加倍并取余
num1=r1;
end
else if(cheat==1)
num1=rand%8;
end
else if(stop==1)
begin
if(flag1==0)
begin
if(out1==num1)
begin
flag1=1;
out1<=out1+4'b1;
end
else
out1<=out1+4'b1;
end
else if(flag1==1)
begin
if(out1!=num1)
out1<=out1+4'b1;
else
flag1=2;
end
else if(flag1==2)
begin
out1<=out1;
t1<=out1;//记录最后的输出,作为tip闪烁提示的判断条件
end
end
else ;
end
else ;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n ==1'b0)
out2 <= 4'd0;
else if ( clk_cnt1 == 26'd20000000 ) begin
if(stop==0)
begin
t2=-2;
flag2=0;
out2 <= out2+4'b1;
if(cheat==0)
begin
r2=(c*545)%8;
num2=r2;
end
else if(cheat==1)
num2=rand%8;
end
else if(stop==1)
begin
if(flag2==0)
begin
if(out2==num2)
begin
flag2=1;
out2<=out2+4'b1;
end
else
out2<=out2+4'b1;
end
else if(flag2==1)
begin
if(out2!=num2)
out2<=out2+4'b1;
else
flag2=2;
end
else if(flag2==2)
begin
out2<=out2;
t2<=out2;
end
end
else ;
end
else ;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n ==1'b0)
out3 <= 4'd0;
else if ( clk_cnt2 == 26'd30000000 ) begin
if(stop==0)
begin
t3=-3;
flag3=0;
out3 <= out3+4'b1;
if(cheat==0)begin
r3=(c*978)%8;
num3=r3;end
else if(cheat==1)
num3=rand%8;
end
else if(stop==1)
begin
if(flag3==0)
begin
if(out3==num3)
begin
flag3=1;
out3<=out3+4'b1;
end
else
out3<=out3+4'b1;
end
else if(flag3==1)
begin
if(out3!=num3)
out3<=out3+4'b1;
else
flag3=2;
end
else if(flag3==2)
begin
out3<=out3;
t3<=out3;
end
end
else ;
end
else ;
end
always @(posedge sys_clk or negedge sys_rst_n) begin //对t1,t2,t3进行判断,如果相同则表示老虎机最后三个输出相等,tip闪烁
if (sys_rst_n ==1'b0)
tip <= 4'd0;
else if ( clk_cnt2 == 26'd30000000 ) begin
if(t1==t2 && t2==t3 && stop==1)
tip=~tip;
else tip=0;
end
end
// sel dsip 4 bit data
always @(segled_bit_sel) begin //根据位选信号将不同输出赋给不同的数码管
if ( segled_bit_sel == 4'b0001 )
disp_data = out1 ;
else if ( segled_bit_sel == 4'b0010 )
disp_data = out2 ;
else if (segled_bit_sel == 4'b0100 )
disp_data = out3 ;
else
disp_data = 8 ;
end
// SEGLED decode from disp data
always @(*) begin //数码管段选判断,将数码管输出置为相应的十进制数字
case (disp_data)
7 :
begin
segled_a = 1 ;
segled_b = 1 ;
segled_c = 1 ;
segled_d = 0 ;
segled_e = 0 ;
segled_f = 0 ;
segled_g = 0 ;
segled_h = 0 ;
end
6 :
begin
segled_a = 1 ;
segled_b = 0 ;
segled_c = 1 ;
segled_d = 1 ;
segled_e = 1 ;
segled_f = 1 ;
segled_g = 1 ;
segled_h = 0 ;
end
5 :
begin
segled_a = 1 ;
segled_b = 0 ;
segled_c = 1 ;
segled_d = 1 ;
segled_e = 0 ;
segled_f = 1 ;
segled_g = 1 ;
segled_h = 0 ;
end
4 :
begin
segled_a = 0 ;
segled_b = 1 ;
segled_c = 1 ;
segled_d = 0 ;
segled_e = 0 ;
segled_f = 1 ;
segled_g = 1 ;
segled_h = 0 ;
end
3 :
begin
segled_a = 1 ;
segled_b = 1 ;
segled_c = 1 ;
segled_d = 1 ;
segled_e = 0 ;
segled_f = 0 ;
segled_g = 1 ;
segled_h = 0 ;
end
2 :
begin
segled_a = 1 ;
segled_b = 1 ;
segled_c = 0 ;
segled_d = 1 ;
segled_e = 1 ;
segled_f = 0 ;
segled_g = 1 ;
segled_h = 0 ;
end
1 :
begin
segled_a = 0 ;
segled_b = 1 ;
segled_c = 1 ;
segled_d = 0 ;
segled_e = 0 ;
segled_f = 0 ;
segled_g = 0 ;
segled_h = 0 ;
end
0 :
begin
segled_a = 1 ;
segled_b = 1 ;
segled_c = 1 ;
segled_d = 1 ;
segled_e = 1 ;
segled_f = 1 ;
segled_g = 0 ;
segled_h = 0 ;
end
default :
begin
segled_a = 0 ;
segled_b = 0 ;
segled_c = 0 ;
segled_d = 0 ;
segled_e = 0 ;
segled_f = 0 ;
segled_g = 0 ;
segled_h = 0 ;
end
endcase
end
// assign seg , low active
always @(*) begin //由于开发板的有效信号为0,将所有段选信号置反
seg_a = ~segled_a ;
seg_b = ~segled_b ;
seg_c = ~segled_c ;
seg_d = ~segled_d ;
seg_e = ~segled_e ;
seg_f = ~segled_f ;
seg_g = ~segled_g ;
seg_h = ~segled_h ;
end
// assign bit sel to SEGLED pin, low active
assign seg_c1 = ~( segled_bit_sel == 4'b0001 );//同段选信号,将位选信号置反
assign seg_c2 = ~( segled_bit_sel == 4'b0010 );
assign seg_c3 = ~( segled_bit_sel == 4'b0100 );
assign seg_c4 = ~( segled_bit_sel == 4'b1000 );
endmodule
//end of RTL code
最终显示情况:
1、正常模式下(只打开第一个拨码开关)
显示结果为随机数字,此次显示三个数字不同,因此没有进行闪烁胜利提示。
2、作弊模式下(第一个和第二个拨码开关均打开)
最终显示数字必定相同到一个确定的随机数字,此次显示为4,因此第五号发光二极管闪烁提示。
图1、闪烁灯亮 图2、闪烁灯灭