【数字逻辑 & Verilog】全面剖析数据选择器——从基础到拓展,从理论到设计的实现,从表面到本质

0 前言

0.1 使用环境

  • EDA工具:Vivado 2017.4
  • 硬件描述语言:Verilog HDL

0.2 涉及知识

  • 数字逻辑
  • Verilog

1 基础模块:一位四选一数据选择器

1.1 设计部分:层次建模

1.1.1 需求分析

设计一个一位的四选一数据选择器,有一个低电平输入有效的使能端E

1.1.2 层次建模

四选一数据选择器
门1
门2
门3
...

1.1.3 功能分析

  • 输入端口
    • 数据输入端 X 4
      • D0~D3(Date)
    • 地址输入端 X 2(因为2个端口可以有2^2=4种状态)
      • A1,A0(Address)
    • 使能端 X 1
      • E(Enable)
  • 输入端口
    • 数据输出端 X 1
      • OUT

一位四选一数据选择器的简化真值表为:

E的barA1A0OUT
0xx0
100D0
101D1
110D2
111D3

逻辑表达式为:
OUT = (A1bar·A0bar·D0 + A1bar·A0·D1 + A1·A0bar·D2 + A1·A0·D3)·E

1.2 实现部分:设计的实现

1.2.1 设计块

根据1.1.3节的逻辑表达式,使用数据流建模,直接得到设计块部分。注意,这里并没有使用前面所示的端口名称,这并不影响,不过不要学习我,还是保持一致比较好!

// 使用数据流建模,实现一位的四选一数据选择器
module choose_4to1(
    input i0,i1,i2,i3,   // 数据输入端
    input s1,s0,         // 地址输入端
    input e,             // 使能端
    output out           // 输出
    );
    
    assign out = ((~s1)&(~s0)&i0 | (~s1)&s0&i1 | s1&(~s0)&i2 | s1&s0&i3) & e;
    
endmodule

1.2.2 激励块

调用设计块实例,施加激励信号,利用系统函数显示结果,验证设计块的正确性。

module test(

    );
    
    reg I0 = 1,I1 = 0,I2 = 1,I3 = 0;
    reg S1,S0;
    reg E = 1;
    wire OUT;
    
    choose_4to1 CT0 (I0,I1,I2,I3,S1,S0,E,OUT);
    
    initial
        #1 $monitor("S1 = %b,  S2 = %b,  OUT = %b\n",S1,S0,OUT);
    
    initial
    begin
        $display("I0 = %b, I1 = %b, I2 = %b, I3 = %b\n\n",I0,I1,I2,I3);
        #1 S1 = 0;  S0 = 0;
        #1 S1 = 0;  S0 = 1;
        #1 S1 = 1;  S0 = 0;
        #1 S1 = 1;  S0 = 1;
    end
    
endmodule

1.2.3 验证

根据输出结果,验证设计块是否正确,若不正确,则改正后再次验证,直到正确为止。
经检验,设计块正确
经过验证,设计块是正确的!

1.3 科技黑箱1:1位四选一数据选择器

目前,一位四选一数据选择器,已经能够直接使用,我们根据起端口和功能,设计出更复杂的功能模块。
一位四选一数据选择器

2 向量型扩展:多位四选一数据选择器

我们知道,数据在计算机中采用二进制信息进行表示,对于第1节中的数据选择器,输入数据只能是1个二进制位,这显然满足不了现实需求,因此我们对其进行扩展,例如,能够实现100,266等类似数据的选择

这里为了简单器件,说明设计思想,我们选择将1位扩展为4位

2.1 设计部分:4位四选一数据选择器

2.1.1 层次建模

4位四选一数据选择器
1位四选一数据选择器实例DT0
DT1
DT2
ET3
门1
门2
...

2.1.2 设计模型

模型

2.1.3 设计思想:分治思想

举例,假设选择的结果的5

合(十进制)分(二进制)
91001
50101
70111
81000
处理(选)0101
合(十进制)5

