HDLBits_Verilog向量基础知识

HDLBits链接


向量

在Verilog中,对向量维度的声明在向量名之前,这点与C语言有所差异,但使用向量的时候维度信息仍在向量名后。

1
2
wire [99:0] my_vector;      // Declare a 100-element vector
assign out = my_vector[10]; // Part-select one bit out of the vector

向量的更多知识

声明向量

type [upper:lower] vector_name;

type 指定了向量的数据类型,通常为reg或wire类型。如果声明的是输入、输出向量的话,类型前还可以加上input和output,如下所示:

1
2
3
4
5
6
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]),一旦用特定的索引规则定义了向量,就要用相同的方式去使用它

tips:向量声明时数字的顺序很重要,它决定了向量是以大端存储或者小端存储。举例说明,若声明wire [3:0] w,则w[0]为w的最低位,w[3]为w的最高位;若声明为wire [0:3] w,则与上述结果相反。所以在向量的定义和使用时一定要保持大小端一致!

未定义的中间变量容易出错

使用wire型变量前请先声明,不要图一时之便直接未定义就拿来做中间变量,这样导致的错误会很难发现。

1
2
3
4
5
6
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.

向量名前为维度,向量名后为数目,不写统统视作1。(可类比C语言中的数组,向量名前为数组元素的数据类型,向量名后为数组长度;或者形象化的表述为:维度即为每个房子的大小,数目为房子的总数目)

1
2
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;如果左右两边的长度不匹配,则根据情况对向量进行补零或截断。

向量的部分选择操作可以访问向量的一部分。(向量声明来自上面”声明向量”部分)

1
2
3
4
5
6
7
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.

tips:取用向量中特定维度的数时一定注意要与向量的定义一致,注意大小端匹配的问题。

题目描述

构建一个电路将一个4byte的数中的字节顺序调转,常用于数据的大小端转换。

AaaaaaaaBbbbbbbbCcccccccDddddddd => DdddddddCcccccccBbbbbbbbAaaaaaaa

Solution:

1
2
3
4
5
6
7
8
9
10
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

tips:在向量赋值的左右两端都可以选择对部分数据进行操作。


对向量进行门操作

位操作运算符VS逻辑操作运算符

对两个Nbit位宽的向量而言,按位操作输出为Nbit位宽向量;而逻辑操作将整个向量视为布尔值(true =非零,false =零)并产生一个1bit的输出。

题目描述1:对向量特定部分的数据进行按位操作和逻辑操作。

Solution1:

1
2
3
4
5
6
7
8
9
10
11
12
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_logical = a || b;
assign out_or_bitwise = a | b;
assign out_not[5:3] = ~b;
assign out_not[2:0] = ~a;
endmodule

题目描述2:

对输入向量进行按位的与、或和异或操作。

Solution2:

1
2
3
4
5
6
7
8
9
10
module top_module( 
input [3:0] in,
output out_and,
output out_or,
output out_xor
);
assign out_and = &in; // &in = in[3]&in[2]&in[1]&in[0]
assign out_or = |in;
assign out_xor = ^in;
endmodule

tips:合理利用缩位运算符精简代码,按位进行逻辑运算,结果为1bit数。

  • 与缩位运算符:& 
  • 或缩位运算符:| 
  • 异或缩位运算符:^ 
  • 与、或、异或运算符和非运算符组成的复合运算符:&,|, ~,^

向量拼接操作

向量拼接操作需要知道待拼接的各向量的位宽,否则你怎么知道结果的位宽?所以{1,2,3}是无效的,因为你未指定向量位宽。

向量的拼接操作在赋值的左右两端均可使用。

1
2
3
4
5
6
7
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.

tips:1、位宽合并需知道各成分的确切位宽,否则结果位宽不定。2、位宽合并时在assign两端均可实现,最好左右两端位宽相同,否则未被指定的数据会被置零。

题目描述:

给定几个输入向量,将它们拼接在一起,然后将它们分割成几个输出向量。有6个5位输入向量:a, b, c, d, e, f,总共30位输入。对于32位的输出,有4个8位的输出向量:w、x、y和z。输出应该是输入向量与两个“1”位的串联:

1

Solution:

1
2
3
4
5
6
7
module top_module (
input [4:0] a, b, c, d, e, f,
output [7:0] w, x, y, z );
wire [31:0] temp;
assign temp = {a,b,c,d,e,f,2'b11};
assign {w,x,y,z}=temp;
endmodule

向量翻转

题目描述:

给定一个8位输入向量[7:0],颠倒它的位顺序。

Solution1:

1
2
3
4
5
6
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

Solution2:

1
2
3
4
5
6
7
8
9
10
11
module top_module( 
input [7:0] in,
output [7:0] out
);
integer i;
always@(*)begin
for(i = 0;i < 8;i = i + 1)begin
out[i]=in[7-i];
end
end
endmodule

tips:建议使用Solution2实现,可扩展性强。


向量复制操作

向量复制操作允许重复一个向量并将它们连接在一起:{num{vector}}

例如:

1
2
3
4
{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.

tips:复制向量有捷径,花括外面带数字。主要用于符号位的拓展。如4’b0101带符号拓展为8bit的话为8’b00000101,4’b1101带符号拓展为8bit数为8’b11111101。

题目描述:建立一个电路,将8位数字扩展到32位。这需要将符号位的24个副本(即将位[7]复制24次)与8位数字本身连接起来。

Solution:

1
2
3
4
5
6
7
module top_module (
input [7:0] in,
output [31:0] out );

assign out = { {24{in[7]}} , in };

endmodule

题目描述2:给定5个1位信号(a、b、c、d和e),计算25位输出向量中所有25个成对的1位比较。如果两个被比较的位相等,输出应该是1。

1
2
3
4
5
6
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;

2

tips:该操作可以定位特定signal所在位置,两向量重复数据有规律可循,可用复制的方式来产生,简化代码。

Solution:

1
2
3
4
5
6
7
module top_module (
input a, b, c, d, e,
output [24:0] out );

assign out = ~{5{a,b,c,d,e}} ^ {{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}};

endmodule

总结:

学习了向量操作的相关知识,包括声明、选择部分数据、合并、复制等操作。