-
- 方式1:用assign描述,用阻塞赋值=
-
- 方式2:用always@(*)描述,用非阻塞赋值<=
-
- 方式1:三目运算符 ? : ;
-
- 方式2:if...else if.....else(有优先级)
-
- 方式3:case....default...(并行)
//方式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
测试结果(modelsim):
分析出的电路:
例2.三态门控制
//三态门控制 assign oe = sel; assign io = oe?out[0]:1'bz; //z高阻态(输入)
三态门和二选一多路器类似,不再进行仿真测试;
module half_adder( input a, input b, output out, //结果输出 output cout //进位输出 ); //功能描述 assign out = a ^ b; assign cout = a & b; endmodule
`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
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):
分析出的电路:
例4.数码管显示译码器
通常我们用的数码管有共阳极和共阴极之分,共阳极段码给0,位选给1全部点亮;共阴极段码给1,位选给0全部点亮;数码管都是7段数码管显示+一位小数点,Basys3开发板数码管原理图和数码管显示原理如下图:
这样每个数字都会有对应的7段编码,但我们熟悉的是二进制码或者BCD码,所以需要设计一个显示译码器,将输入的4bitBCD码转换为数码管对应的8bit段码;由Basys3原理图可知,要让数码管显示,还需要选中位选,所以还需要设计一个2-4译码器,用两个开关控制哪一位显示,设计图如下:
Verilog描述组合逻辑电路中的译码器通常采用case语句,完整的代码如下:
///////////////////////////////////////////////////////////////////////////////////////////////// // 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
仿真结果如下图:
综合分析出的电路如图:
小结 —— 组合逻辑电路的设计方法
1、verilog描述方法
对于组合逻辑电路,有两个步骤,一是描述端口,二是描述功能(最重要的是得出真值表,然后根据真值表得出逻辑表达式,描述功能);
2、testbench编写方法
对于组合逻辑电路的testbench测试文件的编写:
1)定义时间步进/时间精度:`timescale 1ns/1ps
2)定义一些测试模块输入所用到的寄存器,用于产生对测试模块输入信号(即将测试模块input类型信号改为reg类型信号);
定义用于观察的输出信号接到测试模块的输出(即将测试模块output类型信号改为wire类型信号);
3)例化测试模块(注意要定义例化模块名称)
4) 开始测试
①基本结构 initial begin ....... end......$stop;
②延时100ns的表示方法 #100; (注意一定要加上分号)
③穷举出所有可能的情况