HDLBits刷题笔记Verilog Language_vectors(二)

10. Vectors

小知识点:Verilog中的向量通过定义一个名称将相关的同类型信号进行分组,使得它们更易被调用或操作。声明向量时,需将维数(向量由信号组成,所以也可称位宽)放在向量名之前,且一般以 [n-1:0] 的格式来声明n维(位)的向量,这与C语言的数组不同。在部分选择向量中的bit时,则将位宽放于向量名之后。举个小栗子:

wire [99:0] my_vector;		// Verilog中的100位wire型向量的声明
int your_array[100];		// C语言中的100个元素int型数组的声明

assign out = my_vector[10];	// Verilog中对向量的第11位信号的选用
a = your_array[10];			// C语言中对数组的第11个元素的访问调用

Practice: 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 …

大白话:建一个电路,它有一个3位向量的输入,一个3位向量的输出和三个1位信号的输出。连法如图所示。

转自HDLBits
答案(先做再看哦,且不唯一,仅供参考):

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 o2 = vec[2];
    assign o1 = vec[1];
    assign o0 = vec[0];

endmodule

11. Vectors in more detail

小知识点①:向量对多个相关信号进行分组,通过向量名可使这些信号更便于操作。向量的声明格式:类型 [高位:低位] 向量名; 如果是在声明输入或输出端口,则需在类型中加上端口的类型,例如上题的答案。

   向量的位顺序(或“方向”,端序)取决于向量的最低有效位是较大的指数还是较小的指数(如声明为[3:0] v的向量,最低有效位是v[0],即“小端”;若声明为[0:3] v,则最低有效位是v[3],即“大端”。按字节寻址的存储器也分大小端,字地址用该字低位字节的地址来表示,即小端;字地址用该字高位字节的地址来表示,即大端。学过的同学可以类比一下两者的联系与区别)。在Verilog中,一旦向量以特定的位顺序声明,就必须以相同的方式使用它(例如,若声明为wire [3:0] v,但以v[0:3]赋值,是不允许的)。保持一致的位顺序是很重要的,因为如果将不同位顺序的向量一起赋值或使用,就会出现奇怪的bug。

浅出:怎么声明就怎么用,左边的是高位,右边的是低位,一般都把更大的指数放高位,最小的放最低有效位,具体可看下面的栗子:

wire [2:0] v; // v[2]是最高位,v[1]是次高位,v[0]是最低有效位,一般都是这么声明
assign v = 3’b110; // v[2]=1(最高位的1),v[1]=1(第二位的1),v[0]=0(末位)

wire [0:2] v; // 最高位变成了v[0],次高位是v[1],最低位是v[2],这样容易记错
assign v = 3’b110; // v[0]=1(最高位的1),v[1]=1(第二位的1),v[2]=0(末位)
小知识点②:使用隐式声明的向量会产生难以检测的bug。在Verilog中,可以通过assign语句或将未声明的内容附加到模块端口来隐式地声明信号。综合器会把未声明的内容隐式地声明成1个bit宽的信号,如果你想把它当向量来用,就会产生bug。可以用宏定义`default_nettype none指令来禁止隐式声明功能。

浅出:不要用没有声明的向量!不要用没有声明的向量!不要用没有声明的向量!另外可以通过宏定义`default_nettype none来禁止综合器的隐式声明,以防出现奇怪的bug。栗子:

my_module i1 (d,e);	/* d和e没有声明,所以会被隐式声明为1bit位宽的信号,
					   如果这两个端口被当作向量,就可能产生bug。*/
wire [2:0] a, c;	// 声明了a和c向量,注意,没有b
assign a = 3'b101; 	// a=101,a赋值为3位的二进制数101
assign b = a;		/* b=1,b没有声明就用了,它被隐式声明为位宽1bit的wire信号,
					   从而只能默认将a的最低位赋值给它,即b=1。*/
assign c = b;		// c=001,默认把b赋值给c的最低位,c本应和a一样,这就是一个bug

小知识点③:声明向量时,位宽写在向量名的前面,它定义了这个向量的维数,即向量所包含的信号位数。对于模拟器(与硬件不同)来说,向量的bits被"packed"打包到了一个blob中。在声明reg类型时,还可以在向量名后面再加“维数”,以形成向量组(有些将其称作二位数组或向量数组,我觉得向量组最贴切),即存储器。

