Skip to content
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

[Arcilator] Allow running verif.simulation ops #8233

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

fabianschuiki
Copy link
Contributor

Add the LowerVerifSimulations pass to the Arc dialect. This pass converts any verif.simulation ops in the input into a test harness module and a func.func that instantiates the module and applies the necessary stimulus to it using arc.sim.* ops.

Also add the pass to the arcilator pipeline. This allows the user to run any verif.simulation ops in the input design by specifying the --run --jit-entry=<simulation-name> command line options. We may want to streamline this in the future.

Also add an integration test which verifies an adder circuit and prints the simulation results.

@fabianschuiki fabianschuiki added Arc Involving the `arc` dialect verif labels Feb 12, 2025
@fabianschuiki fabianschuiki force-pushed the fschuiki/arcilator-verif-simulation branch from 8a3a802 to 5a66259 Compare February 12, 2025 20:48
%failure = seq.compreg %6, %clock reset %init, %false : i1
%6 = comb.or %failure, %5 : i1

// Print some statistics about the test.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice use of DPI!

Copy link
Member

@maerhart maerhart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really cool work! 😎

getOperation().getLoc(), builder.getStringAttr("exit"),
builder.getFunctionType({builder.getI32Type()}, {}));
SymbolTable::setSymbolVisibility(func, SymbolTable::Visibility::Private);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to check the type of the existing function in the else case?


SmallVector<StringAttr> symbols;
getOperation().walk([&](verif::SimulationOp op) {
auto *symbolTableOp = SymbolTable::getNearestSymbolTable(op);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need to have verif.simulation nested in arbitrary ops or could we require builtin.module as parent to simplify this?

auto *symbolTableOp = SymbolTable::getNearestSymbolTable(op);
assert(symbolTableOp);
if (symbolTableOp == getOperation())
symbols.push_back(op.getSymNameAttr());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you collecting the symbols here but never using them?

Comment on lines +121 to +122
auto &funcBody = funcOp.getBody().emplaceBlock();
builder.setInsertionPointToEnd(&funcBody);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super-nit: builder has a method createBlock that does this in one line

Comment on lines +133 to +135
auto &instBody = instOp.getBody().emplaceBlock();
auto instArg = instBody.addArgument(instType, op.getLoc());
builder.setInsertionPointToEnd(&instBody);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Comment on lines +144 to +151
builder.create<SimSetInputOp>(op.getLoc(), instArg, clockName, lowOp);
builder.create<SimSetInputOp>(op.getLoc(), instArg, initName, trueOp);
builder.create<SimStepOp>(op.getLoc(), instArg);
builder.create<SimSetInputOp>(op.getLoc(), instArg, clockName, highOp);
builder.create<SimStepOp>(op.getLoc(), instArg);
builder.create<SimSetInputOp>(op.getLoc(), instArg, clockName, lowOp);
builder.create<SimSetInputOp>(op.getLoc(), instArg, initName, falseOp);
builder.create<SimStepOp>(op.getLoc(), instArg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: might be worth storing the location in a temporary

Comment on lines +176 to +178
// Compute `exit_code | (exit_code != 0)` as a way of guaranteeing that
// the exit code will be non-zero even if bits are truncated by the operating
// system.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smart way to deal with the fact that exit typically ANDs with 0xFF. Maybe worth mentioning in the simulation op documentation that on non-zero exit code the least significant bit is always set? But I guess also not really, because we are very vague about that anyway.

Comment on lines +27 to +34
// CHECK: arc.sim.set_input [[A]], "clock" = [[C0]]
// CHECK: arc.sim.set_input [[A]], "init" = [[I1]]
// CHECK: arc.sim.step [[A]]
// CHECK: arc.sim.set_input [[A]], "clock" = [[C1]]
// CHECK: arc.sim.step [[A]]
// CHECK: arc.sim.set_input [[A]], "clock" = [[C0]]
// CHECK: arc.sim.set_input [[A]], "init" = [[I0]]
// CHECK: arc.sim.step [[A]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the simulation op description didn't mention that done is not checked when init=1. Which, of course, makes sense, but might be worth mentioning.


#include "circt/Dialect/Arc/ArcOps.h"
#include "circt/Dialect/Arc/ArcPasses.h"
#include "circt/Dialect/Comb/CombOps.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused?

%0 = comb.add %count, %c1_i19 : i19

// Generate inputs to the adder.
%1, %2 = func.call @generateAdderInputs(%count) : (i19) -> (i42, i42)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking this would need to be a module instantiation since the region describes hardware, right? And the function would need to use comb instead of arith.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's a good point. This happens to survive the Arcilator pipeline unharmed. A DPI call would probably fit better here, to reflect what you would type up in some SV input.

@fabianschuiki fabianschuiki force-pushed the fschuiki/verif-simulation branch from 2ab5136 to d99eb7a Compare February 13, 2025 21:43
@fabianschuiki fabianschuiki force-pushed the fschuiki/arcilator-verif-simulation branch from 5a66259 to bdf9e04 Compare February 13, 2025 21:43
Base automatically changed from fschuiki/verif-simulation to main February 14, 2025 16:18
Add the `LowerVerifSimulations` pass to the Arc dialect. This pass
converts any `verif.simulation` ops in the input into a test harness
module and a `func.func` that instantiates the module and applies the
necessary stimulus to it using `arc.sim.*` ops.

Also add the pass to the arcilator pipeline. This allows the user to
run any `verif.simulation` ops in the input design by specifying the
`--run --jit-entry=<simulation-name>` command line options. We may want
to streamline this in the future.

Also add an integration test which verifies an adder circuit and prints
the simulation results.
@fabianschuiki fabianschuiki force-pushed the fschuiki/arcilator-verif-simulation branch from bdf9e04 to 588a431 Compare February 14, 2025 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Arc Involving the `arc` dialect verif
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants