
实验 7 是基于多路选择器设计了一个计分板,通过 4 个阵列按钮输入改变对应 4 位数码管的值并显示。实验 7 结合了输入、输出,掌握以后其实就可以开始着手准备大程。与组合电路不同,大程的难点在于有意义的状态机设计,需要考虑时序电路中的延时对输出造成的影响。如本次实验中设计的计分板稍作修改就可以用作游戏积分的显示模块。

掌握数据选择器的工作原理和逻辑功能
掌握数据选择器的使用方法
掌握 4 位数码管扫描显示方法
4 位数码管显示应用—记分板设计
任务1:数据选择器设计
任务2:记分板设计
2.2.1 Mux4to1: 4选1多路选择器
根据事件简化真值表

图表1 4选1多路选择器真值表
输出是控制信号全部最小项与或结构控制结构不变,每路输入向量化

图表2 4选1多路选择器逻辑电路图
扩展至16选4如下图所示:

图表3 16选4多路选择器逻辑电路图
生成元件后如下图所示:

图表4 4选1多路选择器逻辑符号
2.2.2 动态扫描显示
动态扫描显示方案
扫描信号来自计数器:时序转化为组合电路
由板载时钟 clk (100MHz)作为计数器时钟,分频后的高两位信号(clk_div[18:17])作为扫描控制信号,输入2-4译码器控制哪个数码管显示(位选择),同时输入4选1多路复用器选择需要显示哪个数据(段码选择)
计数器的分频系数要适当,几ms切换一次,眼睛舒适即可

图表5 动态扫描示意图
动态扫描同步输出模块的电路图如下:

图表6 动态扫描同步输出模块逻辑电路图

图表7 动态扫描同步输出模块逻辑电路图

图表8 动态扫描同步输出模块逻辑电路图
2.2.3 辅助模块:时钟计数分频器
32位时钟计数分频器
可输出 2~232 分频信号,可用于一般非同步类时钟信号
延时较高,要求不高的时钟也可以用
本实验中用 clkdiv(18:17) 作为扫描控制信号,控制 4 位数码管的动态扫描具体实现代码如下所示:
module clkdiv(
input clk,
input rst,
output reg [31:0] clkdiv
);
// clock diveder - 时钟分频器
always @ (posedge clk or posedge rst) begin
if (rst) clkdiv <= 0;
else clkdiv <= clkdiv + 1'b1;
end
endmodule 根据原理,设计通用计数分频模块clkdiv,并将其输出的clkdiv(18:17)作为数据选择器的位选信号;clkdiv(17)作为消抖模块的时钟信号。

