一、二选一选择器是做什么的?
二选一选择器,也叫 2 选 1 多路选择器,英文是 Multiplexer,简称 MUX。
它有三个输入:
-
a:数据输入 0 -
b:数据输入 1 -
sel:选择信号
一个输出:
y:输出结果
逻辑关系很简单:
-
当
sel = 0时,输出y = a -
当
sel = 1时,输出y = b
真值表如下:
| sel | a | b | y |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 1 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 |
一句话总结:
sel等于 0 时选a,sel等于 1 时选b。
二、Vivado 2018.3 新建工程
打开 Vivado 2018.3,按下面步骤操作:
-
点击
Create Project -
点击
Next -
输入工程名,例如
mux2_1 -
选择工程路径,注意路径不要有中文和空格
-
Project Type 选择
RTL Project -
勾选
Do not specify sources at this time,先不添加文件 -
选择目标器件:
-
如果你有开发板,选择对应芯片型号
-
如果只是仿真学习,随便选一个即可
-
-
点击
Finish
工程创建完成后,我们就进入 Vivado 主界面。
三、添加 Verilog 设计文件
在左侧 Flow Navigator 中,找到:
Project Manager -> Add Sources
然后:
-
选择
Add or create design sources -
点击
Next -
点击
Create File -
File type 选择
Verilog -
File name 填写
mux2_1 -
点击
OK -
点击
Finish
这时 Vivado 会生成一个 mux2_1.v 文件。双击打开它,输入下面的代码。
写法一:使用 assign,最适合小白
module mux2_1(
input a,
input b,
input sel,
output y
);
assign y = sel ? b : a;
endmodule
解释一下:
-
module mux2_1:定义一个模块,名字叫mux2_1 -
input a:输入端口a -
input b:输入端口b -
input sel:输入端口sel -
output y:输出端口y -
assign:连续赋值语句,常用于组合逻辑 -
sel ? b : a:三元运算符-
sel为 1 时,结果是b -
sel为 0 时,结果是a
-
所以这句话的意思就是:
如果
sel = 1,y = b;否则y = a。
四、理解 Verilog 的"输入"
Verilog 模块就像一块芯片:
-
input是芯片的输入引脚 -
output是芯片的输出引脚
在模块内部:
-
input只能读,不能赋值 -
output是模块向外输出的结果
比如:
verilog
input a;
input b;
input sel;
output y;
意思就是:
-
外面给这个模块送进来三个信号:
a、b、sel -
模块算出一个结果
y送出去
在 Vivado 里"输入 Verilog 代码",其实就是:
-
新建
.v文件 -
在文件里写
module ... endmodule -
保存
-
Vivado 自动检查语法
-
综合工具把你的代码翻译成硬件电路
小白要记住几个基本规则:
-
Verilog 区分大小写
-
每条语句结尾通常要有分号
; -
文件名最好和模块名一致,例如
mux2_1.v里写module mux2_1 -
注释用
//或/* ... */ -
组合逻辑常用
assign或always @(*)
五、添加 Testbench 仿真文件
设计文件写完后,还需要一个测试文件,叫 Testbench。
Testbench 的作用是:
给被测模块提供输入信号,观察输出信号是否正确。
继续在 Vivado 中操作:
-
Add Sources -
选择
Add or create simulation sources -
点击
Next -
点击
Create File -
File type 选择
Verilog -
File name 填写
mux2_1_tb -
点击
OK -
点击
Finish
然后打开 mux2_1_tb.v,输入:
verilog
`timescale 1ns / 1ps
module mux2_1_tb;
reg a;
reg b;
reg sel;
wire y;
mux2_1 uut (
.a(a),
.b(b),
.sel(sel),
.y(y)
);
initial begin
a = 0; b = 0; sel = 0;
#10;
a = 0; b = 1; sel = 0;
#10;
a = 1; b = 0; sel = 0;
#10;
a = 1; b = 1; sel = 0;
#10;
a = 0; b = 0; sel = 1;
#10;
a = 0; b = 1; sel = 1;
#10;
a = 1; b = 0; sel = 1;
#10;
a = 1; b = 1; sel = 1;
#10;
$finish;
end
endmodule
解释一下 Testbench:
-
reg a; reg b; reg sel;- 因为
a、b、sel需要在initial里被赋值,所以声明为reg
- 因为
-
wire y;y由被测模块输出驱动,所以用wire
-
mux2_1 uut (...)-
例化被测模块
-
uut是实例名,可以自己起 -
.a(a)表示把 Testbench 里的a连接到模块的a
-
-
initial begin ... end- 仿真开始后只执行一次
-
#10-
延时 10 个时间单位
-
因为前面写了 ```timescale 1ns / 1ps``,所以是延时 10ns
-
-
$finish- 结束仿真
这段 Testbench 会把 8 种输入组合都测试一遍。
六、运行行为仿真
在 Vivado 左侧 Flow Navigator 中:
Simulation -> Run Simulation -> Run Behavioral Simulation
如果弹出窗口,点击 OK。
仿真运行后,会出现波形窗口。你可以:
-
找到
mux2_1_tb -
展开信号
-
观察
a、b、sel、y -
点击
Zoom Fit查看完整波形
预期结果:
-
当
sel = 0时,y应该等于a -
当
sel = 1时,y应该等于b
如果波形正确,说明二选一选择器设计成功。
七、综合、实现和生成比特流
如果只是学习仿真,到上一步就可以了。
如果你想下载到 FPGA 开发板,还需要继续:
-
Flow Navigator -> Synthesis -> Run Synthesis -
综合完成后,选择
Run Implementation -
实现完成后,选择
Generate Bitstream -
生成比特流后,打开
Open Hardware Manager -
连接开发板,下载程序
不过,上板之前还需要添加引脚约束文件 XDC。
八、上板时的 XDC 约束示例
不同开发板的引脚不同,下面只是示例,不能直接照抄:
tcl
# 假设 a 接 SW0,b 接 SW1,sel 接 SW2,y 接 LED0
set_property -dict {PACKAGE_PIN P5 IOSTANDARD LVCMOS33} [get_ports a]
set_property -dict {PACKAGE_PIN P6 IOSTANDARD LVCMOS33} [get_ports b]
set_property -dict {PACKAGE_PIN P7 IOSTANDARD LVCMOS33} [get_ports sel]
set_property -dict {PACKAGE_PIN M5 IOSTANDARD LVCMOS33} [get_ports y]
注意:
-
引脚编号必须查你开发板的原理图
-
电平标准也要看开发板,常见是
LVCMOS33 -
如果 LED 是低电平点亮,输出逻辑可能需要取反
九、小白常见问题
1. 为什么 Testbench 里输入用 reg,输出用 wire?
因为输入信号需要在 initial 或 always 里赋值,只有 reg 可以这样做。
输出信号由被测模块驱动,所以用 wire。
2. 为什么设计文件里 output y 可以直接用 assign?
因为 assign 驱动的是线网类型,output y 默认就是 wire。
3. 为什么 always 写法里要写 output reg y?
因为 always 块中赋值的变量必须是 reg 类型。
4. 仿真波形全是红色 X 怎么办?
常见原因:
-
输入信号没有初始化
-
模块例化端口连接错误
-
Testbench 没有设置成仿真顶层
-
模块名和文件名不一致
5. always @(*) 和 always @(a or b or sel) 有什么区别?
@(*) 会自动包含所有输入信号,不容易漏写,推荐使用。
十、总结
本文完成了 FPGA 入门中非常经典的一个例子:二选一选择器。
你学会了:
-
在 Vivado 2018.3 中新建工程
-
添加 Verilog 设计文件
-
用
assign和always两种方式实现二选一选择器 -
理解
input、output、wire、reg -
编写 Testbench
-
运行行为仿真
-
查看波形验证功能
虽然二选一选择器很简单,但它包含了 Verilog 输入、模块定义、端口连接、组合逻辑和仿真验证的完整流程。
把这个例子练熟之后,你可以继续尝试:
-
四选一选择器
-
半加器、全加器
-
计数器
-
分频器
-
流水灯