目录

1. Verilog Language

1.1 Basics

1.1.1 Simple wire

1.1.2 Four wires

1.1.3 Inverter

1.1.4 AND gate

1.1.5 NOR gate

1.1.6 XNOR gate

1.1.7 Declaring wires

1.1.8 7458 chip

1.2 Vectors

1.2.1 Vectors

1.2.2 Vectors in more detail

1.2.3 Vector part select

1.2.4 Bitwise operators

1.2.5 Four-input gates

1.2.6 Vector concatenation operator

1.2.7 Vector reversal

1.2.8 Replication operator

1.2.9 More replication

1.3 Modules:Hierarchy

1.3.1 Modules

1.3.2 Connecting ports by name

1.3.3 Connecting ports by name

1.3.4 Three modules

1.3.5 Modules and vectors

1.3.6 Adder 1 Module add

1.3.7 Adder 2 - Module fadd

1.3.8 Carry-select adder

1.3.9 Adder-subtractor

1.4 Procedures

1.4.1 Always blocks(combination)

1.4.2 Always blocks(clocked)

1.4.3 If statement

1.4.4 If statement latches

1.4.5 Case statement

1.4.6 Priority encoder

1.4.7 Priority encoder with casez

1.4.8 Avoiding latches

1.5 More Verilog Features

1.5.1 Conditional ternary operator

1.5.2 Reduction operators

1.5.3 Reduction: Even wider gates

1.5.4 Combinational for-loop:Vector reversal 2

1.5.5 Combinational for-loop:255bit population count

1.5.6 Generate for-loop:100bit binary adder2

1.5.7 Generate for-loop:100-Digit BCD adder


1. Verilog Language

1.1 Basics

1.1.1 Simple wire

Wire线网型

Create a module with one input and one output that behaves like a wire.

创建一个单输入单输出的模块,该行为类似于线连

Unlike physical wires, wires (and other signals) in Verilog are directional. This means information flows in only one direction, from (usually one) source to the sinks (The source is also often called a driver that drives a value onto a wire). In a Verilog "continuous assignment" (assign left_side = right_side;), the value of the signal on the right side is driven onto the wire on the left side. The assignment is "continuous" because the assignment continues all the time even if the right side's value changes. A continuous assignment is not a one-time event.

与物理连线不同,Verilog中的连线(或者信号)具有方向性。这意味着信息只向一个方向传输,从源端口流向接收端口(源也经常被成为将值驱动到线路上的驱动程序)。在Verilog的”连续赋值“中(assign left_side = right_side),右侧信号的值会被赋值到左侧信号的wire上。赋值是”连续的“,因为赋值一直在发生,只要右侧的值改变左侧是值也会立即改变。持续赋值不是一个时间的事件。

The ports on a module also have a direction (usually input or output). An input port is driven by something from outside the module, while an output port drives something outside. When viewed from inside the module, an input port is a driver or source, while an output port is a sink.

模块上的端口也有方向性(通常为输入端或输出端)。输入端口驱通过模块外部驱动,输出端口驱动其他模块。从模块内部看,输入端口是驱动信号或者源,而输出端口是接收端口

The diagram below illustrates how each part of the circuit corresponds to each bit of Verilog code. The module and port declarations create the black portions of the circuit. Your task is to create a wire (in green) by adding an assign statement to connect in to out. The parts outside the box are not your concern, but you should know that your circuit is tested by connecting signals from our test harness to the ports on your top_module.

下图说明了电路的每个部分如何对应于Verilog代码的每个比特位。模块和端口声明创建了电路图中的黑色部分。您的任务是通过添加一个assign语句来连接输入in和输出out。黑色方框外部的部分不是你所关心的内容,单你应该知道,你的电路时通过将我们的测试线束的信号连接到top_module上的端口来进行测试的。

In addition to continuous assignments, Verilog has three other assignment types that are used in procedural blocks, two of which are synthesizable. We won't be using them until we start using procedural blocks.

除了连续赋值,Verilog还有三种其他的用于程序块的赋值类型,其中两种是可综合的。我们在开始使用程序块之前,先不使用他们。

1.1.2 Four wires

Create a module with 3 inputs and 4 outputs that behaves like wires that makes these connections:

创建一个具有3输入4输出的模块,该模块功能实现下述线网连接:

a -> w
b -> x
b -> y
c -> z

The diagram below illustrates how each part of the circuit corresponds to each bit of Verilog code. From outside the module, there are three input ports and four output ports.

下图说明电路的每个部分对应Verilog的每个比特位。从模块外部看,有3个输入端口和4个输出端口。

When you have multiple assign statements, the order in which they appear in the code does not matter. Unlike a programming language, assign statements ("continuous assignments") describe connections between things, not the action of copying a value from one thing to another.

当你有多个赋值语句时,他们出现在代码中的顺序不重要。与编程语言不同,Verilog的赋值语句(连续赋值语句)描述硬件之间的连接,而不是将一个变量值复制到另一个变量的操作。

One potential source of confusion that should perhaps be clarified now: The green arrows here represent connections between wires, but are not wires in themselves. The module itself already has 7 wires declared (named a, b, c, w, x, y, and z). This is because input and output declarations actually declare a wire unless otherwise specified. Writing input wire a is the same as input a. Thus, the assign statements are not creating wires, they are creating the connections between the 7 wires that already exist.

现在应该澄清的一个容易混淆的潜在的数据源:这里的绿色箭头代表wire之间的连接关系,但实际并不是导线。模块本身已经声明了7个wire的连接(a,b,c,w,x,y,z),这是因为在input和output声明已经声明是wire,若有单独声明则另外讨论。代码中,input wire a和input a是一样的。因此,assign语句不是在创建连线wires,而是在创建7个已经存在的wires之间的联系。

Expected solution length: Around 4 lines.

期望解决方案长度:4行。

1.1.3 Inverter

Create a module that implements a NOT gate.

创建一个实现NOT门(非门)的模块。

This circuit is similar to wire, but with a slight difference. When making the connection from the wire in to the wire out we're going to implement an inverter (or "NOT-gate") instead of a plain wire.

这个电路类似与wire,但是有细微区别。当从线in连接到线out时,我们期望实现一个逆变器(非门),而不是不同的wire连接

Use an assign statement. The assign statement will continuously drive the inverse of in onto wire out.

使用赋值语句,赋值语句将连续驱动in的逆赋值给out

Expected solution length: Around 1 line.

期待解决方案长度:1行

1.1.4 AND gate

Create a module that implements an AND gate.

创建一个实现AND gate(与门)的模块。

This circuit now has three wires (ab, and out). Wires a and b already have values driven onto them by the input ports. But wire out currently is not driven by anything. Write an assign statement that drives out with the AND of signals a and b.

这个电路有三条线(a,b和out)。wire a和b已经有输入端口驱动其数值。但是wire out目前不受任何驱动。写一个赋值语句,驱动out输出a和b的与计算。

Note that this circuit is very similar to the NOT gate, just with one more input. If it sounds different, it's because I've started describing signals as being driven (has a known value determined by something attached to it) or not driven by something. Input wires are driven by something outside the module. assign statements will drive a logic level onto a wire. As you might expect, a wire cannot have more than one driver (what is its logic level if there is?), and a wire that has no drivers will have an undefined value (often treated as 0 when synthesizing hardware).

注意,这个电路与NOT gate(非门)非常相似,只是多了一个输入。如果听起来不一样,是因为,我们已经开始描述信号驱动(有附加到他的某个东西决定的已知值),而不是通过某种东西驱动。输入线由模块外所驱动。赋值语句通过逻辑级驱动到线wire上。正如您说期望的,一个wire不能有多个驱动程序(如果有多个驱动,那驱动的逻辑是什么呢?),没有驱动程序的线路加ing有一个未定义的值(在综合硬件时通常被视为0)。

Expected solution length: Around 1 line.

期望解决方案长度:1行

1.1.5 NOR gate

Create a module that implements a NOR gate. A NOR gate is an OR gate with its output inverted. A NOR function needs two operators when written in Verilog.

创建一个实现NOR gate(或非门)的模块。或非门的输出是或门的倒置。在Verilog语言中,或非门函数需要两个操作符。

