vector用于使用一个名称对相关信号进行分组,以使其更易于操作。例如,电线[7:0] w;声明一个名为w的8位向量,该vector在功能上等同于具有8条独立的导线。
注意,向量的声明将维(dimensions)放置在vector名称之前,这与C语法相比并不常见。但是,零件选择的尺寸要在矢量名称之后。
比如说:
wire [99:0] my_vector; // Declare a 100-element vector
assign out = my_vector[10]; // Part-select one bit out of the vector
question:
构建一个具有一个3位输入的电路,然后输出相同的矢量,并将其分成三个单独的1位输出。将输出o0连接到输入矢量的位置0,将o1连接到位置1,依此类推。
注意,图中旁边带有数字的刻度线表示向量(或“总线”)的宽度,而不是为向量中的每一位绘制单独的线。
solution:
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 = outv[0];
assign o1 = outv[1];
assign o2 = outv[2];
endmodule
向量必须声明:
type [upper:lower] vector_name;
type指定向量的数据类型。这通常是wire或reg。如果要声明输入或输出端口,则该类型还可以另外包括端口类型(例如,input or 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.
向量的endianness,即字节序(或非正式地称为“方向”)是指最低有效位是具有较低的索引(较小的字节序,例如[3:0])还是具有较高的索引(较大的字节序,例如[[ 0:3])。
在Verilog中,一旦以特定的字节序声明了向量,就必须始终以相同的方式使用它。例如,在宣告vec为wire [3:0] vec时写入vec [0:3];是非法的。与字节序一致是一种好习惯,因为如果将不同字节序的向量一起分配或使用,则会发生奇怪的错误。
在Verilog中,网络类型的信号可以通过Assign语句或通过将未声明的内容附加到模块端口来隐式创建。隐式网络始终是一位导线(one-bit wires),如果我们打算使用矢量,则会导致错误。隐式网络通常是难以发现的错误的来源。
可以使用(`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.
添加`default_nettype none会使第二行代码出错,从而使该错误更明显。
在声明中,向量索引写在向量名称之前。这声明了数组的打包维(the “packed” dimensions),其中位被“打包”到了一个Blob中(这在模拟器中是相关的,但在硬件中不相关)。未打包的dimensions在名称后声明。它们通常用于声明内存数组。
比如说:
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.
使用向量名来处理整个向量,例如:
assign w = a;
取整个4位向量a并将其分配给整个8位向量w(从上面获取声明)。如果左右边的长度不匹配,则将其适当地零扩展或截断。
部分选择运算符可用于访问向量的一部分:
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.
question:
建立一个组合电路,将输入半字(16位,[15:0])分成低[7:0]和高[15:8]个字节。
solution:
`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_lo[7:0] = in[7:0];
assign out_hi[7:0] = in[15:8];
endmodule
question:
可以将32位向量视为包含4个字节(位[31:24],[23:16]等)。建立一个电路,该电路将反转4字节字的字节顺序。
比如说:
AaaaaaaaBbbbbbbbCcccccccDddddddd
=>
DdddddddCcccccccBbbbbbbbAaaaaaaa
注意:当需要交换数据的字节序时,例如在小字节序x86系统和许多Internet协议中使用的大字节序格式之间交换数据时,通常使用此操作。
module top_module(
input [31:0] in,
output [31:0] out );//
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
question:
构建一个具有两个3位输入的电路,该输入可计算两个向量的按位或,两个向量的逻辑或以及两个向量的反(NOT)。将b的倒数放在out_not的上半部分(即位[5:3])中,将a的倒数放在下半部分中。
使用向量时,两种运算符类型之间的区别变得很重要。两个N位向量之间的按位运算会复制该向量的每个位并产生N位输出,而逻辑运算会将整个向量视为布尔值(真=非零,假=零),并且产生1位输出。
注意:
question中提到了按位或、逻辑或。实际上,按位与、按位或、按位非是对计算机位的操作,属于计算机二进制位运算范畴;而逻辑与、逻辑或、逻辑非是对boolean值的操作,属于离散数学范畴。
按位与、按位或、按位非按照规则对每一位进行逻辑运算,其中1代表真0代表假。
前者是条件计算操作,用来判断真假,后者是用位操作符进行计算。
solution:
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[2] = a[2] | b[2];
assign out_or_bitwise[1] = a[1] | b[1];
assign out_or_bitwise[0] = a[0] | b[0];*/
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
查看仿真波形,可以了解按位或、逻辑或的不同之处。
question:
建立一个具有四个输入in [3:0]的组合电路。 有3个输出: out_and:4输入与门的输出。 out_or:4输入或门的输出。 out_xor:4输入XOR门的输出。
solution:
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]));*/
assign out_and = ∈
assign out_or = |in;
assign out_xor = ^in;
endmodule
使用部分选择来选择向量的部分。串联运算符{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
注意:“b”表示2进制,“d”表示十进制,“h”表示十六进制(即可以表示4位2进制数)。类似的还有,“o”表示八进制。
另外,串联需要知道每个分量的宽度。因此,{1、2、3}是非法的,并导致出现错误消息:串联中不允许使用不定尺寸的常量。可以在分配的左侧和右侧使用串联运算符:
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.
question:
给定几个输入向量,将它们连接在一起,然后将它们分成几个输出向量。有六个5位输入向量:a,b,c,d,e和f,总共有30位输入。有四个8位输出向量:w,x,y和z,用于32位输出。输出应该是输入矢量的后接两个1位:
solution:
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
question:
给定8位输入向量[7:0],请反转其位顺序。
分配out [7:0] = in [0:7];不起作用,因为Verilog不允许翻转向量位顺序。串联运算符可以节省一些编码。
solution:
module top_module(
input [7:0] in,
output [7:0] out
);
assign {out[7:0]} = {in[0],in[1],in[2],in[3],in[4],in[5],in[6],in[7]};
endmodule
串联运算符允许将向量串联在一起以形成更大的向量。但是有时您想将同一事物连接在一起很多次,而做类似assign a = {b,b,b,b,b,b,b};
的事情仍然很繁琐。复制运算符(replicates vector)允许重复向量并将它们串联在一起: {num {vector}}
这将复制num次。 num必须为常数。两组"{}" (即braces)都是必需的。
比如说:
{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.
question:
建立一个将8位数字符号扩展为32位的电路。
一个复制运算符的常见用法是将一个较小的数字符号扩展为一个较大的数字,同时保留其符号值。这是通过将较小数字的符号位(最高有效位)复制到左侧来完成的。
例如,将4’b0101(5)符号扩展为8位将产生8’b00000101(5),而将4’b1101(-3)符号扩展为8位将导致8’b11111101(-3)。
因此我们这里需要串联24个符号位的副本(即24次复制bit [7]),然后是8位数字本身。
solution:
module top_module (
input [7:0] in,
output [31:0] out );//
// assign out = { replicate-sign-bit , the-input };
assign out = {{24{in[7]}},in};
endmodule
给定五个1位信号(a,b,c,d和e),在25位输出向量中计算所有25个成对的一位比较。如果要比较的两位相等,则输出应为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;
question:
如图所示,使用复制和串联运算符可以完成此操作。 顶部向量是每个输入的5个重复的串联 底部向量是5个输入的串联的5个重复。
solution:
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 = ~{{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}}^{{5{a,b,c,d,e}}};
endmodule