mirror of https://github.com/llvm/circt.git
[Arc][Sim] Lower Sim DPI func to func.func and support dpi call in Arc (#7386)
This PR implements initial support for lowering Sim DPI operations to Arc. * sim::LowerDPIFuncPass implements lowering from `sim.dpi.func` to `func.func` that respects C-level ABI. * arc::LowerStatePass is modified to allocate states and call functions for `sim.dpi.call` op. Currently unclocked call is not supported yet.
This commit is contained in:
parent
1a8f82e7a6
commit
9828707817
|
@ -23,4 +23,9 @@ def ProceduralizeSim : Pass<"sim-proceduralize", "hw::HWModuleOp"> {
|
|||
let dependentDialects = ["circt::hw::HWDialect, circt::seq::SeqDialect, mlir::scf::SCFDialect"];
|
||||
}
|
||||
|
||||
def LowerDPIFunc : Pass<"sim-lower-dpi-func", "mlir::ModuleOp"> {
|
||||
let summary = "Lower sim.dpi.func into func.func for the simulation flow";
|
||||
let dependentDialects = ["mlir::func::FuncDialect", "mlir::LLVM::LLVMDialect"];
|
||||
}
|
||||
|
||||
#endif // CIRCT_DIALECT_SIM_SEQPASSES
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// RUN: arcilator %s --run --jit-entry=main | FileCheck %s
|
||||
// REQUIRES: arcilator-jit
|
||||
|
||||
// CHECK: c = 0
|
||||
// CHECK-NEXT: c = 5
|
||||
sim.func.dpi @dpi(in %a : i32, in %b : i32, out c : i32) attributes {verilogName = "adder_func"}
|
||||
func.func @adder_func(%arg0: i32, %arg1: i32, %arg2: !llvm.ptr) {
|
||||
%0 = arith.addi %arg0, %arg1 : i32
|
||||
llvm.store %0, %arg2 : i32, !llvm.ptr
|
||||
return
|
||||
}
|
||||
hw.module @adder(in %clock : i1, in %a : i32, in %b : i32, out c : i32) {
|
||||
%seq_clk = seq.to_clock %clock
|
||||
|
||||
%0 = sim.func.dpi.call @dpi(%a, %b) clock %seq_clk : (i32, i32) -> i32
|
||||
hw.output %0 : i32
|
||||
}
|
||||
func.func @main() {
|
||||
%c2_i32 = arith.constant 2 : i32
|
||||
%c3_i32 = arith.constant 3 : i32
|
||||
%one = arith.constant 1 : i1
|
||||
%zero = arith.constant 0 : i1
|
||||
arc.sim.instantiate @adder as %arg0 {
|
||||
arc.sim.set_input %arg0, "a" = %c2_i32 : i32, !arc.sim.instance<@adder>
|
||||
arc.sim.set_input %arg0, "b" = %c3_i32 : i32, !arc.sim.instance<@adder>
|
||||
arc.sim.set_input %arg0, "clock" = %one : i1, !arc.sim.instance<@adder>
|
||||
|
||||
arc.sim.step %arg0 : !arc.sim.instance<@adder>
|
||||
arc.sim.set_input %arg0, "clock" = %zero : i1, !arc.sim.instance<@adder>
|
||||
%0 = arc.sim.get_port %arg0, "c" : i32, !arc.sim.instance<@adder>
|
||||
arc.sim.emit "c", %0 : i32
|
||||
|
||||
arc.sim.step %arg0 : !arc.sim.instance<@adder>
|
||||
arc.sim.set_input %arg0, "clock" = %one : i1, !arc.sim.instance<@adder>
|
||||
%2 = arc.sim.get_port %arg0, "c" : i32, !arc.sim.instance<@adder>
|
||||
arc.sim.emit "c", %2 : i32
|
||||
}
|
||||
return
|
||||
}
|
|
@ -11,5 +11,6 @@ add_circt_conversion_library(CIRCTConvertToArcs
|
|||
CIRCTArc
|
||||
CIRCTHW
|
||||
CIRCTSeq
|
||||
CIRCTSim
|
||||
MLIRTransforms
|
||||
)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "circt/Dialect/Arc/ArcOps.h"
|
||||
#include "circt/Dialect/HW/HWOps.h"
|
||||
#include "circt/Dialect/Seq/SeqOps.h"
|
||||
#include "circt/Dialect/Sim/SimOps.h"
|
||||
#include "circt/Support/Namespace.h"
|
||||
#include "mlir/IR/PatternMatch.h"
|
||||
#include "mlir/Pass/Pass.h"
|
||||
|
@ -25,7 +26,7 @@ using llvm::MapVector;
|
|||
static bool isArcBreakingOp(Operation *op) {
|
||||
return op->hasTrait<OpTrait::ConstantLike>() ||
|
||||
isa<hw::InstanceOp, seq::CompRegOp, MemoryOp, ClockedOpInterface,
|
||||
seq::ClockGateOp>(op) ||
|
||||
seq::ClockGateOp, sim::DPICallOp>(op) ||
|
||||
op->getNumResults() > 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ add_circt_dialect_library(CIRCTArcTransforms
|
|||
CIRCTOM
|
||||
CIRCTSV
|
||||
CIRCTSeq
|
||||
CIRCTSim
|
||||
CIRCTSupport
|
||||
MLIRFuncDialect
|
||||
MLIRLLVMDialect
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "circt/Dialect/Comb/CombOps.h"
|
||||
#include "circt/Dialect/HW/HWOps.h"
|
||||
#include "circt/Dialect/Seq/SeqOps.h"
|
||||
#include "circt/Dialect/Sim/SimOps.h"
|
||||
#include "circt/Support/BackedgeBuilder.h"
|
||||
#include "mlir/Analysis/TopologicalSortUtils.h"
|
||||
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
||||
|
@ -117,7 +118,12 @@ struct ModuleLowering {
|
|||
LogicalResult lowerPrimaryInputs();
|
||||
LogicalResult lowerPrimaryOutputs();
|
||||
LogicalResult lowerStates();
|
||||
template <typename CallTy>
|
||||
LogicalResult lowerStateLike(Operation *op, Value clock, Value enable,
|
||||
Value reset, ArrayRef<Value> inputs,
|
||||
FlatSymbolRefAttr callee);
|
||||
LogicalResult lowerState(StateOp stateOp);
|
||||
LogicalResult lowerState(sim::DPICallOp dpiCallOp);
|
||||
LogicalResult lowerState(MemoryOp memOp);
|
||||
LogicalResult lowerState(MemoryWritePortOp memWriteOp);
|
||||
LogicalResult lowerState(TapOp tapOp);
|
||||
|
@ -139,7 +145,7 @@ static bool shouldMaterialize(Operation *op) {
|
|||
return !isa<MemoryOp, AllocStateOp, AllocMemoryOp, AllocStorageOp,
|
||||
ClockTreeOp, PassThroughOp, RootInputOp, RootOutputOp,
|
||||
StateWriteOp, MemoryWritePortOp, igraph::InstanceOpInterface,
|
||||
StateOp>(op);
|
||||
StateOp, sim::DPICallOp>(op);
|
||||
}
|
||||
|
||||
static bool shouldMaterialize(Value value) {
|
||||
|
@ -390,13 +396,14 @@ LogicalResult ModuleLowering::lowerPrimaryOutputs() {
|
|||
LogicalResult ModuleLowering::lowerStates() {
|
||||
SmallVector<Operation *> opsToLower;
|
||||
for (auto &op : *moduleOp.getBodyBlock())
|
||||
if (isa<StateOp, MemoryOp, MemoryWritePortOp, TapOp>(&op))
|
||||
if (isa<StateOp, MemoryOp, MemoryWritePortOp, TapOp, sim::DPICallOp>(&op))
|
||||
opsToLower.push_back(&op);
|
||||
|
||||
for (auto *op : opsToLower) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "- Lowering " << *op << "\n");
|
||||
auto result = TypeSwitch<Operation *, LogicalResult>(op)
|
||||
.Case<StateOp, MemoryOp, MemoryWritePortOp, TapOp>(
|
||||
auto result =
|
||||
TypeSwitch<Operation *, LogicalResult>(op)
|
||||
.Case<StateOp, MemoryOp, MemoryWritePortOp, TapOp, sim::DPICallOp>(
|
||||
[&](auto op) { return lowerState(op); })
|
||||
.Default(success());
|
||||
if (failed(result))
|
||||
|
@ -405,38 +412,32 @@ LogicalResult ModuleLowering::lowerStates() {
|
|||
return success();
|
||||
}
|
||||
|
||||
LogicalResult ModuleLowering::lowerState(StateOp stateOp) {
|
||||
// We don't support arcs beyond latency 1 yet. These should be easy to add in
|
||||
// the future though.
|
||||
if (stateOp.getLatency() > 1)
|
||||
return stateOp.emitError("state with latency > 1 not supported");
|
||||
|
||||
// Grab all operands from the state op and make it drop all its references.
|
||||
// This allows `materializeValue` to move an operation if this state was the
|
||||
// last user.
|
||||
auto stateClock = stateOp.getClock();
|
||||
auto stateEnable = stateOp.getEnable();
|
||||
auto stateReset = stateOp.getReset();
|
||||
auto stateInputs = SmallVector<Value>(stateOp.getInputs());
|
||||
template <typename CallOpTy>
|
||||
LogicalResult ModuleLowering::lowerStateLike(
|
||||
Operation *stateOp, Value stateClock, Value stateEnable, Value stateReset,
|
||||
ArrayRef<Value> stateInputs, FlatSymbolRefAttr callee) {
|
||||
// Grab all operands from the state op at the callsite and make it drop all
|
||||
// its references. This allows `materializeValue` to move an operation if this
|
||||
// state was the last user.
|
||||
|
||||
// Get the clock tree and enable condition for this state's clock. If this arc
|
||||
// carries an explicit enable condition, fold that into the enable provided by
|
||||
// the clock gates in the arc's clock tree.
|
||||
auto info = getOrCreateClockLowering(stateClock);
|
||||
info.enable = info.clock.getOrCreateAnd(
|
||||
info.enable, info.clock.materializeValue(stateEnable), stateOp.getLoc());
|
||||
info.enable, info.clock.materializeValue(stateEnable), stateOp->getLoc());
|
||||
|
||||
// Allocate the necessary state within the model.
|
||||
SmallVector<Value> allocatedStates;
|
||||
for (unsigned stateIdx = 0; stateIdx < stateOp.getNumResults(); ++stateIdx) {
|
||||
auto type = stateOp.getResult(stateIdx).getType();
|
||||
for (unsigned stateIdx = 0; stateIdx < stateOp->getNumResults(); ++stateIdx) {
|
||||
auto type = stateOp->getResult(stateIdx).getType();
|
||||
auto intType = dyn_cast<IntegerType>(type);
|
||||
if (!intType)
|
||||
return stateOp.emitOpError("result ")
|
||||
return stateOp->emitOpError("result ")
|
||||
<< stateIdx << " has non-integer type " << type
|
||||
<< "; only integer types are supported";
|
||||
auto stateType = StateType::get(intType);
|
||||
auto state = stateBuilder.create<AllocStateOp>(stateOp.getLoc(), stateType,
|
||||
auto state = stateBuilder.create<AllocStateOp>(stateOp->getLoc(), stateType,
|
||||
storageArg);
|
||||
if (auto names = stateOp->getAttrOfType<ArrayAttr>("names"))
|
||||
state->setAttr("name", names[stateIdx]);
|
||||
|
@ -455,18 +456,18 @@ LogicalResult ModuleLowering::lowerState(StateOp stateOp) {
|
|||
OpBuilder nonResetBuilder = info.clock.builder;
|
||||
if (stateReset) {
|
||||
auto materializedReset = info.clock.materializeValue(stateReset);
|
||||
auto ifOp = info.clock.builder.create<scf::IfOp>(stateOp.getLoc(),
|
||||
auto ifOp = info.clock.builder.create<scf::IfOp>(stateOp->getLoc(),
|
||||
materializedReset, true);
|
||||
|
||||
for (auto [alloc, resTy] :
|
||||
llvm::zip(allocatedStates, stateOp.getResultTypes())) {
|
||||
llvm::zip(allocatedStates, stateOp->getResultTypes())) {
|
||||
if (!isa<IntegerType>(resTy))
|
||||
stateOp->emitOpError("Non-integer result not supported yet!");
|
||||
|
||||
auto thenBuilder = ifOp.getThenBodyBuilder();
|
||||
Value constZero =
|
||||
thenBuilder.create<hw::ConstantOp>(stateOp.getLoc(), resTy, 0);
|
||||
thenBuilder.create<StateWriteOp>(stateOp.getLoc(), alloc, constZero,
|
||||
thenBuilder.create<hw::ConstantOp>(stateOp->getLoc(), resTy, 0);
|
||||
thenBuilder.create<StateWriteOp>(stateOp->getLoc(), alloc, constZero,
|
||||
Value());
|
||||
}
|
||||
|
||||
|
@ -475,24 +476,50 @@ LogicalResult ModuleLowering::lowerState(StateOp stateOp) {
|
|||
|
||||
stateOp->dropAllReferences();
|
||||
|
||||
auto newStateOp = nonResetBuilder.create<CallOp>(
|
||||
stateOp.getLoc(), stateOp.getResultTypes(), stateOp.getArcAttr(),
|
||||
auto newStateOp = nonResetBuilder.create<CallOpTy>(
|
||||
stateOp->getLoc(), stateOp->getResultTypes(), callee,
|
||||
materializedOperands);
|
||||
|
||||
// Create the write ops that write the result of the transfer function to the
|
||||
// allocated state storage.
|
||||
for (auto [alloc, result] :
|
||||
llvm::zip(allocatedStates, newStateOp.getResults()))
|
||||
nonResetBuilder.create<StateWriteOp>(stateOp.getLoc(), alloc, result,
|
||||
nonResetBuilder.create<StateWriteOp>(stateOp->getLoc(), alloc, result,
|
||||
info.enable);
|
||||
|
||||
// Replace all uses of the arc with reads from the allocated state.
|
||||
for (auto [alloc, result] : llvm::zip(allocatedStates, stateOp.getResults()))
|
||||
for (auto [alloc, result] : llvm::zip(allocatedStates, stateOp->getResults()))
|
||||
replaceValueWithStateRead(result, alloc);
|
||||
stateOp.erase();
|
||||
stateOp->erase();
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult ModuleLowering::lowerState(StateOp stateOp) {
|
||||
// We don't support arcs beyond latency 1 yet. These should be easy to add in
|
||||
// the future though.
|
||||
if (stateOp.getLatency() > 1)
|
||||
return stateOp.emitError("state with latency > 1 not supported");
|
||||
|
||||
auto stateInputs = SmallVector<Value>(stateOp.getInputs());
|
||||
|
||||
return lowerStateLike<arc::CallOp>(stateOp, stateOp.getClock(),
|
||||
stateOp.getEnable(), stateOp.getReset(),
|
||||
stateInputs, stateOp.getArcAttr());
|
||||
}
|
||||
|
||||
LogicalResult ModuleLowering::lowerState(sim::DPICallOp callOp) {
|
||||
// Clocked call op can be considered as arc state with single latency.
|
||||
auto stateClock = callOp.getClock();
|
||||
if (!stateClock)
|
||||
return callOp.emitError("unclocked DPI call not implemented yet");
|
||||
|
||||
auto stateInputs = SmallVector<Value>(callOp.getInputs());
|
||||
|
||||
return lowerStateLike<func::CallOp>(callOp, stateClock, callOp.getEnable(),
|
||||
Value(), stateInputs,
|
||||
callOp.getCalleeAttr());
|
||||
}
|
||||
|
||||
LogicalResult ModuleLowering::lowerState(MemoryOp memOp) {
|
||||
auto allocMemOp = stateBuilder.create<AllocMemoryOp>(
|
||||
memOp.getLoc(), memOp.getType(), storageArg, memOp->getAttrs());
|
||||
|
|
|
@ -29,6 +29,7 @@ add_circt_dialect_library(CIRCTSim
|
|||
CIRCTHW
|
||||
CIRCTSeq
|
||||
CIRCTSV
|
||||
MLIRFuncDialect
|
||||
MLIRIR
|
||||
MLIRPass
|
||||
MLIRTransforms
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "circt/Dialect/Sim/SimOps.h"
|
||||
#include "circt/Dialect/HW/ModuleImplementation.h"
|
||||
#include "circt/Dialect/SV/SVOps.h"
|
||||
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
||||
#include "mlir/IR/PatternMatch.h"
|
||||
#include "mlir/Interfaces/FunctionImplementation.h"
|
||||
|
||||
|
@ -69,12 +70,15 @@ ParseResult DPIFuncOp::parse(OpAsmParser &parser, OperationState &result) {
|
|||
|
||||
LogicalResult
|
||||
sim::DPICallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
|
||||
auto referencedOp = dyn_cast_or_null<sim::DPIFuncOp>(
|
||||
symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
|
||||
auto referencedOp =
|
||||
symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr());
|
||||
if (!referencedOp)
|
||||
return emitError("cannot find function declaration '")
|
||||
<< getCallee() << "'";
|
||||
if (isa<func::FuncOp, sim::DPIFuncOp>(referencedOp))
|
||||
return success();
|
||||
return emitError("callee must be 'sim.dpi.func' or 'func.func' but got '")
|
||||
<< referencedOp->getName() << "'";
|
||||
}
|
||||
|
||||
void DPIFuncOp::print(OpAsmPrinter &p) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
add_circt_dialect_library(CIRCTSimTransforms
|
||||
LowerDPIFunc.cpp
|
||||
ProceduralizeSim.cpp
|
||||
|
||||
|
||||
|
@ -12,8 +13,10 @@ add_circt_dialect_library(CIRCTSimTransforms
|
|||
CIRCTSV
|
||||
CIRCTComb
|
||||
CIRCTSupport
|
||||
MLIRFuncDialect
|
||||
MLIRIR
|
||||
MLIRPass
|
||||
MLIRLLVMDialect
|
||||
MLIRSCFDialect
|
||||
MLIRTransformUtils
|
||||
)
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
//===- LowerDPIFunc.cpp - Lower sim.dpi.func to func.func ----*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===
|
||||
//
|
||||
// This pass lowers Sim DPI func ops to MLIR func and call.
|
||||
//
|
||||
// sim.dpi.func @foo(input %a: i32, output %b: i64)
|
||||
// hw.module @top (..) {
|
||||
// %result = sim.dpi.call @foo(%a) clock %clock
|
||||
// }
|
||||
//
|
||||
// ->
|
||||
//
|
||||
// func.func @foo(%a: i32, %b: !llvm.ptr) // Output is passed by a reference.
|
||||
// func.func @foo_wrapper(%a: i32) -> (i64) {
|
||||
// %0 = llvm.alloca: !llvm.ptr
|
||||
// %v = func.call @foo (%a, %0)
|
||||
// func.return %v
|
||||
// }
|
||||
// hw.module @mod(..) {
|
||||
// %result = sim.dpi.call @foo_wrapper(%a) clock %clock
|
||||
// }
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/Sim/SimOps.h"
|
||||
#include "circt/Dialect/Sim/SimPasses.h"
|
||||
#include "circt/Support/Namespace.h"
|
||||
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
||||
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
|
||||
#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
|
||||
#include "mlir/Transforms/DialectConversion.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
#define DEBUG_TYPE "sim-lower-dpi-func"
|
||||
|
||||
namespace circt {
|
||||
namespace sim {
|
||||
#define GEN_PASS_DEF_LOWERDPIFUNC
|
||||
#include "circt/Dialect/Sim/SimPasses.h.inc"
|
||||
} // namespace sim
|
||||
} // namespace circt
|
||||
|
||||
using namespace mlir;
|
||||
using namespace circt;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Pass Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
||||
struct LoweringState {
|
||||
DenseMap<StringAttr, func::FuncOp> dpiFuncDeclMapping;
|
||||
circt::Namespace nameSpace;
|
||||
};
|
||||
|
||||
struct LowerDPIFuncPass : public sim::impl::LowerDPIFuncBase<LowerDPIFuncPass> {
|
||||
|
||||
LogicalResult lowerDPI();
|
||||
LogicalResult lowerDPIFuncOp(sim::DPIFuncOp simFunc,
|
||||
LoweringState &loweringState,
|
||||
SymbolTable &symbolTable);
|
||||
void runOnOperation() override;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
LogicalResult LowerDPIFuncPass::lowerDPIFuncOp(sim::DPIFuncOp simFunc,
|
||||
LoweringState &loweringState,
|
||||
SymbolTable &symbolTable) {
|
||||
ImplicitLocOpBuilder builder(simFunc.getLoc(), simFunc);
|
||||
auto moduleType = simFunc.getModuleType();
|
||||
|
||||
llvm::SmallVector<Type> dpiFunctionArgumentTypes;
|
||||
for (auto arg : moduleType.getPorts()) {
|
||||
// TODO: Support a non-integer type.
|
||||
if (!arg.type.isInteger())
|
||||
return simFunc->emitError()
|
||||
<< "non-integer type argument is unsupported now";
|
||||
|
||||
if (arg.dir == hw::ModulePort::Input)
|
||||
dpiFunctionArgumentTypes.push_back(arg.type);
|
||||
else
|
||||
// Output must be passed by a reference.
|
||||
dpiFunctionArgumentTypes.push_back(
|
||||
LLVM::LLVMPointerType::get(arg.type.getContext()));
|
||||
}
|
||||
|
||||
auto funcType = builder.getFunctionType(dpiFunctionArgumentTypes, {});
|
||||
func::FuncOp func;
|
||||
|
||||
// Look up func.func by verilog name since the function name is equal to the
|
||||
// symbol name in MLIR
|
||||
if (auto verilogName = simFunc.getVerilogName()) {
|
||||
func = symbolTable.lookup<func::FuncOp>(*verilogName);
|
||||
// TODO: Check if function type matches.
|
||||
}
|
||||
|
||||
// If a referred function is not in the same module, create an external
|
||||
// function declaration.
|
||||
if (!func) {
|
||||
func = builder.create<func::FuncOp>(simFunc.getVerilogName()
|
||||
? *simFunc.getVerilogName()
|
||||
: simFunc.getSymName(),
|
||||
funcType);
|
||||
// External function needs to be private.
|
||||
func.setPrivate();
|
||||
}
|
||||
|
||||
// Create a wrapper module that calls a DPI function.
|
||||
auto funcOp = builder.create<func::FuncOp>(
|
||||
loweringState.nameSpace.newName(simFunc.getSymName() + "_wrapper"),
|
||||
moduleType.getFuncType());
|
||||
|
||||
// Map old symbol to a new func op.
|
||||
loweringState.dpiFuncDeclMapping[simFunc.getSymNameAttr()] = funcOp;
|
||||
|
||||
builder.setInsertionPointToStart(funcOp.addEntryBlock());
|
||||
SmallVector<Value> functionInputs;
|
||||
SmallVector<LLVM::AllocaOp> functionOutputAllocas;
|
||||
|
||||
size_t inputIndex = 0;
|
||||
for (auto arg : moduleType.getPorts()) {
|
||||
if (arg.dir == hw::ModulePort::InOut)
|
||||
return funcOp->emitError() << "inout is currently not supported";
|
||||
|
||||
if (arg.dir == hw::ModulePort::Input) {
|
||||
functionInputs.push_back(funcOp.getArgument(inputIndex));
|
||||
++inputIndex;
|
||||
} else {
|
||||
// Allocate an output placeholder.
|
||||
auto one = builder.create<LLVM::ConstantOp>(builder.getI64IntegerAttr(1));
|
||||
auto alloca = builder.create<LLVM::AllocaOp>(
|
||||
builder.getType<LLVM::LLVMPointerType>(), arg.type, one);
|
||||
functionInputs.push_back(alloca);
|
||||
functionOutputAllocas.push_back(alloca);
|
||||
}
|
||||
}
|
||||
|
||||
builder.create<func::CallOp>(func, functionInputs);
|
||||
|
||||
SmallVector<Value> results;
|
||||
for (auto functionOutputAlloca : functionOutputAllocas)
|
||||
results.push_back(builder.create<LLVM::LoadOp>(
|
||||
functionOutputAlloca.getElemType(), functionOutputAlloca));
|
||||
|
||||
builder.create<func::ReturnOp>(results);
|
||||
|
||||
simFunc.erase();
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult LowerDPIFuncPass::lowerDPI() {
|
||||
LLVM_DEBUG(llvm::dbgs() << "Lowering sim DPI func to func.func\n");
|
||||
auto op = getOperation();
|
||||
LoweringState state;
|
||||
state.nameSpace.add(op);
|
||||
auto &symbolTable = getAnalysis<SymbolTable>();
|
||||
for (auto simFunc : llvm::make_early_inc_range(op.getOps<sim::DPIFuncOp>()))
|
||||
if (failed(lowerDPIFuncOp(simFunc, state, symbolTable)))
|
||||
return failure();
|
||||
|
||||
op.walk([&](sim::DPICallOp op) {
|
||||
auto func = state.dpiFuncDeclMapping.at(op.getCalleeAttr().getAttr());
|
||||
op.setCallee(func.getSymNameAttr());
|
||||
});
|
||||
return success();
|
||||
}
|
||||
|
||||
void LowerDPIFuncPass::runOnOperation() {
|
||||
if (failed(lowerDPI()))
|
||||
return signalPassFailure();
|
||||
}
|
|
@ -353,3 +353,16 @@ hw.module @BlackBox(in %clk: !seq.clock) {
|
|||
}
|
||||
// CHECK-NOT: hw.module.extern private @BlackBoxExt
|
||||
hw.module.extern private @BlackBoxExt(in %a: i42, in %b: i42, out c: i42, out d: i42)
|
||||
|
||||
|
||||
func.func private @func(%arg0: i32, %arg1: i32) -> i32
|
||||
// CHECK-LABEL: arc.model @adder
|
||||
hw.module @adder(in %clock : i1, in %a : i32, in %b : i32, out c : i32) {
|
||||
%0 = seq.to_clock %clock
|
||||
%1 = sim.func.dpi.call @func(%a, %b) clock %0 : (i32, i32) -> i32
|
||||
// CHECK: arc.clock_tree
|
||||
// CHECK-NEXT: %[[A:.+]] = arc.state_read %in_a : <i32>
|
||||
// CHECK-NEXT: %[[B:.+]] = arc.state_read %in_b : <i32>
|
||||
// CHECK-NEXT: %[[RESULT:.+]] = func.call @func(%6, %7) : (i32, i32) -> i32
|
||||
hw.output %1 : i32
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// RUN: circt-opt --sim-lower-dpi-func %s | FileCheck %s
|
||||
|
||||
sim.func.dpi @foo(out arg0: i32, in %arg1: i32, out arg2: i32)
|
||||
// CHECK-LABEL: func.func private @foo(!llvm.ptr, i32, !llvm.ptr)
|
||||
// CHECK-LABEL: func.func @foo_wrapper(%arg0: i32) -> (i32, i32) {
|
||||
// CHECK-NEXT: %0 = llvm.mlir.constant(1 : i64) : i64
|
||||
// CHECK-NEXT: %1 = llvm.alloca %0 x i32 : (i64) -> !llvm.ptr
|
||||
// CHECK-NEXT: %2 = llvm.mlir.constant(1 : i64) : i64
|
||||
// CHECK-NEXT: %3 = llvm.alloca %2 x i32 : (i64) -> !llvm.ptr
|
||||
// CHECK-NEXT: call @foo(%1, %arg0, %3) : (!llvm.ptr, i32, !llvm.ptr) -> ()
|
||||
// CHECK-NEXT: %4 = llvm.load %1 : !llvm.ptr -> i32
|
||||
// CHECK-NEXT: %5 = llvm.load %3 : !llvm.ptr -> i32
|
||||
// CHECK-NEXT: return %4, %5 : i32, i32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: func.func @bar_wrapper(%arg0: i32) -> (i32, i32) {
|
||||
// CHECK-NEXT: %0 = llvm.mlir.constant(1 : i64) : i64
|
||||
// CHECK-NEXT: %1 = llvm.alloca %0 x i32 : (i64) -> !llvm.ptr
|
||||
// CHECK-NEXT: %2 = llvm.mlir.constant(1 : i64) : i64
|
||||
// CHECK-NEXT: %3 = llvm.alloca %2 x i32 : (i64) -> !llvm.ptr
|
||||
// CHECK-NEXT: call @bar_c_name(%1, %arg0, %3) : (!llvm.ptr, i32, !llvm.ptr) -> ()
|
||||
// CHECK-NEXT: %4 = llvm.load %1 : !llvm.ptr -> i32
|
||||
// CHECK-NEXT: %5 = llvm.load %3 : !llvm.ptr -> i32
|
||||
// CHECK-NEXT: return %4, %5 : i32, i32
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-LABEL: func.func @bar_c_name
|
||||
|
||||
sim.func.dpi @bar(out arg0: i32, in %arg1: i32, out arg2: i32) attributes {verilogName="bar_c_name"}
|
||||
func.func @bar_c_name(%arg0: !llvm.ptr, %arg1: i32, %arg2: !llvm.ptr) {
|
||||
func.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func.func private @baz_c_name(!llvm.ptr, i32, !llvm.ptr)
|
||||
// CHECK-LABEL: func.func @baz_wrapper(%arg0: i32) -> (i32, i32)
|
||||
// CHECK: call @baz_c_name(%1, %arg0, %3) : (!llvm.ptr, i32, !llvm.ptr) -> ()
|
||||
sim.func.dpi @baz(out arg0: i32, in %arg1: i32, out arg2: i32) attributes {verilogName="baz_c_name"}
|
||||
|
||||
// CHECK-LABEL: hw.module @dpi_call
|
||||
hw.module @dpi_call(in %clock : !seq.clock, in %enable : i1, in %in: i32,
|
||||
out o1: i32, out o2: i32, out o3: i32, out o4: i32, out o5: i32, out o6: i32) {
|
||||
// CHECK-NEXT: %0:2 = sim.func.dpi.call @foo_wrapper(%in) clock %clock : (i32) -> (i32, i32)
|
||||
// CHECK-NEXT: %1:2 = sim.func.dpi.call @bar_wrapper(%in) : (i32) -> (i32, i32)
|
||||
// CHECK-NEXT: %2:2 = sim.func.dpi.call @baz_wrapper(%in) : (i32) -> (i32, i32)
|
||||
// CHECK-NEXT: hw.output %0#0, %0#1, %1#0, %1#1, %2#0, %2#1 : i32, i32, i32, i32, i32, i32
|
||||
%0, %1 = sim.func.dpi.call @foo(%in) clock %clock : (i32) -> (i32, i32)
|
||||
%2, %3 = sim.func.dpi.call @bar(%in) : (i32) -> (i32, i32)
|
||||
%4, %5 = sim.func.dpi.call @baz(%in) : (i32) -> (i32, i32)
|
||||
|
||||
hw.output %0, %1, %2, %3, %4, %5 : i32, i32, i32, i32, i32, i32
|
||||
}
|
|
@ -19,14 +19,15 @@ hw.module @stop_finish(in %clock : !seq.clock, in %cond : i1) {
|
|||
|
||||
// CHECK-LABEL: sim.func.dpi @dpi(out arg0 : i1, in %arg1 : i1, out arg2 : i1)
|
||||
sim.func.dpi @dpi(out arg0: i1, in %arg1: i1, out arg2: i1)
|
||||
func.func private @func(%arg1: i1) -> (i1, i1)
|
||||
|
||||
hw.module @dpi_call(in %clock : !seq.clock, in %enable : i1, in %in: i1) {
|
||||
// CHECK: sim.func.dpi.call @dpi(%in) clock %clock enable %enable : (i1) -> (i1, i1)
|
||||
%0, %1 = sim.func.dpi.call @dpi(%in) clock %clock enable %enable: (i1) -> (i1, i1)
|
||||
// CHECK: sim.func.dpi.call @dpi(%in) clock %clock : (i1) -> (i1, i1)
|
||||
%2, %3 = sim.func.dpi.call @dpi(%in) clock %clock : (i1) -> (i1, i1)
|
||||
// CHECK: sim.func.dpi.call @dpi(%in) enable %enable : (i1) -> (i1, i1)
|
||||
%4, %5 = sim.func.dpi.call @dpi(%in) enable %enable : (i1) -> (i1, i1)
|
||||
// CHECK: sim.func.dpi.call @dpi(%in) : (i1) -> (i1, i1)
|
||||
%6, %7 = sim.func.dpi.call @dpi(%in) : (i1) -> (i1, i1)
|
||||
// CHECK: sim.func.dpi.call @func(%in) enable %enable : (i1) -> (i1, i1)
|
||||
%4, %5 = sim.func.dpi.call @func(%in) enable %enable : (i1) -> (i1, i1)
|
||||
// CHECK: sim.func.dpi.call @func(%in) : (i1) -> (i1, i1)
|
||||
%6, %7 = sim.func.dpi.call @func(%in) : (i1) -> (i1, i1)
|
||||
}
|
||||
|
|
|
@ -42,3 +42,12 @@ hw.module @proc_print_sv() {
|
|||
sim.proc.print %lit
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
hw.module.extern @non_func(out arg0: i1, in %arg1: i1, out arg2: i1)
|
||||
|
||||
hw.module @dpi_call(in %clock : !seq.clock, in %in: i1) {
|
||||
// expected-error @below {{callee must be 'sim.dpi.func' or 'func.func' but got 'hw.module.extern'}}
|
||||
%0, %1 = sim.func.dpi.call @non_func(%in) : (i1) -> (i1, i1)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ target_link_libraries(arcilator
|
|||
CIRCTOM
|
||||
CIRCTSeqToSV
|
||||
CIRCTSeqTransforms
|
||||
CIRCTSimTransforms
|
||||
CIRCTSupport
|
||||
CIRCTTransforms
|
||||
MLIRBuiltinToLLVMIRTranslation
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "circt/Dialect/Emit/EmitDialect.h"
|
||||
#include "circt/Dialect/HW/HWPasses.h"
|
||||
#include "circt/Dialect/Seq/SeqPasses.h"
|
||||
#include "circt/Dialect/Sim/SimDialect.h"
|
||||
#include "circt/Dialect/Sim/SimPasses.h"
|
||||
#include "circt/InitAllDialects.h"
|
||||
#include "circt/InitAllPasses.h"
|
||||
#include "circt/Support/Passes.h"
|
||||
|
@ -249,6 +251,7 @@ static void populateHwModuleToArcPipeline(PassManager &pm) {
|
|||
opts.tapMemories = observeMemories;
|
||||
pm.addPass(arc::createInferMemoriesPass(opts));
|
||||
}
|
||||
pm.addPass(sim::createLowerDPIFunc());
|
||||
pm.addPass(createCSEPass());
|
||||
pm.addPass(arc::createArcCanonicalizerPass());
|
||||
|
||||
|
@ -567,6 +570,7 @@ static LogicalResult executeArcilator(MLIRContext &context) {
|
|||
mlir::scf::SCFDialect,
|
||||
om::OMDialect,
|
||||
seq::SeqDialect,
|
||||
sim::SimDialect,
|
||||
sv::SVDialect
|
||||
>();
|
||||
// clang-format on
|
||||
|
|
Loading…
Reference in New Issue