关于 "case" 语法生成的锁存器
About the latches generated by "case" syntax
我理解在systemverilog 中使用case 语法时,我们需要完整描述所有组合或添加默认值以避免闩锁。
这是我的示例代码,没有生成锁存器:
module test(
input logic[2:0] op,
output logic a,b,c
);
always_comb
begin
case(op)
0: {a,b,c} = {1'b1,1'b1,1'b0};
1: {a,b,c} = {1'b1,1'b0,1'b0};
2: {a,b,c} = {1'b0,1'b1,1'b0};
default: {a,b,c} = {1'b0,1'b0,1'b0};
endcase
end
endmodule
正如我一开始所说,如果添加默认值,则不会生成闩锁。
请看第二个代码,这是一个ALU设计:
module ALU(
output logic[31:0] Result,
output logic Zero, Overflow, Negative, Carryout,
input logic [5:0]ALUOp_i,
input logic [31:0] ALU_A_i, ALU_B_i,
input logic [4:0] Shamt
);
logic [31:0] adder_b;
always_comb
begin
casez(ALUOp_i)
/*Add_trap*/ 0,1: {Carryout,Result} = {ALU_A_i[31],ALU_A_i} + {ALU_B_i[31],ALU_B_i};
/*Add_notrap*/
/*Subtrap*/ 2,3:
/*Sub_notrap*/ begin
adder_b = ALU_B_i ^ {32{1'b1}};
{Carryout,Result} = {ALU_A_i[31],ALU_A_i} + {adder_b[31],adder_b} + 1;
end
/*SLL*/ 8: Result = ALU_B_i << Shamt;
/*SLLV*/ 9: Result = ALU_B_i << ALU_A_i;
/*SRA*/ 10: Result = ALU_B_i >>> Shamt;
/*SRAV*/ 11: Result = ALU_B_i >>> ALU_A_i;
/*SRL*/ 12: Result = ALU_B_i >> Shamt;
/*SRLV*/ 13: Result = ALU_B_i >> ALU_A_i;
/*AND*/ 14: Result = ALU_A_i && ALU_B_i;
/*OR*/ 15: Result = ALU_A_i || ALU_B_i;
/*XOR*/ 16: Result = ALU_A_i ^^ ALU_B_i;
default:
begin
Result = 0;
Carryout = 0;
adder_b = 0;
end
endcase
end
endmodule
上面的代码会生成锁存器,这里是Quartus II给出的结果:
Warning (10240): Verilog HDL Always Construct warning at ALU.sv(16):
inferring latch(es) for variable "Carryout", which holds its previous
value in one or more paths through the always construct
Warning (10240): Verilog HDL Always Construct warning at ALU.sv(16):
inferring latch(es) for variable "adder_b", which holds its previous
value in one or more paths through the always construct
Error (10166): SystemVerilog RTL Coding error at ALU.sv(16):
always_comb construct does not infer purely combinational logic.
我确实在案例的最后添加了一个默认值,有人可以解释一下这是怎么回事吗?非常感谢。
虽然您关于通过 case
语句的多个路径(并且需要 default
子句)的说法是正确的,但如果不是每个分支中都存在信号,也会生成锁存器。在这种情况下,Carryout
和 adder_b
仅出现在某些路径中。因此,您的综合工具假设您希望存储这些值,这会生成一个锁存器。
您需要为 case
的每个分支中的那些信号分配一些值。例如:
/*SLL*/ 8: begin
Result = ALU_B_i << Shamt;
ader_b = 0;
Carryout = 0;
end
对其他分支重复上述步骤。
这里简洁明了的解决方案是在 always_comb
块的开头分配默认值 Carryout
。最后一个赋值将获胜,因此任何没有为 Carryout
赋值的分支将获得默认值。
最后一个分配如何获胜的简单示例如下所示:
always_comb begin
Carryout = 1'b0;
if(some_condition) begin
Carryout = 1'b1;
end
end
在上面的代码中,Carryout
被赋值为 0,然后如果 some_condition
为真,它被重新赋值为 1。如果 some_condition
为假,那么它只是保留 "default" 值为 0。这一切都发生在同一时间步,因此输出上没有瞬态毛刺。
我理解在systemverilog 中使用case 语法时,我们需要完整描述所有组合或添加默认值以避免闩锁。
这是我的示例代码,没有生成锁存器:
module test(
input logic[2:0] op,
output logic a,b,c
);
always_comb
begin
case(op)
0: {a,b,c} = {1'b1,1'b1,1'b0};
1: {a,b,c} = {1'b1,1'b0,1'b0};
2: {a,b,c} = {1'b0,1'b1,1'b0};
default: {a,b,c} = {1'b0,1'b0,1'b0};
endcase
end
endmodule
正如我一开始所说,如果添加默认值,则不会生成闩锁。 请看第二个代码,这是一个ALU设计:
module ALU(
output logic[31:0] Result,
output logic Zero, Overflow, Negative, Carryout,
input logic [5:0]ALUOp_i,
input logic [31:0] ALU_A_i, ALU_B_i,
input logic [4:0] Shamt
);
logic [31:0] adder_b;
always_comb
begin
casez(ALUOp_i)
/*Add_trap*/ 0,1: {Carryout,Result} = {ALU_A_i[31],ALU_A_i} + {ALU_B_i[31],ALU_B_i};
/*Add_notrap*/
/*Subtrap*/ 2,3:
/*Sub_notrap*/ begin
adder_b = ALU_B_i ^ {32{1'b1}};
{Carryout,Result} = {ALU_A_i[31],ALU_A_i} + {adder_b[31],adder_b} + 1;
end
/*SLL*/ 8: Result = ALU_B_i << Shamt;
/*SLLV*/ 9: Result = ALU_B_i << ALU_A_i;
/*SRA*/ 10: Result = ALU_B_i >>> Shamt;
/*SRAV*/ 11: Result = ALU_B_i >>> ALU_A_i;
/*SRL*/ 12: Result = ALU_B_i >> Shamt;
/*SRLV*/ 13: Result = ALU_B_i >> ALU_A_i;
/*AND*/ 14: Result = ALU_A_i && ALU_B_i;
/*OR*/ 15: Result = ALU_A_i || ALU_B_i;
/*XOR*/ 16: Result = ALU_A_i ^^ ALU_B_i;
default:
begin
Result = 0;
Carryout = 0;
adder_b = 0;
end
endcase
end
endmodule
上面的代码会生成锁存器,这里是Quartus II给出的结果:
Warning (10240): Verilog HDL Always Construct warning at ALU.sv(16): inferring latch(es) for variable "Carryout", which holds its previous value in one or more paths through the always construct
Warning (10240): Verilog HDL Always Construct warning at ALU.sv(16): inferring latch(es) for variable "adder_b", which holds its previous value in one or more paths through the always construct
Error (10166): SystemVerilog RTL Coding error at ALU.sv(16): always_comb construct does not infer purely combinational logic.
我确实在案例的最后添加了一个默认值,有人可以解释一下这是怎么回事吗?非常感谢。
虽然您关于通过 case
语句的多个路径(并且需要 default
子句)的说法是正确的,但如果不是每个分支中都存在信号,也会生成锁存器。在这种情况下,Carryout
和 adder_b
仅出现在某些路径中。因此,您的综合工具假设您希望存储这些值,这会生成一个锁存器。
您需要为 case
的每个分支中的那些信号分配一些值。例如:
/*SLL*/ 8: begin
Result = ALU_B_i << Shamt;
ader_b = 0;
Carryout = 0;
end
对其他分支重复上述步骤。
这里简洁明了的解决方案是在 always_comb
块的开头分配默认值 Carryout
。最后一个赋值将获胜,因此任何没有为 Carryout
赋值的分支将获得默认值。
最后一个分配如何获胜的简单示例如下所示:
always_comb begin
Carryout = 1'b0;
if(some_condition) begin
Carryout = 1'b1;
end
end
在上面的代码中,Carryout
被赋值为 0,然后如果 some_condition
为真,它被重新赋值为 1。如果 some_condition
为假,那么它只是保留 "default" 值为 0。这一切都发生在同一时间步,因此输出上没有瞬态毛刺。