本帖最后由 abner_ma 于 2021-1-28 18:26 编辑

设计方法:    分析真值表规律

      两种描述方式:



    • 方式1:用assign描述,用阻塞赋值=





    • 方式2:用always@(*)描述,用非阻塞赋值<=



    选择功能的三种描述方式:



    • 方式1:三目运算符 ? :  ;





    • 方式2:if...else if.....else(有优先级)





    • 方式3:case....default...(并行)           


  例1.mux2二选一数据选择器

       1326586-20180519194940071-1281528653.png

//方式1(先列出端口,后定义端口属性)                                    
  • module mux2(
  •     a,
  •     b,
  •     sel,
  •     out
  • );
  •     //端口属性定义(输入/输出,位宽)
  •     input  a;
  •     input  b;
  •     input  sel;        //sel = 0,out输出a
  •     output out;     //位宽1位
  •     //功能描述
  •     //阻塞赋值语句
  •     assign out = (sel == 0)?a:b;
  •     //assign out = (!sel)?a:b;
  •     //assign out = sel?b:a;
  • endmodule
  •   
  • //方式2(在声明端口的同时定义属性)
  • module mux2(
  •         //端口属性定义
  •     input  a,
  •     input  b,
  •     input  sel,
  •     output out  //此处没有分号
  • );
  •     //功能描述
  •     //阻塞赋值语句
  •     assign out = (sel == 0)?a:b;
  • endmodule
  •   testbench测试文件(组合逻辑电路一般都采用穷举法):
  • `timescale 1ns / 1ps
  • module mux2_tb();
  •    
  •     reg a;
  •     reg b;
  •     reg sel;
  •     wire c;
  •    
  •     //例化测试模块
  •     mux2 mu2_test(
  •        .a(a),
  •        .b(b),
  •        .sel(sel),
  •        .c(c)
  •         );
  •    
  •     initial begin
  •         a = 0;  b = 0;  sel = 0;
  •         #100;   //延时100ns(时间步进前面第一行代码已经设置为1ns)
  •         a = 0;  b = 0;  sel = 1;
  •         #100;
  •         a = 0;  b = 1;  sel = 0;
  •         #100;
  •         a = 0;  b = 1;  sel = 1;
  •         #100;
  •         a = 1;  b = 0;  sel = 0;
  •         #100;
  •         a = 1;  b = 0;  sel = 1;
  •         #100;
  •         a = 1;  b = 1;  sel = 0;
  •         #100;
  •         a = 1;  b = 1;  sel = 1;
  •         #100;
  •         $stop;
  •         end   
  • endmodule
  • 复制代码


       1326586-20180521194430997-690100637.png

      分析出的电路:

       1326586-20180521195411497-1798169798.png

       例2.三态门控制

    //三态门控制    assign oe = sel;    assign io = oe?out[0]:1'bz;    //z高阻态(输入)

      三态门和二选一多路器类似,不再进行仿真测试;

       例3.加法器
            1、半加器halfadder

    1326586-20180519195327319-1720128488.png

    module half_adder(
  •     input  a,
  •     input  b,
  •     output out,     //结果输出
  •     output cout     //进位输出
  •     );
  •     //功能描述
  •     assign out  = a ^ b;
  •     assign cout = a & b;
  •    
  • endmodule
  •   testbench测试代码:
  • `timescale 1ns / 1ps
  • module half_adder_tb();
  •     reg a;
  •     reg b;
  •     wire out;
  •     wire cout;
  •    
  •     //例化测试模块
  •     half_adder half_adder_test(
  •         .a(a),
  •         .b(b),
  •         .out(out),     //结果输出
  •         .cout(cout)     //进位输出
  •         );
  •     //开始测试
  •     initial begin
  •         a = 0;  b = 0;
  •         #100;
  •         a = 0;  b = 1;
  •         #100;
  •         a = 1;  b = 0;
  •         #100;
  •         a = 1;  b = 1;
  •         #100;
  •         $stop;
  •         end
  • endmodule
  • 复制代码


      测试结果(modelsim):

       1326586-20180521202217710-1099362354.png

      分析出的电路(和上一篇数字电路设计的一模一样,由一个异或门和与门构成):

       1326586-20180521202417103-1426186809.png


        2、全加器adder
               设计方式1:层次化设计,由两个半加器和一个或门组成
               设计方式2:根据真值表采用verilog直接描述(如下图)

    1326586-20180519195413044-1552003230.png

    module adder(
  •     input  a,
  •     input  b,
  •     input  cin,  //进位输入
  •     output out,  //结果输出
  •     output cout  //进位输出
  •     );
  •     //功能描述
  •     assign out  = a ^ b ^ cin;
  •     assign cout = a&b | a&cin | b&cin;
  • endmodule
  •   testbench测试文件
  • `timescale 1ns / 1ps
  • module adder_tb();
  •     reg  a;
  •     reg  b;
  •     reg  cin;
  •     wire out;
  •     wire cout;
  •    
  •     //例化测试模块
  •     adder adder_test(
  •         .a(a),
  •         .b(b),
  •         .cin(cin),   //进位输入
  •         .out(out),   //结果输出
  •         .cout(cout)  //进位输出
  •         );
  •     //开始测试
  •     initial begin
  •                 a = 0;  b = 0;  cin = 0;
  •         #100;   a = 0;  b = 1;  cin = 0;
  •         #100;   a = 1;  b = 0;  cin = 0;
  •         #100;   a = 1;  b = 1;  cin = 0;
  •         #100;   a = 0;  b = 0;  cin = 1;
  •         #100;   a = 0;  b = 1;  cin = 1;
  •         #100;   a = 1;  b = 0;  cin = 1;
  •         #100;   a = 1;  b = 1;  cin = 1;
  •         #100;   $stop;
  •     end
  • endmodule
  •   测试结果(modelsim):
  • 复制代码

       1326586-20180521205651402-797316699.png

      分析出的电路:

       1326586-20180521210445484-665078520.png

      例4.数码管显示译码器

        通常我们用的数码管有共阳极和共阴极之分,共阳极段码给0,位选给1全部点亮;共阴极段码给1,位选给0全部点亮;数码管都是7段数码管显示+一位小数点,Basys3开发板数码管原理图和数码管显示原理如下图:

         1326586-20180521213234164-1271848031.png

       1326586-20180521212020651-163122516.png

        这样每个数字都会有对应的7段编码,但我们熟悉的是二进制码或者BCD码,所以需要设计一个显示译码器,将输入的4bitBCD码转换为数码管对应的8bit段码;由Basys3原理图可知,要让数码管显示,还需要选中位选,所以还需要设计一个2-4译码器,用两个开关控制哪一位显示,设计图如下:

       1326586-20180522103314697-889972336.png

    /
  • // Module Name: seg_display
  • // Description: 数码管显示模块,由一个显示译码器模块decoder_display和一个2-4译码器decoder2_4构成;
  • //
  • module seg_display(
  •     input       [3:0]data_display,      //数码管待显示数据
  •     input       [1:0]wei,               //选择哪一位显示
  •     output      [6:0]segments,           //数码管段码
  •     output      [3:0]wei_sel             //数码管位码
  •     );
  •     //功能描述
  •     //例化显示译码模块
  •     decoder_display decoder_display_0(
  •         .data_in(data_display),
  •         .segments(segments)
  •         );
  •     //例化位选模块
  •     decoder2_4 decoder2_4_0(
  •         .data_in(wei),
  •         .wei_sel(wei_sel)
  •         );
  • endmodule
  • //数码管显示译码模块
  • //note:只包含7位段码,不包括小数点控制
  • module decoder_display(
  •     input       [3:0]data_in,
  •     output  reg [6:0]segments
  •     );
  •     //显示译码功能描述
  •     always@(*)
  •         case(data_in)
  •             //对应段                  abc_defg
  •             4'h0:       segments = 7'b000_0001;
  •             4'h1:       segments = 7'b100_1111;
  •             4'h2:       segments = 7'b001_0010;
  •             4'h3:       segments = 7'b000_0110;
  •             4'h4:       segments = 7'b100_1100;
  •             4'h5:       segments = 7'b010_0100;
  •             4'h6:       segments = 7'b010_0000;
  •             4'h7:       segments = 7'b000_1111;
  •             4'h8:       segments = 7'b000_0000;
  •             4'h9:       segments = 7'b000_1100;
  •             4'hA:       segments = 7'b000_1000;
  •             4'hB:       segments = 7'b110_0000;
  •             4'hC:       segments = 7'b011_0001;
  •             4'hD:       segments = 7'b100_0010;
  •             4'hE:       segments = 7'b011_0000;
  •             4'hF:       segments = 7'b011_1000;
  •             default:    segments = 7'b111_1111;
  •         endcase
  • endmodule
  • module decoder2_4(
  •     input        [1:0]data_in,
  •     output  reg [3:0]wei_sel  //4位数码管选中位
  •     );
  •     //位选2-4译码器功能描述
  •     always@(*)
  •         case(data_in)
  •             //对应位
  •             4'h0:       wei_sel  = 4'b1110;
  •             4'h1:       wei_sel  = 4'b1101;
  •             4'h2:       wei_sel  = 4'b1011;
  •             4'h3:       wei_sel  = 4'b0111;
  •             default:    wei_sel = 4'b1111;
  •         endcase
  • endmodule
  •   testbench测试代码如下:
  • `timescale 1ns / 1ps
  • //
  • // Module Name: seg_display_tb
  • // Description: 数码管显示模块seg_display测试模块
  • //
  • module seg_display_tb();
  •     reg       [3:0]data_display;      //数码管待显示数据
  •     reg       [1:0]wei;               //选择哪一位显示
  •     wire      [6:0]segments;          //数码管段码
  •     wire      [3:0]wei_sel;           //数码管位码
  •    
  •     //例化测试模块
  •     seg_display seg_display_test(
  •         .data_display(data_display),   //数码管待显示数据
  •         .wei(wei),                     //选择哪一位显示
  •         .segments(segments),           //数码管段码
  •         .wei_sel(wei_sel)              //数码管位码
  •         );
  •     //开始测试
  •     initial begin
  •             wei = 2'h0;            //选中第一位显示0-F
  •             data_display = 4'h0;   //显示"0"
  •       #10; data_display = 4'h1;    //显示"1"
  •       #10; data_display = 4'h2;    //显示"2"
  •       #10; data_display = 4'h3;    //显示"3"
  •       #10; data_display = 4'h4;    //显示"4"
  •       #10; data_display = 4'h5;    //显示"5"
  •       #10; data_display = 4'h6;    //显示"6"
  •       #10; data_display = 4'h7;    //显示"7"
  •       #10; data_display = 4'h8;    //显示"8"
  •       #10; data_display = 4'h9;    //显示"9"
  •       #10; data_display = 4'ha;    //显示"A"
  •       #10; data_display = 4'hb;    //显示"b"
  •       #10; data_display = 4'hc;    //显示"C"
  •       #10; data_display = 4'hd;    //显示"d"
  •       #10; data_display = 4'he;    //显示"E"
  •       #10; data_display = 4'hf;    //显示"F"
  •      
  •       #10; wei = 2'h1;             //选中第二位显示"F"
  •       #10; wei = 2'h2;             //选中第三位显示"F"
  •       #10; wei = 2'h3;             //选中第四位显示"F"
  •       
  •       #100; $stop;                 //测试停止     
  •     end
  •    
  • endmodule
  • 复制代码

      仿真结果如下图:

    1326586-20180522113754922-1335304401.png

      综合分析出的电路如图:

       1326586-20180522113901600-2047924132.png

      小结 —— 组合逻辑电路的设计方法

      1、verilog描述方法

         对于组合逻辑电路,有两个步骤,一是描述端口,二是描述功能(最重要的是得出真值表,然后根据真值表得出逻辑表达式,描述功能);

      2、testbench编写方法

          对于组合逻辑电路的testbench测试文件的编写:

          1)定义时间步进/时间精度:`timescale 1ns/1ps

          2)定义一些测试模块输入所用到的寄存器,用于产生对测试模块输入信号(即将测试模块input类型信号改为reg类型信号);

             定义用于观察的输出信号接到测试模块的输出(即将测试模块output类型信号改为wire类型信号);

          3)例化测试模块(注意要定义例化模块名称)

          4) 开始测试

             ①基本结构  initial begin .......   end......$stop;

             ②延时100ns的表示方法  #100;  (注意一定要加上分号)

             ③穷举出所有可能的情况