HDLBits刷题日记(四)
打算利用碎片化时间重温一遍Verilog语法,好记忆不如烂笔头,在此记录一下HDLBits刷题的过程,记录一些知识点,方便日后再次复习。
1、Verilog Language - Vectors
Problem 15 : 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 and 4'd10 are both 4'b1010 in binary
连接操作符的基本语法使用 { } 将较小的向量括起来,每个 { } 内的向量使用逗号作为间隔。
连接运算符中的向量务必需要标注位宽,不然综合器怎么能知道你的结果需要多宽的位宽。因此 { 1,2,3 } 这样的操作是非法的,并会产生一个 Error:unsized constants are not allowed in concatenations.
连接运算符可用于赋值的左侧和右侧。
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.
assign {cout,sum} = a + b + cin;
// 在表示全加器时,可以使用一句 assign 语句实现结果和进位的赋值。
Given several input vectors, concatenate them together then split them up into several output vectors. There are six 5-bit input vectors: a, b, c, d, e, and f, for a total of 30 bits of input. There are four 8-bit output vectors: w, x, y, and z, for 32 bits of output. The output should be a concatenation of the input vectors followed by two 1 bits:
给定几个输入向量,将它们连接在一起,然后将它们分成几个输出向量。有六个 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 { ... } = { ... };
// assign w = {a,b[4:2]};
// assign x = {b[1:0],c,d[4]};
// assign y = {d[3:0],e[4:1]};
// assign z = {e[0],f,2'b11};
assign {w,x,y,z} = {a,b,c,d,e,f,2'b11};
endmodule
Problem 16 : Vector reversal 1
Given an 8-bit input vector [7:0], reverse its bit ordering.
给定一个 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]};
// assign {out[0],out[1],out[2],out[3],out[4],out[5],out[6],out[7]} = in;
endmodule
假设输入为超级超级长的向量,比如 2048 bit 这种,此时不在合适使用上面的方式了。
此时可以使用一个 for 循环或者使用 generate 语句来实现。
- for 循环
module top_module(
input [7:0] in,
output [7:0] out
);
integer i;
always @(*) begin
for (i=0; i<8; i++) //Use integer for pure Verilog.
out[i] = in[8-i-1];
end
endmodule
我们可以在创建一个组合逻辑 always 块,在块中的组合逻辑将会按照一定的顺序运行。for 循环描述了电路的行为,而不是电路的结构,因此,for 循环必须置于比如 always 块这样的过程块中。(描述电路行为)
for 循环中的“循环”指的是代码层面的循环,其实,电路是不存在循环这种的东西的,无论是信号而是门电路,都不存在循环一说。实际上,for 循环表示的代码将被综合器解析,for 循环将被分别解析为硬件电路。(不过在仿真中,确实按照循环处理)。
所以 for 循环可以理解为代码循环的语法,减少编码量,但真正的硬件电路不存在循环。
Verilog 的语法需要提前定义 integer 变量,即整形。
- generate 语句
module top_module(
input [7:0] in,
output [7:0] out
);
genvar i;
generate
for (i=0; i<8; i=i+1) begin: gen_name
assign out[i] = in[7-i];
end
endgenerate
endmodule
虽然在 generate ,endgenerate 之间使用的仍然是 for 循环,但生成块的概念和上面的 for 循环完全不同。
生成块可以例化 assign 语句,模块,信号和变量的声明以及 always initial 这样的过程块。循环生成块是生成块中的一种类型,在综合过程中同样被综合器进行编译,这个过程可以看做综合过程中动态生成更多 Verilog 代码的预处理过程。在上面的例子中,generate 块在综合的过程中,综合了 8 句 assign 赋值语句。
总的来说,for 循环强调了对电路的行为描述,在综合的过程中循环展开,而生成块则用于综合过程中,动态生成代码,两者有本质上的不同。
Problem 17 : Replication operator
连接操作符允许我们将短小的向量连接在一起构成更宽的向量。很方便,但有的时候需要将多个重复的向量连接在一起,诸如 assign a = {b,b,b,b,b,b}; 这样的操作仍然很繁琐。而重复操作符语法就可以在这种情况下允许你将一个向量重复多次,并将它们连接在一起,语法是这样:{num{vector}} ,也就是{ 重复次数 { 向量 } }
{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.
One common place to see a replication operator is when sign-extending a smaller number to a larger one, while preserving its signed value. This is done by replicating the sign bit (the most significant bit) of the smaller number to the left. For example, sign-extending 4'b0101 (5) to 8 bits results in 8'b00000101 (5), while sign-extending 4'b1101 (-3) to 8 bits results in 8'b11111101 (-3).
Build a circuit that sign-extends an 8-bit number to 32 bits. This requires a concatenation of 24 copies of the sign bit (i.e., replicate bit[7] 24 times) followed by the 8-bit number itself.
重复操作符的一个常见用法是将较小的数字符号扩展到较大的数字,同时保留其符号值。这是通过复制较小数字的符号位(最高有效位)来实现的。例如,将 4'b0101(5)符号扩展到 8 位,结果为 8'b00000101(5),而将 4'b1101(-3)符号扩展到 8 位,结果为 8'b11111101(-3)。
构建一个电路,将 8 位数字符号扩展到 32 位。这需要将符号位(即,复制 bit[7] 24 次)连接在一起,然后是 8 位数字本身。
module top_module(
input [7:0] in,
output [31:0] out
);
assign out = { {24{in[7]}}, in }; // 24 copies of in[7] followed by in itself.
endmodule
Problem 18 : More Replication
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 output should be 1 if the two bits being compared are equal.
给定 5 个 1 bit 信号(a、b、c、d 和 e),将 5 个 1bit 信号分别组成下图中两个 25 bit 信号,输出向量为这两个 25 bit 向量的逐位操作的结果。如果被比较的两个比特相等,则输出为 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;

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