FPGA笔记03_多路选择器

时间:2023年11月2日15点51分
目的:使用FPGA实现一个2路选择器 2路输入1路输出

总体思路
step1:绘制整体框图
step2:绘制真值表 绘制波形图
step3:代码编写

编者注:step1 和 step 2 在文章 升腾 mini《FPGA Verilog开发实战指南——基于Xilinx Artix7》2021.5.25.pdf 中有详细描述,此处不再缀述。主要对step3中的代码进行分析。

stpe1 模块框图设计

在这里插入图片描述

stpe2 波形图绘制与真值表

真值表
这个真值表其实是无效的,因为我们关心的sel的0或者1 去选择out=in1 或者等于out=in2,故和in1 取值无关 书上分析也是不正确的。

波形图
在这里插入图片描述

stpe3 代码编写

实现 2 选 1 多路选择器功能的 Verilog 代码形式有很多种,我们这里主要列举三种实现方法,这三种方法对应的核心语法各不相同。

(1) always 中 if-else 实现方法

 module mux2_1 //模块的开头以“module”开始,然后是模块名“mux2_1”
 (
 input wire in1, //输入端 1,信号名后就是端口列表“();”(端口列表里
 //面列举了该模块对外输入、输出信号的方式、类型、
 //位宽、名字),该写法采用了 Verilog-2001 标准,这
 //样更直观且实例化时也更方便,之前的 Verilog-1995
 //标准是将模块对外输入、输出信号的方式、类型、位
 //宽都放到外面
 
 input wire in2, //输入端 2,当数据只有一位宽时位宽表示可以省略
 //且输入只能是 wire 型变量
 
 input wire sel, //选择端,每行信号以“,”结束,最后一个后面不加“,”

 output reg out //结果输出,输出可以是 wire 型变量也可以是 reg 型变
 //量如果输出在 always 块中被赋值(即在“<=”的左边)
 //就要用 reg 型变量,如果输出在 assign 语句中被赋值
 //(即在“=”的左边)就要用 wire 型变量
 ); //端口列表括号后有个“;”不要忘记 
 //out:组合逻辑输出 sel 选择的结果
 always@(*)//“*”为通配符,表示只要 if 括号中的条件或赋值号右边的变量发生变化
 //则立即执行下面的代码,“(*)”在此 always 中等价于“(sel, in1, in2)”写法
 
 if(sel == 1'b1)//当“if...else...”中只有一个变量时不需要加“begin...end”
 //也显得整个代码更加简洁
out = in1; //always 块中如果表达的是组合逻辑关系时使用“=”进行赋值
 //每句赋值以“;”结束
 else
 out = in2;
 
 //模块的结尾以“endmodule”结束
 //每个模块只能有一组“module”和“endmodule”,所有的代码都要在它们中间编写
 endmodule

(2) always 中 case 实现方法

module mux2_1
 (
 input wire in1, //输入端 1
 input wire in2, //输入端 2
 input wire sel, //选择端
 
 output reg out //结果输出
 );
 
 //out:组合逻辑输出选择结果
 always@(*)
 case(sel)
 1'b1 : out = in1;
 1'b0 : out = in2;
 //如果 sel 不能列举出所有的情况一定要加 default
 //此处 sel 只有两种情况,并且完全列举了,所以 default 可以省略
 default : out = in1;
 endcase
 endmodule

(3) assign 中条件运算符(三元运算符)实现方法

 module mux2_1
 (
 input wire in1, //输入端 1
 input wire in2, //输入端 2
 input wire sel, //选择端 
 output wire out //结果输出
 );
 
 //out:组合逻辑输出选择结果
 //此处使用的是条件运算符(三元运算符),当括号里面的条件成立时
 //执行"?”后面的结果;如果括号里面的条件不成立时,执行“:”后面的结果
 assign out = (sel == 1'b1) ? in1 : in2;
 endmodule

仿真模块

`timescale 1ns/1ns //时间尺度、精度单位定义,决定“#(不可被综合,但在可
 //综合代码中也可以写,只是会在仿真时表达效果,而综合
 //时会自动被综合器优化掉)”后面的数字表示的时间尺度和
 //精度,具体表达含义为:“时间尺度/时间精度”。为了以后
 //编写方便我们将该句放在所有“.v”文件的开头,后面的代
 //码示例将不再显示该句
 
 module tb_mux2_1();//testbench 的格式和待测试 RTL 模块的格式相同
 //也是以“module”开始以“endmodule”结束,所有的代码都要
 //在它们中间编写。不同的是在 testbench 中端口列表为空
 //因为 testbench 不对外进行信号的输入输出,只是自己产生
 //激励信号提供给内部实例化待测 RTL 模块使用,所以端口列表
 //中没有内容,只是列出“()”,当然可以将“()”省略,括号
 //后有个“;”不要忘记
 
 //要在 initial 块和 always 块中被赋值的变量一定要是 reg 型
 //在 testbench 中待测试 RTL 模块的输入永远是 reg 型变量
 reg in1;
 reg in2;
 reg sel;
 
 //输出信号,我们直接观察,也不用在任何地方进行赋值
 //所以是 wire 型变量(在 testbench 中待测试 RTL 模块的输出永远是 wire 型变量)
 wire out;
 
 //initial 语句是可以被综合的,一般只在 testbench 中表达而不在 RTL 代码中表达
 //initial 块中的语句上电后只执行一次,主要用于初始化仿真中要输入的信号
 //初始化值在没有特殊要求的情况下给 0 或 1 都可以。如果不赋初值,仿真时信号
 //会显示为不定态(ModelSim 中的波形显示红色)
 initial 
 begin //在仿真中 begin...end 块中的内容都是顺序执行的,
 //在没有延时的情况下几乎没有差别,看上去是同时执行的,
 //如果有延时才能表达的比较明了;
 //而在 rtl 代码中 begin...end 相当于括号的作用,
 //在同一个 always 块中给多个变量赋值的时候要加上
 in1 <= 1'b0;
 in2 <= 1'b0;
 sel <= 1'b0;
 end
 
 //in1:产生输入随机数,模拟输入端 1 的输入情况
 always #10 in1 <= {$random} % 2;//取模求余数,产生随机数 1'b0、1'b1
 //每隔 10ns 产生一次随机数
 
 //in2:产生输入随机数,模拟输入端 2 的输入情况
 always #10 in2 <= {$random} % 2;
  //sel:产生输入随机数,模拟选择端的输入情况
 always #10 sel <= {$random} % 2;
 
 //下面的语句是为了在 ModelSim 仿真中直接打印出来信息便于观察信号变化的状态
 //也可以不使用下面的语句而直接观察仿真出的波形
 //------------------------------------------------------------
 initial begin
 $timeformat(-9, 0, "ns", 6);//设置显示的时间格式,此处表示的是(打印时间单
 //位为纳秒,小数点后打印的小数位为 0 位,时间值
 //后打印的字符串为“ns”,打印的最小数量字符为 6 个)
 
 //只要监测的变量(时间、in1, in2, sel, out)发生变化,就会打印出相应的信息
 $monitor("@time %t:in1=%b in2=%b sel=%b out=%b",$time,in1,in2,sel,out);
 end
 //------------------------------------------------------------
 
 //待测试 RTL 模块的实例化,相当于将待测试模块放到测试模块中,并将输入输出对应连接上
 //测试模块中产生激励信号给待测试模块的输入,以观察待测试模块的输出信号是否正确
 //------------------------mux2_1_inst------------------------
 mux2_1 mux2_1_inst
 //第一个是被实例化模块的名子,第二个是我们自己定义的在另一个
 //模块中实例化后的名字。同一个模块可以在另一个模块中或不同的
 //另外模块中被多次实例化,第一个名字相同,第二个名字不同
 (
 //前面的“in1”表示被实例化模块中的信号,后面的“in1”表示实例化该模块并要和这个
 //模块的该信号相连接的信号(可以取名不同,一般取名相同,方便连接和观察)
 //“.”可以理解为将这两个信号连接在一起
 .in1(in1), //input in1
 .in2(in2), //input in2
 .sel(sel), //inputsel

 .out(out) //output out
 );
  endmodule

在这里插入图片描述

关于always语句和assign语句的解释 【文心一言】

在Verilog语言中,always语句和assign语句都是用来描述信号的行为。

always语句用于描述信号的时序行为,它包含一个或多个触发器,当触发条件满足时,触发器中的语句就会被执行。always语句通常用于描述时钟信号或其他周期性信号的行为。

下面是一个使用always语句的例子:

module counter (
    input clk,
    output reg [3:0] count
);

    always @(posedge clk) begin
        count <= count + 1;
    end

endmodule

在上面的例子中,always语句包含一个触发器,当时钟信号clk的上升沿到来时,触发器中的语句就会被执行。在这个例子中,触发器中的语句是将count的值加1。

assign语句用于将一个值赋给信号,它可以是组合逻辑电路的行为。assign语句通常用于描述组合逻辑电路的行为。

下面是一个使用assign语句的例子:

module mux (
    input [3:0] sel,
    input [7:0] in0,
    input [7:0] in1,
    output reg [7:0] out
);

    assign out = (sel[0] == 1'b0) ? in0 : in1;

endmodule

在上面的例子中,assign语句将in0的值赋给out,当sel[0]为0时;否则,将in1的值赋给out。这是一个2:1多路选择器的实现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值