浅出:整体看作一个包含若干个向量的向量组,即对应一个存储器。向量名前的是向量的维数(位宽),即一个向量包含的信号位数;向量名后的可以看作是向量的个数,即有几个同维度的向量。向量就是存储器里面的存储单元。向量组=存储器;向量=存储单元;向量维数=存储单元位宽;向量个数=存储单元个数。具体看下面的栗子:

reg [7:0] mem [0:255];		 /* 定义了一个存储器,有256个存储单元,地址为0~255,
						   	    每个存储单元是8位大小,即一个字节 */
assign mem[0] = 8'b01010011; /* 与一维reg向量不同,存储器的一个存储单元不能部分选择,
								而每个单元可以且必须单独赋值,无法对整个存储器赋值 */

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

大白话:分别将输入的半字(16位)的低8位和高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

BB两句:in向量在声明时是16位,assign语句中通过部分选择把它对半分了,关于部分选择的介绍我全部放到了下一小节。👇

12. Vector part select

小知识点:通过向量名可以访问整个向量,也可以通过部分选择操作符“[ ]”来访问向量的一部分(其实前面已经用过不止一次了),且assign的两侧都可以对向量进行部分选择。一个32位的向量可以分成4个字节(bits [31:24], [23:16], etc.)。

   另外,在assign语句中,若等号左右两侧信号的位宽不同,就会进行截断或者补零操作。具体如下面的栗子:
wire [3:0] left_short, right_short;	// 4位短向量
wire [5:0] left_long, right_long;	// 6位长向量
assign right_short = 4'b1010;
assign right_long = 6'b111010;
// 左侧位宽大于右侧,则左侧的低位等于右侧,左侧的高位赋零。
assign left_long = right_short;	// left_long=001010,左侧高位补了两个0
// 左侧位宽小于右侧,则右侧的低位赋予左侧,右侧的高位截断。
assign left_short = right_long; // left_short=1010,右侧高位的两个1被丢弃

Practice: Build a circuit that will reverse the byte ordering of the word:

AaaaaaaaBbbbbbbbCcccccccDddddddd → DdddddddCcccccccBbbbbbbbAaaaaaaa

大白话:把上面那串abcd倒转成dcba。

答案(先做再看哦,且不唯一,仅供参考):

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

BB两句:当需要改变端序时,通常会使用此操作(比如 x86 体系按小端存储数据,而Internet协议中均使用大端,在两者间进行数据交换前,均要进行大小端转换)。大小端在上一节提到过,有兴趣的童鞋可以看计算机组成之类的书籍。

13. Bitwise operators

小知识点:前面提到过,各种布尔操作符有“按位”和“逻辑”两种类型,在使用向量时,这两种操作符类型之间的区别非常重要。两个n位向量之间的“按位”操作复制向量的每一位的操作并产生一个n位的输出,而“逻辑”操作将整个向量作为一个布尔值并产生一个1位的输出。布尔值:真=非零,假=零。

浅出:布尔操作符就是上一篇讲到过的与或非之类的逻辑运算符。由于向量不止一位,所以要分清是“按位”操作,还是“逻辑”操作。“按位”就是n位的向量一位一位运算过去,最后产生的输出肯定也是n位的(如按位与:1011 & 0110 = 0010);“逻辑”就是把n位的向量作为一个整体,只有每一位都为0(整体等于零),它才是假,可记成一位的“0”,不然它整体就不等于零,也就是真,记“1”,最后的输出也仅剩1位(如逻辑与:10 && 11=1,10 && 00=0)。

Practice: Build a circuit that has two 3-bit inputs that computes …

大白话:对a、b两个3bit的向量分别进行按位或,逻辑或,以及按位取反(b按位取反的结果放在输出的高位,a的放在低位)。

转自HDLBits
答案(先做再看哦,且不唯一,仅供参考):

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

BB两句:完事了不要急着出来,翻到下面去看一下波形图(波形图的23456…是结果的十六进制表示,需要转换成二进制),看完就会更明白了。

14. Four-input gates

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

大白话:一个电路,有4位输入信号(in[3:0]),且分别输出4位输入的与,或,异或。

答案(法一):

module top_module(
    input [3:0] in,
    output out_and,
    output out_or,
    output out_xor
);
    assign out_and = in[3] & in[2] & in[1] & in[0];
    assign out_or  = in[3] | in[2] | in[1] | in[0];
    assign out_xor = in[3] ^ in[2] ^ in[1] ^ in[0];