An assign statement drives a wire (or "net", as it's more formally called) with a value. This value can be as complex a function as you want, as long as it's a combinational (i.e., memory-less, with no hidden state) function. An assign statement is a continuous assignment because the output is "recomputed" whenever any of its inputs change, forever, much like a simple logic gate.

赋值语句驱动带有值的线(更正式的叫法是:网)。这个值可以是你想要的任意复杂的函数,只要他是一个组合函数(即无内存,无隐藏状态)。赋值语句是连续的赋值,因为只要输入input改变,输出output就会被重新计算,就像一个简单的逻辑门一样。

Expected solution length: Around 1 line.

期望解决方案长度:1行

1.1.6 XNOR gate

异或非门、同或门

Create a module that implements an XNOR gate.

创建一个模块,实现异或非门。

Expected solution length: Around 1 line.

期待解决方案长度:1行

1.1.7 Declaring wires

The circuits so far have been simple enough that the outputs are simple functions of the inputs. As circuits become more complex, you will need wires to connect internal components together. When you need to use a wire, you should declare it in the body of the module, somewhere before it is first used. (In the future, you will encounter more types of signals and variables that are also declared the same way, but for now, we'll start with a signal of type wire).

到目前为止,电路已经足够简单,输出是输入的简单函数。随着电路越来越复杂,你将需要用wire去把内部元件连接到一起。当你需要使用wire时,你应该在模块主体中,首次使用它之前,事先中声明。(在将来,你将会遇到更多类型的信号和变量,它们也以相同的方式被声明,但是现在,我们将从信号类型wire开始入手)。

举例:

In the above module, there are three wires (in, out, and not_in), two of which are already declared as part of the module's input and output ports (This is why you didn't need to declare any wires in the earlier exercises). The wire not_in needs to be declared inside the module. It is not visible from outside the module. Then, two NOT gates are created using two assign statements. Note that it doesn't matter which of the NOT gates you create first: You still end up with the same circuit.

在上述模块中,有三条wire(in,out和not_in),其中两个已经被声明为模块输入和输出端口的一部分(这就是为什么你不需要在早期练习中不需要声明的原因)。线not_in需要在模块内部被声明。not_in在模块外部是不可见的。然后,使用assign赋值语句创建两个NOT gate非门。先创建那个非门并不重要:你最终会综合出相同的电路。

练习:

Implement the following circuit. Create two intermediate wires (named anything you want) to connect the AND and OR gates together. Note that the wire that feeds the NOT gate is really wire out, so you do not necessarily need to declare a third wire here. Notice how wires are driven by exactly one source (output of a gate), but can feed multiple inputs.

实现以下电路。创建两条中间wire(命名任意),将AND与门和OR或门连接在一起。注意,为NOT非门提供的wire实际是wire out,所以你不必再声明第三根wire了。请注意wires是如何通过一个源来驱动的(门的输出),但可以馈送多个输入。

If you're following the circuit structure in the diagram, you should end up with four assign statements, as there are four signals that need a value assigned.

如果你遵循图中的电路结构,你应该使用4个assign,因为有4个信号需要赋值

(Yes, it is possible to create a circuit with the same functionality without the intermediate wires.)

(是的,可以在没有中间导线的情况下创建具有相同功能的电路。)

1.1.8 7458 chip

The 7458 is a chip with four AND gates and two OR gates. This problem is slightly more complex than 7420.

7458是一个有四个AND与门和两个OR或门的芯片。这个问题比7420稍微复杂一点。

Create a module with the same functionality as the 7458 chip. It has 10 inputs and 2 outputs. You may choose to use an assign statement to drive each of the output wires, or you may choose to declare (four) wires for use as intermediate signals, where each internal wire is driven by the output of one of the AND gates. For extra practice, try it both ways.

创建一个模块,使之与7458芯片具有相同的功能。你可以选择使用赋值语句去驱动每个输出线,你也可以选择声明四个wire作为中间信号,其中每个内部线由一个AND与门的输出驱动。作为额外联系,两种方法都试试。

Expected solution length: Around 2–10 lines.

期待解决方案长度:2-10行

1.2 Vectors

1.2.1 Vectors

Vectors are used to group related signals using one name to make it more convenient to manipulate. For example, wire [7:0] w; declares an 8-bit vector named w that is functionally equivalent to having 8 separate wires.

向量用于将相关信号进行分组,使用一个名称,使其操作更方便。例如,wire [7:0] w;声明了一个8位向量,命名为w,其功能相当于8条wire。

Notice that the declaration of a vector places the dimensions before the name of the vector, which is unusual compared to C syntax. However, the part select has the dimensions after the vector name as you would expect.

注意,与c语言不同,声明向量的时候将长度[7:0]放在向量的名称w之前。然而,正如你期待的那样,part select的尺寸再向量名称之后。

wire [99:0] my_vector; // Declare a 100-element vector

//该句声明一个包含100个元素的向量。

assign out = my_vector[10]; // Part-select one bit out of the vector

//只选择向量的一部分,只选择该向量的一个bit位的数值进行连续赋值,其中的第11根wire。

Build a circuit that has one 3-bit input, then outputs the same vector, and also splits it into three separate 1-bit outputs. Connect output o0 to the input vector's position 0, o1 to position 1, etc.

构建一个电路,具有3bit的输入,然后输出相同的向量,将其分成三个单独的比特位进行输出。将输出o0连接到输入向量的第0位,将o1连接到输入向量的第1位,以此类推。

In a diagram, a tick mark with a number next to it indicates the width of the vector (or "bus"), rather than drawing a separate line for each bit in the vector.

图中可知,旁边带有数字的标记表示向量(或总线)的位宽,而不是为向量中的每个位单独接线。

1.2.2 Vectors in more detail

Vectors are used to group related signals using one name to make it more convenient to manipulate. For example, wire [7:0] w; declares an 8-bit vector named w that is equivalent to having 8 separate wires.

向量用于将相关信号进行分组,使用一个名称,使其操作更方便。例如,wire [7:0] w;声明了一个8位向量,命名为w,其功能相当于8条wire。

Declaring Vectors

声明向量

Vectors must be declared:

向量必须被声明:

type [upper:lower] vector_name;

数据类型 [高位:低位] 向量名;

type specifies the datatype of the vector. This is usually wire or reg. If you are declaring a input or output port, the type can additionally include the port type (e.g., input or output) as well. Some examples:

type指定的向量类型,通常是wire型或reg型。如果你想声明输入或者输出端口,类型也可以包括端口类型(例如 input 或者 output)。举例说明:

wire [7:0] w;         // 8-bit wire
reg  [4:1] x;         // 4-bit reg
output reg [0:0] y;   // 1-bit reg that is also an output port (this is still a vector)
input wire [3:-2] z;  // 6-bit wire input (negative ranges are allowed)
output [3:0] a;       // 4-bit output wire. Type is 'wire' unless specified otherwise.
wire [0:7] b;         // 8-bit wire where b[0] is the most-significant bit.

The endianness (or, informally, "direction") of a vector is whether the the least significant bit has a lower index (little-endian, e.g., [3:0]) or a higher index (big-endian, e.g., [0:3]). In Verilog, once a vector is declared with a particular endianness, it must always be used the same way. e.g., writing vec[0:3] when vec is declared wire [3:0] vec; is illegal. Being consistent with endianness is good practice, as weird bugs occur if vectors of different endianness are assigned or used together.

向量的字节顺序(or,information,"direction")是指最低的有效位是否具有较低的下标(低位在前[3:0])。。。不会翻译了

与字节顺序一致是个好的编码习惯,如果不同顺序的字节进行赋值或一起使用,可能会出现奇怪的bug。

Implicit nets

隐式网

Implicit nets are often a source of hard-to-detect bugs. In Verilog, net-type signals can be implicitly created by an assign statement or by attaching something undeclared to a module port. Implicit nets are always one-bit wires and causes bugs if you had intended to use a vector. Disabling creation of implicit nets can be done using the `default_nettype none directive.

隐式网通常是难以察觉到的bug来源。再Verilog中,可以通过赋值语句或将未声明的内容附加到模块端口来创建隐式类型net-type类型的信号。隐式网通常是1比特的wire类型,如果你原本打算使用向量,则不出现bug。可以使用‘default_nettype none’指令禁用隐式网络的创建。

wire [2:0] a, c;     // Two vectors

assign a = 3'b101;    // a = 101

assign b = a;      // b = 1 implicitly-created wire

assign c = b;      // c = 001 <-- bug

my_module i1 (d,e);   // d and e are implicitly one-bit wide if not declared.
           // This could be a bug if the port was intended to be a vector.

Adding `default_nettype none would make the second line of code an error, which makes the bug more visible.

添加`default_nettype none将使第二行代码报错,这样会使错误更加明显。

Unpacked vs. Packed Arrays

未打包数组与打包数组

You may have noticed that in declarations, the vector indices are written before the vector name. This declares the "packed" dimensions of the array, where the bits are "packed" together into a blob (this is relevant in a simulator, but not in hardware). The unpacked dimensions are declared after the name. They are generally used to declare memory arrays. Since ECE253 didn't cover memory arrays, we have not used packed arrays in this course. See Date Types Part-X for more details.

你可能已经注意到,在声明中,向量的长度写在了向量名之前。这声明了数组打包的维度,其中bit位被打包在一起放进了blob(与仿真相关,但与硬件无关)。不打包的维度在向量名后面声明。他们通常用于声明内存组数。由于ECE253不包括内存数组,我们在本课程中不使用打包数组。更多细节请参见数据类型部分。

reg [7:0] mem [255:0];   // 256 unpacked elements, each of which is a 8-bit packed vector of reg.
reg mem2 [28:0];         // 29 unpacked elements, each of which is a 1-bit reg.

Accessing Vector Elements: Part-Select

访问向量元素:部分选择

Accessing an entire vector is done using the vector name. For example:

访问整个向量是用向量名完成的,例如:

assign w = a;

takes the entire 4-bit vector a and assigns it to the entire 8-bit vector w (declarations are taken from above). If the lengths of the right and left sides don't match, it is zero-extended or truncated as appropriate.

将4个bit位大小的向量a赋值给8个bit位大小的向量w(声明省略)。如果右侧向量的长度与左侧向量的长度不匹配,用0或者阶段来补齐剩下的比特位。

The part-select operator can be used to access a portion of a vector:

part-select操作可以访问向量的某个部分。

w[3:0]      // Only the lower 4 bits of w
x[1]        // The lowest bit of x
x[1:1]      // ...also the lowest bit of x
z[-1:-2]    // Two lowest bits of z
b[3:0]      // Illegal. Vector part-select must match the direction of the declaration.
b[0:3]      // The *upper* 4 bits of b.
assign w[3:0] = b[0:3];    // Assign upper 4 bits of b to lower 4 bits of w. w[3]=b[0], w[2]=b[1], etc.

A Bit of Practice

Build a combinational circuit that splits an input half-word (16 bits, [15:0] ) into lower [7:0] and upper [15:8] bytes.

创建一个组合电路,将输入的半个字节(16bits)分成低8位和高8位。

1.2.3 Vector part select

A 32-bit vector can be viewed as containing 4 bytes (bits [31:24], [23:16], etc.). Build a circuit that will reverse the byte ordering of the 4-byte word.

一个32比特的向量可以4个4个bit的看。创建一个电路,使4字节的字节顺序颠倒。

AaaaaaaaBbbbbbbbCcccccccDddddddd => DdddddddCcccccccBbbbbbbbAaaaaaaa

This operation is often used when the endianness of a piece of data needs to be swapped, for example between little-endian x86 systems and the big-endian formats used in many Internet protocols.

当一段数据的字节顺序需要交换时,使用此操作。例如,小端顺序的x86系统和许多internet协议中使用的大端数据格式之间进行交换。

1.2.4 Bitwise operators

Build a circuit that has two 3-bit inputs that computes the bitwise-OR of the two vectors, the logical-OR of the two vectors, and the inverse (NOT) of both vectors. Place the inverse of b in the upper half of out_not (i.e., bits [5:3]), and the inverse of a in the lower half.

构建一个电路,具有两个3比特的输入,并将两个向量的比特位进行或运算,两个向量进行逻辑或运算,并对两个向量进行非运算。将b进行颠倒,放在out_not高比特位上,将a颠倒放在低比特上。

Bitwise vs. Logical Operators

按位运算与逻辑运算符

Earlier, we mentioned that there are bitwise and logical versions of the various boolean operators (e.g., norgate). When using vectors, the distinction between the two operator types becomes important. A bitwise operation between two N-bit vectors replicates the operation for each bit of the vector and produces a N-bit output, while a logical operation treats the entire vector as a boolean value (true = non-zero, false = zero) and produces a 1-bit output.

前面,我们提到各种布尔运算符有按位运算和逻辑运算两个版本。使用向量的时候,这两种运算符之间的区别变得尤为重要。两个n位向量的按位运算,是复制向量的每个位进行操作,并产生n位的输出结果,而逻辑运算符是将整个向量是为布尔值(true为非0,false为0),并产生1位的输出结果。

Look at the simulation waveforms at how the bitwise-OR and logical-OR differ.

观察仿真波形,看看按位运算和逻辑运算之间的不同。

1.2.5 Four-input gates

Build a combinational circuit with four inputs, in[3:0].

创建4输入组合逻辑电路,in[3:0]。

There are 3 outputs:

  • out_and: output of a 4-input AND gate.
  • out_or: output of a 4-input OR gate.
  • out_xor: output of a 4-input XOR gate.

To review the AND, OR, and XOR operators, see andgate, norgate, and xnorgate.

复习与、或和异或运算符,与门、或门和异或门

See also: Even wider gates.

以及更多的门电路

1.2.6 Vector concatenation operator

向量连接运算符

Part selection was used to select portions of a vector. The concatenation operator {a,b,c} is used to create larger vectors by concatenating smaller portions of a vector together.

部分选择功能可与用于选择向量的一部分。连接运算符{a,b,c}可以将短的向量连接到一起创建长向量。

{3'b111, 3'b000} => 6'b111000
{1'b1, 1'b0, 3'b101} => 5'b10101
{4'ha, 4'd10} => 8'b10101010     // 4'ha and 4'd10 are both 4'b1010 in binary

Concatenation needs to know the width of every component (or how would you know the length of the result?). Thus, {1, 2, 3} is illegal and results in the error message: unsized constants are not allowed in concatenations.

连接需要知道每个部分的位宽(或者知道结果的位宽)。因此{1,2,3}是非法的,导致报错:在串联中不想允许使用未知大小的常量。

The concatenation operator can be used on both the left and right sides of assignments.

串联运算符用于赋值操作符的左侧,也可以用于右侧。

input [15:0] in;
output [23:0] out;
assign {out[7:0], out[15:8]} = in;         // Swap two bytes. Right side and left side are both 16-bit vectors.
assign out[15:0] = {in[7:0], in[15:8]};    // This is the same thing.
assign out = {in[7:0], in[15:8]};       // This is different. The 16-bit vector on the right is extended to// match the 24-bit vector on the left, so out[23:16] are zero.// In the first two examples, out[23:16] are not assigned.

A Bit of Practice

Given several input vectors, concatenate them together then split them up into several output vectors. There are six 5-bit input vectors: a, b, c, d, e, and f, for a total of 30 bits of input. There are four 8-bit output vectors: w, x, y, and z, for 32 bits of output. The output should be a concatenation of the input vectors followed by two 1 bits:

给定几个输入向量,将他们串联到一起,然后将他们分割出几个输出向量。有6个5比特的输入向量:a,b,c,d,e,f,一共30比特的输入。有4个8比特的输出向量:w,x,y,z,一共32比特的输出。输出应该是输入向量的串联,最后两位设成1。

1.2.7 Vector reversal

Given an 8-bit input vector [7:0], reverse its bit ordering.

给定一个8bit的输入向量[7:0],翻转它的顺序。

See also: Reversing a longer vector.

参见:翻转一个更长的向量。

1.2.8 Replication operator

The concatenation operator allowed concatenating together vectors to form a larger vector. But sometimes you want the same thing concatenated together many times, and it is still tedious to do something like assign a = {b,b,b,b,b,b};. The replication operator allows repeating a vector and concatenating them together:

连接操作符允许将向量连接在一起形成一个更长的向量。但有时候你想要很多相同的向量连接在一起,执行 assign a = {b,b,b,b,b,b}这样的语句很冗长。复制操作符可以重复的将向量连接到一起。

{num{vector}}

This replicates vector by num times. num must be a constant. Both sets of braces are required.

这个向量被复制了num次。num必须是常量。这两组花括号也是必须的。

Examples:

{5{1'b1}}           // 5'b11111 (or 5'd31 or 5'h1f)
{2{a,b,c}}          // The same as {a,b,c,a,b,c}
{3'd5, {2{3'd6}}}   // 9'b101_110_110. It's a concatenation of 101 with// the second vector, which is two copies of 3'b110.

A Bit of Practice

One common place to see a replication operator is when sign-extending a smaller number to a larger one, while preserving its signed value. This is done by replicating the sign bit (the most significant bit) of the smaller number to the left. For example, sign-extending 4'b0101 (5) to 8 bits results in 8'b00000101 (5), while sign-extending 4'b1101 (-3) to 8 bits results in 8'b11111101 (-3).

复制操作符的常见情况就是将小的值扩展成为一个较大的值,同时保留其符号值。这是通过复制左边最小的数字的符号(最有效位)来实现的。例如,将4'b0101 (5)拓展成8位,结果是8'b00000101(5)。将4'b1101 (-3)扩展成八位,结果是8'b11111101 (-3).

Build a circuit that sign-extends an 8-bit number to 32 bits. This requires a concatenation of 24 copies of the sign bit (i.e., replicate bit[7] 24 times) followed by the 8-bit number itself.

创建一个电路,将8bit的数字扩展成32位。使扩展出来的24位是8bit数据的复制。

1.2.9 More replication

Given five 1-bit signals (a, b, c, d, and e), compute all 25 pairwise one-bit comparisons in the 25-bit output vector. The output should be 1 if the two bits being compared are equal.

给定5个1bit的信号(a,b,c,d,e),在25位输出的向量中,分别依次对其进行比较,共25次。

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;

As the diagram shows, this can be done more easily using the replication and concatenation operators.

如图所示,使用复制和连接符可以更容易实现该功能。

  • The top vector is a concatenation of 5 repeats of each input
  • The bottom vector is 5 repeats of a concatenation of the 5 inputs

上边的向量是对每个输入复制5次后再串联

下边的向量是对5个输入序列的5次复制

1.3 Modules:Hierarchy

1.3.1 Modules

By now, you're familiar with a module, which is a circuit that interacts with its outside through input and output ports. Larger, more complex circuits are built by composing bigger modules out of smaller modules and other pieces (such as assign statements and always blocks) connected together. This forms a hierarchy, as modules can contain instances of other modules.

到目前为止,你已经熟悉了模块的概念,它是一个通过输入输出端口与模块外交互的电路 。更大更复杂的电路通过较小的模块和连接在一起的其他部分(如赋值语句或者always语句块)组合成更大的模块来构建的。这就形成了一个层次结构,模块中可以包含其他模块的实例。

The figure below shows a very simple circuit with a sub-module. In this exercise, create one instance of module mod_a, then connect the module's three pins (in1in2, and out) to your top-level module's three ports (wires ab, and out). The module mod_a is provided for you — you must instantiate it.

下图中展示里一个非常简单的子模块电路图。在本次练习中,创建一个mod_a的实例,然后将该模块的三个引脚(in1,in2,out)连接到顶层模块的三个端口(wires a,b,out)。模块mod_a是你所提供的模块,所以你必须将其例化。

When connecting modules, only the ports on the module are important. You do not need to know the code inside the module. The code for module mod_a looks like this:

当在进行模块间的连接时,只需要关注模块上的端口。不需要知道模块内部的代码是如何实现的。模块mod_a的代码如下:

module mod_a ( input in1, input in2, output out );// Module body
endmodule

The hierarchy of modules is created by instantiating one module inside another, as long as all of the modules used belong to the same project (so the compiler knows where to find the module). The code for one module is not written inside another module's body (Code for different modules are not nested).

模块的层次结构是通过在另一个模块中例化一个模块来创建的,只要所有使用的模块属于同一个项目(这样编译器就知道在哪里找到这个模块)。一个模块的代码不会写在另一个模块的主体中(不同的模块代码不会嵌套)。

You may connect signals to the module by port name or port position. For extra practice, try both methods.

你可以通过端口名称或端口位置将信号连接到模块。为了额外的联系,请尝试两种方法进行实现。

Connecting Signals to Module Ports

连接端口信号

There are two commonly-used methods to connect a wire to a port: by position or by name.

有两种常用的方式将线连接到端口:通过端口位置或者通过端口名字

By position

The syntax to connect wires to ports by position should be familiar, as it uses a C-like syntax. When instantiating a module, ports are connected left to right according to the module's declaration. For example:

应该熟悉通过端口位置将线连接到端口的语法,因为它与c语法相似。当你例化一个模块的时候,端口根据模块的声明从左到右进行连接。例如:

mod_a instance1 ( wa, wb, wc );

This instantiates a module of type mod_a and gives it an instance name of "instance1", then connects signal wa (outside the new module) to the first port (in1) of the new module, wb to the second port (in2), and wc to the third port (out). One drawback of this syntax is that if the module's port list changes, all instantiations of the module will also need to be found and changed to match the new module.

这将例化一个mod_a类型的模块,并将其命名位instance 1,然后将信号wa(新模块之外)连接到第一个端口in1,wb连接到第二个端口in2,,wc连接到第三个端口out。这种语法有一个缺点,如果你模块列表发生了变化,需要找到该模块的所有例化,并进行修改,以匹配新的模块。

By name

Connecting signals to a module's ports by name allows wires to remain correctly connected even if the port list changes. This syntax is more verbose, however.

通过名称将信号连接到模块端口,即使端口列表发生变化,导线也能够保持正确的连接。但是这种语法冗长。

mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) );

The above line instantiates a module of type mod_a named "instance2", then connects signal wa (outside the module) to the port named in1wb to the port named in2, and wc to the port named out. Notice how the ordering of ports is irrelevant here because the connection will be made to the correct name, regardless of its position in the sub-module's port list. Also notice the period immediately preceding the port name in this syntax.

上面的代码例化了一个mod_a模块类型,命名位instance 2,然后将wa连接到名字为in1的端口,wb连接到名字为in2的端口,wc连接到名字叫out的端口。注意,这里端口的顺序是不相关的,因为连接指向了正确的名称,而不管它在子模块端口给列表中的位置。还需要注意的是,在语法中,紧挨着端口名称前面的小句点。

1.3.2 Connecting ports by name

This problem is similar to the previous one (module). You are given a module named mod_a that has 2 outputs and 4 inputs, in that order. You must connect the 6 ports by position to your top-level module's ports out1out2abc, and d, in that order.

这个问题类似于前一个问题。给定一个名为mod_a的模块,有两个输出和四个输入,按此顺序。你必须按位置将6个端口依次连接到顶层模块的端口out1,out2,a,b,c,d。

You are given the following module:

module mod_a ( output, output, input, input, input, input );

1.3.3 Connecting ports by name

This problem is similar to module. You are given a module named mod_a that has 2 outputs and 4 inputs, in some order. You must connect the 6 ports by name to your top-level module's ports:

这个问题类似于模块。你被给定了模块mod_a,其具有2输出端口和4输入端口。你需要按照名称连接到顶层端口上:

You are given the following module:

module mod_a ( output out1, output out2, input in1, input in2, input in3, input in4);

1.3.4 Three modules

You are given a module my_dff with two inputs and one output (that implements a D flip-flop). Instantiate three of them, then chain them together to make a shift register of length 3. The clk port needs to be connected to all instances.

给定一个模块my_dff,具有两个输入和一个输出(实现D触发器)。例化三个该模块,将他们连接在一起实现一个长度为3的位移寄存器。

The module provided to you is:

module my_dff ( input clk, input d, output q );

Note that to make the internal connections, you will need to declare some wires. Be careful about naming your wires and module instances: the names must be unique.

注意,进行内部连接时,你需要声明一些线。在命名线和例化名称是需要小心:名称必须是唯一的。

 

1.3.5 Modules and vectors

This exercise is an extension of module_shift. Instead of module ports being only single pins, we now have modules with vectors as ports, to which you will attach wire vectors instead of plain wires. Like everywhere else in Verilog, the vector length of the port does not have to match the wire connecting to it, but this will cause zero-padding or trucation of the vector. This exercise does not use connections with mismatched vector lengths.

这个练习是module_shift的扩展。除了模块端口作为唯一信号引脚,现在我们还可以用向量的模块作为端口,这样你将连接的是向量而不再是导线。像Verilog中其他地方一样,端口的向量长度不必与它连接的导线位宽匹配,但这回导致向量使用0填充或者中断。本次来呢西不适用向量长度不匹配的连接。

You are given a module my_dff8 with two inputs and one output (that implements a set of 8 D flip-flops). Instantiate three of them, then chain them together to make a 8-bit wide shift register of length 3. In addition, create a 4-to-1 multiplexer (not provided) that chooses what to output depending on sel[1:0]: The value at the input d, after the first, after the second, or after the third D flip-flop. (Essentially, sel selects how many cycles to delay the input, from zero to three clock cycles.)

给定一个模块my_dff8,该模块有两个输入端口和一个输出端口。实现了一组8位宽D触发器。例化三个模块,将他们连接在一起实现一个长度为3的8位宽位移寄存器。另外,创建一个4to1多路复用器(未提供),根据sel[1:0]选择输出的内容:输入d的值,决定在哪个触发器的位置之后输出。(本质上,sel选择延迟输入多少个拍:0-3拍)

The module provided to you is: module my_dff8 ( input clk, input [7:0] d, output [7:0] q );

The multiplexer is not provided. One possible way to write one is inside an always block with a case statement inside. (See also: mux9to1v)

没有提供多路复用器,一种可能的方法是在always中写入case语句(参见mux9to1v)

1.3.6 Adder 1 Module add

You are given a module add16 that performs a 16-bit addition. Instantiate two of them to create a 32-bit adder. One add16 module computes the lower 16 bits of the addition result, while the second add16 module computes the upper 16 bits of the result, after receiving the carry-out from the first adder. Your 32-bit adder does not need to handle carry-in (assume 0) or carry-out (ignored), but the internal modules need to in order to function correctly. (In other words, the add16 module performs 16-bit a + b + cin, while your module performs 32-bit a + b).

给定一个模块add16,执行16位的加法操作。例化两个该模块,创建一个32位的加法器。其中一个16位加法器计算低16位的结果,在收到第一个加法器的进位信号之后,第二个加法器计算高16位的结果。32位加法器不需要处理其进位的输入(默认为0)或输出(忽略),但为了程序的正确,模块内部需要处理进位操作。(换句话说,16位的模块需要执行输入a,输出b和进位cin,而从外部看的32位加法器,只需要执行输入a和输出b即可)

Connect the modules together as shown in the diagram below. The provided module add16 has the following declaration:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

1.3.7 Adder 2 - Module fadd

In this exercise, you will create a circuit with two levels of hierarchy. Your top_module will instantiate two copies of add16 (provided), each of which will instantiate 16 copies of add1 (which you must write). Thus, you must write two modules: top_module and add1.

在本次练习中,你将创建一个具有两层结构的电路。你的top_module将会例化两个add16(已知),其中的每个将会例化16个add1(1位加法器,你需要自己编写)。因此,你需要编写两个模块:top_module和add1。

Like module_top, you are given a module add16 that performs a 16-bit addition. You must instantiate two of them to create a 32-bit adder. One add16 module computes the lower 16 bits of the addition result, while the second add16 module computes the upper 16 bits of the result. Your 32-bit adder does not need to handle carry-in (assume 0) or carry-out (ignored).

与module_top类似,给定一个add16模块实现16位加法器。你需要例化两个实现32位加法器。其中一个add16计算低16位,另外一个计算高16位。从外部看,你创建的32位加法器不需要进位输入(默认为0)和进位输出(忽略)端口。

Connect the add16 modules together as shown in the diagram below. The provided module add16 has the following declaration:

如下图所示,将add16模块连接在一起。所提供的16位加法器具有以下声明:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

Within each add16, 16 full adders (module add1, not provided) are instantiated to actually perform the addition. You must write the full adder module that has the following declaration:

每个16位加法器中,例化了16个1位加法器(未提供)来实现其功能。你需要编写完整的加法器模块,该模块具有以下声明:

module add1 ( input a, input b, input cin, output sum, output cout );

Recall that a full adder computes the sum and carry-out of a+b+cin.

回想一下,以为加法器需要计算sum和a+b+cin的进位输出carry_out

In summary, there are three modules in this design:

综上所述,本次设计包含三个模块:

  • top_module — Your top-level module that contains two of...
  • add16, provided — A 16-bit adder module that is composed of 16 of...
  • add1 — A 1-bit full adder module.

If your submission is missing a module add1, you will get an error message that says Error (12006): Node instance "user_fadd[0].a1" instantiates undefined entity "add1".

如果你缺少模块module add1,你会得到如下报错: Error (12006): Node instance "user_fadd[0].a1" instantiates undefined entity "add1".

1.3.8 Carry-select adder

One drawback of the ripple carry adder (See precious exercise) is that the delay for an adder to compute the carry out (from the carry-in, in the worst case) is fairly slow, and the second-stage adder cannot begin computing its carry-out until the first-stage adder has finished. This makes the adder slow. One improvement is a carry-select adder, shown below. The first-stage adder is the same as before, but we duplicate the second-stage adder, one assuming carry-in=0 and one assuming carry-in=1, then using a fast 2-to-1 multiplexer to select which result happened to be correct.

波纹进位加法器的缺点是加法器进位的延迟很慢(不好的是,从进位开始计算),并且第二阶段的加法器直到完成第一阶段的加法计算后,才能进行进位计算操作。

In this exercise, you are provided with the same module add16 as the previous exercise, which adds two 16-bit numbers with carry-in and produces a carry-out and 16-bit sum. You must instantiate three of these to build the carry-select adder, using your own 16-bit 2-to-1 multiplexer.

在本次练习中,你被给定一个与之前练习中相同的add16模块,该模块将两个16位数字与进位相加,并生成一个新的进位值。你需要例化三个该模块,可以使用你的16位2对1多路复用器实现创建一个进位选择加法器。

Connect the modules together as shown in the diagram below. The provided module add16 has the following declaration:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

1.3.9 Adder-subtractor

加减器

An adder-subtractor can be built from an adder by optionally negating one of the inputs, which is equivalent to inverting the input then adding 1. The net result is a circuit that can do two operations: (a + b + 0) and (a + ~b + 1). See Wikipedia if you want a more detailed explanation of how this circuit works.

加减器可以通过加法器构建,选中其中的一个输入求负,相当于对一个输入求反后再相加。最终的结果是电路可以进行两种操作:(a + b + 0) 和 (a + ~b + 1)。如果你想知道该电路如何工作的更详细的解释,请参考维基百科。

Build the adder-subtractor below.

构建下述加减法器:

You are provided with a 16-bit adder module, which you need to instantiate twice:

给定一个16位加法器模块,你需要对其进行两次例化:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );

Use a 32-bit wide XOR gate to invert the b input whenever sub is 1. (This can also be viewed as b[31:0] XORed with sub replicated 32 times. See replication operator.). Also connect the sub input to the carry-in of the adder.

当sub位1时,使用32位的异或门对输入b进行逆运算。(也可以看成b[31:0]与sub的32个赋值进行异或运算)还要将sub作为输入连接到加法器的进位。

1.4 Procedures

1.4.1 Always blocks(combination)

Since digital circuits are composed of logic gates connected with wires, any circuit can be expressed as some combination of modules and assign statements. However, sometimes this is not the most convenient way to describe the circuit. Procedures (of which always blocks are one example) provide an alternative syntax for describing circuits.

因为数字电路时由导线连接逻辑门电路而组成的,任何电路都可以描述成模块和赋值语句的组合。然而,有时这并不是描述电路最便捷的方法。进程(其中包括always语句块)提供了另一种描述电路的方法。

For synthesizing hardware, two types of always blocks are relevant:

对于综合出硬件电路,两种类型的always语句块时相关联的:

  • Combinational: always @(*)
  • Clocked: always @(posedge clk)

Combinational always blocks are equivalent to assign statements, thus there is always a way to express a combinational circuit both ways. The choice between which to use is mainly an issue of which syntax is more convenient. The syntax for code inside a procedural block is different from code that is outside. Procedural blocks have a richer set of statements (e.g., if-then, case), cannot contain continuous assignments*, but also introduces many new non-intuitive ways of making errors. (*Procedural continuous assignments do exist, but are somewhat different from continuous assignments, and are not synthesizable.)

组合逻辑的always语句块等效于赋值语句,因此这两种方法都可以用来描述组合电路。选择使用哪种方法主要问题是那种语句更方便。进程块内部的语法与外部的语法不同。程序块有丰富的语句集(例如if-then,case),不能包含连续赋值*,但是程序块也引入了一些新的不容易发现的错误方式。(*过程性连续赋值确实存在,但与连续赋值有些不同,其并不能进行综合)

For example, the assign and combinational always block describe the same circuit. Both create the same blob of combinational logic. Both will recompute the output whenever any of the inputs (right side) changes value.  assign out1 = a & b | c ^ d; always @(*) out2 = a & b | c ^ d;

例如,赋值和组合逻辑always语句块描述同一个电路。两者都创建了相同的组合逻辑。当输入改变时,两者都计算了输出值。

assign out1 = a & b | c ^ d;

always @(*) out2 = a & b | c ^ d;

For combinational always blocks, always use a sensitivity list of (*). Explicitly listing out the signals is error-prone (if you miss one), and is ignored for hardware synthesis. If you explicitly specify the sensitivity list and miss a signal, the synthesized hardware will still behave as though (*) was specified, but the simulation will not and not match the hardware's behaviour. (In SystemVerilog, use always_comb.)

对于组合逻辑always语句块而言,总是使用*作为敏感列表。依依列出敏感信号容易出错(假设你漏掉了一个敏感信号),并且在综合硬件时会被忽略。如果你一一罗列了敏感列表里的每个信号并且漏了一个,综合出的电路仍然会表现得像(*)一样,但是仿真时不会匹配硬件行为。(在SystemVerilog中,使用always_comb。)

A note on wire vs. reg: The left-hand-side of an assign statement must be a net type (e.g., wire), while the left-hand-side of a procedural assignment (in an always block) must be a variable type (e.g., reg). These types (wire vs. reg) have nothing to do with what hardware is synthesized, and is just syntax left over from Verilog's use as a hardware simulation language.

关于wire和reg的注意事项:赋值语句的左边必须是线网类型(例如wire),而过程赋值语句的左边必须是变量类型(例如reg)。这两种类型(wire 和 reg)与综合出的硬件电路无关,只是Verilog语言作为硬件仿真语言遗留下来的语法。

A bit of practice

Build an AND gate using both an assign statement and a combinational always block. (Since assign statements and combinational always blocks function identically, there is no way to enforce that you're using both methods. But you're here for practice, right?...)

使用赋值语句和组合逻辑always语句块构建一个AND门。(因为赋值语句和组合逻辑always语句块函数功能相同,没办法强制同时使用两种方法。但是你可以作为练习,对吧)

1.4.2 Always blocks(clocked)

For hardware synthesis, there are two types of always blocks that are relevant:

对于硬件综合,有两种always语句块的类型相似的:

  • Combinational: always @(*)
  • Clocked: always @(posedge clk)

Clocked always blocks create a blob of combinational logic just like combinational always blocks, but also creates a set of flip-flops (or "registers") at the output of the blob of combinational logic. Instead of the outputs of the blob of logic being visible immediately, the outputs are visible only immediately after the next (posedge clk).

时钟always语句块是创建一个组合逻辑的二进制对象,类似于组合逻辑always语句块,但同时也在组合逻辑二进制对象中输出处创建了一组触发器(或寄存器)。不像逻辑二进制模型那样是可视化的,这样的输出会在下一个时钟周期后才会触发(上升沿)。

Blocking vs. Non-Blocking Assignment

阻塞赋值与非阻塞赋值

There are three types of assignments in Verilog:

在Verilog中有三种类型的赋值:

  • Continuous assignments (assign x = y;). Can only be used when not inside a procedure ("always block").
  • 连续赋值(assign x = y;)。只能在非过程中使用(alway block)
  • Procedural blocking assignment: (x = y;). Can only be used inside a procedure.
  • 程序性阻塞赋值(x = y;)。只能在进程中使用。
  • Procedural non-blocking assignment: (x <= y;). Can only be used inside a procedure.
  • 程序性非阻塞赋值 (x <= y;)。只能在进程中使用

In a combinational always block, use blocking assignments. In a clocked always block, use non-blocking assignments. A full understanding of why is not particularly useful for hardware design and requires a good understanding of how Verilog simulators keep track of events. Not following this rule results in extremely hard to find errors that are both non-deterministic and differ between simulation and synthesized hardware.

在组合逻辑always语句块中,使用阻塞赋值。在有时钟的always语句块中,使用非阻塞赋值。充分理解为什么这么规定,其实对硬件设计帮助不大,但是可以很好的理解verilog仿真时如何跟踪时间的。不遵循此规则的结果是,很难发现仿真硬件和综合硬件之间不确定且不同的错误。

A bit of practice

Build an XOR gate three ways, using an assign statement, a combinational always block, and a clocked always block. Note that the clocked always block produces a different circuit from the other two: There is a flip-flop so the output is delayed.

使用三种方式创建一个异或门,赋值语句,组合always语句,时序always语句。注意,时序always语句会产生不同于其他两个电路的结果:产生一个触发器,使输出延迟一拍。

1.4.3 If statement

An if statement usually creates a 2-to-1 multiplexer, selecting one input if the condition is true, and the other input if the condition is false.

if语句常用来创建2选1多路选择器,如果条件为true,则选择第一个输入,如果条件位false,选择第二个输入

always @(*) beginif (condition) beginout = x;endelse beginout = y;end
end

This is equivalent to using a continuous assignment with a conditional operator:

这与带有条件的赋值操作符类似:

assign out = (condition) ? x : y;

However, the procedural if statement provides a new way to make mistakes. The circuit is combinational only if out is always assigned a value.

然而,id语句也会带来新的报错。只有当out被赋值,电路才是组合的。

A bit of practice

Build a 2-to-1 mux that chooses between a and b. Choose b if both sel_b1 and sel_b2 are true. Otherwise, choose a. Do the same twice, once using assign statements and once using a procedural if statement.

构建一个2:1多路选择器,选择a和b。如果sel_b1和sel_b2都为ture,则选择b。其他情况选择a。执行两次相同的操作,其中一次用赋值语句,另外一次在过程中使用if语句。

1.4.4 If statement latches

A common source of errors: How to avoid making latches

一个常见的错误:如何避免锁存。

When designing circuits, you must think first in terms of circuits:

当设计电路时,你首先需要从实际电路的角度去思考:

  • I want this logic gate
  • 想要什么逻辑门。
  • I want a combinational blob of logic that has these inputs and produces these outputs
  • 想要一个组合逻辑们,有哪些输入和哪些输出。
  • I want a combinational blob of logic followed by a set of flip-flops
  • 想要实现一个组合逻辑语句块,后面跟着一组触发器。

What you must not do is write the code first, then hope it generates a proper circuit.

不要上来就写代码,然后期望它生成一个预期的电路,这样会本末倒置。

  • If (cpu_overheated) then shut_off_computer = 1;
  • If (~arrived) then keep_driving = ~gas_tank_empty;

Syntactically-correct code does not necessarily result in a reasonable circuit (combinational logic + flip-flops). The usual reason is: "What happens in the cases other than those you specified?". Verilog's answer is: Keep the outputs unchanged.

语法正确的代码不一定会产生合理的电路(组合逻辑+触发器)。通常原因是:“在你的意料之外会产生其他问题。”Verilog的答案时:保持输出不变。(锁存)

This behaviour of "keep outputs unchanged" means the current state needs to be remembered, and thus produces a latch. Combinational logic (e.g., logic gates) cannot remember any state. Watch out for Warning (10240): ... inferring latch(es)" messages. Unless the latch was intentional, it almost always indicates a bug. Combinational circuits must have a value assigned to all outputs under all conditions. This usually means you always need else clauses or a default value assigned to the outputs.

这种保持输出不变的行为意味着电路状态需要寄存下来,从而产生一个锁存器。

Demonstration

The following code contains incorrect behaviour that creates a latch. Fix the bugs so that you will shut off the computer only if it's really overheated, and stop driving if you've arrived at your destination or you need to refuel.

下列代码是创建锁存器的错误行为。修正bug,你只会在过热时关闭电脑,或在到达目的地或者需要加油时才会停止驾驶。

always @(*) beginif (cpu_overheated)shut_off_computer = 1;
endalways @(*) beginif (~arrived)keep_driving = ~gas_tank_empty;
end

1.4.5 Case statement

Case statements in Verilog are nearly equivalent to a sequence of if-elseif-else that compares one expression to a list of others. Its syntax and functionality differs from the switch statement in C.

Verilog中的case语句几乎等同于if-elseif-else语句,用于将一个表达式与另一个表达式作比较。它的语法和功能不同于c语言中的switch语句。

always @(*) begin     // This is a combinational circuitcase (in)1'b1: begin out = 1'b1;  // begin-end if >1 statementend1'b0: out = 1'b0;default: out = 1'bx;endcase
end
  • The case statement begins with case and each "case item" ends with a colon. There is no "switch".case语句由case开始,每个case项以冒号结束。这里没有switch
  • Each case item can execute exactly one statement. This makes the "break" used in C unnecessary. But this means that if you need more than one statement, you must use begin ... end.每个case项只执行一条语句。所以就不不要C语言中的break。但这意味着如果你想执行多条语句,你需要使用begin end。
  • Duplicate (and partially overlapping) case items are permitted. The first one that matches is used. C does not allow duplicate case items.允许重复(部分重叠)的case项。(case项重复时,)第一个匹配的case项将会被使用。而C时不允许重复case项出现的。

A bit of practice

Case statements are more convenient than if statements if there are a large number of cases. So, in this exercise, create a 6-to-1 multiplexer. When sel is between 0 and 5, choose the corresponding data input. Otherwise, output 0. The data inputs and outputs are all 4 bits wide.

如果有大量的case时,case语句比if语句更方便。因此,在本次练习中,创建一个6:1多路选择器。sel取值在0-5之间,选择相应的数据输入。否则,输出0,数据输入为4bit位宽。

Be careful of inferring latches (See.always_if2)

小心锁存

1.4.6 Priority encoder

priority encoder is a combinational circuit that, when given an input bit vector, outputs the position of the first 1 bit in the vector. For example, a 8-bit priority encoder given the input 8'b10010000 would output 3'd4, because bit[4] is first bit that is high.

优先编码器是一个组合逻辑电路,当给定输入向量时,会输出该向量的第一个bit位。例如,一个8位的优先编码器,给定输入为8'b10010000,输出会得到3'd4,因为第4位时第一个电平位high的位。

Build a 4-bit priority encoder. For this problem, if none of the input bits are high (i.e., input is zero), output zero. Note that a 4-bit number has 16 possible combinations.

创建一个4位的优先编码器。对于这个问题,如果没有输入位是高的(例如输入为0),则输出0。注意,4位的数字可以有16种组合方式。

1.4.7 Priority encoder with casez

Build a priority encoder for 8-bit inputs. Given an 8-bit vector, the output should report the first (least significant) bit in the vector that is 1. Report zero if the input vector has no bits that are high. For example, the input 8'b10010000 should output 3'd4, because bit[4] is first bit that is high.

创建一个8位输入的优先编码器。给定一个8位向量,输出应该返回向量中第一个1的位(最低有效位)。如果输入向量没有高bit数据,则输出为0。例如,输入为8'b10010000 ,输出则为3’d4(3‘b100),因为第五个比特bit[4]的值位高。

From the previous exercise (always_case2), there would be 256 cases in the case statement. We can reduce this (down to 9 cases) if the case items in the case statement supported don't-care bits. This is what casez is for: It treats bits that have the value z as don't-care in the comparison.

从前面的练习(always_case2)中,case语句中有256种组合方式。如果case语句中的case项支持使用无关项的话,可以减少到9个case。这就是casez的作用:在比较种,将值为z的bit视为无关项。

For example, this would implement the 4-input priority encoder from the previous exercise:

always @(*) begincasez (in[3:0])4'bzzz1: out = 0;   // in[3:1] can be anything4'bzz1z: out = 1;4'bz1zz: out = 2;4'b1zzz: out = 3;default: out = 0;endcase
end

A case statement behaves as though each item is checked sequentially (in reality, a big combinational logic function). Notice how there are certain inputs (e.g., 4'b1111) that will match more than one case item. The first match is chosen (so 4'b1111 matches the first item, out = 0, but not any of the later ones).

case语句的行为就好像每一项都按顺序检查(实际上是一个大的组合逻辑函数)。注意某些输入(例如4’b1111)如何匹配多个case项。选择第一个匹配项。(因此4‘b1111匹配了上述代码中的第一项,所以out=0,后面的就不再进行匹配了)

  • There is also a similar casex that treats both x and z as don't-care. I don't see much purpose to using it over casez.
  • 还有一种类似的casex,将x和z都视为无关项。我觉得用它来对比casez没啥意义
  • The digit ? is a synonym for z. so 2'bz0 is the same as 2'b?0
  • ?是z的同义词,所以2'bz0和2'bz0一样吗

It may be less error-prone to explicitly specify the priority behaviour rather than rely on the ordering of the case items. For example, the following will still behave the same way if some of the case items were reordered, because any bit pattern can only match at most one case item:

显示的指定优先级的行为而不是依赖于case语句的顺序,更不容易出错。例如,如果某些case项被重新排序,下列的代码仍然可以以相同的方式运行。因为任何bit模式只能匹配一个项。

    casez (in[3:0])4'bzzz1: ...4'bzz10: ...4'bz100: ...4'b1000: ...default: ...endcase

1.4.8 Avoiding latches

Suppose you're building a circuit to process scancodes from a PS/2 keyboard for a game. Given the last two bytes of scancodes received, you need to indicate whether one of the arrow keys on the keyboard have been pressed. This involves a fairly simple mapping, which can be implemented as a case statement (or if-elseif) with four cases.

假设你正在构建一个电路来处理ps/2游戏的键盘扫描码。给定接收到的扫描码的最后两个字节,你需要指定键盘上的方向键是否被按下。这涉及到一个相当简单的映射,可以通过4个case(或if-else)语句来实现。

Your circuit has one 16-bit input, and four outputs. Build this circuit that recognizes these four scancodes and asserts the correct output.

你的电路有一个16比特的输入和四个输出。创建能够识别这四种扫描码的电路,并生成正确的输出。

To avoid creating latches, all outputs must be assigned a value in all possible conditions (See also always_if2). Simply having a default case is not enough. You must assign a value to all four outputs in all four cases and the default case. This can involve a lot of unnecessary typing. One easy way around this is to assign a "default value" to the outputs before the case statement:

为了避免产生锁存,所有的输出必须在所有可能的情况下赋值(参考alway_if2)。仅仅有一个default是不够的。你必须为所有的四种case和default case情况分配值。这可能涉及许多不必要的输入。一个简单的方式就是在case语句之前就给default case赋默认值:

always @(*) beginup = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;case (scancode)... // Set to 1 as necessary.endcase
end

This style of code ensures the outputs are assigned a value (of 0) in all possible cases unless the case statement overrides the assignment. This also means that a default: case item becomes unnecessary.

除非case语句覆盖了值,这种代码风格可以保证输出在所有可能的情况下都被赋值(0)。

Reminder: The logic synthesizer generates a combinational circuit that behaves equivalently to what the code describes. Hardware does not "execute" the lines of code in sequence.

提醒:逻辑语句生成组合电路,其行为等价于代码所描述的电路。硬件不会按顺序执行每一行代码。

1.5 More Verilog Features

1.5.1 Conditional ternary operator

条件三元运算符

Verilog has a ternary conditional operator ( ? : ) much like C:

Verilog有一个三元条件运算符(?:),很像C语言。

(condition ? if_true : if_false)

This can be used to choose one of two values based on condition (a mux!) on one line, without using an if-then inside a combinational always block.

可以通过一行代码就实现根据条件对值进行二选一,不需要在组合逻辑always语句中使用if-then。

Examples:

(0 ? 3 : 5)     // This is 5 because the condition is false.
(sel ? b : a)   // A 2-to-1 multiplexer between a and b selected by sel.always @(posedge clk)         // A T-flip-flop.启动型双稳态触发器q <= toggle ? ~q : q;always @(*)                   // State transition logic for a one-input FSMcase (state)A: next = w ? B : A;B: next = w ? A : B;endcaseassign out = ena ? q : 1'bz;  // A tri-state buffer((sel[1:0] == 2'h0) ? a :     // A 3-to-1 mux(sel[1:0] == 2'h1) ? b :c )

A Bit of Practice

Given four unsigned numbers, find the minimum. Unsigned numbers can be compared with standard comparison operators (a < b). Use the conditional operator to make two-way min circuits, then compose a few of them to create a 4-way min circuit. You'll probably want some wire vectors for the intermediate results.

给定四个无符号数,求最小值。无符号数可以与标准标准比较运算符(a<b)进行比较,使用条件运算符创建双向最小电路,然后将其中的几个组成一个四路最小电路。你可能需要一些向量来表示中间结果。

Expected solution length: Around 5 lines.

1.5.2 Reduction operators

缩减运算符

You're already familiar with bitwise operations between two values, e.g., a & b or a ^ b. Sometimes, you want to create a wide gate that operates on all of the bits of one vector, like (a[0] & a[1] & a[2] & a[3] ... ), which gets tedious if the vector is long.

你已经熟悉了两个值之间的位操作(如a&b或者a^b)。有时,你想创建一个宽的门,对一个响亮的所有比特位进行操作(如a[0]& a[1] & a[2] & a[3]),如果向量很长,那么代码就会很冗长。

The reduction operators can do AND, OR, and XOR of the bits of a vector, producing one bit of output:

归约运算符可以对一个向量的位进行与或异或操作,产生1比特输出:

& a[3:0]     // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0]     // OR:  b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0]     // XOR: c[2]^c[1]^c[0]

These are unary operators that have only one operand (similar to the NOT operators ! and ~). You can also invert the outputs of these to create NAND, NOR, and XNOR gates, e.g., (~& d[7:0]).

这些是只有一个操作数的一元运算符(类似于NOT运算符等等)。你还可以反转这些输出来创建NAND,NOR和XNOR门(例如~& d[7:0])。

Now you can revisit 4-input gates and 100-input gates.

现在你可以重新访问4输入门和100输入门

A Bit of Practice

Parity checking is often used as a simple method of detecting errors when transmitting data through an imperfect channel. Create a circuit that will compute a parity bit for a 8-bit byte (which will add a 9th bit to the byte). We will use "even" parity, where the parity bit is just the XOR of all 8 data bits.

奇偶校验通常用作不完善通道传输数据时检测错误的一种简单方法。创建一个8比特字节的计算奇偶校验的电路(这将为字节添加第9位)。我们将使用“偶数”奇偶校验,其中奇偶校验位时所有8个数据位的异或。

1.5.3 Reduction: Even wider gates

Build a combinational circuit with 100 inputs, in[99:0].

使用[99:0]创建一个具有100输入的电路。

There are 3 outputs:

  • out_and: output of a 100-input AND gate.
  • out_or: output of a 100-input OR gate.
  • out_xor: output of a 100-input XOR gate.

1.5.4 Combinational for-loop:Vector reversal 2

Given a 100-bit input vector [99:0], reverse its bit ordering.

给定一个100位输入向量,按照顺序翻转其比特位。

1.5.5 Combinational for-loop:255bit population count

A "population count" circuit counts the number of '1's in an input vector. Build a population count circuit for a 255-bit input vector.

population count电路计算输入向量1的个数。创建一个255位输入的population count电路。

module top_module( input [254:0] in,output [7:0] out );integer i;always @(*)beginout = 8'd0;for(i=0;i<255;i++)beginout = out+ in[i];endend
endmodule

1.5.6 Generate for-loop:100bit binary adder2

Create a 100-bit binary ripple-carry adder by instantiating 100 full adders. The adder adds two 100-bit numbers and a carry-in to produce a 100-bit sum and carry out. To encourage you to actually instantiate full adders, also output the carry-out from each full adder in the ripple-carry adder. cout[99] is the final carry-out from the last full adder, and is the carry-out you usually see.

通过例化100个完整加法器创建一个100位的二进制波纹位加法器。加法器将两给100位的数字和进位carry-in相加,产生100位的输出和进位输出carry-out。为了鼓励你实际例化完整的加法器,还需要再波纹进位加法器中输出每个完整的加法器的进位。cout[99]时最后一个完整加法器的最后一个执行结果,也是您通常看到的结果。

module top_module( input [99:0] a, b,input cin,output [99:0] cout,output [99:0] sum );genvar i;generate for(i=0;i<100;i++)begin : adderif(i==0)assign {cout[0],sum[0]} = a[0] + b[0] + cin;elseassign {cout[i],sum[i]} = a[i] + b[i] + cout[i-1];endendgenerate
endmodule

1.5.7 Generate for-loop:100-Digit BCD adder

You are provided with a BCD one-digit adder named bcd_fadd that adds two BCD digits and carry-in, and produces a sum and carry-out.

给定一个BCD一位数加法器,命名位bcd_fadd,其可将两个BCD码与进位输入相加,产生和值并生成进位输出。

module bcd_fadd (input [3:0] a,input [3:0] b,input     cin,output   cout,output [3:0] sum );

Instantiate 100 copies of bcd_fadd to create a 100-digit BCD ripple-carry adder. Your adder should add two 100-digit BCD numbers (packed into 400-bit vectors) and a carry-in to produce a 100-digit sum and carry out.

例化100个bcd_fadd,创建一个100位的BCD码加法器。你说创建的加法器,实现将100个BCD码(每个BCD码占4位,大包相当于位宽为400的长向量)与进位输入相加,产生100位BCD码的和并生成进位输出。

module top_module( input [399:0] a, b,input cin,output cout,output [399:0] sum );wire [99:0] cin_cout;genvar i;generate for(i=0;i<100;i++)begin : bcd_faddif(i==0)bcd_fadd bcd_fadd_0 (.a(a[3:0]),.b(b[3:0]),.cin(cin),.cout(cin_cout[0]),.sum(sum[3:0]));elsebcd_fadd bcd_fadd_1 (.a(a[4*i+3:4*i]),.b(b[4*i+3:4*i]),.cin(cin_cout[i-1]),.cout(cin_cout[i]),.sum(sum[4*i+3:4*i]));endassign cout = cin_cout[99];endgenerate
endmodule

HDLBits_第1章_Verilog Language(已完结)相关推荐

  1. 《机器学习》周志华课后习题答案——第五章(1-7已完结)

    第五章课后习题答案 文章目录 第五章课后习题答案 一.试述将线性函数f(x) = wTx用作神经元激活函数的缺陷? 二.试述使用图5.2(b)激活函数的神经元与对率回归的联系 三.对于图5.7中的Vi ...

  2. 神仙程序媛小姐姐的23个Java设计模式 ,全站式保姆的Java教程导航帖(已完结)

    Java的23个Java设计模式 ,已完结,以后的时间不定期补番.还有一些了解即可,不需要使用,现实开发用不掉 之前,分多篇讲解Java23种设计模式,毕竟太散,现将全部内容进行汇总.大家以后学习23 ...

  3. 本系列介绍了rtmp直播推流全过程(已完结)

    本系列介绍了rtmp直播推流全过程 完整项目地址 (已完结) 以下文章是针对每一个情况,介绍音视频相关知识,以及实现的原理,总共分五章: 第一章:直播推流全过程:视频数据源之YUV(1) RGB或YU ...

  4. 《机器学习》周志华课后习题答案——第八章 (1-2已完结)

    <机器学习>周志华课后习题答案---第五章 (1-2已完结) 文章目录 <机器学习>周志华课后习题答案---第五章 (1-2已完结) 一.如图所示 二.如图所示 一.如图所示 ...

  5. 四大基本反应类型的关系_死神:漫画已完结,却依旧没有填坑,尸魂界四大贵族都有谁?...

    死神:漫画已完结,却依旧没有填坑,尸魂界四大贵族都有谁? 尸魂界作为亡者的归宿,拥有灵力的人便可以修炼成为死神.但是在尸魂界还有一些特殊的存在,那就是尸魂界的贵族,和平民不同的是,他们有着自己的传承和 ...

  6. 小姐姐用一周的时间,偷偷带你学Python,从小白到进阶,全站式保姆的Python基础教程导航帖(已完结)

    第一部分.基础知识目录 小姐姐带你们偷偷的学Python,然后你们要惊呆所有人(第一天) 小姐姐带你们偷偷的学Python,然后你们要惊呆所有人(第二天) 小姐姐带你们偷偷的学Python,然后你们要 ...

  7. [区块链安全-Ethernaut]区块链智能合约安全实战-已完结

    [区块链安全-Ethernaut]区块链智能合约安全实战-已完结 准备 0. Hello Ethernaut 准备工作 创建实例并分析 合约交互 总结 1. Fallback 创建实例并分析 合约交互 ...

  8. 交管12123手机APP怎么查询已完结的科目考试成绩?

    一.打开手机"交管12123"软件,进入首页,如图 二.点击首页下方"网办进度",进入页面如图 三.点击上方第四个分项"全部",得到上图 四 ...

  9. js绑定c++(JSB或者jsbinding)的小白教程! (已完结)

    js绑定c++(JSB或者jsbinding)的小白教程! (已完结) Creator 1 / 77 S.H.I.E.L.D 16-07-8 18  仅在Windows下测试通过 官方自动绑定教程24 ...

最新文章

  1. 数据结构和算法之——跳表
  2. php连接电脑,PHP_深入理解php的MySQL连接类,无意间在电脑里发现还有这么 - phpStudy...
  3. 经典类与新式类的继承顺序
  4. php smarty 源码,Smarty php源码 v3.1.39
  5. 网上商城首页实现总结(一)
  6. css 超出文字头尾相接滚动_前端的一些雕虫小技,从100%和滚动条说起
  7. c语言实现配置文件的读写
  8. centos 6.5 apache mysql php_CentOS 6.5系统安装配置LAMP(Apache+PHP5+MySQL)服务器环境
  9. toj 4065 The Coco-Cola Store
  10. 车和家李想批国内电动车虚假里程宣传:特斯拉都打到家门口了别自嗨了
  11. throws java_基于Java中throw和throws的区别(详解)
  12. STC学习:电子音乐
  13. mysql创建一张日期表_MySQL创建一张日期表
  14. 利用JSP编程技术实现一个简单的购物车程序
  15. 微信小程序做留言板mysql_微信小程序实现留言板(Storage)
  16. 4┃音视频直播系统之浏览器中通过 WebRTC 进行桌面共享
  17. CMS并发清理阶段为什么是安全的
  18. xlsx导出以excel文件导出数据
  19. 单位跳跃函数,斜坡函数
  20. git 拉取指定远程分支的内容

热门文章

  1. 京东宙斯杯应用大赛 推荐宝 刷票记录 有图有真相
  2. 写一个批量制作散点图并导出的matlab程序
  3. 计算机教师幽默介绍,数学老师幽默自我介绍关于数字
  4. centos 7 升级 git 版本(通过安装 ius 源的方式)
  5. API批量操作、zabbix 监控
  6. php防CC攻击代码
  7. Three.js入门学习笔记07:外部模型导入-C4D转成json文件供网页使用-fbx导入
  8. Lesson 12 Life on a desert island 内容鉴赏
  9. 【高效运维】Nginx下配置WS、WSS
  10. 《读者》的“卷首语” (三)