简单解释一下:

  1. 合: 将十进制的数字拆成4位二进制数
  2. 分: 4个数的第一位进入第一个数据选择器,第二位进入第二个数据选择器……
  3. 选: 每个数据选择器分别选出一个二进制数
  4. 合: 将得到的4位二进制数转换为十进制数输出

2.1.4 模型类比

一位全加器四位全加器,也是同样的过程。

2.2 实现部分:设计的实现

2.2.1 设计块

`timescale 1ns / 1ps

// 使用数据流建模,实现一位的四选一数据选择器
module choose_4to1(
    input i0,i1,i2,i3,   // 数据输入端
    input s1,s0,         // 地址输入端
    input e,             // 使能端
    output out           // 输出
    );
    
    assign out = ((~s1)&(~s0)&i0 | (~s1)&s0&i1 | s1&(~s0)&i2 | s1&s0&i3) & e;
    
endmodule

// 向量型扩展,实现4位四选一数据选择器
module choose_4to1_4sizes(
    input [3:0] d0,d1,d2,d3,
    input s1,s0,
    input e,
    output [3:0] out
    );
    
    // 4个1位四选一数据选择器实例
    choose_4to1 DT0 (d0[0],d1[0],d2[0],d3[0],s1,s0,e,out[0]);
    choose_4to1 DT1 (d0[1],d1[1],d2[1],d3[1],s1,s0,e,out[1]);
    choose_4to1 DT2 (d0[2],d1[2],d2[2],d3[2],s1,s0,e,out[2]);
    choose_4to1 DT3 (d0[3],d1[3],d2[3],d3[3],s1,s0,e,out[3]);
    
endmodule

2.2.2 激励块

`timescale 1ns / 1ps

module test(

    );
    
    reg [3:0] I0 = 9,I1 = 5,I2 = 7,I3 = 8;
    reg S1,S0;
    reg E = 1;
    wire [3:0] OUT;
    
    choose_4to1_4sizes CT0 (I0,I1,I2,I3,S1,S0,E,OUT);
    
    initial
        #1 $monitor("S1 = %b,  S2 = %b,  OUT = %d\n",S1,S0,OUT);
    
    initial
    begin
        $display("I0 = %d, I1 = %d, I2 = %d, I3 = %d\n\n",I0,I1,I2,I3);
        #1 S1 = 0;  S0 = 0;
        #1 S1 = 0;  S0 = 1;
        #1 S1 = 1;  S0 = 0;
        #1 S1 = 1;  S0 = 1;
    end
    
endmodule

2.2.3 结果验证

在这里插入图片描述

2.3 科技黑箱2:4位四选一数据选择器

一位四选一数据选择器
依然还是这个模型,区别是:这里的数据输入和输出端口,支持4个二进制位

3 数组型扩展:1位八选一数据选择器

利用使能端,通过屏蔽理论,将一位四选一数据选择器,扩展为一位八选一数据选择器

3.0 数组型扩展的核心原则

考虑所有的端口

  • 该分开的分开,注意连接顺序
  • 该连接的连接,注意连接方法

3.1 设计部分:1位八选一数据选择器

3.1.1 层次建模

八选一数据选择器
四选一数据选择器1
四选一数据选择器2
门1
门2
....

3.1.2 设计模型

产生的变化:

  • 地址输入端:增加一个
    • 利用使能端
    • E为1时,高4位有效
    • E为0时,低4位有效
  • 两个四选一数据选择器:屏蔽理论——利用非门
    • 高4位
    • 低4位
  • 输出端口要合并,使用逻辑或
  • 共同的部分连一起

注意对应关系:例如,高4位的0,1,2,3要分别对应4,5,6,7,顺序不能颠倒!
模型
对于RTL级建模来说,这个逻辑图,不画也罢,直接写出其逻辑关系即可!

另外,这里也要为以后考虑,也就是,需要再这个模型上,再加上一个使能端E,以便于后续的扩展。

3.2 设计块

// 一位八选一数据选择器
module choose_8to1 (
    input i0,i1,i2,i3,i4,i5,i6,i7,  // 数据输入端
    input s2,s1,s0, // 地址输入端
    input e,        // 新的使能端
    output out      // 数据输出端
    );
    
    wire out1,out2; // 两个四选一的输出
    
    choose_4to1 DT4 (i4,i5,i6,i7,s1,s0,s2,out1);    // 高4位
    choose_4to1 DT5 (i0,i1,i2,i3,s1,s0,~s2,out2);   // 低4位
    
    assign out = (out1 | out2) & e;
    
endmodule

3.3 激励块

module test(

    );
    
    reg I0 = 1,I1 = 0,I2 = 1,I3 = 0,I4 = 1,I5 = 0,I6 = 1,I7 = 0;
    reg S2,S1,S0;
    reg E = 1;
    wire OUT;
    
    choose_8to1 CT0 (I0,I1,I2,I3,I4,I5,I6,I7,S2,S1,S0,E,OUT);
    
    initial
        #1 $monitor("S2 = %b,  S1 = %b,  S0 = %b,  OUT = %b\n",S2,S1,S0,OUT);
    
    initial
    begin
        $display("I0 = %b, I1 = %b, I2 = %b, I3 = %b\nI4 = %b, I5 = %b, I6 = %b, I7 = %b\n\n"
                    ,I0,I1,I2,I3,I4,I5,I6,I7);
        #1 S2 = 0;  S1 = 0;  S0 = 0;
        #1 S2 = 0;  S1 = 0;  S0 = 1;
        #1 S2 = 0;  S1 = 1;  S0 = 0;
        #1 S2 = 0;  S1 = 1;  S0 = 1;
        
        #1 S2 = 1;  S1 = 0;  S0 = 0;
        #1 S2 = 1;  S1 = 0;  S0 = 1;
        #1 S2 = 1;  S1 = 1;  S0 = 0;
        #1 S2 = 1;  S1 = 1;  S0 = 1;
    end
    
endmodule

4 混合型扩展:多位八选一数据选择器

  • 先扩展为4位四选一数据选择器
  • 再扩展为4位八选一数据选择器

4.1 层次建模

4位八选一数据选择器
4位四选一
1位四选一1
1位四选一2
3
4
4位四选一

4.2 模块实现

4.2.1 设计块

// 4位八选一数据选择器
module choose_8to1_4sizes (
    input [3:0] i0,i1,i2,i3,i4,i5,i6,i7,  // 数据输入端
    input s2,s1,s0, // 地址输入端
    input [3:0] e,        // 新的使能端
    output [3:0] out      // 数据输出端
    );
    
    wire [3:0] out1,out2; // 两个四选一的输出
        
    choose_4to1_4sizes DT4_0 (i4,i5,i6,i7,s1,s0,s2,out1);    // 高4位
    choose_4to1_4sizes DT4_1 (i0,i1,i2,i3,s1,s0,~s2,out2);   // 低4位
    
    assign out = (out1 | out2) & e;

endmodule

4.2.2 激励块

module test(

    );
    
    reg [3:0] I0 = 1,I1 = 2,I2 = 3,I3 = 4,I4 = 5,I5 = 6,I6 = 7,I7 = 8;
    reg S2,S1,S0;
    reg [3:0] E = 4'b1111;
    wire [3:0] OUT;
    
    choose_8to1_4sizes CT0 (I0,I1,I2,I3,I4,I5,I6,I7,S2,S1,S0,E,OUT);
    
    initial
        #1 $monitor("S2 = %b,  S1 = %b,  S0 = %b,  OUT = %d\n",S2,S1,S0,OUT);
    
    initial
    begin
        $display("I0 = %b, I1 = %b, I2 = %b, I3 = %b\nI4 = %b, I5 = %b, I6 = %b, I7 = %b\n\n"
                    ,I0,I1,I2,I3,I4,I5,I6,I7);
        #1 S2 = 0;  S1 = 0;  S0 = 0;
        #1 S2 = 0;  S1 = 0;  S0 = 1;
        #1 S2 = 0;  S1 = 1;  S0 = 0;
        #1 S2 = 0;  S1 = 1;  S0 = 1;
        
        #1 S2 = 1;  S1 = 0;  S0 = 0;
        #1 S2 = 1;  S1 = 0;  S0 = 1;
        #1 S2 = 1;  S1 = 1;  S0 = 0;
        #1 S2 = 1;  S1 = 1;  S0 = 1;
    end
    
