我理解在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给出的结果:
警告(10240):Verilog HDL始终在ALU.sv(16)构建警告: 推断变量“Carryout”的锁存器,它保持其先前的 通过always构造的一个或多个路径中的值
警告(10240):Verilog HDL始终在ALU.sv(16)构建警告: 推断变量“adder_b”的锁存器,它保存其先前的 通过always构造的一个或多个路径中的值
错误(10166):ALU.sv(16)处的SystemVerilog RTL编码错误: always_comb构造并不能推断纯粹的组合逻辑。
我在案例的最后添加了一个默认值,有人可以解释一下发生了什么吗?非常感谢。
答案 0 :(得分:5)
此处干净简便的解决方案是在Carryout
块的开头指定默认值always_comb
。最后一项任务将获胜,因此任何未向Carryout
分配值的分支都将获得默认值。
上一个作业如何获胜的一个简单示例如下所示:
always_comb begin
Carryout = 1'b0;
if(some_condition) begin
Carryout = 1'b1;
end
end
在上面的代码Carryout
被分配为0,然后如果some_condition
为真,则将其重新分配给1.如果some_condition
为假,则它只保留“默认”值为0.这一切都发生在同一时间步,因此输出上没有瞬态毛刺。
答案 1 :(得分:1)
虽然通过case
语句(并且需要default
子句)的多个路径是正确的,但如果每个分支中不存在信号,也会生成锁存器。在这种情况下,Carryout
和adder_b
仅出现在某些路径中。因此,您的综合工具假定您希望存储值,从而生成锁存器。
您需要为case
的每个分支中的那些信号分配一些值。例如:
/*SLL*/ 8: begin
Result = ALU_B_i << Shamt;
ader_b = 0;
Carryout = 0;
end
重复其他分支。