目录
前言
理论学习
逻辑电路分类:
多路选择器
实战演练
一、设计规划
1.1 实验(项目)目标
1.2 硬件资源
二、程序设计
2.1、模块框图
2.2、波形图绘制
2.3、端口信号信息与真值表
2.4、代码编写
三、逻辑仿真
3.1、仿真文件编写
3.2、仿真波形图对比
四、上板验证
1、管脚约束
2、实际验证
学习说明:以下为本人学习笔记
学习视频:是根据野火FPGA视频教程——第六讲
https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3
学习资料:《数字电子技术基础》清华大学出版社
相关数字电路基础理论学习请参考本人的另一个笔记,可以让你深刻理解相关知识《数字电子技术基础》4.3 若干常用的组合逻辑电路 学习笔记_FPGA-桥的博客-CSDN博客《数字电子技术基础》常用的组合逻辑电路编码器和译码器、数据选择器和数据分配器、加法器、数值比较器、奇偶发生/校验器https://blog.csdn.net/ARM_qiao/article/details/124052097
多路选择器别称数据选择器,在多路数据传送过程中能够根据需要将其任意一路选出来的电路叫做数据选择器,也称多路选择器(Data Selector)或多路开关(Multiplexer)。
现以双4选1数据选择器74HC153为例,说明它的工作原理。
设计并实现2选1多路选择器,主要功能是通过控制信号S确定A路或B路作为输出信号。当S=1时,输出信号为O=A;当S=0时,信号输出为O=B。
使用按键与LED灯作为实验的模拟,选取KEY1、KEY2、KEY3分别作为信号A、信号B和选通信号S,LED灯D6作为输出信号O。
sel = 0时,out = in_2;sel = 1时,out = in_1
端口信号信息 | |||
信号 | 位宽 | 类型 | 功能描述 |
in1 | 1bit | input | 输入信号1 |
in2 | 1bit | input | 输入信号2 |
sel | 1bit | input | 选通信号 |
out | 1bit | output | 输出信号 |
端口真值表 | |||
输入(input) | 输出(output) | ||
in1 | in2 | sel | out |
0 | 1 | 0 | 1 |
1 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 1 |
代码实现方式比较多,这里主要列举三种实现方法。
`timescale 1ns / 1ps
//
// Company: 追逐者——桥的小作坊
// Engineer:
//
// Create Date: 2022/04/10 09:19:21
// Design Name: 不同条件语句实现2-1数据选择器
// Module Name: mux2_1
// Project Name: MUX2_1
// Target Devices: Artix_7 野火开发板
// Tool Versions: Vivado 2018_3
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments: 学习代码
//
//
module mux2_1( //语法:module 模块名称 (端口信号列标);
input wire in_1 , //输入信号1,语法:输入输出方式 类型 位宽(当为1bit时可以省略,也可[0:0]) 名字
input wire in_2 , //输入信号2,输入信号只能是,wire:线网型数据变量
input wire sel, //选择信号,每行信号以‘,’结束,所有量后不加‘,’
output reg out //输出信号,输出变量类型既可以是wire型也可reg型,
//wire型:输出在assign语句中被赋值(即在“=”左边)
//reg型:输出在always块中被赋值(即在“<=”的左边)
); //注意加“;”
//实现方式一:always 中 if-else 实现方法
///*
always@(*) //"*"为通配符,表示只要 if 括号中的条件或赋值号右边的变量发生变化
//则立即执行下面的代码,“(*)”在此 always 中等价于“(sel, in1, in2)”写法
if(sel == 1'b1)
//begin //当“if...else...”中只有一个变量时不需要加“begin...end”
out = in_1;
//end
else
out = in_2;
//*/
//实现方式二: always 中 case 实现方式
/*
always @(*)
case(sel)
1'b1 : out = in_1;
1'b0 : out = in_2;
//default : out = in_1; //当case不能列举全部情况时需要加入default,这里可以省略
endcase
*/
//实现方式三:assign中条件运算符(三元运算符)实现方式
/*
assign out = (sel == 1'b1) ? in_1 : in_2;
*/
endmodule
有人可能会有稍稍的疑问,就是为什么 always 块中被赋值的一定要是 reg 型变量,他 并没有生成寄存器而是实现的的组合逻辑的功能?因为在 Verilog 语言中,寄存器的特点 是,它需要在仿真运行器件中保存其值,也就是说这个变量在仿真时需要占据内存空间, 而上面的 always 块只对 sel、in1、in2 三个变量的输入敏感,如果没有这三个变量的变化事件,则 out 变量将需要保存其值,因此它们必须被定义为 reg 型变量,但是在综合之后,并不对应硬件锁存器或者触发器(后面会讲到什么时候会出现综合成这两种的情况)。
`timescale 1ns / 1ps
//
// Company: 追逐者——桥的小作坊
// Engineer:
//
// Create Date: 2022/04/10 11:20:04
// Design Name: 不同条件语句实现2-1数据选择器的仿真文件
// Module Name: tb_mux2_1
// Project Name: MUX2_1
// Target Devices:
// Tool Versions: Vivado 2018_3
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module tb_mux2_1(); //不同的是在 testbench 中端口列表为空
//因为 testbench 不对外进行信号的输入输出,只是自己产生
//激励信号提供给内部实例化待测 RTL 模块使用,所以端口列表
//中没有内容,只是列出“()”,当然可以将“()”省略,括号
//后有个“;”不要忘记
//要在 initial 块和 always 块中被赋值的变量一定要是 reg 型
//在 testbench 中待测试 RTL 模块的输入永远是 reg 型变量
reg in_1;
reg in_2;
reg sel;
//输出信号,我们直接观察,也不用在任何地方进行赋值
//所以是 wire 型变量(在 testbench 中待测试 RTL 模块的输出永远是 wire 型变量)
wire out;
//initial 语句是可以被综合的,一般只在 testbench 中表达而不在 RTL 代码中表达
//initial 块中的语句上电后只执行一次,主要用于初始化仿真中要输入的信号
//初始化值在没有特殊要求的情况下给 0 或 1 都可以。如果不赋初值,仿真时信号
//会显示为不定态(ModelSim 中的波形显示红色)
initial
begin //在仿真中 begin...end 块中的内容都是顺序执行的,
//在没有延时的情况下几乎没有差别,看上去是同时执行的,
//如果有延时才能表达的比较明了;
//而在 rtl 代码中 begin...end 相当于括号的作用,
//在同一个 always 块中给多个变量赋值的时候要加上
in_1 <= 1'b0;
in_2 <= 1'b0;
sel <= 1'b0;
end
//in_1:产生输入随机数,模拟输入端 1 的输入情况
always #10 in_1 <= {$random} % 2; //取模求余,只能产生1'b0、1'b1两个随机数
always #10 in_2 <= {$random} % 2;
always #10 sel <= {$random} % 2;
//下面的语句是为了在 ModelSim 仿真中直接打印出来信息便于观察信号变化的状态
initial
begin
$timeformat(-12, 0, "ps", 6); //设置时间格式:纳秒,小数点后0位,打印字符ps,
//打印的最小量字符为6个
//只要监测的变量(时间、in_1, in_2, sel, out)发生变化,就会打印相应的信息
$monitor("@time %t:in_1=%b in_2=%b sel=%b out=%b",$time, in_1, in_2, sel, out);
end
//待测试RTL模块实例化,相当于将设计块放到激励块,即激励快调用设计块
mux2_1 mut2_1_inst(
.in_1(in_1), //.代表链接
.in_2(in_2),
.sel(sel),
.out(out)
);
endmodule
运行RTL视图,可以看到代码原理图及管脚约束
仿真波形图可以看出与绘制的波形图一致。
约束文件代码:
#按键引脚绑定
set_property IOSTANDARD LVCMOS33 [get_ports in_1]
set_property PACKAGE_PIN V17 [get_ports in_1]
set_property IOSTANDARD LVCMOS33 [get_ports in_2]
set_property PACKAGE_PIN W17 [get_ports in_2]
set_property IOSTANDARD LVCMOS33 [get_ports sel]
set_property PACKAGE_PIN AA18 [get_ports sel]
#led引脚绑定
set_property IOSTANDARD LVCMOS33 [get_ports out]
set_property PACKAGE_PIN M21 [get_ports out]
#4线spi模式
set_property CFGBVS VCCO [ current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]