HDLBits(2) Vectors合集

5 篇文章 3 订阅
这篇博客介绍了Verilog中向量的使用,包括逻辑与、逻辑或、按位与、按位或、按位异或和按位取反等位运算符,以及向量的部分选择、连接操作。通过实例展示了如何进行向量的拆分、合并、字节序反转、位反转和复制。此外,还讨论了在组合逻辑中使用for循环和generate-for循环实现位操作的可能性。
摘要由CSDN通过智能技术生成

0 写在前面:一些总结

逻辑与(&&)、逻辑或(||)、按位与(&)、按位或(|)、按位异或(^)、按位取反(~)

Vector 0 :Vectors

向量用于使用一个名称对相关信号进行分组,使其更便于操作。例如,wire [7:0] w;声明一个名为w的8位向量,它在功能上相当于有8个单独的连线。

请注意,vector的声明将维放在vector名称之前,这与C语法不同。然而,正如您所期望的那样,部件选择在向量名称之后有维度。
记得!向量是用wire进行定义!

wire [99:0] my_vector;//声明一个包含100个元素的vector对象

Assign out = my_vector[10];//部分选择向量中的一位!!!

Problem Statement

构建一个电路,它有一个3位输入,然后输出相同的矢量将它分成三个单独的1位输出。将输出o0连接到输入向量的位置0,将o1连接到位置1,等等。

在图表中,一个标有数字的勾号表示向量(或“总线”)的宽度,而不是为向量中的每一位画一条单独的线。
在这里插入图片描述

Writing Code

我的答案

//注意这里直接定义的是wire类型的
module top_module ( 
    input wire [2:0] vec,
    output wire [2:0] outv,//输出相同的矢量
    output wire o2,
    output wire o1,
    output wire o0  //输出三个单独的
    ); 
    //赋值,连接
	assign outv = vec;
    assign o0 = vec[0];
    assign o1 = vec[1];
    assign o2 = vec[2];
    
endmodule

官方答案

module top_module(
	input [2:0] vec, 
	output [2:0] outv,
	output o2,
	output o1,
	output o0
);
	
	assign outv = vec;

	// This is ok too: assign {o2, o1, o0} = vec;这个可以试试!!!方便!!
	assign o0 = vec[0];
	assign o1 = vec[1];
	assign o2 = vec[2];
	
endmodule

Vector 1 :Vectors in more detail

向量用于使用一个名称对相关信号进行分组,使其更便于操作。例如,wire [7:0] w;声明一个名为w的8位向量,相当于有8个单独的连线。

声明向量

声明的形式为:

type  [upper:lower]  vector_name;

Type指定向量的数据类型。这通常是wire或reg如果你正在声明一个输入或输出端口,类型还可以包括端口类型(例如,输入或输出)。一些例子:

wire [7:0]w;/ / 8位的线网型变量
reg [4:1] x;/ / 4比特的寄存器变量
output reg [0:0] y;// 1位的寄存器变量,也是一个输出端口(这仍然是一个向量)
input wire[3:-2]z;// 6位线网型变量输入(允许负范围)
output [3:0] a;// 4位输出线网型变量。除非另有说明,否则类型为“wire”。

//相当于平时直接output a;这样定义端口的话,默认就为wire类型了!!!

wire [0:7] b; // 8位线,其中b[0]是最高有效位。

向量的字节序(或者通俗地说,“方向”)是指最低有效位是否具有较低的索引(小端序,例如[3:0])或较高的索引(大端序,例如[0:3])。在Verilog中,一旦用特定的字节顺序声明了vector,就必须始终以相同的方式使用它。例如,当vec被声明为wire [3:0] vec时,写入vec[0:3];是非法的。与字节顺序保持一致是很好的实践,因为如果分配或一起使用不同字节顺序的向量,就会出现奇怪的错误。

未打包与打包的数组

您可能已经注意到,在声明中,向量索引写在向量名之前。这声明了数组的“打包”维度,其中的位被“打包”成一个blob(这在模拟器中是相关的,但在硬件中不是)。未打包的尺寸在名称后声明。它们通常用于声明内存数组。因为ECE253没有涉及内存数组,所以我们在本课程中没有使用打包数组。详情请参见http://www.asic-world.com/systemverilog/data_types10.html。

Reg [7:0] mem [255:0];// 256个未打包的元素,每个元素都是一个8位打包的reg向量。
reg mem2 [28:0];// 29个未打包的元素,每个元素都是1位的reg。
//默认的打包数量是1

