zoukankan      html  css  js  c++  java
  • [文档].艾米电子 多路选择器与多路分解器,Verilog

    对读者的假设

    已经掌握:

    内容

    1 多路选择器Multiplexer

    此处所说的多路选择器,为组合逻辑电路中的多路多路选择器:多路输入,一路输出。

    1.1 不带优先级的多路选择器

    1.1.1 使用case语句描述

    此处以4选1多路选择器为例:

    代码1.1 4选1多路选择器(可综合)

    `timescale 1ns/1ns
    module multiplexer(
      input iA,
      input iB,
      input iC,
      input iD,
      input [1:0] iSel,
      output reg oQ  
    );
    
    always @ (*) 
      case (iSel)
        2'b00 : oQ = iA;
        2'b01 : oQ = iB;
        2'b10 : oQ = iC;
        2'b11 : oQ = iD;
      endcase
    
    endmodule

    第10~16行,使用case语句来实现4选1多路选择器。因为是2^n个case选项,所以此处没有使用default语句。下面我会使用一个3选1的多路选择器来说明default的作用。

    always @ (*) 
      case (iSel)
        2'b00 : oQ = iA;
        2'b01 : oQ = iB;
        2'b10 : oQ = iC;
        2'b11 : oQ = iD;
      endcase

    图1.1 4选1多路选择器的RTL视图

    图1.1 4选1多路选择器的RTL视图

    由图1.1所示,在2^n个case选项时,没有加上default语句,其综合的结果为并行的MUX。

    代码1.2 4选1多路选择器testbench(不可综合,仅用于仿真)

    `timescale 1ns/1ns
    module multiplexer_tb;
    reg i_a, i_b, i_c, i_d;
    reg [1:0] i_sel;
    wire o_q; 
    
    initial begin
      i_a = 1; i_b = 0; i_c = 0; i_d = 0;
      #20 i_a = 1; i_b = 0; i_c = 1; i_d = 1;
      #20 i_a = 0; i_b = 0; i_c = 1; i_d = 0;
      #20 i_a = 1; i_b = 1; i_c = 1; i_d = 0;
    end 
    
    initial begin
      i_sel = 2'b00;
      #20 i_sel = 2'b01;
      #20 i_sel = 2'b10;
      #20 i_sel = 2'b11;
      #20 $stop;
    end 
    
    multiplexer multiplexer_inst(
      .iA    (i_a),
      .iB    (i_b),
      .iC    (i_c),
      .iD    (i_d),
      .iSel  (i_sel),
      .oQ    (o_q) 
    ); 
    
    endmodule

    第3~5行声明了一些变量,使用的标准为:映射为所需测试模块的输入信号,即需要使用initial或always来给出激励,因此多声明为reg类型;而映射为所需测试模块的输出信号,不需要激励,声明为wire类型即可。

    reg i_a, i_b, i_c, i_d;
    reg [1:0] i_sel;
    wire o_q;

    第21行,$stop表示仿真在此时刻终止。更多$打头的函数,请参考Verilog语法书。

    #20 $stop;

    图1.2  4选1多路选择器的功能仿真波形

    图1.2  4选1多路选择器的功能仿真波形

    图1.3  4选1多路选择器的门级仿真波形

    图1.3  4选1多路选择器的门级仿真波形

    由图1.2和图1.3所示,在2^n个case选项时,没有加上default语句,其功能仿真和门级仿真保持一致。

    1.1.2 怎么多出个锁存器?

    此处以3选1多路选择器为例。

    代码1.3 3选1多路选择器1(可综合)

    module multiplexer(
      input iA,
      input iB,
      input iC,
      input [1:0] iSel,
      output reg oQ  
    );
    
    always @ (*) 
      case (iSel)
        2'b00 : oQ = iA;
        2'b01 : oQ = iB;
        2'b10 : oQ = iC;
      endcase
    
    endmodule

    图1.4 3选1多路选择器1的RTL视图

    图1.4 3选1多路选择器1的RTL视图

    如图1.4所示,综合出来了锁存器。下面我们讨论如何消除这个锁存器。

    方法1 加(* full_case *)修饰

    代码1.4 3选1多路选择器2(可综合)

    module multiplexer(
      input iA,
      input iB,
      input iC,
      input [1:0] iSel,
      output reg oQ  
    );
    
    always @ (*) 
      (* full_case *) case (iSel)
        2'b00 : oQ = iA;
        2'b01 : oQ = iB;
        2'b10 : oQ = iC;
      endcase
    
    endmodule

    第10行,加(* full_case *)修饰,强制综合成full case。

    (* full_case *) case (iSel)

    图1.4 3选1多路选择器2的RTL视图

    图1.5 3选1多路选择器2的RTL视图

    方法2 使用default语句

    代码1.5 3选1多路选择器3(可综合)

    module multiplexer(
      input iA,
      input iB,
      input iC,
      input [1:0] iSel,
      output reg oQ  
    );
    
    always @ (*) 
      case (iSel)
        2'b00 : oQ = iA;
        2'b01 : oQ = iB;
        2'b10 : oQ = iC;
        default: oQ = 1'b0;
      endcase
    
    endmodule

    第14行,加上default语句,来描述缺省动作。

        default: oQ = 1'b0;

     

    图1.5 3选1多路选择器3的RTL视图

    图1.6 3选1多路选择器3的RTL视图

    方法3 在敏感列表后,赋初值

    代码1.6 3选1多路选择器4(可综合)

    module multiplexer(
      input iA,
      input iB,
      input iC,
      input [1:0] iSel,
      output reg oQ  
    );
    
    always @ (*)
    begin
      oQ = 1'b0;
      case (iSel)
        2'b00 : oQ = iA;
        2'b01 : oQ = iB;
        2'b10 : oQ = iC;
      endcase
    end
    
    endmodule

    注意第9~17,在代码1.3的基础上,于敏感列表之后,加入oQ的初值。

    always @ (*)
    begin
      oQ = 1'b0;
      case (iSel)
        2'b00 : oQ = iA;
        2'b01 : oQ = iB;
        2'b10 : oQ = iC;
      endcase
    end

    图1.7 3选1多路选择器4的RTL视图

    图1.7 3选1多路选择器4的RTL视图

    方法123的比较

    代码1.7 3选1多路选择器的testbench(不可综合,仅用于仿真)

    `timescale 1ns/1ns
    module multiplexer_tb;
    reg i_a, i_b, i_c;
    reg [1:0] i_sel;
    wire o_q;
    
    initial begin
      i_a = 0; i_b = 0; i_c = 0;
      #20 i_a = 1; i_b = 0; i_c = 0;
      #20 i_a = 1; i_b = 0; i_c = 1;
      #20 i_a = 0; i_b = 0; i_c = 1;
      #20 i_a = 1'bx; i_b = 1'bx; i_c = 1'bx;
    end
    
    initial begin
      i_sel = 2'bxx;
      #20 i_sel = 2'b00;
      #20 i_sel = 2'b01;
      #20 i_sel = 2'b10;
      #20 i_sel = 2'b11;
      #20 $stop;
    end
    
    multiplexer multiplexer_inst(
      .iA    (i_a),
      .iB    (i_b),
      .iC    (i_c),
      .iSel  (i_sel),
      .oQ    (o_q) 
    );
    
    endmodule

    图1.8 方法1的功能仿真波形

    图1.8 方法1的功能仿真波形

    图1.9 方法1的门级仿真波形

    图1.9 方法1的门级仿真波形

    由图1.8和图1.9所示,虽然方法1加了(* full_case *)修饰后,3选1多路选择器的综合结果没有产生锁存器,但是其功能仿真波形却当作锁存器来处理,而其门级仿真波形则是当作无关项来处理。即方法1,其前后仿真结果是不一致的。至于其他的特点,本文暂不讨论。想要了解更多full case的信息,请阅读参考2。

    图1.10 方法2的功能仿真波形

    图1.10 方法2的功能仿真波形

    图1.11 方法2的门级仿真波形

    图1.11 方法2的门级仿真波形

    如图1.10和1.11所示,方法2使用了default语句,其前后仿真和综合的RTL视图一致。萧鸿森大哥提醒我们:正确的做法是,default指定输出0或者某个选项。

    图1.12 方法3的功能仿真波形 

    图1.12 方法3的功能仿真波形

    图1.13 方法3的门级仿真波形

    图1.13 方法3的门级仿真波形

    如图1.12和1.13所示,方法3在敏感列表后,赋初值也可以很好解决锁存器的问题。

    1.1.3 结论

    虽然方法123都可以解决锁存器的问题,但是:方法1的前后仿真结果不同;方法3的其他特点暂时没有研究。因此此处只推荐大家使用方法2,即要实现并行的mux,当case选项不是2^n个的时候,加上default指定输出0或某个选项是最安全的(虽然Verilog语法没有强制加入default)。

    1.2 带优先级的多路选择器

    1.2.1 使用if-else-if描述

    此处以3选1多路选择器为例。

    代码1.8 使用if-else-if描述的3选1多路选择器(可综合)

    module multiplexer(
      input iA,
      input iB,
      input iC,
      input [1:0] iSel,
      output reg oQ  
    );
    
    always @ (*) 
      if (iSel == 2'b0) 
        oQ = iA;
      else if (iSel == 2'b01) 
        oQ = iB;
      else
        oQ = iC;
    
    endmodule

    第9~15行,使用if-else-if描述带优先级的多路选择器。有人看到我的代码会有一点小疑问?为什么always块没有加begin-end的。其实呢这个begin-end就相当于C语言里面的{},与C语言类似,当后面的语句条数为1时,可以省略begin-end。而if-else-if恰好为一个完整的语句。

    always @ (*) 
      if (iSel == 2'b0) 
        oQ = iA;
      else if (iSel == 2'b01) 
        oQ = iB;
      else
        oQ = iC;

    图1.14 使用if-else-if描述的3选1多路选择器的RTL视图

    图1.14 使用if-else-if描述的3选1多路选择器的RTL视图

    1.2.2 使用“: ?”描述

    既然使用if-else-if可以描述带优先级的多路选择器,那么使用“: ?”当然也是可行的。

    代码1.9 使用“: ?”描述的3选1多路选择器(可综合)

    module multiplexer(
      input iA,
      input iB,
      input iC,
      input [1:0] iSel,
      output oQ  
    );
    
    assign oQ = (iSel == 2'b00) ? iA :
                (iSel == 2'b01) ? iB : iC;
                
    endmodule

    第9~10行,使用“: ?”描述带优先级的多路选择器。

    assign oQ = (iSel == 2'b00) ? iA :
                (iSel == 2'b01) ? iB : iC;

    实际上等同于,为什么使用上面这种写法呢?因为“: ?”的运算优先级比较低,所以不必加括号。

    assign oQ = (iSel == 2'b00) ? iA :
                ((iSel == 2'b01) ? iB : iC);

    注意:第6行的声明,由于是使用assign语句赋值,因此无需声明成reg类型。

      output oQ  

    图1.15 使用“: ?”描述的3选1多路选择器的RTL视图

    图1.15 使用“: ?”描述的3选1多路选择器的RTL视图

    如图1.15所示,使用“: ?”描述多路选择器与if-else-if描述完全一致,因此两种描述方式可以互换。

    2 多路分解器De-Multiplexer

    多路分解器,也叫数据选择器,顾名思义即多路输入,一路输出。有了前面的基础,那么描述一个多路分解器,便是轻而易举的事情了。

    代码2.1 使用case描述多路分解器

    module de_multiplexer(
      input iA,
      input [1:0] iSel,
      output reg oQ_0,
      output reg oQ_1,
      output reg oQ_2
    );
    
    always @ (*)
    begin
      oQ_0 = 1'b0;
      oQ_1 = 1'b0;
      oQ_2 = 1'b0;
      case (iSel)
        2'b00 : oQ_0 = iA;
        2'b01 : oQ_1 = iA;
        2'b10 : oQ_2 = iA;
        default : ;
      endcase
    end
    
    endmodule

    第9~20行,在敏感列表后,追加初始值,可以有效地避免生成锁存器。

    always @ (*)
    begin
      oQ_0 = 1'b0;
      oQ_1 = 1'b0;
      oQ_2 = 1'b0;
      case (iSel)
        2'b00 : oQ_0 = iA;
        2'b01 : oQ_1 = iA;
        2'b10 : oQ_2 = iA;
        default : ;
      endcase
    end
     

    图2.1 代码2.1综合后的RTL视图

    图2.1 代码2.1综合后的RTL视图

    代码2.2 代码2.1的testbench

    `timescale 1ns/1ns
    module de_multiplexer_tb;
    reg i_a;
    reg [1:0] i_sel;
    wire o_q_0, o_q_1, o_q_2;
    
    initial begin
      i_a = 0;
      forever #10 i_a = ~i_a;
    end 
    
    initial begin
      i_sel = 2'b00;
      #20 i_sel = 2'b01;
      #20 i_sel = 2'b10;
      #20 i_sel = 2'b11;
      #20 $stop;
    end 
    
    de_multiplexer de_multiplexer_inst(
      .iA(i_a),
      .iSel(i_sel),
      .oQ_0(o_q_0),
      .oQ_1(o_q_1),
      .oQ_2(o_q_2)
    );
    
    endmodule

    图2.1 代码2.1的功能仿真波形

    图2.1 代码2.1的功能仿真波形

    图2.2 代码2.1的门级仿真波形

    图2.2 代码2.1的门级仿真波形

    图2.1和图2.2的所示波形与代码2.1一致。

    辅助阅读

    1. Wikipedia.Multiplexer

    参考

    1. 刘福奇, 刘波.Verilog HDL应用程序设计实例精讲.电子工业出版社

    2. Cliff Cummings' Award-Winning Verilog & SystemVerilog Papers. "full_case parallel_case", the Evil Twins of Verilog Synthesis

    3. 萧鸿森.(原創) 多工器MUX coding style整理 (SOC) (Verilog) (Quartus II)

    4. Altera.Recommended HDL Coding Styles.General Coding Guidelines

    另见

    [与艾米一起学FPGA/SOPC].[逻辑实验文档连载计划]

  • 相关阅读:
    复旦大学数学学院18级高等代数II期中考试第七大题的三种证法及其推广
    复旦大学2019--2020学年第一学期高等代数I期末考试情况分析
    复旦大学2019--2020学年第一学期(19级)高等代数I期末考试第六大题解答
    复旦大学2019--2020学年第一学期(19级)高等代数I期末考试第七大题解答
    复旦大学2019--2020学年第一学期(19级)高等代数I期末考试第八大题解答
    复旦高等代数I(19级)每周一题
    复旦大学数学学院 18 级本科生对每周一题的评价
    复旦大学2018--2019学年第二学期高等代数II期末考试情况分析
    复旦大学2018--2019学年第二学期(18级)高等代数II期末考试第六大题解答
    CentOS7安装MySQL
  • 原文地址:https://www.cnblogs.com/yuphone/p/1817944.html
Copyright © 2011-2022 走看看