图表9 时钟分频模块
2.2.4 设计按键数据输入模块
按键去抖动处理
抖动原因:按键按下或放开时,存在机械震动
抖动时间一般在10~20 ms
按键去抖动方法:延时,以避开机械抖动具体实现代码如下所示:
创建数字模块
module CreateNumber(
input wire clk,
input wire [3:0] btn,
output reg [15:0] num
);
wire [3:0] A,B,C,D;
wire [3:0] temp_btn;
initial num <= 16'b 0001001000110100;
assign A = num[3:0] + 1'b1;
assign B = num[7:4] + 1'b1;
assign C = num[11:8] + 1'b1;
assign D = num[15:12] + 1'b1;
pbdebounce p0(.clk(clk),.button(btn[0]),.pbreg(temp_btn[0]));
pbdebounce p1(.clk(clk),.button(btn[1]),.pbreg(temp_btn[1]));
pbdebounce p2(.clk(clk),.button(btn[2]),.pbreg(temp_btn[2]));
pbdebounce p3(.clk(clk),.button(btn[3]),.pbreg(temp_btn[3]));
always@(posedge temp_btn[0]) num[ 3: 0] <= A;
always@(posedge temp_btn[1]) num[ 7: 4] <= B;
always@(posedge temp_btn[2]) num[11: 8] <= C;
always@(posedge temp_btn[3]) num[15:12] <= D;
endmodule 防抖动模块
module pbdebounce(
input wire clk_1ms,
input wire button,
output reg pbreg
);
reg [7:0] pbshift;
always@(posedge clk_1ms) begin
pbshift=pbshift<<1;
pbshift[0]=button;
if (pbshift==8'b0)
pbreg=0;
if (pbshift==8'hFF)
pbreg=1;
end
endmodule 2.2.5 计分板的逻辑功能
btn[3:0] 这4个按钮控制数码管数值变化。每个按钮按动一次,对应数码管的值加1
用 SW0~SW3 这4个开关控制每个数码管的小数点
用 SW4~SW7 这4个开关控制每个数码管的消隐
SEGMENT[7:0] 对应7段数码管的 a-p
BN 开关控制按钮行状态。BN = 0 时,按钮行有效,否则失效。

图表10 BN开关
实验设备装有Xilinx ISE 14.7的计算机 1台SWORD开发板 1套
实验材料无
4.1.1 原理图绘制多路选择器
4.1.1.1 Mux4to1
1. 新建工程,命名为 Mux4to1,top-level source type 为 schematic
2. 新建类型为 schematic 的源文件,命名为 Mux4to1_Number
3. 用原理图方式设计,具体如下所示:

图表11 4选1多路选择器电路逻辑图
4. 生成逻辑符号和.vf文件:点击 Process 窗口下 Design Utilities -> Create schematic symbol,在工程文件夹里可以找到相应的 .sym 文件。
4.1.1.2 Mux4to1b4
1. 新建工程,命名为 Mux4to1b4_sch,top-level source type 为 schematic
2. 新建类型为 schematic 的源文件,命名为 Mux4to1b4
3. 用原理图方式设计,具体如下所示:

图表12 4位4选1多路选择器逻辑电路图
4. 生成逻辑符号和.vf文件:点击 Process 窗口下 Design Utilities -> Create schematic symbol,在工程文件夹里可以找到相应的.sym文件
5. 输入仿真信号进行测试,具体代码如下:
`timescale 1ns / 1ps
module Mux4to14b_Mux4to14b_sch_tb();
// Inputs
reg [1:0] S;
reg [3:0] I0;
reg [3:0] I1;
reg [3:0] I2;
reg [3:0] I3;
// Output
wire [3:0] O;
// Bidirs
// Instantiate the UUT
Mux4to14b UUT (
.S(S),
.I0(I0),
.I1(I1),
.I2(I2),
.I3(I3),
.O(O)
);
// Initialize Inputs
integer i;
initial begin
S = 00;
I0 = 0001;
I1 = 0010;
I2 = 0100;
I3 = 1000;
for( i = 0; i <= 3;i = i+1)begin
{S} <= i; #50;
end
end
endmodule 4.2.1 Verilog 代码实现 clkdiv 模块设计
1. 新建工程,Top Level Source Type 为 HDL,命名为 clkdiv_Number
2. 新建类型为 Verilog Module 的源文件,命名为 clkdiv_Number
3. 用 Verilog 代码进行设计,具体实现代码如下:
module clkdiv_3190103044(
input clk,
input rst,
output reg[31:0]clkdiv
);
always @ (posedge clk or posedge rst) begin
if (rst) clkdiv <= 0;
else clkdiv <= clkdiv + 1'b1;
end
endmodule 4. 生成逻辑符号和.vf文件:点击 Process 窗口下 Design Utilities -> Create schematic symbol,在工程文件夹里可以找到相应的.sym文件
4.2.2 计分板实现
1. 新建工程,Top Level Source Type 为 schematic,命名为 scoreBoard_Number
2. 新建类型为 verilog module 的源文件,命名为 CreateNumber
3. 用 Verilog 代码进行设计,具体实现代码如下:
module CreateNumber(
input wire clk,
input wire [3:0] btn,
output reg [15:0] num
);
wire [3:0] A,B,C,D;
wire [3:0] temp_btn;
initial num <= 16'b 0001001000110100;
assign A = num[3:0] + 1'b1;
assign B = num[7:4] + 1'b1;
assign C = num[11:8] + 1'b1;
assign D = num[15:12] + 1'b1;
pbdebounce p0(.clk(clk),.button(btn[0]),.pbreg(temp_btn[0]));
pbdebounce p1(.clk(clk),.button(btn[1]),.pbreg(temp_btn[1]));
pbdebounce p2(.clk(clk),.button(btn[2]),.pbreg(temp_btn[2]));
pbdebounce p3(.clk(clk),.button(btn[3]),.pbreg(temp_btn[3]));
always@(posedge temp_btn[0]) num[ 3: 0] <= A;
always@(posedge temp_btn[1]) num[ 7: 4] <= B;
always@(posedge temp_btn[2]) num[11: 8] <= C;
always@(posedge temp_btn[3]) num[15:12] <= D;
endmodule 4. 新建类型为 verilog module 的源文件,命名为 pbdebounce(防抖动模块)
5. 用 Verilog 代码进行设计,具体实现代码如下:
module pbdebounce(
input wire clk,
input wire button,
output reg pbreg
);
reg [7:0] pbshift;
always@(posedge clk) begin
pbshift = pbshift<<1;
pbshift[0] = button;
if (pbshift == 8'b0)
pbreg=0;
if (pbshift == 8'hFF)
pbreg=1;
end
endmodule 6. Check Syntax 无误后,点击 Process 窗口下 Design Utilities -> Create schematic symbol生成逻辑符号和.vf文件
7. 新建类型为 schematic 的源文件,命名为 scoreBoard_Number,右键设置为 Top Source
8. 将 MyMC14495.v 、MyMC14495.sym 、Mux4to1.v 、Mux4to1.sym 、Mux4to1b4.v 、Mux4to1b4.sym、clkdiv.v 、clkdiv.sym 添加到工程目录,并通过 Add Source 将其添加到工程中
9. 用原理图方式实现计分板的逻辑功能,具体如下:

图表13 计分板逻辑电路图
10. 进行引脚约束,下载到 SWORD 板进行验证。
新建类型为 Implementation constraints file 的源文件,命名为 constraints,具体代码如下所示:
/*
输入
时钟:clk
使能控制:SW[7:4]为 LES[3:0]
小数点输入:SW[3:0]为point[3:0]
按键输入数字:SW[15:12]为btn[3:0]
输出
a-g, p = SEGMENT
AN[3:0]
*/
NET "clk" LOC = AC18 | IOSTANDARD = LVCMOS18;
NET "RST" LOC = AF10 | IOSTANDARD = LVCMOS15;
NET "SW[0]" LOC = AA10 | IOSTANDARD = LVCMOS15;#POINT
NET "SW[1]" LOC = AB10 | IOSTANDARD = LVCMOS15;
NET "SW[2]" LOC = AA13 | IOSTANDARD = LVCMOS15;
NET "SW[3]" LOC = AA12 | IOSTANDARD = LVCMOS15;
NET "SW[4]" LOC = Y13 | IOSTANDARD = LVCMOS15;#LES
NET "SW[5]" LOC = Y12 | IOSTANDARD = LVCMOS15;
NET "SW[6]" LOC = AD11 | IOSTANDARD = LVCMOS15;
NET "SW[7]" LOC = AD10 | IOSTANDARD = LVCMOS15;
NET "SEGMENT[0]" LOC = AB22 | IOSTANDARD = LVCMOS33 ;#a
NET "SEGMENT[1]" LOC = AD24 | IOSTANDARD = LVCMOS33 ;#b
NET "SEGMENT[2]" LOC = AD23 | IOSTANDARD = LVCMOS33 ;
NET "SEGMENT[3]" LOC = Y21 | IOSTANDARD = LVCMOS33 ;
NET "SEGMENT[4]" LOC = W20 | IOSTANDARD = LVCMOS33 ;
NET "SEGMENT[5]" LOC = AC24 | IOSTANDARD = LVCMOS33 ;
NET "SEGMENT[6]" LOC = AC23 | IOSTANDARD = LVCMOS33 ;#g
NET "SEGMENT[7]" LOC = AA22 | IOSTANDARD = LVCMOS33 ;#point
NET "AN[3]" LOC = AC22 | IOSTANDARD = LVCMOS33 ;
NET "AN[2]" LOC = AB21 | IOSTANDARD = LVCMOS33 ;
NET "AN[1]" LOC = AC21 | IOSTANDARD = LVCMOS33 ;
NET "AN[0]" LOC = AD21 | IOSTANDARD = LVCMOS33 ;
NET "btn[3]" LOC = V18 | IOSTANDARD = LVCMOS18;
NET "btn[2]" LOC = V19 | IOSTANDARD = LVCMOS18 ;
NET "btn[1]" LOC = V14 | IOSTANDARD = LVCMOS18 ;
NET "btn[0]" LOC = W14 | IOSTANDARD = LVCMOS18 ;
NET "K_ROW" LOC = V17 | IOSTANDARD = LVCMOS18 ;
NET "BN" LOC = AF13 | IOSTANDARD = LVCMOS15 ;
运行仿真代码后波形图如下图所示,与理论结果一致,故元件逻辑功能正确。

图表14 仿真波形
将计分板下载到 SWORD 实验板上,根据相应的引脚约束进行物理验证。
引脚对应按键如下图所示:

图表15 对应引脚示意图




结果与理论预期一致,因此计分板逻辑功能正确。
有C语言的基础学习Verilog的语法能容易一些,网上也有很多 verilog 语言的教程。思路就是赋值、延时。需要搞清楚阻塞赋值和非阻塞赋值的区别与使用环境。
1. 希望将多条线路引到总线时,可以先实现一条线,确认总线、字线命名无误后再将其他线连上
2. 在生成元件以后,若再修改原理图,需要删除原有的 .v 文件再生成才能覆盖原结果
3. 切记线的命名要与后续引脚约束和其他元器件的代码相统一

欢迎评论区或私信讨论,感谢三连与批评指正。