endmodule

4.2.3 结果验证

在这里插入图片描述

4.3 重要知识点与思维方法

4.3.1 解决错误的方法论

  • 分析程序的行为是什么,而不是为什么程序不按照你想的来
  • 注意使用单元测试
    • 保证低层模块没有问题,再实现高层模块
    • 如果高层模块出了问题,先从高层找问题
    • 再逐一找到之前没有问题的低层模块,进行修正

4.3.2 按位运算与逻辑运算

  1. 按位运算是二进制位的每一位进行运算,其结果位数为:两个操作数的最小位数
  2. 逻辑运算把操作数都当成0,1,x,z,其结果也是0,1,x,z(这里是粗略说明,详细内容可以百度一下对应真值表)
4.3.2.1 按位运算:位宽不匹配时的注意事项

当按位运算的两操作数位宽不相等时,短位宽数会在左侧补0,使两操作数位宽一致。

在上述4位八选一数据选择器的设计块中,模块结尾处有这样一条语句:assign out = (out1 | out2) & e;

并且,我在前面使用的是位宽为4的使能端,这里,我想有必要详细解释一下为什么这样做,并且我会告诉你更好的解决方案。

我们知道以下的事实:

  1. 使能端的作用: 它就相当于选择器的开关,打开它选择器才能正常工作,关闭它选择器就不能工作。
  2. 开关的两种形式: 低电平有效和高电平有效,这里我采用的是高电平有效,也就是e = 1的时候才有效(但是事实上,大多数情况我们都采用低电平有效的使能端,可是,你想过为什么吗?我在后面将解释为什么要这样做)

接下来,我将会讲解为什么使用4位的使能端:

4位的使能端,只有当e = 4b'_1111的时候才能启动选择器,如果它是1位的,我们则只能为他赋值e = 1'b_1,此后它将与4位的数据进行逻辑与运算,我们知道,位宽不匹配的时候,短位宽将会自动补0再运算,此时的e将会被虚拟地增加3个0e = 4'b_0001,显然这样运算的结果是错误的。

我想你能够明白为什么高电平有效的使能端,在这里要使用4位位宽了。

不过,这样会对未来造成麻烦,我非常不建议你这样做,试想一下,如果用这样的八选一数据选择器,去扩展为十六选一的,将会出现一些问题。

那么,如何保证使能端依然是1位呢?

请注意一个事实,那就是,位宽不匹配的时候,系统会自动补0,因为补的是0,所以,我们应该采用低电平有效的使能端,将前面的逻辑表达式改一下就可以assign out = (out1 | out2) & (~e);

5 组合型扩展:一位/多位双四选一数据选择器

这个就是简单的组合而已,没什么好说的,除了使能端共用,输入输出端口依然还是各自独立的。

6 深入浅出:将数据选择器讲给孩子听

尝试将专业的知识将给孩子听,并让他听懂,就说明你真的掌握了,我给你一些提示:

  • 输入:香蕉,苹果,橘子,大鸭梨
  • 控制:开关 X 1,控制端 X 2
  • 输出:某一个水果

7 一句话总结

数据选择器就是,将多组数据输入,通过地址控制和开关控制,输出其中一组指定的数据。

8 科技黑箱:直接应用“数据选择器”解决现实问题

——现实生活中有项目需求,但是没有问题,也没有答案

8.0 对数据选择器新的理解

使用数据选择器,实现:数据输入端的并联,不同的地址,输出同一个结果

8.1 需求:红绿灯故障检测系统

  1. 正常情况:三盏灯只有一个是亮的
  2. 故障情况:正常情况均为故障

现要求,能够实现故障的检测,出现故障的时候系统会自动报警,请使用数据选择器设计逻辑电路

8.2 设计部分:组合逻辑电路

8.2.1 信息符号化

两个核心要点:

  1. 信息的符号表示
  2. 逻辑值的含义
  • 三盏灯 1代表亮,0代表不亮
    • 红灯 R
    • 黄灯 Y
    • 绿灯 G
  • 故障检测 F
    • F = 1,代表故障
    • F = 0,代表没有故障
  • 检测开关:使能端E,低电平有效

8.2.2 求逻辑表达式

真值表如下:

EbarRYG输出:Fbar
1xxxx
00000
00011
00101
00110
01001
01010
01100
01110
0xxxx

逻辑表达式如下:
Fbar = Rbar·Ybar·G + Rbar·Y·Gbar + R·Ybar·Gbar
= m0·G + m1·Gbar + m2·Gbar + m3·0

8.2.3 器件选择

使用四选一数据选择器

  • 地址输入端:R,Y
  • 数据输入端:G,Gbar
  • 使能端:E
  • 数据输出端:F

8.3 实现部分:Verilog实现逻辑电路

8.3.0 构建模型

红绿灯故障检测

8.3.1 设计块

// 红绿灯故障检测
module RYG_Flault_test (
    input G,    // 数据输入
    input R,Y,  // 地址输入
    input E,    // 使能端
    output F
    ); 
    
    wire F1;
    choose_4to1 RYG_test (G,~G,~G,0,R,Y,E,F1); // 为什么不能输出端口不能连接“~F”??
    
    assign F = ~F1;
endmodule

特别注意:输出端口不能连接~F,只能多加一步导一下

8.3.2 激励块

// 红绿灯故障检测系统测试
module test;
    
    reg R,Y;
    reg G;
    reg E = 1;
    wire F;
    
    RYG_Flault_test RYG_Test(G,R,Y,E,F);    // 红绿灯检测模块实例
    
    initial
        $monitor($time,"  R = %b,  Y = %b,  G = %b,  F = %b\n",R,Y,G,F); // 显示红绿灯的状况
        
    initial
    begin
        #1 R = 0;   Y = 0;   G = 0;
        #1 R = 0;   Y = 0;   G = 1;
        #1 R = 0;   Y = 1;   G = 0;
        #1 R = 0;   Y = 1;   G = 1;
        
        #1 R = 1;   Y = 0;   G = 0;
        #1 R = 1;   Y = 0;   G = 1;
        #1 R = 1;   Y = 1;   G = 0;
        #1 R = 1;   Y = 1;   G = 1;
    end    
        
endmodule

8.3.3 结果验证

结果

9 深入剖析数据选择器的本质

数据选择器的本质,是多路选择器!,它可以应用于任何需要做出功能选择的电路中去,它是非常常见并且实用的,数据选择器的设计思想可以推广到任意功能的选择上去,而不仅仅是数据的选择。

10 组合逻辑电路的设计

各种器件的本质,不过是逻辑表达式,对于期间的使用,需要进行实际需求表达式与器件逻辑表达式的对应关系即可!

这个部分我将会在其他文章阐述,敬请期待。

11 Verilog之行为级建模的魅力

事实上,对于以上的Verilog代码,并不需要那样繁琐,也不需要那么多行,为什么我依然坚持使用呢?

  1. 充分深入底层逻辑,并通过实战帮助你理解数据选择器,为后面使用多路选择器打基础
  2. 充分使用层次建模思想解决问题,锻炼核心思维

请记住,即便有了更为简单的方法,我们依然不应该完全放弃底层知识的学习,没有底层积累,是不能设计出优秀的顶层设计的。

行为级仿真代码留给读者自行完成。

  • 11
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XV_

感谢您的认可,我会继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值