向量用于使用一个名称对相关信号进行分组,以使其更易于操作。例如,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 o0=vec[0];
assign o1=vec[1];
assign o2=vec[2];
assign outv[2:0]=vec[2:0];//or assign outv=vec;
endmodule
向量用于使用一个名称对相关信号进行分组,以使其更易于操作。例如,wire [7:0] w;声明一个名为 w 的 8 位向量,相当于有 8 个单独的导线。
必须声明向量:
键入 [上:下] vector_name;
type [upper:lower] vector_name;
类型指定向量的数据类型。这通常是电线或注册。如果要声明输入或输出端口,则该类型还可以包括端口类型(例如,输入或输出)。一些例子:
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.
向量的字节序(或非正式地称为“方向”)是最低有效位是否具有较低的索引(小端序,例如 [3:0])或较高的索引(大端序,例如 [0:3])。在 Verilog 中,一旦向量声明具有特定的字节序,就必须始终以相同的方式使用它。例如,在宣布时书写是非法的。与字节序保持一致是一种很好的做法,因为如果将不同字节序的向量分配或一起使用,就会出现奇怪的错误。vec[0:3] vecwire [3:0] vec;
隐式网络通常是难以检测的错误的来源。 在 Verilog 中,网络型(net)信号可以通过语句或通过将未声明的内容附加到模块端口来隐式创建。
在Verilog当中,如果在一个assign语句或者一个未被声明的模块当中出现/引用未声明的向量,Implicit nets 就会被隐性地生成在模块当中。Implicit nets一般都是一位wire变量,如果作为向量使用,很可能会报错。
用指令default_nettype none可以禁止Implicit nets 的生成。
assign`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
在 Verilog-2001 中,所有数据类型都可以声明为数组。reg、wire 和所有其他网络类型也可以声明矢量宽度。在对象名称之前声明的维度称为“矢量宽度”维度。在对象名称之后声明的维度称为“数组”维度。
SystemVerilog 使用术语打包数组来指代对象名称之前声明的维度(Verilog-2001 将其称为矢量宽度)。术语未打包数组用于指对象名称后声明的维度。
module packed_unpacked_data();
// packed array
bit [7:0] packed_array = 8'hAA;
// unpacked array
reg unpacked_array [7:0] = '{0,0,0,0,0,0,0,1};
initialbegin
$display ("packed array[0] = %b", packed_array[0]);
$display ("unpacked array[0] = %b", unpacked_array[0]);
$display ("packed array = %b", packed_array);
// Below one is wrong syntax
//$display("unpacked array[0] = %b",unpacked_array);
#1 $finish;
end
endmodule
输出
packed array[0] = 0
unpacked array[0] = 1
packed array = 10101010
您可能已经注意到,在声明中,向量索引写在向量名称之前。这声明了数组的“打包”维度,其中位被“打包”到一个 blob 中(这在模拟器中相关,但在硬件中无关)。未打包的维度在名称后声明。它们通常用于声明内存数组。由于ECE253没有涵盖内存阵列,因此我们在本课程中没有使用打包阵列。有关更多详细信息,请参阅 http://www.asic-world.com/systemverilog/data_types10.html。
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.
构建一个组合电路,将输入半字(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
可以将 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];// assign out[31:24] = ...;
endmodule
构建一个具有两个 3 位输入的电路,用于计算两个向量的按位 OR、两个向量的逻辑 OR 以及两个向量的逆 (NOT)。将 的反半部分(即位 [5:3]),将 的反半部分放在下半部分。bout_nota
前面,我们提到各种布尔运算符有按位和逻辑版本.使用向量时,两种运算符类型之间的区别变得很重要。两个 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=~{b,a};
endmodule
构建一个具有四个输入的组合电路,in[3:0]。
有 3 个输出:
out_and:4输入AND门的输出。
out_or:4输入OR门的输出。
out_xor:4输入异或门的输出。
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 = & in;更简单的写法
//assign out_or = | in;
//assign out_xor = ^ in;
endmodule
Part selection选择用于选择矢量的部分。串联运算符 {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
串联需要知道每个组件的宽度(或者你怎么知道结果的长度?因此,{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.
给定几个输入向量,将它们连接在一起,然后将它们分成几个输出向量。有六个 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 {w,x,y,z}={a,b,c,d,e,f,2'b11};
endmodule
给定一个 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
串联运算符允许将向量连接在一起以形成更大的向量。但是有时您希望将同一件事连接在一起很多次,并且执行诸如分配 a = {b,b,b,b,b,b}; 之类的事情仍然很乏味。复制运算符允许重复一个向量并将它们连接在一起:
{num{vector}}
这将按数字次复制矢量。num 必须是一个常量。两组括号都是必需的。
例子:
{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.
查看复制运算符的一个常见位置是将较小的数字符号扩展到较大的数字,同时保留其值。这是通过将较小数字的符号位(最高有效位)复制到左侧来完成的。例如,将 4'b0 101 (5) 符号扩展为 8 位会产生 8'b 0000 0101 (5),而将 4'b 1 101 (-3) 符号扩展为 8 位会产生 8'b 1111 1101 (-3)。
构建一个将 8 位数字符号扩展到 32 位的电路。这需要连接符号位的 24 个副本(即复制位 [7] 24 次),后跟 8 位数字本身。
module top_module (
input [7:0] in,
output [31:0] out );//
assign out={{24{in[7]}},in};
// assign out = { replicate-sign-bit , the-input };
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;
如图所示,使用复制和串联运算符可以更轻松地完成此操作。
顶部向量是每个输入的 5 个重复的串联
底部向量是 5 个输入串联的 5 个重复
module top_module (
input a, b, c, d, e,
output [24:0] out );//
wire [24:0]cmp1={{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}};
wire [24:0]cmp2={{5{a,b,c,d,e}}};
assign out=~cmp1^cmp2;
// The output is XNOR of two vectors created by
// concatenating and replicating the five inputs.
// assign out = ~{ ... } ^ { ... };
endmodule
2.3 Verilog 数据类型 | 菜鸟教程 (runoob.com)