访问向量元素:部分选择

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

assign w = a;

取整个4位向量a并将其赋值给整个8位向量w(声明取自上面)。如果左右两边的长度不匹配,则酌情对其进行零扩展或截断

部分选择操作符可以用来访问向量的一部分:

w[3:0] //仅w的较低4位

x[1] // x的最小位!!!!!
x [1:1 ]      // ...也是x的最小位

z[-1:-2] // z的两个最小位!!!!
b[3:0]     //非法的!!!!!!!!向量部分选择必须与声明的方向匹配。
b[0:3] // b的高4位。
assign w[3:0] = b[0:3];//将b的上4位赋值给w的下4位w[3]=b[0], w[2]=b[1],等等。

Problem Statement

建立一个组合电路,将输入的半字(16位,[15:0])分成较低的[7:0]和较高的[15:8]字节。

Writing Code

`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

Vector 2 :Vector part select

Problem Statement

一个32位的向量可以被看作包含4个字节(bits[31:24],[23:16],等等)。建立一个电路,将4字节字的字节顺序颠倒过来。

AaaaaaaaBbbbbbbbCcccccccDddddddd = > DdddddddCcccccccBbbbbbbbAaaaaaaa

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

Hint

部分选择可以在赋值的左边和右边使用.

Writing Code

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

Vectorgates:Bitwise operators

构建一个电路,它有两个3位输入,计算两个向量的按位或运算,两个向量的逻辑或运算,以及两个向量的逆运算(NOT)将b的逆放在out_not的上半部分(即bits[5:3]),将a的逆放在下半部分。

位运算符与逻辑运算符

前面,我们提到了各种布尔运算符的按位和逻辑版本(例如,norgate)。在使用向量时,这两种操作符类型之间的区别变得很重要。两个n位向量之间的逐位操作复制向量的每个位的操作并产生一个n位输出,而逻辑操作将整个向量视为布尔值(true =非零,false =零)并产生一个1位输出。

查看模拟波形,看看按位或和逻辑或的区别。
在这里插入图片描述

Hint

即使不能对线进行多次赋值,也可以使用赋值左侧的部件选择。你不需要在一个语句中赋值给整个向量。

Writing Code

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;//将b的逆放在out_not的上半部分(即bits[5:3])
    assign out_not[2:0]   = ~a;//将a的逆放在下半部分。

endmodule

需要注意的是,比如最先声明端口时候是[3:0],那么后面对各个位赋值的时候都得是这个顺序,比如[2:0],不能是[0:2]

Gates4:Four-input gates

Problem Statement

在[3:0]中建立一个有四个输入的组合电路。

有3个输出:

out_and: 4位输入与门的输出。
out_or: 4位输入或门的输出。
out_xor: 4位输入异或门的输出。
意思就是,in是作为4位输入的,这几位来进行与或非的操作

Writing Code

module top_module( 
    input [3:0] in,
    output out_and,
    output out_or,
    output out_xor
);

    assign out_and  =  & in;//相当于  按位与
    assign out_or   = | in;
    assign out_xor  = ^ in;
    
endmodule

Vector 3:vector concatenation operator

向量连接操作符

部分选择用于选择矢量的部分。连接运算符{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和4'd10都是二进制的4'b1010

连接需要知道每个组件的宽度(或者您如何知道结果的长度?)因此,{1,2,3}是非法的,并导致错误消息:unsized常量在连接中是不允许的。

连接操作符可以在赋值的左右两边使用。

input [15:0] in;
output [23:0] out;
assign {out[7:0], out[15:8]} = in;//交换两个字节。左右两边都是16位向量。
assign out[15:0] = {in[7:0], in[15:8]};//这是一样的。
assign out = {in[7:0], in[15:8]};//这个不一样。右边的16位向量被扩展为
//匹配左边的24位向量,因此out[23:16]为零。
//在前两个例子中,out[23:16]没有指定。
//!!!!!!!

可以总结一下最后两个例子:如果左右两边指定的是一样的位数,那么就对应的进行操作符连接。
但是如果指定不一样的位数,那么扩展出来,结果就是有一部分就被赋值为0了(主动赋值)

Problem Statement

给定几个输入向量,将它们连接在一起,然后将它们分成几个输出向量。有六个5位的输入向量:a、b、c、d、e和f,总共有30位的输入。对于32位的输出,有4个8位的输出向量:w、x、y和z。输出应该包括两个输入的1位向量
在这里插入图片描述

Writing Code

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

可以这样左右两边都统一的赋值并且分配

Vectorr:vector reversal 1

Problem Statement

给定一个8位输入向量[7:0],反转其位序。

Hint

分配[7:0]= in[0:10];无效,因为Verilog不允许反转向量位序。
连接操作符可以节省一些编码,允许使用1个而不是8个赋值语句。

Writing Code

这是我的答案

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[7] = in[0];
    assign out[6] = in[1];
    assign out[5] = in[2];
    assign out[4] = in[3];
    assign out[3] = in[4];
    assign out[2] = in[5];
    assign out[1] = in[6];
    assign out[0] = in[7];
    
endmodule

可以学习一下官方的答案

//我知道你很想知道如何使用循环来实现:

//创建一个组合的always块。这将创建计算相同结果的组合逻辑
//作为顺序代码。for循环描述的是电路的行为,而不是结构,所以它们只能被使用
//程序块内部(例如,always block)。
//创建的电路(导线和门)不做任何迭代:它只产生相同的结果
//如果迭代发生。实际上,逻辑合成器将在编译时进行迭代
//找出要生成的电路。(相反,Verilog模拟器将按顺序执行循环
/ /在模拟)。
always @(*) begin	
		for (int i=0; i<8; i++)	// int is a SystemVerilog type. Use integer for pure Verilog.
			out[i] = in[8-i-1];
	end

//使用generate-for循环也可以这样做。生成循环看起来像程序的for循环,
//但在概念上有很大的不同,而且不容易理解。生成循环用于进行实例化
//“事物”(与过程循环不同,它不描述动作)。这些“东西”是赋值语句,
//模块实例化,net/variable声明和过程块(当不在内部时可以创建的东西)
/ /程序)。生成循环(和genvars)完全在编译时计算。你可以考虑生成
//块作为预处理的一种形式,生成更多的代码,然后通过逻辑合成器运行。
//在下面的例子中,generate-for循环首先在编译时创建8个赋值语句
/ /合成。
//注意,由于它的预期用途(在编译时生成代码),存在一些限制
//如何使用它们。例子:1。Quartus需要一个generate-for循环来拥有一个命名的begin-end块
//附加(在这个例子中,命名为“my_block_name”)。2. 在循环体中,genvars是只读的。
generate
		genvar i;
		for (i=0; i<8; i = i+1) begin: my_block_name
			assign out[i] = in[8-i-1];
		end
	endgenerate
*/

这个需要一定的时间消化一下

Vector4:Replication operator

复制操作符
连接运算符允许将向量连接在一起形成一个更大的向量。但有时你想把同样的东西多次连接在一起,像赋值a = {b,b,b,b,b}这样的事情还是很乏味的。
复制操作符允许
重复一个向量
并将它们连接在一起:

{num{向量}}

这将按num次复制vector。Num必须是常量。这两组大括号都是必需的。

例子:

{5{1'b1}} // 5'b11111(或5'd31或5'h1f)
{2{a,b,c}} //同{a,b,c,a,b,c}
{3'd5, {2{3'd6}}} // 9'b101_110_110。它是101和
//第二个向量,它是3'b110的两个副本。

A Bit of Practice

看到复制操作符的一个常见情况是将较小的数字符号扩展为较大的数字,同时保留其有符号的值。这是通过复制较小数字的符号位(最高有效位)到左边来完成的。例如,将4’b0101(5)扩展到8位将得到8’b00000101(5),而将4’b1101(-3)扩展到8位将得到8’b11111101(-3)。
简而言之,正数就是把符号位0复制到左边
负数就是把符号位1复制到右边

建立一个电路,将8位数字扩展到32位。这需要24个符号位的副本(即复制[7]位24次),然后是8位数字本身
[7]位就是符号位呀!

Writing Code

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

    // assign out = { replicate-sign-bit , the-input };
    assign out = {{24{in[7]}},in};
endmodule

记得24{in[7]这个外面也要有大括号!!!!!

Vector5:more replication

Problem Statement

给定5个1位信号(a, b, c, d,和e),在25位输出向量中计算所有25个成对的1位比较。如果被比较的两位相等,输出应该是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次重复

Writing Code

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}};
//为了达到“相同的为1”其实就是同或
//先取反,再异或,其实就是同或。。。。用~^这个应该也可以!!
endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值