endmodule

(法二):

assign out_and = & in;
assign out_or  = | in;
assign out_xor = ^ in;

BB两句:法二的是缩减运算符,效果和法一相同,但是写起来简便不少。

15. Vector concatenation operator

小知识点:部分选择操作符“[ ]”用于选择向量的一部分。而连接操作符“{ , }”将较小的向量连接起来以产生更大的向量。连接运算符中的每一个元素都需要标注位宽(不然综合器无法知道结果的位宽),因此像{1,2,3}这样的用法是非法的。同样,连接操作符在assign语句的等号两边都可以使用。具体看下面的三颗栗子:

{1'b1, 1'b0, 3'b101} // => 5'b10101
assign {out[7:0], out[15:8]} = in;
assign out[15:0] = {in[7:0], in[15:8]};

Practice: Given several input vectors, concatenate them together then split them up into several output vectors. There are …

大白话:六个5位的输入(a, b, c, d, e, f),四个8位的输出(w, x, y, z),照着下图的关系对应输出(输入不够的两位用“11”补)。

转自HDLBits
答案(先做再看哦,且不唯一,仅供参考):

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

16. Vector reversal 1

Practice: Given an 8-bit input vector, reverse its bit ordering.

大白话:一个8位的输入向量,把它的位顺序反转后输出。

答案(先做再看哦,且不唯一,仅供参考):

module top_module(
    input [7:0] in,
    output [7:0] out
);
	assign {out[0],out[1],out[2],out[3],out[4],out[5],out[6],out[7]} = in;
	// 或assign out = {in[0],in[1],in[2],in[3],in[4],in[5],in[6],in[7]};

endmodule

BB两句:学过C语言的同学可以想一下怎么用for循环来实现,因为还没有讲到always块和generate生成块,所以这里就不细说了。

17. Replication operator

小知识点:连接操作符能将向量连接在一起,形成一个更大的向量。但是有时候想要将几个相同的东西连接在一起,如: assign a = {b,b,b,b,b,b}; 这很tedious。复制操作符可以将一个向量复制指定次数并将它们连接在一起:{ 复制次数 { 向量 } } 。复制次数必须是个常数,且复制操作符共有两组大括号。

   复制操作符常用来扩展有符号数的位数,并保证其值不变。这可通过将其符号位(最高有效位)复制到它的左边来实现(例如,将4'b0101(符号位为0,值为5)扩展为8位的结果是8'b00000101(值仍为5),将4'b1101(符号位为1,值为-3)扩展为8位后是8'b11111101(-3),注意:这些有符号数是补码的形式,而非原码)。

Practice: Build a circuit that sign-extends an 8-bit number to 32 bits…

大白话:建立一个电路,把8位有符号数扩展至32位,即将其符号位向左复制24次。

答案(先做再看哦,且不唯一,仅供参考):

module top_module (
    input [7:0] in,
    output [31:0] out );

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

endmodule

BB两句:一定要数清楚大括号,另外有符号数的扩展是补码,而非原码。

18. More replication

Practice: 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 …

大白话:五个1bit信号(a、b、c、d、e),照着下图连成两组25位宽的向量,并按图中顺序逐位进行逻辑运算(被比较的两位相等,则输出1)。

转自HDLBits
答案(先做再看哦,且不唯一,仅供参考):

module top_module (
    input a, b, c, d, e,
    output [24:0] out );

    assign out = ~{{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}} ^ {5{a,b,c,d,e}};
	// 或assign out = {{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}} ^~ {5{a,b,c,d,e}};
	// 或assign out = ~({{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}} ^ {5{a,b,c,d,e}});

endmodule

BB两句:^~是按位同或运算符,等同于异或取反,或者一个先取反后再与另一个异或。

重点

  1. 向量的声明格式:类型 [高位:低位] 向量名; ;

  2. 部分选择操作符:[ : ];连接操作符:{ , };复制操作符:{num{vector}};

  3. 布尔操作符的“按位”和“逻辑”类型;

  4. assign语句等号两侧的向量位宽不同时的截断或补零;

  5. 声明存储器:reg [高位:低位] 存储器名 [边界地址:边界地址]; ;

  6. 宏定义`default_nettype none禁止综合器的隐式声明;

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值