1.GettingStarted
1.1 Getting Started
问题陈述:
构建一个没有输入和一个输出的电路。该输出应始终驱动 1(或逻辑高电平)。
module top_module( output one );
// Insert your code here
assign one = 1;
endmodule
1.2 Output Zero
问题陈述:
构建一个没有输入和一个输出常数
0的电路
module top_module(
output zero
);// Module body starts after semicolon
assign zero = 0;
endmodule
2.Verilog Language
2.1 Basics
2.1.1 Simple wire
问题陈述:
创建一个具有一个输入和一个输出的模块,其行为类似于电线。与物理电线不同,Verilog 中的电线(和其他信号)是定向的。这意味着信息只在一个方向上流动,从(通常是一个)源到接收器(源通常也称为将值驱动到电线上的驱动器)。在Verilog的“连续分配”()中,右侧的信号值被驱动到左侧的导线上。赋值是“连续的”,因为即使右侧的值发生变化,赋值也会一直继续。连续分配不是一次性事件。
assign left_side = right_side;模块上的端口也有一个方向(通常是输入或输出)。输入端口由模块外部的某个东西驱动,而输出端口则由外部的东西驱动。从模块内部查看时,输入端口是驱动器或源,而输出端口是接收器。下图说明了电路的每个部分如何对应于Verilog代码的每个位。模块和端口声明创建电路的黑色部分。您的任务是通过添加要连接到的语句来创建一条线路(绿色)。盒子外的部件不是您关心的问题,但您应该知道,您的电路是通过将来自我们的测试线束的信号连接到您的.assign in out top_module
module top_module( input in, output out );
assign out = in;
endmodule
2.1.2 Four wires
问题陈述:
创建一个具有 3 个输入和 4 个输出的模块,其行为类似于进行以下连接的电线:
a -> w
b -> x
b -> y
c -> z
下图说明了电路的每个部分如何对应于Verilog代码的每个位。 在模块外部,有三个输入端口和四个输出端口。当有多个
赋值语句时,它们在代码中的显示顺序 无关紧要。与编程语言不同,赋值语句(“连续赋值” )描述的是事物之间的联系,而不是将值从一个事物复制到另一个事物的操作。现在也许应该澄清的一个潜在的混淆来源:这里的绿色箭头代表 电线之间的连接,但本身不是电线。模块本身已经声明了 7 根电线(命名为 a、b、c、w、x、y 和 z)。这是因为和声明 除非另有说明,否则实际声明电线。写作与 .因此,这些语句不是在创建电线,而是在创建之间的连接 已经存在的 7 根电线。input output input wire a input a assign
module top_module(
input a,b,c,
output w,x,y,z );
assign w=a;
assign x=b;
assign y=b;
assign z=c;
endmodule
逻辑门回顾->逻辑门 - 维基百科
2.1.3 Inverter
问题陈述:
创建一个实现 NOT 门的模块。该电路类似于线,但略有不同。当从电线到电线进行连接时,我们将实现逆变器(或“非栅极”)而不是普通电线。
in out使用 assign 语句。该语句将连续驱动 的逆 on wire。assign in out
module top_module( input in, output out );
assign out = ~in;
endmodule
2.1.4 AND gate
问题陈述:
创建一个实现 AND 门的模块。该电路现在有三根导线(、 和 )。电线,并且已经有输入端口驱动的值。但电线目前不受任何驱动。写一个语句,用信号的 AND 和 驱动。
a b out a b out assign out a b请注意,此电路与NOT 门,只是多了一个输入。如果听起来不同,那是因为我已经开始将信号描述为被驱动(具有由附加在它上面的东西决定的已知值)或不是由某种东西驱动的。 由模块外部的东西驱动。 语句将逻辑电平驱动到导线上。正如你所料,一根电线不能有多个驱动器(如果有的话,它的逻辑电平是多少?),而没有驱动器的电线将有一个未定义的值(在合成硬件时通常被视为 0)。Input wires assign
module top_module(
input a,
input b,
output out );
assign out = a & b;
endmodule
2.1.5 NOR gate
问题陈述:
创建一个实现 NOR 门的模块。NOR门是输出反相的OR门。用 Verilog 编写的 NOR 函数需要两个运算符。语句驱动带有值的导线(或更正式地称为“net”)。此值可以是任意复杂的函数,只要它是组合函数(即无内存,没有隐藏状态)函数即可。语句是一个连续赋值,因为每当其任何输入发生变化时,输出都会被“重新计算”,就像一个简单的逻辑门一样。
assign assign
module top_module(
input a,
input b,
output out );
assign out = ~(a|b);
endmodule
2.1.6 XNOR gate
问题陈述:
创建一个实现 XNOR 门的模块。
module top_module(
input a,
input b,
output out );
assign out = a~^b;
endmodule
2.1.7 Declaring wires
问题陈述:
实现以下电路。创建两根中间线(随心所欲地命名)以将 AND 和 OR 门连接在一起。请注意,为 NOT 门供电的导线实际上是
接线的,因此您不一定需要在此处声明第三根导线。请注意,导线如何仅由一个电源(栅极的输出)驱动,但可以馈送多个输入。如果您遵循图中的电路结构,则最终应该得到四个赋值语句,因为有四个信号需要赋值。(是的,可以在没有中间线的情况下创建具有相同功能的电路。
`default_nettype none
module top_module(
input a,
input b,
input c,
input d,
output out,
output out_n );
wire x;
wire y;
wire z;
assign x= a & b;
assign y = c & d;
assign z = x|y;
assign out = z;
assign out_n = ~z;
endmodule
2.1.8 7458 chip
问题陈述:
7458 是一款具有 4 个 AND 门和 2 个 OR 门的芯片。这个问题比7420.创建一个与 7458 芯片具有相同功能的模块。它有 10 个输入和 2 个输出。您可以选择使用语句来驱动每根输出线,也可以选择声明(四根)线用作中间信号,其中每根内部线由其中一个 AND 门的输出驱动。如需额外的练习,请尝试两种方式。
assign
module top_module (
input p1a, p1b, p1c, p1d, p1e, p1f,
output p1y,
input p2a, p2b, p2c, p2d,
output p2y );
/*
//first
assign p1y = (p1a&p1b&p1c)|(p1d&p1e&p1f);
assign p2y = (p2a&p2b)|(p2c&p2d);
*/
wire a;
wire b;
wire c;
wire d;
assign a = p1a&p1b&p1c;
assign b = p1d&p1e&p1f;
assign c = p2a&p2b;
assign d = p2c&p2d;
assign p1y = a|b;
assign p2y = c|d;
endmodule
2.2 Vectors
2.2.1 Vectors
问题陈述:
向量用于使用一个名称对相关信号进行分组,以便更方便操作。例如,
wire [7:0] w;声明一个名为w的 8 位向量,该向量在功能上等效于具有 8 根单独的导线。请注意,向量的声明将维度放在向量名称之前,这与 C 语法相比是不寻常的。但是,正如您所期望的那样,零件选择在矢量名称之后具有尺寸。wire [99:0] my_vector; // Declare a 100-element vector
assign out = my_vector [10] ; // Part-select one bit out of the vector
构建一个具有一个 3 位输入的电路,然后输出相同的矢量,并将其拆分为三个独立的 1 位输出。将输出连接到输入向量的位置 0、位置 1 等。
o0o1在关系图中,旁边带有数字的刻度线表示向量(或“总线”)的宽度,而不是为向量中的每个位绘制一条单独的线。
module top_module (
input wire [2:0] vec,
output wire [2:0] outv,
output wire o2,
output wire o1,
output wire o0 ); // Module body starts after module declaration
assign outv = vec;
assign o0 = vec[0];
assign o1 = vec[1];
assign o2 = vec[2];
endmodule
2.2.2 Vectors in more detail
问题陈述:
构建一个组合电路,将输入半字(16 位,[15:0])拆分为下 [7:0] 和上 [15:8] 字节。
`default_nettype none // Disable implicit nets. Reduces some types of bugs.
module top_module(
input wire [15:0] in,
output wire [7:0] out_hi,
output wire [7:0] out_lo );
assign out_hi = in[15:8];
assign out_lo = in[7:0];
endmodule
2.2.3 Vector part select
问题陈述:
32 位向量可以看作是包含 4 个字节(位 [31:24]、[23:16] 等)。构建一个电路,该电路将颠倒 4 字节字的字节顺序。
AaaaaaaaBbbbbbbbCcccccccDddddddd => DdddddddCcccccccBbbbbbbbAaaaaaaa
当需要交换一段数据的字节序时,通常会使用此操作,例如,在 little-endian x86 系统和许多 Internet 协议中使用的大端格式之间。
module top_module(
input [31:0] in,
output [31:0] out );//
// assign out[31:24] = ...;
assign out[31:24]=in[7:0];
assign out[23:16]=in[15:8];
assign out[15:8]=in[23:16];
assign out[7:0]=in[31:24];
endmodule
2.2.4 Bitwise operators
问题陈述:
构建一个具有两个 3 位输入的电路,用于计算两个向量的按位 OR、两个向量的逻辑 OR 以及两个向量的逆 (NOT)。将 的倒数放在 的上半部分(即位 [5:3]),将 的倒数放在下半部分。
b out_not a前面,我们提到过各种布尔运算符有按位和逻辑版本(例如,诺盖特).使用向量时,两种运算符类型之间的区别变得很重要。两个 N 位向量之间的按位运算复制向量的每个位的运算并生成 N 位输出,而逻辑运算将整个向量视为布尔值(true = 非零,false = 零)并生成 1 位输出。
查看仿真波形,了解按位 OR 和逻辑 OR 的不同之处。
module top_module(
input [2:0] a,
input [2:0] b,
output [2:0] out_or_bitwise,
output out_or_logical,
output [5:0] out_not
);
assign out_or_bitwise = a|b;
assign out_or_logical = a||b;
assign out_not[5:3] = ~b;
assign out_not[2:0] = ~a;
endmodule
2.2.5 Four-input gates
问题陈述:
构建一个具有四个输入的组合电路,
in[3:0]。有 3 个输出:
- out_and:4 输入 AND 门的输出。
- out_or:4 输入 OR 门的输出。
- out_xor:4 输入 XOR 门的输出。
要查看 AND、OR 和 XOR 运算符,请参阅安德盖特,诺盖特和XNORGATE公司.
另请参阅:更宽的闸门.
module top_module(
input [3:0] in,
output out_and,
output out_or,
output out_xor
);
assign out_and = in[0]&in[1]&in[2]&in[3];
assign out_or = in[0]|in[1]|in[2]|in[3];
assign out_xor = in[0]^in[1]^in[2]^in[3];
endmodule
2.2.6 Vector concatenationoperator
给定多个输入向量,将它们连接在一起,然后将它们拆分为多个输出向量。有六个 5 位输入向量:a、b、c、d、e 和 f,总共 30 位输入。有四个 8 位输出向量:w、x、y 和 z,用于 32 位输出。输出应该是输入向量的串联,后跟两个
1位:
module top_module (
input [4:0] a, b, c, d, e, f,
output [7:0] w, x, y, z );//
// assign { ... } = { ... };
assign {w,x,y,z}={a,b,c,d,e,f,2'b11};
endmodule
2.2.7 Vector reversal 1
问题陈述:
给定一个 8 位输入向量 [7:0],反转其位顺序。
module top_module(
input [7:0] in,
output [7:0] out
);
assign out = {in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]};
endmodule
2.2.8 Replication operator
问题陈述:
查看复制运算符的一个常见位置是将较小的数字扩展到较大的数字,同时保留其有符号值。这是通过向左复制较小数字的符号位(最高有效位)来完成的。例如,将符号扩展
4'b0101(5) 到 8 位会产生8'b00000101(5),而将符号扩展到4'b1101(-3) 到 8 位会产生8'b11111101(-3)。构建一个将 8 位数字符号扩展到 32 位的电路。这需要将 24 个符号位副本(即复制位 [7] 24 次)串联起来,然后是 8 位数字本身。
module top_module (
input [7:0] in,
output [31:0] out );//
// assign out = { replicate-sign-bit , the-input };
assign out = {{24{in[7]}},in[7:0]};
endmodule
2.2.9 More replication
问题陈述:
给定 5 个 1 位信号(a、b、c、d 和 e),计算 25 位输出向量中的所有 25 个成对的 1 位比较。如果要比较的两个位相等,则输出应为 1。
out[24] = ~a ^ a; // a == a, so out[24] is always 1. out[23] = ~a ^ b; out[22] = ~a ^ c; ... out[ 1] = ~e ^ d; out[ 0] = ~e ^ e;如图所示,使用复制和串联运算符可以更轻松地完成此操作。
- 顶部向量是每个输入的 5 个重复的串联
- 底部向量是 5 个输入串联的 5 次重复
module top_module (
input a, b, c, d, e,
output [24:0] out );//
// The output is XNOR of two vectors created by
// concatenating and replicating the five inputs.
// assign out = ~{ ... } ^ { ... };
assign out[24:0] = ~{{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}}^{5{a,b,c,d,e}};
endmodule
2.3 Modules: Hierarchy
2.3.1 Modules
问题陈述:
到现在为止,您已经熟悉了 ,这是一种通过输入和输出端口与其外部交互的电路。更大、更复杂的电路是通过将更大的模块和连接在一起的其他部分(例如 assign 语句和始终块)组成而成的。这形成了一个层次结构,因为模块可以包含其他模块的实例。
module下图显示了一个带有子模块的非常简单的电路。在本练习中,创建一个模块实例,然后将模块的三个引脚(、 和 )连接到顶级模块的三个端口(电线、 和 )。该模块是为你提供的 - 你必须实例化它。
mod_a``in1``in2``out``a``b``out``mod_a连接模块时,只有模块上的端口很重要。您不需要知道模块内部的代码。模块的代码如下所示:
mod_amodule mod_a ( input in1, input in2, output out ); // Module body endmodule模块的层次结构是通过在另一个模块中实例化一个模块来创建的,只要使用的所有模块都属于同一个项目(因此编译器知道在哪里可以找到模块)。一个模块的代码不会写入另一个模块的主体中(不同模块的代码不嵌套)。
您可以通过端口名称或端口位置将信号连接到模块。如需额外的练习,请尝试这两种方法。
module top_module ( input a, input b, output out );
// mod_a test2(a,b,out);
mod_a test(
.in1(a),
.in2(b),
.out(out)
);
endmodule
ps:感觉有点绕的话参考c里结构体嵌套结构体,传参而已。
2.3.2 Connecting ports byposition
问题陈述:
此问题与上一个问题类似(模块).您将获得一个名为的模块,该模块按此顺序具有 2 个输出和 4 个输入。您必须按位置将 6 个端口连接到您的 顶级模块的端口 、 、 、 和 ,按此顺序排列。
mod_a out1 out2 a b c d您将获得以下模块:
module mod_a ( output, output, input, input, input, input );
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a test(
out1,
out2,
a,
b,
c,
d
);
endmodule
2.3.3 Connecting ports by name
问题陈述:
此问题类似于模块.您将获得一个名为的模块,该模块按某种顺序具有 2 个输出和 4 个输入。您必须按名称将 6 个端口连接到您的 顶级模块的端口:
mod_a
端口输入 mod_a端口输入 top_moduleoutput out1out1output out2out2input in1ainput in2binput in3cinput in4d您将获得以下模块:
module mod_a ( output out1, output out2, input in1, input in2, input in3, input in4);
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a test(
.out1(out1),
.out2(out2),
.in1(a),
.in2(b),
.in3(c),
.in4(d)
);
endmodule
2.3.4 Three modules
问题陈述:
您将获得一个具有两个输入和一个输出的模块(实现 D 触发器)。实例化其中的三个,然后将它们链接在一起以生成长度为 3 的移位寄存器。端口需要连接到所有实例。
my_dff``clk提供给您的模块是:
module my_dff ( input clk, input d, output q );请注意,要进行内部连接,您需要声明一些电线。在命名电线和模块实例时要小心:名称必须是唯一的。
module top_module ( input clk, input d, output q );
wire temp [1:0];
my_dff a(.clk(clk),.d(d),.q(temp[1]));
my_dff b(.clk(clk),.d(temp[1]),.q(temp[0]));
my_dff c(.clk(clk),.d(temp[0]),.q(q));
endmodule
2.3.5 Modules and vectors
问题陈述:
本练习是module_shift.模块端口不再只是单引脚,而是现在有带有矢量作为端口的模块,您将连接有线矢量而不是普通线。与Verilog中的其他任何地方一样,端口的矢量长度不必与连接到它的电线匹配,但这将导致矢量的零填充或截断。本练习不使用矢量长度不匹配的连接。
您将获得一个具有两个输入和一个输出的模块(实现一组 8 D 触发器)。实例化其中的三个,然后将它们链接在一起,以形成长度为 3 的 8 位宽移位寄存器。此外,创建一个 4 对 1 多路复用器(未提供),该多路复用器根据以下因素选择输出内容: 输入 d 处的值、第一个触发器之后、第二个触发器之后或第三个 D 触发器触发器之后的值。(实质上,选择延迟输入的周期数,从零到三个时钟周期。
my_dff8``sel[1:0]``sel提供给您的模块是:
module my_dff8 ( input clk, input [7:0] d, output [7:0] q );不提供多路复用器。一种可能的编写方法是在块内有一个语句。(另请参阅:
always``case多路复用器9to1v)
module top_module (
input clk,
input [7:0] d,
input [1:0] sel,
output [7:0] q
);
wire [7:0] a,b,c;
my_dff8 x(.clk(clk),.d(d),.q(a));
my_dff8 y(.clk(clk),.d(a),.q(b));
my_dff8 z(.clk(clk),.d(b),.q(c));
always @(*) begin
case (sel)
2'b00:q=d;
2'b01:q=a;
2'b10:q=b;
2'b11:q=c;
endcase
end
endmodule
2.3.6 Adder 1
问题陈述:
您将获得一个执行 16 位加法的模块。实例化其中两个以创建一个 32 位加法器。一个 add16 模块计算加法结果的下 16 位,而第二个 add16 模块在接收到第一个加法器的进出后计算结果的前 16 位。您的 32 位加法器不需要处理进位(假设为 0)或进位(忽略),但内部模块需要处理才能正常工作。(换句话说,模块执行 16 位 a + b + cin,而模块执行 32 位 a + b)。
add16``add16如下图所示将模块连接在一起。提供的模块具有以下声明:
add16
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
module top_module(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire [15:0] temp1,temp2;
wire temp;
add16 x(.a(a[15:0]),.b(b[15:0]),.cin(1'b0),.sum(temp1),.cout(temp));
add16 y(.a(a[31:16]),.b(b[31:16]),.cin(temp),.sum(temp2),.cout());
assign sum={temp2,temp1};
endmodule
2.3.7 Adder 2
问题陈述:
在本练习中,您将创建一个具有两个层次结构级别的线路。您将实例化 (provided) 的两个副本,每个副本将实例化 16 个副本(您必须编写)。因此,您必须编写两个模块:和 。
top_module``add16``add1``top_module``add1喜欢module_add,您将获得一个执行 16 位加法的模块。您必须实例化其中两个才能创建 32 位加法器。一个模块计算加法结果的下 16 位,而第二个模块计算结果的前 16 位。您的 32 位加法器不需要处理进位(假设为 0)或进位(忽略)。
add16``add16``add16如下图所示将模块连接在一起。提供的模块具有以下声明:
add16``add16
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );在每个 中,实例化了 16 个完整的加法器(模块,未提供)以实际执行加法。您必须编写具有以下声明的完整加法器模块:
add16``add1
module add1 ( input a, input b, input cin, output sum, output cout );回想一下,一个完整的加法器计算 a+b+cin 的总和和。
总之,此设计中有三个模块:
top_module— 您的顶级模块包含以下两个...
add16, provided — 一个 16 位加法器模块,由 16 个...
add1— 一个 1 位全加法器模块。如果您的提交缺少 ,您将收到一条错误消息,指出 。
module add1``Error (12006): Node instance "user_fadd[0].a1" instantiates undefined entity "add1"
module top_module (
input [31:0] a,
input [31:0] b,
output [31:0] sum
);//
wire [15:0] temp1,temp2;
wire temp;
add16 x(.a(a[15:0]),.b(b[15:0]),.cin(1'b0),.sum(temp1),.cout(temp));
add16 y(.a(a[31:16]),.b(b[31:16]),.cin(temp),.sum(temp2),.cout());
assign sum={temp2,temp1};
endmodule
module add1 ( input a, input b, input cin, output sum, output cout );
assign sum = a^b^cin;
assign cout = (a&b)|(a&cin)|(b&cin);
// Full adder module here
endmodule
2.3.8 Carry-select adder
问题陈述:
纹波进位加法器的一个缺点(参见以前的练习)是加法器计算执行的延迟(在最坏的情况下从进位开始)相当慢,并且第二级加法器在第一级加法器完成之前无法开始计算其进出。这会使加法器变慢。其中一项改进是进位选择加法器,如下所示。第一级加法器与之前相同,但我们复制了第二级加法器,一个假设进位=0,另一个假设进位=1,然后使用快速的2对1多路复用器来选择恰好正确的结果。
在本练习中,您将获得与上一个练习相同的模块,该模块将两个带进位的 16 位数字相加,并生成进位和 16 位求和。您必须实例化其中的三个,才能使用自己的 16 位 2 对 1 多路复用器构建进位选择加法器。
add16如下图所示将模块连接在一起。提供的模块具有以下声明:
add16
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
module top_module(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire cout;
wire [15:0] temp0,temp1;
add16 x(
.a(a[15:0]),
.b(b[15:0]),
.cin(1'b0),
.sum(sum[15:0]),
.cout(cout)
);
add16 y(
.a(a[31:16]),
.b(b[31:16]),
.cin(1'b0),
.sum(temp0[15:0]),
.cout()
);
add16 z(
.a(a[31:16]),
.b(b[31:16]),
.cin(1'b1),
.sum(temp1[15:0]),
.cout()
);
assign sum[31:16] = cout?temp1:temp0;
endmodule
2.3.9 Adder-subtractor
问题陈述:
加法器-减法器可以通过选择性地否定其中一个输入来从加法器构建,这相当于反转输入然后添加 1。最终结果是一个可以执行两种操作的电路:(a + b + 0) 和 (a + ~b + 1)。如果您想更详细地了解此电路的工作原理,请参阅维基百科。
在下面构建加法器-减法器。
系统为您提供了一个 16 位加法器模块,您需要实例化两次:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );当
sub为 1 时,使用 32 位宽的 XOR 门反转b输入。(这也可以看作是b[31:0]异或,子复制了 32 次。看复制运算符.).还要将子输入连接到加法器的进位。
module top_module(
input [31:0] a,
input [31:0] b,
input sub,
output [31:0] sum
);
wire x;
wire [31:0] temp;
assign temp = b[31:0]^{32{sub}};
add16 m(
.a(a[15:0]),
.b(temp[15:0]),
.cin(sub),
.sum(sum[15:0]),
.cout(x)
);
add16 n(
.a(a[31:16]),
.b(temp[31:16]),
.cin(x),
.sum(sum[31:16]),
.cout()
);
endmodule
ps:在Verilog中使用一个较窄(比如1位)的操作数与一个较宽(比如32位)的操作数进行按位操作时(如异或^),较窄的操作数会自动扩展到较宽操作数的大小。根据Verilog的扩展规则,无符号操作数在扩展时高位补0,而符号操作数在扩展时高位补符号位(最左边的位)。
2.4 Procedures
2.4.1 Alwaysblocks(combinational)
问题陈述:
使用 assign 语句和组合 always 块构建 AND 门。(由于 assign 语句和组合始终块函数相同,因此无法强制使用这两种方法。但你是来练习的,对吧?...)
// synthesis verilog_input_version verilog_2001
module top_module(
input a,
input b,
output wire out_assign,
output reg out_alwaysblock
);
assign out_assign = a&b;
always @(*) begin
out_alwaysblock = a&b;
end
endmodule
2.4.2 Always blocks (clocked)
问题陈述:
使用赋值语句、组合 always 块和时钟 always 块以三种方式构建 XOR 门。请注意,时钟始终模块产生的电路与其他两个模块不同:有一个触发器,因此输出是延迟的。
// synthesis verilog_input_version verilog_2001
module top_module(
input clk,
input a,
input b,
output wire out_assign,
output reg out_always_comb,
output reg out_always_ff );
assign out_assign = a^b;
always @(*) begin
out_always_comb = a^b;
end
always @(posedge clk) begin
out_always_ff = a^b;
end
endmodule
2.4.3 If statement
问题陈述:
构建一个在
a和b之间进行选择的 2 对 1 多路复用器。如果sel_b1和sel_b2均为 true,则选择b。否则,请选择a。执行相同的操作两次,一次使用assign语句,一次使用过程 if 语句。
sel_b1 sel_b2 out_assign out_always 0 0 一个 0 1 一个 1 0 一个 1 1 b
// synthesis verilog_input_version verilog_2001
module top_module(
input a,
input b,
input sel_b1,
input sel_b2,
output wire out_assign,
output reg out_always );
assign out_assign = (sel_b1&sel_b2)?b:a;
always @(*) begin
if(sel_b1&sel_b2)
out_always = b;
else
out_always = a;
end
endmodule
2.4.4 If statement latches
问题陈述:
以下代码包含创建闩锁的错误行为。修复这些错误,以便您仅在计算机确实过热时关闭计算机,并在到达目的地或需要加油时停止驾驶。
这是代码描述的电路,而不是要构建的电路。
always @(*) begin if (cpu_overheated) shut_off_computer = 1; end always @(*) begin if (~arrived) keep_driving = ~gas_tank_empty; end
// synthesis verilog_input_version verilog_2001
module top_module (
input cpu_overheated,
output reg shut_off_computer,
input arrived,
input gas_tank_empty,
output reg keep_driving ); //
always @(*) begin
if (cpu_overheated)
shut_off_computer = 1;
else
shut_off_computer = 0;
end
always @(*) begin
if (~arrived)
keep_driving = ~gas_tank_empty;
else
keep_driving = 0;
end
endmodule
2.4.5 Case statement
问题陈述:
如果存在大量案例,则案例陈述比 if 陈述更方便。因此,在本练习中,创建一个 6 对 1 的多路复用器。当
sel介于 0 和 5 之间时,选择相应的数据输入。否则,输出 0。数据输入和输出均为 4 位宽。小心推断闩锁(参见。always_if2)
// synthesis verilog_input_version verilog_2001
module top_module (
input [2:0] sel,
input [3:0] data0,
input [3:0] data1,
input [3:0] data2,
input [3:0] data3,
input [3:0] data4,
input [3:0] data5,
output reg [3:0] out );//
always@(*) begin // This is a combinational circuit
case(sel)
0:out = data0;
1:out = data1;
2:out = data2;
3:out = data3;
4:out = data4;
5:out = data5;
default:out = 4'b0000;
endcase
end
endmodule
ps:
- 二进制数以
b为前缀,如4'b1010表示4位的二进制数1010。 - 十六进制数以
h为前缀,如8'h1A表示8位的十六进制数1A。 - 十进制数则不需要前缀,直接写出数字即可,如
10就是表示十进制的10。也可以用4'd10
2.4.6 Priority encoder
问题陈述:
优先级编码器是一种组合电路,当给定输入位矢量时,输出矢量中前
1位的位置。例如,给定输入8'b10010000的 8 位优先级编码器将输出3'd4,因为 bit[4] 是第一个高电平位。构建 4 位优先级编码器。对于此问题,如果输入位均不高(即输入为零),则输出为零。请注意,一个 4 位数字有 16 种可能的组合。
module top_module (
input [3:0] in,
output reg [1:0] pos );
always@(*)begin
case(1)
in[0]:pos=2'd0;
in[1]:pos=2'd1;
in[2]:pos=2'd2;
in[3]:pos=2'd3;
default:pos=2'd0;
endcase
end
endmodule
ps:在这段代码中case(1)的意图是匹配值为1(true)的第一个输入位。尽管这种用法在语法上是允许的,并且能够产生预期的行为,但它是一种非常规写法,并且可能在实现优先级编码器时产生误解。这里的关键点在于,Verilog中的case语句是按从上到下的顺序执行匹配的。因此,即使多个输入位都是1,只有最高位的“1”对应的动作会被执行,它实现了优先级的效果,其中in[3]作为最高位拥有最高的优先级,in[0]作为最低位拥有最低的优先级。
2.4.7 Priority encoder with casez
问题陈述:
为 8 位输入构建优先级编码器。给定一个 8 位向量,输出应报告向量中的第一个(最低有效)位,即
1。如果输入向量没有高位,则报告零。例如,输入8'b10010000应输出3'd4,因为 bit[4] 是第一个高电平位。从上一个练习(always_case2),案件陈述中将有 256 个案例。如果 case 语句中的案例项支持 don-care 位,我们可以将其减少到 9 个案例。这就是
情况z的用途:它将值为z的位视为比较中的 don-care。例如,这将实现上一个练习中的 4 输入优先级编码器:
always @(*) begin casez (in[3:0]) 4'bzzz1: out = 0; // in[3:1] can be anything 4'bzz1z: out = 1; 4'bz1zz: out = 2; 4'b1zzz: out = 3; default: out = 0; endcase endcase 语句的行为就像按顺序检查每个项目一样(实际上,这是一个大的组合逻辑函数)。请注意,某些输入(例如,
4'b1111)将匹配多个事例项。选择第一个匹配项(因此4'b1111匹配第一个项目,out = 0,但不匹配后面的任何项目)。
- 还有一个类似的
案例,将x和z都视为不在乎。我认为在casez上使用它没有多大意义。- 数字
?是z的同义词。所以2'bz0和2'b?0相同明确指定优先级行为而不是依赖案例项的顺序可能不太容易出错。例如,如果对某些事例项进行了重新排序,则以下行为仍将相同,因为任何位模式最多只能匹配一个事例项:
casez (in[3:0]) 4'bzzz1: ... 4'bzz10: ... 4'bz100: ... 4'b1000: ... default: ... endcase
// synthesis verilog_input_version verilog_2001
module top_module (
input [7:0] in,
output reg [2:0] pos );
always @(*) begin
casez (in)
8'bzzzzzzz1:pos=3'd0;
8'bzzzzzz1z:pos=3'd1;
8'bzzzzz1zz:pos=3'd2;
8'bzzzz1zzz:pos=3'd3;
8'bzzz1zzzz:pos=3'd4;
8'bzz1zzzzz:pos=3'd5;
8'bz1zzzzzz:pos=3'd6;
8'b1zzzzzzz:pos=3'd7;
default: pos=3'd0;
endcase
end
endmodule
2.4.8 Avoiding latches
问题陈述:
假设您正在构建一个电路来处理来自游戏的 PS/2 键盘的扫描码。给定接收到的最后两个字节的扫描码,您需要指示键盘上的一个箭头键是否被按下。这涉及一个相当简单的映射,可以实现为具有四个案例的案例语句(或if-elseif)。
扫描码 [15:0] 箭头键 16'他06B向左箭头 16'他072向下箭头 16'他074向右箭头 16'他075向上箭头 别的东西 没有 您的电路有一个 16 位输入和四个输出。构建此电路,以识别这四个扫描码并断言正确的输出。
为避免产生锁存器,必须在所有可能条件下为所有输出分配一个值(另请参阅always_if2).仅仅有一个
默认案例是不够的。您必须在所有四种情况和默认情况下为所有四个输出分配一个值。这可能涉及大量不必要的键入。解决此问题的一种简单方法是在 case 语句之前为输出分配一个“默认值”:always @(*) begin up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0; case (scancode) ... // Set to 1 as necessary. endcase end这种代码风格可确保在所有可能的情况下为输出分配一个值 (0),除非 case 语句覆盖赋值。这也意味着
默认:案例项变得不必要。提醒: 逻辑合成器生成一个组合电路,其行为与代码描述的内容等效。硬件不会按顺序“执行”代码行。
// synthesis verilog_input_version verilog_2001
module top_module (
input [15:0] scancode,
output reg left,
output reg down,
output reg right,
output reg up );
always @(*) begin
left = 0;
down = 0;
right = 0;
up = 0;
case (scancode)
16'he06b: left = 1;
16'he072: down = 1;
16'he074: right = 1;
16'he075: up = 1;
endcase
end
endmodule
2.5 More Verilog Features
2.5.1 Conditional ternary operator
问题陈述:
给定四个无符号数字,找到最小值。无符号数字可以与标准比较运算符(a < b)进行比较。使用条件运算符创建双向最小电路,然后组合其中的几个电路以创建 4 向最小电路。您可能需要一些用于中间结果的有线向量。
module top_module (
input [7:0] a, b, c, d,
output [7:0] min);//
wire [7:0] m,n;
assign m = a<b?a:b;
assign n = c<d?c:d;
assign min = m<n?m:n;
// assign intermediate_result1 = compare? true: false;
endmodule
2.5.2 Reduction operators
问题陈述:
奇偶校验通常用作通过不完美信道传输数据时检测错误的简单方法。创建一个电路,该电路将计算 8 位字节的奇偶校验位(这将向字节添加第 9 位)。我们将使用“偶数”奇偶校验,其中奇偶校验位只是所有 8 个数据位的 XOR。
module top_module (
input [7:0] in,
output parity);
assign parity = ^in;
endmodule
2.5.3 Reduction:Even wider gates
问题陈述:
构建具有 100 个输入的组合电路,
in[99:0]。有 3 个输出:
- out_and:100 输入 AND 门的输出。
- out_or:100 输入 OR 门的输出。
- out_xor:100 输入 XOR 门的输出。
module top_module(
input [99:0] in,
output out_and,
output out_or,
output out_xor
);
assign out_and = ∈
assign out_or = |in;
assign out_xor = ^in;
endmodule
2.5.4 Combinational for-loop:Vector reversal 2
问题陈述:
给定一个 100 位输入向量 [99:0],反转其位顺序。
module top_module(
input [99:0] in,
output [99:0] out
);
always @(*) begin
for (integer i = 0;i<=99 ;i++ ) begin
out[i]=in[99-i];
end
end
endmodule
ps:verilog里有的不支持int,规范应该用integer
2.5.5 ombinational for-loop: 255-bit population count
问题陈述:
“总体计数”电路计算输入向量中“1”的数量。为 255 位输入向量构建总体计数电路。
module top_module(
input [254:0] in,
output [7:0] out );
always @(*) begin
out=0;
for (integer i=0 ; i<255;i++ ) begin
out=in[i]?out+1:out;
end
end
endmodule
2.5.6 Generate for-loop: 100-bitbinary adder 2
问题陈述:
通过实例化 100 个完整加法器来创建一个 100 位二进制纹波进位加法器。加法器将两个 100 位数字和一个进位数相加,以生成 100 位求和并执行。为了鼓励您实际实例化完整加法器,还要在波纹进位加法器中输出每个完整加法器的进位量。cout[99] 是最后一个完整加法器的最终结转,也是您通常看到的结转。
module top_module(
input [99:0] a, b,
input cin,
output [99:0] cout,
output [99:0] sum );
always @(*) begin
for (integer i = 0;i<100 ;i++ ) begin
if(i==0)
{cout[0], sum[0]} = a[0] + b[0] + cin;
else
{cout[i], sum[i]} = a[i] + b[i] + cout[i-1];
end
end
endmodule
2.5.7 Generate for-loop: 100-digitBCD adder
问题陈述:
您将获得一个名为
bcd_fadd的 BCD 一位数加法器,该加法器将两个 BCD 数字和进位相加,并生成总和和进位。module bcd_fadd ( input [3:0] a, input [3:0] b, input cin, output cout, output [3:0] sum );实例化
100 个 bcd_fadd副本以创建一个 100 位 BCD 纹波进位加法器。您的加法器应添加两个 100 位 BCD 数字(打包到 400 位向量中)和一个进位以生成 100 位总和并执行。
module top_module(
input [399:0] a, b,
input cin,
output cout,
output [399:0] sum );
wire [99:0]temp ;
genvar i;
generate
for (i = 0;i<100 ;i++ ) begin:bcd_fadd
if(i==0)
bcd_fadd bcdinst(
.a(a[3:0]),
.b(b[3:0]),
.cin(cin),
.cout(temp[0]),
.sum(sum[3:0])
);
else
bcd_fadd bcdinst(
.a(a[4*i+3:4*i]),
.b(b[4*i+3:4*i]),
.cin(temp[i-1]),
.cout(temp[i]),
.sum(sum[4*i+3:4*i])
);
end
endgenerate
assign cout = temp[99];
endmodule
3.Circuits
3.1 Combinational Logic
3.1.1 Basic GatesW
3.1.1.1 Wire
问题陈述:
实现以下电路:
module top_module (
input in,
output out);
assign out = in;
endmodule
ps:现在开始就是让之前学习的代码和实际电路图对应起来。也是个不错的复习机会。
3.1.1.2 GND
问题陈述:
实现以下电路:
module top_module (
output out);
assign out = 0;
endmodule
ps:verilog里不指定的数字默认为10进制。‘2’b11’是二进制表示的3,2代表位宽b代表二进制。再比如‘8‘hAB’(一个16进制4位宽bit)。也可不指定位宽比如’’b11‘。但是可读性差
3.1.1.3 NOR
问题陈述:
实现以下电路:
module top_module (
input in1,
input in2,
output out);
assign out = ~(in1|in2);
endmodule
ps:逻辑门复习见逻辑门 - 维基百科
3.1.1.4 Another gate
问题陈述:
实现以下电路:
module top_module (
input in1,
input in2,
output out);
assign out = (in1&(~in2));
endmodule
3.1.1.5 Two gates
问题陈述:
实现以下电路:
module top_module (
input in1,
input in2,
input in3,
output out);
assign out = (in3^(~(in1^in2)));
endmodule
3.1.1.6 More logic gates
问题陈述:
好的,让我们尝试同时构建几个逻辑门。构建一个具有两个输入
(a和b)的组合电路。有 7 个输出,每个输出都有一个逻辑门驱动它:
- out_and:A 和 B
- out_or:a 或 b
- out_xor:A xor b
- out_nand: a nand b
- out_nor:A 也不是 B
- out_xnor: a xnor b
- out_anotb:A 而不是 B
module top_module(
input a, b,
output out_and,
output out_or,
output out_xor,
output out_nand,
output out_nor,
output out_xnor,
output out_anotb
);
assign out_and = a&b;
assign out_or = a|b;
assign out_xor = a^b;
assign out_nand = ~(a&b);
assign out_nor = ~(a|b);
assign out_xnor = ~(a^b);
assign out_anotb = a&(~b);
endmodule
3.1.1.7 7420 chip
问题陈述:
7400 系列集成电路是一系列数字芯片,每个芯片都有几个门。7420 是一款具有两个 4 输入 NAND 门的芯片。
创建一个与 7420 芯片具有相同功能的模块。它有 8 个输入和 2 个输出。
module top_module (
input p1a, p1b, p1c, p1d,
output p1y,
input p2a, p2b, p2c, p2d,
output p2y );
assign p1y = ~(p1a&p1b&p1c&p1d);
assign p2y = ~(p2a&p2b&p2c&p2d);
endmodule
3.1.1.8 Truth tables
问题陈述:
在前面的练习中,我们使用了简单的逻辑门和几个逻辑门的组合。这些电路是组合电路的示例。组合意味着电路的输出是仅其输入的函数(在数学意义上)。这意味着对于任何给定的输入值,只有一个可能的输出值。因此,描述组合函数行为的一种方法是显式列出输入的每个可能值的输出应该是什么。这是一个真值表。
对于 N 个输入的布尔函数,有 2 个N可能的输入组合。真值表的每一行都列出了一个输入组合,因此始终有 2 个N行。输出列显示每个输入值的输出应为什么。
排 输入 输出 数 x3 x2 x1 f 0 0 0 0 0 1 0 0 1 0 2 0 1 0 1 3 0 1 1 1 4 1 0 0 0 5 1 0 1 1 6 1 1 0 0 7 1 1 1 1 上面的真值表适用于三输入、一输出函数。它有 8 行对应 8 种可能的输入组合,以及一列输出列。有四种输出为 1 的输入组合,以及四种输出为 0 的输入组合。
从真值表合成电路
假设我们想构建上面的电路,但我们只能使用一组标准逻辑门。 您将如何构建任意逻辑函数(表示为真值表)?
创建实现真值表函数的电路的一种简单方法是以乘积和形式表示函数。乘积 的总和(表示 OR)(表示 AND)表示在真值表的每行使用一个 N 输入 AND 门(用于检测输入何时与每行匹配),后跟一个 OR 门,该门仅选择导致“1”输出的行。
对于上面的示例,如果输入与第 2 行或第 3 行或第 5 行或第 7 行匹配,则输出为“1”(这是一个 4 输入的 OR 门)。如果 x3=0 和 x2=1 和 x1=0,则输入与第 2 行匹配(这是一个 3 输入的 AND 门)。因此,这个真值表可以通过使用 4 个 OR 门以规范形式实现。
一点练习
创建一个实现上述真值表的组合电路。
module top_module(
input x3,
input x2,
input x1, // three inputs
output f // one output
);
assign f = (x2&(~x3))|(x1&x3);
endmodule
ps:真值表推逻辑门电路见布尔代数 - 维基百科
3.1.1.9 Two-bit equality
问题陈述:
摘自 2015 年期中试题 1k
创建一个具有两个 2 位输入
A[1:0]和B[1:0]的电路,并生成输出z。如果A = B,则 z的值应为 1,否则z应为 0。
module top_module ( input [1:0] A, input [1:0] B, output z );
assign z=((A[0]==B[0])&(A[1]==B[1]))?1:0;
endmodule
3.1.1.10 Simple circuit A
问题陈述:
摘自2015年期中考试第4题
模块 A 应该实现函数
z = (x^y) & x。实现此模块。
module top_module (input x, input y, output z);
assign z = (x^y)&x;
endmodule
3.1.1.11 Simple circuit B
问题陈述:
摘自2015年期中考试第4题
电路B可以用以下仿真波形来描述:
module top_module ( input x, input y, output z );
assign z=~x^y;
endmodule
3.1.1.12 Combine circuits A and B
问题陈述:
摘自2015年期中考试第4题
请参阅此处使用的子模块的mt2015_q4a和mt2015_q4b。顶层设计由子电路 A 和 B 的两个实例组成,如下所示。
module top_module (input x, input y, output z);
wire A,B;
assign A = (x^y)&x;
assign B = ~(x^y);
assign z = (A|B)^(A&B);
endmodule
3.1.1.13 Ring or vibrate?
问题陈述:
假设您正在设计一个电路来控制手机的铃声和振动电机。每当电话需要从来电 () 响铃时,您的电路必须打开铃声 () 或电机 (),但不能同时打开两者。如果手机处于振动模式(),请打开电机。否则,请打开铃声。
input ring``output ringer = 1``output motor = 1``input vibrate_mode = 1尝试仅使用语句,看看是否可以将问题描述转换为逻辑门的集合。
assign设计提示: 在设计电路时,人们经常不得不“向后”考虑问题,从输出开始,然后向后向输入工作。这通常与人们思考(顺序、命令式)编程问题的方式相反,在编程问题中,人们会先查看输入,然后决定一个动作(或输出)。对于顺序程序,人们通常会想“如果(输入是___)那么(输出应该是___)”。另一方面,硬件设计人员经常认为“当(输入是___)时(输出应该是___)”。
上面的问题描述是用适合软件编程的命令式形式编写的(如果响,那就这样做),所以你必须把它转换成更适合硬件实现的声明式形式()。能够思考和转换两种风格是硬件设计所需的最重要的技能之一。
assign ringer = ___
module top_module (
input ring,
input vibrate_mode,
output ringer, // Make sound
output motor // Vibrate
);
assign ringer = ((vibrate_mode==0)&ring)?1:0;
assign motor = ((vibrate_mode==1)&ring)?1:0;
endmodule
ps:这题最大问题就是题目说的不清楚。大概就是ring代表来电话。然后mode决定是ringer还是motor。
3.1.1.14 Thermostat
问题陈述:
加热/冷却恒温器控制加热器(冬季)和空调(夏季)。实施一个电路,根据需要打开和关闭加热器、空调和鼓风机风扇。
恒温器可以处于两种模式之一:加热 () 和冷却 ()。 在加热模式下,当温度太低时打开加热器(),但不要使用空调。在冷却模式下,当天气太热时打开空调(),但不要打开加热器。当加热器或空调打开时,还要打开风扇以使空气循环。此外,用户还可以要求风扇打开(),即使加热器和空调已关闭。
mode = 1``mode = 0``too_cold = 1``too_hot = 1``fan_on = 1尝试仅使用语句,看看是否可以将问题描述转换为逻辑门的集合。
assign
module top_module (
input too_cold,
input too_hot,
input mode,
input fan_on,
output heater,
output aircon,
output fan
);
assign heater = mode & too_cold & (~aircon);
assign aircon = (~mode) & (~heater) & too_hot;
assign fan = aircon | heater | fan_on;
endmodule
ps:鲨臂题目,说也说不清楚。
3.1.1.15 3-bit population count
问题陈述:
“总体计数”电路计算输入向量中“1”的数量。为 3 位输入向量构建总体计数电路。
module top_module(
input [2:0] in,
output [1:0] out );
always @(*) begin
out = 0;
for (integer i = 0;i<3 ;i++ ) begin
if(in[i]==1)
out++;
end
end
endmodule
3.1.1.16 Gates and vectors
问题陈述:
在[3:0]中,您将得到一个四位输入向量。我们想知道每个比特与其邻居之间的一些关系:
- out_both:此输出向量的每个位都应指示相应的输入位及其左侧的相邻位(较高索引 )是否为“1”。例如,
out_both[2]应指示in[2]和in[3]是否都是 1。由于in[3]的左边没有邻居,所以答案是显而易见的,所以我们不需要知道out_both[3]。- out_any:此输出向量的每个位都应指示任何相应的输入位及其右侧的邻居是否为“1”。例如,
out_any[2]应指示in[2]或in[1]是否为 1。由于in[0]右边没有邻居,所以答案是显而易见的,所以我们不需要知道out_any[0]。- out_different:此输出向量的每个位都应指示相应的输入位是否与左侧的相邻位不同。例如,
out_different[2]应指示in[2]是否与in[3]不同。对于这部分,将向量视为环绕,因此in[3]左边的邻居是in[0]。
module top_module(
input [3:0] in,
output [2:0] out_both,
output [3:1] out_any,
output [3:0] out_different );
assign out_both[2:0] = in[2:0]&in[3:1];
assign out_any[3:1] = in[3:1]|in[2:0];
assign out_different[3:0] = in[3:0]^{in[0],in[3:1]};
endmodule
ps:题目描述有问题,也可能是翻译的问题。那个any是或不是及。
3.1.1.17 Even longer vectors
问题陈述:
另请参阅较短的版本:门和矢量.
您将获得一个 100 位输入向量 in[99:0]。我们想知道每个比特与其邻居之间的一些关系:
- out_both:此输出向量的每个位都应指示相应的输入位及其左侧的相邻位是否为“1”。例如,
out_both[98]应指示in[98]和in[99]是否均为 1。由于in[99]左边没有邻居,答案是显而易见的,所以我们不需要知道out_both[99]。- out_any:此输出向量的每个位都应指示任何相应的输入位及其右侧的邻居是否为“1”。例如,
out_any[2]应指示in[2]或in[1]是否为 1。由于in[0]右边没有邻居,所以答案是显而易见的,所以我们不需要知道out_any[0]。- out_different:此输出向量的每个位都应指示相应的输入位是否与左侧的相邻位不同。例如,
out_different[98]应指示in[98]是否与in[99]不同。对于这部分,将向量视为环绕,因此in[99]左边的邻居是in[0]。
module top_module(
input [99:0] in,
output [98:0] out_both,
output [99:1] out_any,
output [99:0] out_different );
assign out_both[98:0] = in[98:0]&in[99:1];
assign out_any[99:1] = in[99:1]|in[98:0];
assign out_different[99:0] = in[99:0]^{in[0],in[99:1]};
endmodule
3.1.2 Multiplexers
3.1.2.1 2-to-1 multiplexer
问题陈述:
创建一个 1 位宽的 2 对 1 多路复用器。当 sel=0 时,选择 a。当 sel=1 时,选择 b。
module top_module(
input a, b, sel,
output out );
assign out = sel?b:a;
endmodule
3.1.2.2 2-to-1 bus multiplexer
问题陈述:
创建一个 100 位宽的 2 对 1 多路复用器。当 sel=0 时,选择 a。当 sel=1 时,选择 b。
module top_module(
input [99:0] a, b,
input sel,
output [99:0] out );
assign out = sel?b:a;
endmodule
3.1.2.3 9-to-1 multiplexer
问题陈述:
创建一个 16 位宽的 9 比 1 多路复用器。sel=0 选择 a,sel=1 选择 b,依此类推。 对于未使用的情况(sel=9 到 15),将所有输出位设置为“1”。
module top_module(
input [15:0] a, b, c, d, e, f, g, h, i,
input [3:0] sel,
output [15:0] out );
always @(*) begin
case (sel)
4'd0:out = a;
4'd1:out = b;
4'd2:out = c;
4'd3:out = d;
4'd4:out = e;
4'd5:out = f;
4'd6:out = g;
4'd7:out = h;
4'd8:out = i;
default: out = {16{1'b1}};
endcase
end
endmodule
3.1.2.4 256-to-1multiplexer
问题陈述:
创建一个 1 位宽、256:1 的多路复用器。256 个输入全部打包到一个 256 位输入向量中。sel=0 应选择
in[0],sel=1 选择位 in[1],sel=2 选择位 in[2],依此类推。
module top_module(
input [255:0] in,
input [7:0] sel,
output out );
always @(*) begin
for (integer i = 0;i<256 ;i++ ) begin
if(sel==i)
out=in[i];
end
end
endmodule
3.1.2.5 256-to-14-bit multiplexer
问题陈述:
创建一个 4 位宽、256:1 的多路复用器。256 个 4 位输入全部封装到单个 1024 位输入向量中。sel=0 应选择
位 in[3:0],sel=1 选择位 in[7:4],sel=2 选择位 in[11:8],依此类推。
module top_module(
input [1023:0] in,
input [7:0] sel,
output [3:0] out );
assign out=in[4*sel+:4];
endmodule
ps:
- 最基本的切片方式是指定一个固定的位范围,例如
vector[high:low]
wire [7:0] vector = 8'b10101010;
wire [3:0] lower_half = vector[3:0]; // 结果是 4'b1010
- Verilog-2001 引入了一种更灵活的切片表达式,允许您基于一个起始索引和一个宽度来提取子向量:
- 使用
+:从给定的起始位向上切片(增加位址)。 - 使用
-:从给定的起始位向下切片(减少位址)。
- 使用
wire [15:0] vector = 16'hf0f0;
wire [7:0] segment1 = vector[7 +: 8]; // 从位7开始向上(增大)的8位,结果是8'hf0
wire [7:0] segment2 = vector[8 -: 8]; // 从位8开始向下(减小)的8位,结果是8'h0f
- Verilog 允许您访问位向量的单一位,例如
vector[index],这将从向量vector中提取第index位的值。我觉得叫索引更合适。
wire [7:0] vector = 8'b10101010;
wire bit3 = vector[3]; // 结果是 1'b0
3.1.3 Arithmetic Circuits
3.1.3.1 Half adder
问题陈述:
创建一个半加法器。半加法器将两个位相加(无进位)并产生求和和进出。
module top_module(
input a, b,
output cout, sum );
assign sum = a^b;
assign cout = a&b;
endmodule
3.1.3.2 Full adder
问题陈述:
创建一个完整的加法器。一个完整的加法器将三位相加(包括进位)相加,并产生求和和进位。
module top_module(
input a, b, cin,
output cout, sum );
assign sum = a ^ b ^ cin;
assign cout = (a&b)|(a&cin)|(b&cin);
endmodule
3.1.3.3 3-bit binary adder
问题陈述:
现在您已经知道如何构建一个完整的加法器,请制作 3 个实例来创建一个 3 位二进制纹波纹进位加法器。加法器将两个 3 位数字和一个进位数相加,以生成 3 位求和并执行。为了鼓励您实际实例化完整加法器,还要在波纹进位加法器中输出每个完整加法器的进位量。cout[2] 是最后一个完整加法器的最终结转,也是您通常看到的结转。
module top_module(
input [2:0] a, b,
input cin,
output [2:0] cout,
output [2:0] sum );
genvar i;
generate
for (i =0 ;i<3 ;i++ ) begin:fadd
if(i==0)
fadd inst(a[0],b[0],cin,cout[0],sum[0]);
else
fadd inst(a[i],b[i],cout[i-1],cout[i],sum[i]);
end
endgenerate
endmodule
module fadd(
input a, b, cin,
output cout, sum );
assign sum = a ^ b ^ cin;
assign cout = (a&b)|(a&cin)|(b&cin);
endmodule
3.1.3.4 Adder
问题陈述:
实现以下电路:
(“FA”是一个完整的加法器)
module top_module(
input [3:0] x,
input [3:0] y,
output [4:0] sum);
wire [3:0] cout;
genvar i;
generate
for (i =0 ;i<4 ;i++ ) begin:fadd
if(i==0)
fadd inst(x[0],y[0],1'b0,cout[0],sum[0]);
else
fadd inst(x[i],y[i],cout[i-1],cout[i],sum[i]);
end
endgenerate
assign sum[4]=cout[3];
endmodule
module fadd(
input a, b, cin,
output cout, sum );
assign sum = a ^ b ^ cin;
assign cout = (a&b)|(a&cin)|(b&cin);
endmodule
3.1.3.5 Signed addition overflow
问题陈述:
假设您有两个 8 位 2 的补码数 a[7:0] 和 b[7:0]。这些数字相加以产生 s[7:0]。还要计算是否发生了(签名的)溢出。
module top_module(
input [7:0] a,
input [7:0] b,
output [7:0] s,
output overflow
); //
wire [7:0] cout;
genvar i;
generate
for (i =0 ;i<8 ;i++ ) begin:fadd
if(i==0)
fadd inst(a[0],b[0],1'b0,cout[0],s[0]);
else
fadd inst(a[i],b[i],cout[i-1],cout[i],s[i]);
end
endgenerate
assign overflow =cout[7]^cout[6];
endmodule
module fadd(
input a, b, cin,
output cout, sum );
assign sum = a ^ b ^ cin;
assign cout = (a&b)|(a&cin)|(b&cin);
endmodule
ps:补码溢出问题见什么是溢出?补码加法运算如何判断是否溢出?_用补码计算真值判断是否溢出-CSDN博客
3.1.3.6 100-bit binary adder
问题陈述:
创建一个 100 位二进制加法器。加法器将两个 100 位数字和一个进位数相加,以生成 100 位求和并执行。
module top_module(
input [99:0] a, b,
input cin,
output cout,
output [99:0] sum );
wire [99:0] temp;
assign {cout,sum} = a+b+cin;//拼接刚好cout接住了最高进位
// genvar i;
// generate
// for (i =0 ;i<100 ;i++ ) begin:fadd
// if(i==0)
// fadd inst(a[0],b[0],cin,temp[0],sum[0]);
// else
// fadd inst(a[i],b[i],temp[i-1],temp[i],sum[i]);
// end
// endgenerate
// assign cout =temp[99];
endmodule
module fadd(
input a, b, cin,
output cout, sum );
assign sum = a ^ b ^ cin;
assign cout = (a&b)|(a&cin)|(b&cin);
endmodule
ps:两种方法都可以
3.1.3.7 4-digit BCD adder
问题陈述:
您将获得一个名为
bcd_fadd的 BCD(二进制编码十进制)一位数加法器,该加法器将两个 BCD 数字相加并进位,并生成总和和进位。module bcd_fadd ( input [3:0] a, input [3:0] b, input cin, output cout, output [3:0] sum );实例化
bcd_fadd的 4 个副本以创建一个 4 位 BCD 纹波进位加法器。您的加法器应添加两个 4 位 BCD 数字(打包到 16 位向量中)和一个进位以生成 4 位总和并执行。
module top_module (
input [15:0] a, b,
input cin,
output cout,
output [15:0] sum );
wire [3:0] temp;
genvar i;
generate
for (i =0 ;i<4 ;i++ ) begin:bcd_fadd
if(i==0)
bcd_fadd inst(
a[3:0],
b[3:0],
cin,
temp[0],
sum[3:0]
);
else
bcd_fadd inst(
a[4*i+:4],
b[4*i+:4],
temp[i-1],
temp[i],
sum[4*i+:4]
);
end
endgenerate
assign cout = temp[3];
endmodule
3.1.4 Karnaugh Map to Circuit
3.1.4.1 3-variable
问题陈述:
实现下面 Karnaugh 地图描述的电路。
在编码之前尝试简化 k-map。尝试总和乘积和乘积总和形式。我们无法检查您是否具有 k-map 的最佳简化。但是我们可以检查你的约简是否等效,我们可以检查你是否可以将 k 图转换为电路。
module top_module(
input a,
input b,
input c,
output out );
assign out = a| b| c;
endmodule
3.1.4.2 4-variable
问题陈述:
ps:卡诺图不会。先跳过。以后再说
3.1.4.3 4-variable
问题陈述:
ps:卡诺图不会。先跳过。以后再说
3.1.4.4 4-variable
问题陈述:
ps:卡诺图不会。先跳过。以后再说
3.1.4.5 Minimum SOP and POS
问题陈述:
ps:卡诺图不会。先跳过。以后再说
3.1.4.6 Karnaugh map
问题陈述:
ps:卡诺图不会。先跳过。以后再说
3.1.4.7 Karnaugh map
问题陈述:
ps:卡诺图不会。先跳过。以后再说
3.1.4.8 K-map implemented witha multiplexer
问题陈述:
ps:卡诺图不会。先跳过。以后再说
3.2 Sequential Logic
3.2.1 Latches and Flip-Flops
3.2.1.1 D flip-flop
问题陈述:
D触发器是一种在时钟信号的(通常)正边沿存储位并定期更新的电路。
当使用时钟始终模块时,逻辑频率合成器会创建 D 触发器(请参阅总是块2).D 触发器是“组合逻辑的斑点后跟触发器”的最简单形式,其中组合逻辑部分只是一根线。
创建单个 D 触发器。
module top_module (
input clk, // Clocks are used in sequential circuits
input d,
output reg q );//
// Use a clocked always block
// copy d to q at every positive edge of clk
// Clocked always blocks should use non-blocking assignments
always @(posedge clk ) begin
q=d;
//q<=d;
end
endmodule
ps:两种赋值方式本质上在模拟时序电路时有以下区别:
- 非阻塞赋值(
<=):
非阻塞赋值语句在always块的每次激活时都会记下要执行的所有赋值操作,但是这些操作的结果直到当前的模拟时间步结束时才会同时更新。这就意味着,即使always块内有多个<=赋值语句,它们也是并行执行的,每个赋值的右边的表达式是在当前时间步开始时的值。
这种行为模拟了现实中硬件寄存器的行为——寄存器在时钟边沿捕获其输入值,然后在下一个时钟周期之前保持这个值不变。因此,非阻塞赋值通常用于时序逻辑,确保能够正确地模拟多个寄存器的行为。
- 阻塞赋值(
=):
阻塞赋值语句在遇到时会立即执行,当前语句执行完之后才会执行下一条语句。如果在always块中有多个阻塞赋值语句,后面的赋值可以看到前面的赋值的结果。
在时序电路中,如果使用阻塞赋值,则可能会导致后面的赋值看到由前面阻塞赋值产生的中间值,这不是我们希望的时钟边缘触发的行为。
3.2.1.2 D flip-flops
问题陈述:
创建 8 D flip-flops。所有 DFF 都应由
clk的正边沿触发。
module top_module (
input clk,
input [7:0] d,
output [7:0] q
);
always @(posedge clk ) begin
q<=d;
end
endmodule
3.2.1.3 DFF with reset
问题陈述:
创建具有高电平有效同步复位的 8 D 触发器。所有 DFF 都应由
clk的正边沿触发。
module top_module (
input clk,
input reset, // Synchronous reset
input [7:0] d,
output [7:0] q
);
always @(posedge clk ) begin
if(reset)
q<=8'b00000000;
else
q<=d;
end
endmodule
3.2.1.4 DFF with reset value
问题陈述:
创建具有高电平有效同步复位的 8 D 触发器。触发器必须重置为 0x34 而不是零。所有 DFF 都应由
clk的负边沿触发。
module top_module (
input clk,
input reset, // Synchronous reset
input [7:0] d,
output [7:0] q
);
always @(negedge clk ) begin
if(reset)
q<=8'h34;
else
q<=d;
end
endmodule
3.2.1.5 DFF with asynchronousreset
问题陈述:
创建具有高电平异步复位功能的 8 D 触发器。所有 DFF 都应由
clk的正边沿触发。
module top_module (
input clk,
input areset, // active high asynchronous reset
input [7:0] d,
output [7:0] q
);
always @(posedge clk or posedge areset) begin
if(areset)
q<=8'h00;
else
q<=d;
end
endmodule
ps:如果你打算上升沿采reset就if(reset),如果你下降沿采reset那就if(!reset)
3.2.1.6 DFF with byte enable
问题陈述:
创建 16 D 触发器。有时,仅修改一组触发器的一部分很有用。字节使能输入控制是否应在该周期内写入 16 个寄存器中的每个字节。
Byteena[1]控制上字节d[15:8],而byteena[0]控制下字节d[7:0]。
RESETN是同步/低电平有效复位。所有 DFF 都应由
clk的正边沿触发。
module top_module (
input clk,
input resetn,
input [1:0] byteena,
input [15:0] d,
output [15:0] q
);
always @(posedge clk ) begin
if(!resetn)
q<=16'h0000;
else
q<={(byteena[1]?d[15:8]:q[15:8]),(byteena[0]?d[7:0]:q[7:0])};
end
endmodule
3.2.1.7 D Latch
问题陈述:
实现以下电路:
请注意,这是一个闩锁,因此预计会发出关于推断闩锁的 Quartus 警告。
module top_module (
input d,
input ena,
output q);
always @(*) begin
if(ena)
q<=d;
end
endmodule
3.2.1.8 DFF
问题陈述:
实现以下电路:
module top_module (
input clk,
input d,
input ar, // asynchronous reset
output q);
always @(posedge clk or posedge ar) begin
if(ar)
q<='d0;
else
q<=d;
end
endmodule
3.2.1.9 DFF
问题陈述:
实现以下电路:
module top_module (
input clk,
input d,
input r, // asynchronous reset
output q);
always @(posedge clk) begin
if(r)
q<='d0;
else
q<=d;
end
endmodule
3.2.1.10 DFF+gate
问题陈述:
实现以下电路:
module top_module (
input clk,
input in,
output out);
always @(posedge clk ) begin
out = in ^ out ;
end
endmodule
3.2.1.11 Mux and DFF
问题陈述:
摘自 ECE253 2015 年期中问题 5
考虑下面的顺序电路:
假设您希望使用具有触发器和多路复用器的子模块的三个实例化,为此电路实现分层 Verilog 代码。为此子模块编写一个名为
top_module的 Verilog 模块(包含一个触发器和多路复用器)。
module top_module (
input clk,
input L,
input r_in,
input q_in,
output reg Q);
always @(posedge clk ) begin
Q=L?r_in:q_in;
end
endmodule
3.2.1.12 Mux and DFF
问题陈述:
考虑如下所示的n位移位寄存器电路:
为该电路的一个级编写一个名为top_module的Verilog模块,包括触发器和多路复用器。
module top_module (
input clk,
input w, R, E, L,
output Q
);
always @(posedge clk ) begin
Q=L?(R):(E?w:Q);
end
endmodule
3.2.1.13 DFFs and gates
问题陈述:
给定如图所示的有限状态机电路,假设在机器启动之前,D触发器最初被重置为零。
构建此电路。
module top_module (
input clk,
input x,
output z
);
wire q1,q2,q3;
always @(posedge clk ) begin
q1<=x^q1;
q2<=x&(~q2);
q3<=x|(~q3);
end
assign z=~(q1|q2|q3);
endmodule
3.2.1.14 Create circuit from truthtable
问题陈述:
JK 触发器具有以下真值表。实现仅带有 D 型触发器和栅极的 JK 触发器。注意:Qold是正时钟边沿之前的D触发器的输出。
J K Q 0 0 Qold 0 1 0 1 0 1 r 1 1 ~Qold
module top_module (
input clk,
input j,
input k,
output reg Q
);
wire D;
// 基于JK触发器的逻辑来定义D
assign D = (j & ~Q) | (~k & Q);
always @(posedge clk) begin
Q <= D;
end
endmoduler
3.2.1.15 Detect an edge
问题陈述:
对于 8 位矢量中的每个位,检测输入信号何时从一个时钟周期的 0 变为下一个时钟周期的 1(类似于正边沿检测)。输出位应设置为发生 0 到 1 转换后的周期。
以下是一些示例。为清楚起见,in[1] 和 pedge[1] 分别显示。
3.2.1.16 Detect both edges
问题陈述:
3.2.1.17 Edge capture register
问题陈述:
3.2.1.18 Dual-edge triggered flip-flop
问题陈述:
3.2.2 Counters
问题陈述:
3.2.3 Shift Registers
问题陈述:
3.2.4 More Circuits
问题陈述:
3.2.5 FiniteStateMachines
问题陈述:
3.3 Combinational Logic
2.5.7 Building Larger Circuits
问题陈述: