HDLBits——Vectors

HDLBits —— Vectors

Vectors(向量;总线)

要求:

Vector0.png

构造一个电路,拥有 1 个 3 bit 位宽的输入端口,4 个输出端口。其中一个输出端口直接输出输入的向量,剩下 3 个输出端口分别各自输出 3 bit 中的 1 bit。上图中,箭头上的小斜杠旁边的数字代表该向量(总线)的位宽,用于将向量同 wire 信号区别开来。

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

endmodule

改进:assign {o2, o1, o0} = vec;

PS:如果想要片选多个 bit,那么可以通过如下操作实现。

assign w = vec[1:0]; 
Timing Diagram:

image-20211125123807813

Vector in more detail

要求:

分别输出 16 位输入信号的高 8 位 和低 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_hi = in[15:8];
    assign out_lo = in[7:0];

endmodule
Timing Diagram:

image-20211125131135171

Vector part select(向量片选)

要求:

一个 32 位的向量可以看做由 4 个字节组成(bits[31:24],[23:16],[15:8],[7:0])。构建一个电路,将输入向量的字节顺序颠倒,也就是字节序大小端转换。

aaaaaaaabbbbbbbbccccccccdddddddd => ddddddddccccccccbbbbbbbbaaaaaaaa

这项操作常见于不同的大小端体系之间的数据交换,比如 x86 体系使用小端模式存储数据,而因特网协议中均使用大端模式,数据在本地和网络进行数据交换之前,均要进行大小端转换。

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

    assign out[31:24] = in[7:0];
    assign out[7:0] = in[31:24];
    assign out[23:16] = in[15:8];
    assign out[15:8] = in[23:16];

endmodule

改进:用一句 assign 语句代替,使用 {} 位拼接符。

assign out = {in[7-:8],in[15-:8],in[23-:8],in[31-:8]};
Timing Diagram:

image-20211125132816834

Bitwise operators(按位运算符)

要求:

模块有两个 3bit 宽的输入变量 a,b ,要求输出 a,b 逐位或、逻辑或、按位取反的结果,其中 b 在高位。

Vectorgates.png

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 = {a[2] | b[2],a[1] | b[1],a[0] | b[0]};
    assign out_or_logical = a || b;
    assign out_not = ~{b,a};

endmodule

PS:逐位逻辑运算符 VS 逻辑运算符

逐位逻辑运算符:对于 N 比特输入向量之间的逻辑比较,会在 N 比特上逐位进行,并产生一个 N 比特长的运算结果。

逻辑运算符:任何类型的输入都会被视作布尔值零->假,非零->真,将布尔值进行逻辑比较后,输出一个 1 比特的结果。

Timing Diagram:

image-20211125134553818

Four-input gates

要求:

分别构建一个 4 输入与门,或门以及异或门。

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];
    
endmodule

也可:使用缩减运算符的语法。

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

Timing Diagram:

image-20211125140821442

Vector concatenation operator(连接操作符)

要求:

方格表示模块 32 位输入向量,按照上下对应关系,输出为下方的 4 个 8 比特向量。

Vector3.png

Solution:
module top_module (
    input [4:0] a, b, c, d, e, f,
    output [7:0] w, x, y, z );//

    assign {w[7:0],x[7:0],y[7:0],z[7:0]} = {a[4:0],b[4:0],c[4:0],d[4:0],e[4:0],f[4:0],2'b11};


endmodule

也可: assign {w,x,y,z} = {a,b,c,d,e,f,2’b11}; 变量默认用了所有位,但数字必须说明是多少位。

PS:连接操作符的基本语法使用 { } 将较小的向量括起来,每个 { } 内的向量使用逗号作为间隔。连接运算符中的向量务必需要标注位宽,不然综合器不知道结果需要多宽。

习惯上把位连接符用在赋值语句的右侧,表示将较小的向量连接成较大的向量,赋予左值。但实际上位连接符同样可以用在赋值语句左侧,比如:

assign {cout,sum} = a + b + cin;

在表示全加器时,可以使用一句 assign 语句实现结果和进位的赋值。

Timing Diagram:

image-20211125143049455

Vector reversal 1

要求:

给定一个 8bit 输入向量,将其反向输出。

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

改进:循环实现(如果位数很高,比如2014位,手动调整就太麻烦了)

循环一:使用 for 循环

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

    integer i;
    always @(*) begin
        for(i=0; i<8; i=i+1)begin
             out[i] = in[7-i];
        end
    end

endmodule

PS:for 循环描述了电路的行为,而不是电路的结构,因此,for 循环必须置于比如 always 块这样的过程块(在块中的组合逻辑将会按照一定的顺序运行)中。而实际上电路是不存在循环,for 循环表示的代码将被综合器解析为硬件电路。

注意:

  1. Verilog 的语法需要提前定义 integer 变量,即整形,不能在循环里面定义。
  2. always 语句中所有信号必须是 reg 变量。
  3. 循环只能用 +1,不能用 ++。

循环二:使用 generate 生成块

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

    generate
        genvar i;
        for(i=0; i<8; i=i+1) begin:my_block_name
            assign out[i] = in[7-i];
        end
    endgenerate

endmodule

PS:for 循环和 Verilog 中其他的几种循环语句 while ,forever,repeat 本质上都用于控制语句的执行次数。但生成块主要用于动态生成语句,生成块可以例化 assign 语句模块信号和变量的声明以及 always initial 这样的过程块。循环生成块是生成块中的一种类型,在综合过程中同样被综合器进行编译,这个过程可以看做综合过程中动态生成更多 Verilog 代码的预处理过程。

总的来说,for 循环强调了对电路的行为描述,在综合的过程中循环展开,而生成块则用于综合过程中,动态生成代码,两者有本质上的不同。

注意:

  1. 在生成块中的 for 循环中不能使用 integer 作为循环变量,而必须使用 genvar 变量。
  2. begin 后面还有冒号,name。
Timing Diagram:

image-20211125144236950

Replication operator(重复操作符)

要求:

重复操作符的应用场景之一是在有符号数的扩展,即将符号位填充待扩展的比特。比如要将 4bit 的 4’b0101 有符号数扩展为 8bit ,0 是符号位,那么扩展之后为 8’b0000 0101。

构建一个电路,将一个 8bit 有符号数扩展为 32bit 数。

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

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

PS:语法为 { 重复次数 { 向量 } },重复次数必须是一个常量,而且重复操作符有两对 { },别漏了外层。

More Replication

要求:

将 5 个 1bit 信号分别组成下图中两个 25 bit 信号,输出向量为这两个 25bit 向量的逐位操作的结果。如果两个待比较信号某比特相同,则结果向量对应的该比特位 1 。

Vector5.png

Solution:
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}});

endmodule

部分题目改进思路来源于 LogicJitterGibbs,感谢大神。

果然站在巨人的的肩膀上做起来更快更有效率。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值