Chap.3 Combinational Logic Using SystemVerilog Gate Models
这一章主要考了基本的组合逻辑、testbench、delay三种方式怎么加、阻塞赋值和非阻塞赋值。
《Digital system design with SystemVerilog》-Zwoliński, Mark
《SystemVerilog数字系统设计》-夏宇闻
Write a description of a three-input NAND gate with a delay of 5 ps using a continuous assignment.
`timescale 1ns/10ps
// 仿真时间单位/时间精度
// must be at least as precise as .
`timescale 1ps/1ps
module mod_a ( output out, input in1, input in2);
在实例化模块时,有两种常用的方式来进行模块端口的信号连接:
assign #10ps z = x & y;
`timescale 1ps/1ps
module nand3 (output wire y, input wire a,b,c);
assign #5ps y = ~(a & b & c);//表示,输出y有着5ps的延迟
endmodule
//`include "nand3.sv"
`timescale 1ps/1ps
module nand3_tb;
wire out_y;
reg in_a,in_b,in_c;
nand3 g1 (.y(out_y),.a(in_a),.b(in_b),.c(in_c));
initial
begin
in_a = '1;
in_b = '1;
in_c = '1;
#5ps in_a = '0;
#5ps in_b ='0;
#5ps in_c ='0;
end
initial begin
$monitor ("%t | a = %d| b = %d| c = %d| y = %d", $time, in_a, in_b,in_c, out_y);
$dumpfile("dump.vcd");
$dumpvars();
end
endmodule
Write a description of a three-input NAND gate with a parameterizable delay
using a continuous assignment.
The statement:
assign #5ps z = x & y;
defines the exact delay for an AND gate. Different technologies, and indeed different instances, will have different delays. We could declare a number of alternative modules for an AND gate, each with a different delay. It would be better to write the statement as:
assign #delay z = x & y;
and to define delay as a parameter to the SystemVerilog model.
module And2 #(parameter delay) (output wire z,
input wire x, y);
assign #delay z = x & y;
endmodule
When the gate is used in a netlist, a value is passed to the model as a parameter:
And2 #(5ps) g2 (p, b, q);
`timescale 1ps/1ps
module nand3 #(parameter delay) (output wire y, input wire a,b,c);
assign #delay y = ~(a & b & c);
endmodule
//`include "nand3.sv"
`timescale 1ps/1ps
module nand3_tb;
wire out_y;
reg in_a,in_b,in_c;
nand3 #(5ps) g1 (.y(out_y),.a(in_a),.b(in_b),.c(in_c));
initial
begin
in_a = '0;
in_b = '0;
in_c = '0;
#5ps in_a = '1;
#5ps in_b ='1;
#5ps in_c ='1;
end
initial begin
$monitor ("%t | a = %d| b = %d| c = %d| y = %d", $time, in_a, in_b,in_c, out_y);
$dumpfile("dump.vcd");
$dumpvars();
end
endmodule
A full adder has the truth table of Table 3.2 for its sum (S) and carry (Co) outputs, in terms of its inputs, A, B and carry in (Ci).
< center>Table 3.2 Truth Table for Exercise 3.3< center>
A | B | Ci | S | Co |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 |
1 | 1 | 0 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
Derive expressions for S and Co using only AND and OR operators. Hence write a SystemVerilog description of a full adder as a netlist of AND and OR gates and inverters. Do not include any gate delays in your models.
3.4 Write a SystemVerilog testbench to test all combinations of inputs to the full adder of Exercise 3.3. Verify the correctness of your full adder and of the testbench using a SystemVerilog simulator.
full adder 全加器
全加器就是考虑进位的加法运算器。
根据真值表画卡诺图,然后利用卡诺图写表达式,然后代数化简。这里要求的是用AND和OR门,特指二输入的AND和OR门。
因为我所使用的是纯组合逻辑电路,所以我这里可以只用阻塞赋值(Non-blocking,其对应的电路结构往往与电平触发有关)。
要求使用的是systemverilog自带的AND、NOT、OR函数。如果编程的方法是指定了这几个门,那么综合后就是这三种门,在硬件上也会寻找这三种门。但是,如果编程只写了有运算符的逻辑表达式,那么经过编译后或者综合后,可能编译器生成了所使用的门不止这些种类这些数量。具体看需求和要求了。
`timescale 1ns / 1ps
module full_adder(output wire s,co , input wire a,b,ci);
assign s = (a*~b+~a*b)*~ci + (~a*~b+a*b)*ci;
assign co = a*b+(a*~b+~a*b)*ci;
endmodule
可以看到并没有指定门的形式为与门、或门、非门的组合。具体可以看下文写法二。
//`include "full_adder.sv"
`timescale 1ns / 1ps
module full_adder_tb;
// Inputs
reg in_a;
reg in_b;
reg in_ci;
// Outputs
wire out_s;
wire out_co;
// Instantiate the Unit Under Test (UUT)
full_adder u_full_adder (
.s(out_s),
.co(out_co),
.a(in_a),
.b(in_b),
.ci(in_ci)
);
initial
begin
#10 in_a=1'b0;in_b=1'b0;in_ci=1'b0;
#10 in_a=1'b0;in_b=1'b0;in_ci=1'b1;
#10 in_a=1'b0;in_b=1'b1;in_ci=1'b0;
#10 in_a=1'b0;in_b=1'b1;in_ci=1'b1;
#10 in_a=1'b1;in_b=1'b0;in_ci=1'b0;
#10 in_a=1'b1;in_b=1'b0;in_ci=1'b1;
#10 in_a=1'b1;in_b=1'b1;in_ci=1'b0;
#10 in_a=1'b1;in_b=1'b1;in_ci=1'b1;
#10$stop;
end
initial
begin
$monitor(" time=%0d A=%b B=%b Cin=%b S=%b Co=%b",$time,in_a,in_b,in_ci,out_s,out_co);
end
endmodule
`timescale 1ns / 1ps
module full_adder_1(output wire s,co , input wire a,b,ci);
wire e, f, g,h,i,j,k,l,m,n,o,p;
//d=~ci; e=~a, f=~b,g=a*b,h=a*~b,i=~a*b;j=~a*~b;k=(a*~b+~a*b);l=(a*~b+~a*b)*~ci;
//m= (~a*~b+a*b);n=(~a*~b+a*b)*ci;o=s = (a*~b+~a*b)*~ci + (~a*~b+a*b)*ci;p=(a*~b+~a*b)*ci;
not g0 (d,ci);
not g1 (e,a);
not g2 (f,b);
and g3 (g,a,b);
and g4 (h,a,f);
and g5 (i,e,b);
and g6 (j,e,f);
or g7 (k,h,i);
and g8 (l,k,d);
or g9 (m,j,g);
and g10 (n,m,ci);
or g11 (s,l,n);
and g12 (p,k,ci);
or g13 (co,g,p);
/*
assign s = (a*~b+~a*b)*~ci + (~a*~b+a*b)*ci;
assign co = a*b+(a*~b+~a*b)*ci;
*/
endmodule
//`include "full_adder.sv"
`timescale 1ns / 1ps
module full_adder_1_tb;
// Inputs
reg in_a;
reg in_b;
reg in_ci;
// Outputs
wire out_s;
wire out_co;
// Instantiate the Unit Under Test (UUT)
full_adder_1 u_full_adder (
.s(out_s),
.co(out_co),
.a(in_a),
.b(in_b),
.ci(in_ci)
);
initial
begin
#10 in_a=1'b0;in_b=1'b0;in_ci=1'b0;
#10 in_a=1'b0;in_b=1'b0;in_ci=1'b1;
#10 in_a=1'b0;in_b=1'b1;in_ci=1'b0;
#10 in_a=1'b0;in_b=1'b1;in_ci=1'b1;
#10 in_a=1'b1;in_b=1'b0;in_ci=1'b0;
#10 in_a=1'b1;in_b=1'b0;in_ci=1'b1;
#10 in_a=1'b1;in_b=1'b1;in_ci=1'b0;
#10 in_a=1'b1;in_b=1'b1;in_ci=1'b1;
#10$stop;
end
initial
begin
$monitor(" time=%0d A=%b B=%b Cin=%b S=%b Co=%b",$time,in_a,in_b,in_ci,out_s,out_co);
end
endmodule
这里我为了读数方便,以列表的形式打印出了输出值,方便和真值表对比看功能是否完全实现。
ps,全加器的最简表达式
Modify the gate models of Exercise 3.3 such that each gate has a delay of 1 ns. What is the maximum delay through your full adder? Verify this delay by simulation
The simplest way to include timing information is to model the delay through each gate.1 For example, a delay of 10 ps through a NAND gate would be written as:
nand #10ps g1 (y, a, b);
The hash symbol (#) is used to denote a parameter. We will see further examples of parameters in later chapters. Notice that the delay parameter is placed between the type of gate (nand) and the name of the instance (g1).
本题要求让每一个门都有1ns的延迟。很简单,就在上文的写法二的形式里,给每一个门都加上“#1ns”
`timescale 1ns / 1ps
module full_adder_1(output wire s,co , input wire a,b,ci);
wire e, f, g,h,i,j,k,l,m,n,o,p;
//d=~ci; e=~a, f=~b,g=a*b,h=a*~b,i=~a*b;j=~a*~b;k=(a*~b+~a*b);l=(a*~b+~a*b)*~ci;
//m= (~a*~b+a*b);n=(~a*~b+a*b)*ci;o=s = (a*~b+~a*b)*~ci + (~a*~b+a*b)*ci;p=(a*~b+~a*b)*ci;
not #1ns g0 (d,ci);
not #1ns g1 (e,a);
not #1ns g2 (f,b);
and #1ns g3 (g,a,b);
and #1ns g4 (h,a,f);
and #1ns g5 (i,e,b);
and #1ns g6 (j,e,f);
or #1ns g7 (k,h,i);
and #1ns g8 (l,k,d);
or #1ns g9 (m,j,g);
and #1ns g10 (n,m,ci);
or #1ns g11 (s,l,n);
and #1ns g12 (p,k,ci);
or #1ns g13 (co,g,p);
/*
assign s = (a*~b+~a*b)*~ci + (~a*~b+a*b)*ci;
assign co = a*b+(a*~b+~a*b)*ci;
*/
endmodule
这里我有一个小改动,相比于Problem3.3里。我在testbench激励文件中把信号的输入,改成了非阻塞赋值的方法,因为要计算延时,所以输入应当是同时变化的。
具体区别可看这篇文章:阻塞赋值与非阻塞赋值
//`include "full_adder.sv"
`timescale 1ns / 1ps
module full_adder_1_tb;
// Inputs
reg in_a;
reg in_b;
reg in_ci;
// Outputs
wire out_s;
wire out_co;
// Instantiate the Unit Under Test (UUT)
full_adder_1 u_full_adder (
.s(out_s),
.co(out_co),
.a(in_a),
.b(in_b),
.ci(in_ci)
);
initial
begin
#10 in_a<=1'b0;in_b<=1'b0;in_ci<=1'b0;
#10 in_a<=1'b0;in_b<=1'b0;in_ci<=1'b1;
#10 in_a<=1'b0;in_b<=1'b1;in_ci<=1'b0;
#10 in_a<=1'b0;in_b<=1'b1;in_ci<=1'b1;
#10 in_a<=1'b1;in_b<=1'b0;in_ci<=1'b0;
#10 in_a<=1'b1;in_b<=1'b0;in_ci<=1'b1;
#10 in_a<=1'b1;in_b<=1'b1;in_ci<=1'b0;
#10 in_a<=1'b1;in_b<=1'b1;in_ci<=1'b1;
#10$stop;
end
initial
begin
$monitor(" time=%0d A=%b B=%b Cin=%b S=%b Co=%b",$time,in_a,in_b,in_ci,out_s,out_co);
end
endmodule
用ModelSim打开DataFlow,观察数据的流向。