Skip to content

Commit 8389379

Browse files
[FIRRTL] Lower firrtl.simulation to verif.simulation (#8312)
Add a lowering from `firrtl.simulation` to `verif.simulation` in the FIRRTL-to-HW conversion. The lowering creates a `verif.simulation` op that simply instantiates the module pointed to by `firrtl.simulation`, connecting its `clock` and `init` ports to the simulation op's block arguments, and yielding the `done` and `success` ports back. We may want to inline the instance in the future, but that is purely cosmetic. This now allows FIR files to contain `simulation` unit tests that can be lowered to HW through `firtool` and then executed using `arcilator`.
1 parent df566b7 commit 8389379

File tree

2 files changed

+92
-17
lines changed

2 files changed

+92
-17
lines changed

lib/Conversion/FIRRTLToHW/LowerToHW.cpp

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ struct FIRRTLModuleLowering
575575
CircuitLoweringState &loweringState);
576576
LogicalResult lowerFormalBody(verif::FormalOp formalOp,
577577
CircuitLoweringState &loweringState);
578+
LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
579+
CircuitLoweringState &loweringState);
578580
};
579581

580582
} // end anonymous namespace
@@ -618,6 +620,7 @@ void FIRRTLModuleLowering::runOnOperation() {
618620

619621
SmallVector<hw::HWModuleOp, 32> modulesToProcess;
620622
SmallVector<verif::FormalOp> formalOpsToProcess;
623+
SmallVector<verif::SimulationOp> simulationOpsToProcess;
621624

622625
AnnotationSet circuitAnno(circuit);
623626
moveVerifAnno(getOperation(), circuitAnno, extractAssertAnnoClass,
@@ -668,14 +671,26 @@ void FIRRTLModuleLowering::runOnOperation() {
668671
state.recordModuleMapping(&op, loweredMod);
669672
return success();
670673
})
671-
.Case<FormalOp>([&](auto oldFormalOp) {
674+
.Case<FormalOp>([&](auto oldOp) {
672675
auto builder = OpBuilder::atBlockEnd(topLevelModule);
673-
auto newFormalOp = builder.create<verif::FormalOp>(
674-
oldFormalOp.getLoc(), oldFormalOp.getNameAttr(),
675-
oldFormalOp.getParametersAttr());
676-
newFormalOp.getBody().emplaceBlock();
677-
state.recordModuleMapping(oldFormalOp, newFormalOp);
678-
formalOpsToProcess.push_back(newFormalOp);
676+
auto newOp = builder.create<verif::FormalOp>(
677+
oldOp.getLoc(), oldOp.getNameAttr(),
678+
oldOp.getParametersAttr());
679+
newOp.getBody().emplaceBlock();
680+
state.recordModuleMapping(oldOp, newOp);
681+
formalOpsToProcess.push_back(newOp);
682+
return success();
683+
})
684+
.Case<SimulationOp>([&](auto oldOp) {
685+
auto loc = oldOp.getLoc();
686+
auto builder = OpBuilder::atBlockEnd(topLevelModule);
687+
auto newOp = builder.create<verif::SimulationOp>(
688+
loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
689+
auto &body = newOp.getRegion().emplaceBlock();
690+
body.addArgument(seq::ClockType::get(builder.getContext()), loc);
691+
body.addArgument(builder.getI1Type(), loc);
692+
state.recordModuleMapping(oldOp, newOp);
693+
simulationOpsToProcess.push_back(newOp);
679694
return success();
680695
})
681696
.Default([&](Operation *op) {
@@ -748,6 +763,13 @@ void FIRRTLModuleLowering::runOnOperation() {
748763
if (failed(result))
749764
return signalPassFailure();
750765

766+
// Lower all simulation op bodies.
767+
result = mlir::failableParallelForEach(
768+
&getContext(), simulationOpsToProcess,
769+
[&](auto op) { return lowerSimulationBody(op, state); });
770+
if (failed(result))
771+
return signalPassFailure();
772+
751773
// Move binds from inside modules to outside modules.
752774
for (auto bind : state.binds) {
753775
bind->moveBefore(bind->getParentOfType<hw::HWModuleOp>());
@@ -1418,22 +1440,18 @@ LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
14181440
/// Run on each `verif.formal` to populate its body based on the original
14191441
/// `firrtl.formal` operation.
14201442
LogicalResult
1421-
FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp formalOp,
1443+
FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
14221444
CircuitLoweringState &loweringState) {
1423-
auto builder = OpBuilder::atBlockEnd(&formalOp.getBody().front());
1445+
auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
14241446

14251447
// Find the module targeted by the `firrtl.formal` operation. The `FormalOp`
14261448
// verifier guarantees the module exists and that it is an `FModuleOp`. This
14271449
// we can then translate to the corresponding `HWModuleOp`.
1428-
auto oldFormalOp = cast<FormalOp>(loweringState.getOldModule(formalOp));
1429-
auto moduleName = oldFormalOp.getModuleNameAttr().getAttr();
1450+
auto oldOp = cast<FormalOp>(loweringState.getOldModule(newOp));
1451+
auto moduleName = oldOp.getModuleNameAttr().getAttr();
14301452
auto oldModule = cast<FModuleOp>(
14311453
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1432-
auto newModule =
1433-
dyn_cast_or_null<hw::HWModuleOp>(loweringState.getNewModule(oldModule));
1434-
if (!newModule)
1435-
return oldFormalOp->emitOpError()
1436-
<< "could not find module " << oldModule.getSymNameAttr();
1454+
auto newModule = cast<hw::HWModuleOp>(loweringState.getNewModule(oldModule));
14371455

14381456
// Create a symbolic input for every input of the lowered module.
14391457
SmallVector<Value> symbolicInputs;
@@ -1442,11 +1460,36 @@ FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp formalOp,
14421460
builder.create<verif::SymbolicValueOp>(arg.getLoc(), arg.getType()));
14431461

14441462
// Instantiate the module with the given symbolic inputs.
1445-
builder.create<hw::InstanceOp>(formalOp.getLoc(), newModule,
1463+
builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
14461464
newModule.getNameAttr(), symbolicInputs);
14471465
return success();
14481466
}
14491467

1468+
/// Run on each `verif.simulation` to populate its body based on the original
1469+
/// `firrtl.simulation` operation.
1470+
LogicalResult
1471+
FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1472+
CircuitLoweringState &loweringState) {
1473+
auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1474+
1475+
// Find the module targeted by the `firrtl.simulation` operation.
1476+
auto oldOp = cast<SimulationOp>(loweringState.getOldModule(newOp));
1477+
auto moduleName = oldOp.getModuleNameAttr().getAttr();
1478+
auto oldModule = cast<FModuleLike>(
1479+
*loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1480+
auto newModule =
1481+
cast<hw::HWModuleLike>(loweringState.getNewModule(oldModule));
1482+
1483+
// Instantiate the module with the simulation op's block arguments as inputs,
1484+
// and yield the module's outputs.
1485+
SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1486+
newOp.getBody()->args_end());
1487+
auto instOp = builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1488+
newModule.getNameAttr(), inputs);
1489+
builder.create<verif::YieldOp>(newOp.getLoc(), instOp.getResults());
1490+
return success();
1491+
}
1492+
14501493
//===----------------------------------------------------------------------===//
14511494
// Module Body Lowering Pass
14521495
//===----------------------------------------------------------------------===//

test/Conversion/FIRRTLToHW/lower-to-hw.mlir

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,38 @@ firrtl.circuit "Foo" {
17441744

17451745
// -----
17461746

1747+
firrtl.circuit "Foo" {
1748+
// CHECK-LABEL: hw.module.extern @Foo
1749+
// CHECK-SAME: in %clock : !seq.clock
1750+
// CHECK-SAME: in %init : i1
1751+
// CHECK-SAME: out done : i1
1752+
// CHECK-SAME: out success : i1
1753+
firrtl.extmodule @Foo(
1754+
in clock: !firrtl.clock,
1755+
in init: !firrtl.uint<1>,
1756+
out done: !firrtl.uint<1>,
1757+
out success: !firrtl.uint<1>
1758+
)
1759+
// CHECK-LABEL: verif.simulation @MyTest1
1760+
// CHECK-SAME: {hello = 42 : i64} {
1761+
// CHECK-NEXT: ([[CLOCK:%.+]]: !seq.clock, [[INIT:%.+]]: i1):
1762+
// CHECK-NEXT: [[DONE:%.+]], [[SUCCESS:%.+]] = hw.instance "Foo" @Foo
1763+
// CHECK-SAME: (clock: [[CLOCK]]: !seq.clock, init: [[INIT]]: i1) -> (done: i1, success: i1)
1764+
// CHECK-NEXT: verif.yield [[DONE]], [[SUCCESS]] : i1, i1
1765+
// CHECK-NEXT: }
1766+
firrtl.simulation @MyTest1, @Foo {hello = 42 : i64}
1767+
// CHECK-LABEL: verif.simulation @MyTest2
1768+
// CHECK-SAME: {world = "abc"} {
1769+
// CHECK-NEXT: ([[CLOCK:%.+]]: !seq.clock, [[INIT:%.+]]: i1):
1770+
// CHECK-NEXT: [[DONE:%.+]], [[SUCCESS:%.+]] = hw.instance "Foo" @Foo
1771+
// CHECK-SAME: (clock: [[CLOCK]]: !seq.clock, init: [[INIT]]: i1) -> (done: i1, success: i1)
1772+
// CHECK-NEXT: verif.yield [[DONE]], [[SUCCESS]] : i1, i1
1773+
// CHECK-NEXT: }
1774+
firrtl.simulation @MyTest2, @Foo {world = "abc"}
1775+
}
1776+
1777+
// -----
1778+
17471779
firrtl.circuit "Foo" {
17481780
// CHECK-LABEL: hw.module @Foo
17491781
firrtl.module @Foo(in %a: !firrtl.uint<42>, in %b: !firrtl.uint<1337>) {

0 commit comments

Comments
 (0)