Conditional ternary operator
posedge
posedge
是 Verilog 中用于检测信号上升沿的关键字,表示信号从低电平变为高电平的瞬间。
非阻塞赋值
非阻塞赋值的特性
执行机制
- 并发执行:非阻塞赋值在当前时间步的所有语句计算完成后,才统一更新变量值。
- 延迟生效:赋值语句不会立即改变变量值,而是 “计划” 在当前时间步结束时更新。
代码示例
always @(posedge clk) begin a <= b; // 计划在时间步结束时将b的值赋给a b <= a; // 计划在时间步结束时将a的原始值赋给b end
上述代码中,
a
和b
会在时钟上升沿同时更新值,因此交换了彼此的数据。
特性 | 非阻塞赋值 (<= ) | 阻塞赋值 (= ) |
---|---|---|
执行顺序 | 并发执行,时间步结束时统一更新 | 顺序执行,立即更新变量值 |
适用场景 | 时序逻辑(触发器、状态机) | 组合逻辑(算术运算、条件判断) |
潜在问题 | 无竞争条件 | 时序逻辑中可能引发竞争和毛刺 |
典型应用 | always @(posedge clk) | always @(*) 或 assign |
assign
assign
语句是用于描述组合逻辑的基本方式,它会持续监控右侧表达式的变化,并立即将结果赋值给左侧的信号。这种赋值方式对应实际硬件中的逻辑门电路,适合实现组合逻辑功能。
Solution
module top_module (
input [7:0] a, b, c, d,
output [7:0] min);//
// assign intermediate_result1 = compare? true: false;
wire [7:0] m1 = a < b ? a : b;
wire [7:0] m2 = m1 < c ? m1 : c;
wire [7:0] m3 = m2 < d ? m2 : d;
assign min = m3;
endmodule
Reduction operators
The _reduction_ operators (归约运算符) can do AND, OR, and XOR of the bits of a vector, producing one bit of output:
& a[3:0] // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0] // OR: b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0] // XOR: c[2]^c[1]^c[0]
Solution
module top_module (
input [7:0] in,
output parity);
assign parity = ^ in;
endmodule
Reduction: Even wider gates
Solution
module top_module(
input [99:0] in,
output out_and,
output out_or,
output out_xor
);
assign out_and = & in;
assign out_or = | in;
assign out_xor = ^ in;
endmodule
Combinational for-loop: Vector reversal 2
Solution (Standard)
module top_module (
input [99:0] in,
output reg [99:0] out
);
always @(*) begin
for (int i=0;i<$bits(out);i++)
// $bits() is a system function that returns the width of a signal.
out[i] = in[$bits(out)-i-1];
// $bits(out) is 100 because out is 100 bits wide.
end
endmodule
Solution (2)
module top_module(
input [99:0] in,
output [99:0] out
);
// for语句会占用大量资源,因此也可以采用generate for语句
genvar i;
generate
for(i=0;i<100;i=i+1)begin:reverse_bit
assign out[99-i] = in[i];
end
endgenerate
endmodule
Combinational for-loop: 255-bit population count
Solution
module top_module(
input [254:0] in,
output [7:0] out );
integer i; // 声明循环变量
always @(*) begin
out = 8'd0; // 初始化计数器
for(i = 0; i < $bits(in);i++) begin
if(in[i] == 1'b1)
out = out + 1;
end
end
endmodule
Generate for-loop: 100-bit binary adder 2
Solution
module top_module(
input [99:0] a, b,
input cin,
output [99:0] cout,
output [99:0] sum
);
// 使用generate块来实例化100个全加器
genvar i;
generate
for (i = 0; i < 100; i = i + 1) begin: full_adder
// 每个全加器的进位连接
wire current_cin;
if (i == 0) begin
assign current_cin = cin;
end else begin
assign current_cin = cout[i-1];
end
// 全加器逻辑
assign sum[i] = a[i] ^ b[i] ^ current_cin;
assign cout[i] = (a[i] & b[i]) | (a[i] & current_cin) | (b[i] & current_cin);
end
endgenerate
endmodule
- 在 Verilog 中实例化多个相同结构需要使用 generate 语句。
genvar
是一种特殊的变量类型,专门用于 生成块(generate
块) 中的循环索引。它的主要作用是在编译时展开循环,生成多个相同结构的硬件实例,而不是在运行时执行循环。- 编译时展开
genvar
声明的变量仅在编译时存在,用于控制generate
块的循环次数。综合工具会将循环完全展开为多个独立的硬件实例,而非运行时的循环逻辑。 与普通变量的区别
- 普通变量(如
integer
):用于运行时的计算和控制流。 - genvar:仅用于编译时生成硬件结构,循环展开后变量本身不会存在于实际硬件中。
- 普通变量(如
- 作用域
genvar
只能在generate
块内部使用,且必须在循环开始前声明。
- 编译时展开
Generate for-loop: 100-digit BCD adder
Solution
module top_module(
input [399:0] a, b,
input cin,
output cout,
output [399:0] sum );
genvar i;
generate
for (i = 0; i < 100; i = i + 1) begin: bcd_stage
wire [3:0] a_slice = a[4*i + 3: 4*i]; //注意:4*i 中间的乘号不能省略
wire [3:0] b_slice = b[4*i + 3: 4*i]; //注意:是冒号而不是逗号
wire current_cin = (i == 0) ? cin : bcd_stage[i - 1].current_cout;
// 实例化BCD全加器
bcd_fadd u1 (
.a(a_slice), //注意是用逗号分隔而不是用分号
.b(b_slice),
.cin(current_cin),
.cout(current_cout),
.sum(sum[4*i + 3: 4*i]) );
// 暴露进位输出,供下一阶段使用
wire current_cout ;
assign bcd_stage[i].current_cout = current_cout; //注意:字母别抄错了
end
endgenerate
// 最后一个阶段的进位输出作为模块的cout
assign cout = bcd_stage[99].current_cout;
endmodule