-
Notifications
You must be signed in to change notification settings - Fork 466
Plumb dbg.variable anchors into SMT symbol names #10237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "circt/Dialect/Comb/CombOps.h" | ||
| #include "circt/Dialect/Debug/DebugOps.h" | ||
| #include "circt/Dialect/HW/HWInstanceGraph.h" | ||
| #include "circt/Dialect/HW/HWOps.h" | ||
| #include "circt/Dialect/Seq/SeqOps.h" | ||
|
|
@@ -51,6 +52,8 @@ struct ExternalizeRegistersPass | |
| DenseMap<StringAttr, SmallVector<StringAttr>> addedOutputNames; | ||
| DenseMap<StringAttr, SmallVector<Attribute>> initialValues; | ||
|
|
||
| void materializeDebugVariables(HWModuleOp module); | ||
|
|
||
| LogicalResult externalizeReg(HWModuleOp module, Operation *op, Twine regName, | ||
| Value clock, Attribute initState, Value reset, | ||
| bool isAsync, Value resetValue, Value next); | ||
|
|
@@ -204,7 +207,48 @@ void ExternalizeRegistersPass::runOnOperation() { | |
| module->setAttr("initial_values", | ||
| ArrayAttr::get(&getContext(), | ||
| initialValues[module.getSymNameAttr()])); | ||
| materializeDebugVariables(module); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void ExternalizeRegistersPass::materializeDebugVariables(HWModuleOp module) { | ||
| auto *body = module.getBodyBlock(); | ||
| DenseSet<Value> trackedValues; | ||
| for (auto varOp : body->getOps<debug::VariableOp>()) | ||
| trackedValues.insert(varOp.getValue()); | ||
|
|
||
| DenseSet<StringAttr> outputPortNames; | ||
| for (auto &port : module.getPortList()) | ||
| if (port.isOutput()) | ||
| outputPortNames.insert(port.name); | ||
|
|
||
| OpBuilder builder = OpBuilder::atBlockBegin(body); | ||
| StringRef stateSuffix = "_state"; | ||
| for (auto &port : module.getPortList()) { | ||
| if (port.isOutput()) | ||
| continue; | ||
| if (isa<seq::ClockType>(port.type)) | ||
| continue; | ||
| if (!port.name || port.name.getValue().empty()) | ||
| continue; | ||
|
|
||
| auto value = body->getArgument(port.argNum); | ||
| if (!trackedValues.insert(value).second) | ||
| continue; | ||
|
|
||
| StringAttr debugName = port.name; | ||
| auto portName = port.name.getValue(); | ||
| if (portName.ends_with(stateSuffix)) { | ||
| auto baseName = portName.drop_back(stateSuffix.size()); | ||
| auto nextNameAttr = | ||
| builder.getStringAttr((Twine(baseName) + "_next").str()); | ||
| if (outputPortNames.contains(nextNameAttr)) | ||
| debugName = builder.getStringAttr(baseName); | ||
| } | ||
|
|
||
| debug::VariableOp::create(builder, value.getLoc(), debugName, value, | ||
| /*scope=*/Value{}); | ||
|
Comment on lines
+215
to
+251
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reasoning behind doing this in ExternalizeRegisters? It seems quite distinct from the existing function of the pass, it might be sensible to put this in a separate pass (which it would be nice to split into a different PR from the VerifToSMT changes)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes! I think yes it is sensible to put this in a seperate pass, like That said, I am happy to split this into a dedicated pass and then a follow up PR where VerifToSMT consumes those anchors. I can rework this so ExternalizeRegisters stays focused and the pipeline composes the two passes explicitly. |
||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -457,3 +457,37 @@ func.func @multi_nondet() -> () { | |
| } | ||
| return | ||
| } | ||
|
|
||
| // ----- | ||
|
|
||
| // CHECK-LABEL: func.func @bmc_debug_anchors_to_names() -> i1 { | ||
| // CHECK: smt.declare_fun "port_data" : !smt.bv<8> | ||
| // CHECK: smt.declare_fun "state_data" : !smt.bv<8> | ||
| // CHECK: smt.declare_fun "port_data" : !smt.bv<8> | ||
| // CHECK-NOT: dbg.variable | ||
|
Comment on lines
+463
to
+467
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to check a bit more of the IR here to make sure that the right names are being given to the right functions |
||
|
|
||
| func.func @bmc_debug_anchors_to_names() -> i1 { | ||
| %bmc = verif.bmc bound 2 num_regs 1 initial_values [unit] | ||
| init { | ||
| %c0_i1 = hw.constant 0 : i1 | ||
| %clk = seq.to_clock %c0_i1 | ||
| verif.yield %clk : !seq.clock | ||
| } | ||
| loop { | ||
| ^bb0(%clk: !seq.clock): | ||
| %fromClock = seq.from_clock %clk | ||
| %c-1_i1 = hw.constant -1 : i1 | ||
| %nclk = comb.xor %fromClock, %c-1_i1 : i1 | ||
| %toClock = seq.to_clock %nclk | ||
| verif.yield %toClock : !seq.clock | ||
| } | ||
| circuit { | ||
| ^bb0(%clk: !seq.clock, %data: i8, %state: i8): | ||
| dbg.variable "port_data", %data : i8 | ||
| dbg.variable "state_data", %state : i8 | ||
| %true = hw.constant true | ||
| verif.assert %true : i1 | ||
| verif.yield %data, %state : i8, i8 | ||
| } | ||
| return %bmc : i1 | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if you use llvm::make_early_inc_range you should be able to do this in the loop directly