HDLBits答案(5)_Generate实例化模块
Generate实例化模块
三目运算符
Verilog中有跟C语言类似的三目运算符,这可以用于在一行中根据条件选择两个值中的一个,而不用在组合always块中使用if-then。
语法很简单:1
条件 ? 值1 : 值2
就像生活中做选择题:如果条件成立,选值1;否则选值2。
下面给出一些示例:
1 | (0 ? 3 : 5) // 结果是5,因为条件是false |
题目:四个数找最小值
题目描述:给定4个无符号数,求最小值。无符号数可以用比较运算符(a < b)进行比较。
Solution:
1 | module top_module ( |
小贴士:三目运算符可以嵌套使用,但要注意可读性。如果嵌套太多层,用if-else或者case语句可能更清晰。
缩位运算符
缩位运算符可以对向量的各个位进行与、或和异或操作,产生1位的输出。这就像把向量的所有位”缩”成一位,所以叫缩位运算符。
1 | & a[3:0] // 与缩位: a[3] & a[2] & a[1] & a[0],等价于(a[3:0] == 4'hf) |
我们可以通过翻转上述缩位运算的输出来获得与非、或非和同或缩位运算的输出。
题目1:奇偶校验
题目描述1:奇偶校验经常被用作传输数据时检测错误的简单方法。创建一个电路,为一个8位字节计算一个奇偶校验位(它将在1个字节的基础上增加1位)。我们将使用”偶数”奇偶校验,其中奇偶校验位只是所有8位数据位的异或。
Solution 1:
1 | module top_module ( |
题目2:100位输入的缩位运算
题目描述2:构建一个组合电路,包括100个输入,in[99:0];
3个输出:
out_and:100个位的与运算输出out_or:100个位的或运算输出out_xor:100个位的异或运算输出
Solution 2:
1 | module top_module( |
for循环的组合逻辑:向量顺序翻转
题目描述:给定一个100位的输入向量,翻转它的位顺序。
Solution:
1 | module top_module( |
小贴士:在组合逻辑的always块中使用for循环是可以综合的!但要注意循环次数必须是固定的(编译时就能确定)。
for循环的组合逻辑:255位数统计1的个数
题目描述:”计数”电路对输入向量中的1进行计数。为一个255位输入向量建立一个”计数”电路。
Solution:
1 | module top_module ( |
插叙:锁存器的危害
在继续讲Generate之前,让我们再回顾一下为什么要避免锁存器。之前提到过,这里再总结一下:
锁存器会对电路产生哪些影响呢?
- 锁存器对毛刺敏感,无异步复位端,不能让芯片在上电时处在确定的状态;
- 锁存器会使静态时序分析变得很复杂,不利于设计的可重用。
所以,在ASIC设计中,除了CPU高速电路,或者RAM这种对面积很敏感的电路,一般不提倡用锁存器。
循环生成语句
如果说for循环是让软件代码重复执行多次,那么Generate语句就是让硬件代码重复生成多次!
Generate语句常用于编写可配置的、可综合的RTL设计结构。它可用于创建模块的多个实例化,或者有条件的实例化代码块。
generate循环的语法与for循环语句的语法很相似。但是在使用时必须先在genvar声明中声明循环中使用的索引变量名,然后才能使用它。
genvar声明的索引变量被用作整数用来判断generate循环。genvar声明可以是generate结构的内部或外部区域,并且相同的循环索引变量可以在多个generate循环中,只要这些循环不嵌套。
注意:genvar只有在建模的时候才会出现,在仿真时就已经消失了!
Verilog中generate循环中的generate块可以命名也可以不命名。如果已命名,则会创建一个generate块实例数组。如果未命名,则有些仿真工具会出现警告,因此,最好始终对它们进行命名。
题目1:100位行波进位加法器
题目描述1:通过实例化100个全加法器,创建一个100位行波进位加法器。
Solution 1:
1 | module top_module( |
题目2:100位BCD行波进位加法器
题目描述2:
为您提供了一个名为bcd_fadd的BCD一位加法器,它的输入为两个BCD数字和进位输入信号,并生成求和输出和进位输出信号。
1 | module bcd_fadd ( |
实例化bcd_fadd的100个副本,以创建一个100位的BCD行波进位加法器。
Solution 2:
1 | module top_module( |
入门者避坑指南
在这一章,我们学习了很多实用的设计技巧,初学者在使用这些技巧时容易犯以下错误:
错误1:generate循环中使用普通变量而非genvar
错误表现:1
2
3
4
5
6
7
8
9
10
11
12module bad_example (
input [9:0] a, b,
output [9:0] y
);
integer i; // 错误!应该用genvar
generate
for(i = 0; i < 10; i = i + 1) begin: my_loop
assign y[i] = a[i] & b[i];
end
endgenerate
endmodule
错误原因:
generate循环的索引变量必须是genvar类型,不能是integer。genvar是专门用于generate语句的类型,它在编译时就会被处理掉,不会出现在实际硬件中。
正确做法:1
2
3
4
5
6
7
8
9
10
11
12module good_example (
input [9:0] a, b,
output [9:0] y
);
genvar i; // 正确:使用genvar
generate
for(i = 0; i < 10; i = i + 1) begin: my_loop
assign y[i] = a[i] & b[i];
end
endgenerate
endmodule
调试技巧:
- 记住:generate循环用genvar,always块里的for循环用integer
错误2:generate块没有命名
错误表现:1
2
3
4
5
6
7
8
9
10
11
12module bad_example (
input [9:0] a, b,
output [9:0] y
);
genvar i;
generate
for(i = 0; i < 10; i = i + 1) begin // 错误!没有命名
assign y[i] = a[i] & b[i];
end
endgenerate
endmodule
错误原因:
虽然有些工具可能接受没有命名的generate块,但这不是好的编码风格。有些仿真工具会发出警告,而且在调试时很难引用generate块内部的信号。
正确做法:1
2
3
4
5
6
7
8
9
10
11
12module good_example (
input [9:0] a, b,
output [9:0] y
);
genvar i;
generate
for(i = 0; i < 10; i = i + 1) begin: my_loop // 加上名字
assign y[i] = a[i] & b[i];
end
endgenerate
endmodule
调试技巧:
- 给generate块起个有意义的名字,这样在波形查看器中也更容易找到对应的信号
错误3:组合逻辑for循环中忘记给变量赋初值
错误表现:1
2
3
4
5
6
7
8
9
10
11
12
13module bad_example (
input [3:0] in,
output [2:0] out
);
integer i;
always @(*) begin
// 错误!out没有赋初值
for(i = 0; i < 4; i = i + 1) begin
out = out + in[i];
end
end
endmodule
错误原因:
out在第一次循环时使用了它的旧值,这会导致生成锁存器!
正确做法:1
2
3
4
5
6
7
8
9
10
11
12
13module good_example (
input [3:0] in,
output [2:0] out
);
integer i;
always @(*) begin
out = 3'd0; // 先赋初值!
for(i = 0; i < 4; i = i + 1) begin
out = out + in[i];
end
end
endmodule
调试技巧:
- 在always块开头,先给所有要赋值的变量赋初值!这是避免锁存器的万能良药。
错误4:三目运算符嵌套过深
错误表现:1
2
3
4
5
6
7
8
9
10
11
12
13
14module bad_example (
input [2:0] sel,
input [7:0] a, b, c, d, e, f, g, h,
output [7:0] y
);
// 错误!嵌套太深了,难以阅读
assign y = (sel == 3'd0) ? a :
(sel == 3'd1) ? b :
(sel == 3'd2) ? c :
(sel == 3'd3) ? d :
(sel == 3'd4) ? e :
(sel == 3'd5) ? f :
(sel == 3'd6) ? g : h;
endmodule
错误原因:
虽然语法上没问题,但嵌套太深的三目运算符可读性很差,容易出错。
正确做法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19module good_example (
input [2:0] sel,
input [7:0] a, b, c, d, e, f, g, h,
output reg [7:0] y
);
always @(*) begin
case(sel)
3'd0: y = a;
3'd1: y = b;
3'd2: y = c;
3'd3: y = d;
3'd4: y = e;
3'd5: y = f;
3'd6: y = g;
3'd7: y = h;
default: y = 8'd0;
endcase
end
endmodule
调试技巧:
- 如果选择分支超过3个,考虑用case语句代替三目运算符
- 代码首先是给人看的,其次才是给编译器看的!
错误5:generate循环的边界错误
错误表现:1
2
3
4
5
6
7
8
9
10
11
12
13module bad_example (
input [99:0] a,
output [99:0] b
);
genvar i;
generate
// 错误!应该是i < 100,不是i <= 100
for(i = 0; i <= 100; i = i + 1) begin: my_loop
assign b[i] = ~a[i];
end
endgenerate
endmodule
错误原因:
向量是[99:0],只有100位,索引到100就越界了!
正确做法:1
2
3
4
5
6
7
8
9
10
11
12module good_example (
input [99:0] a,
output [99:0] b
);
genvar i;
generate
for(i = 0; i < 100; i = i + 1) begin: my_loop
assign b[i] = ~a[i];
end
endgenerate
endmodule
调试技巧:
- 仔细检查向量的范围和循环的边界
- 向量[N:0]有N+1位,循环应该从0到N
本章小结
这一章我们学习了很多实用的设计技巧:
- 三目运算符:
条件 ? 值1 : 值2,适合简单的2选1选择 - 缩位运算符:
&、|、^,把向量缩成1位 - 组合逻辑中的for循环:可以综合!注意循环次数要固定,还要记得给变量赋初值
- Generate语句:硬件代码的”复制粘贴”,用于批量生成模块或逻辑
- 必须用genvar声明索引变量
- 最好给generate块命名
- 可以配合if-else进行条件生成
这些技巧可以在设计过程中大大简化工作量,让代码更简洁、更易维护!



