[MSFT] Introduce msft.module.extern op for PyCDE lowering. (#2106)

The op is identical to hw.module.extern, but it can be referred to by
a msft.instance op. This adds a cleaner layering where PyCDE only
generates msft.module/msft.module.extern/msft.instance ops. Before, it
would generate msft.module/hw.module.extern, leading to a mix of
msft.instance/hw.instance for LowerMSFTToHW to consume. This change
introduces a clear contract for PyCDE's output: all module and
instance ops are from the MSFT dialect.

Some of the implementation is duplicated into MSFTOps.cpp from
HWOps.cpp, but refactoring is left as a future exercise, captured in
https://github.com/llvm/circt/issues/2107.
This commit is contained in:
mikeurbach 2021-11-08 18:40:46 -07:00 committed by GitHub
parent dc35179ce5
commit 66acfe31b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 476 additions and 54 deletions

View File

@ -174,7 +174,7 @@ class _SpecializedModule:
mlir.ir.TypeAttr.get(i.attr.type))
for i in self.parameters
]
return hw.HWModuleExternOp(
return msft.MSFTModuleExternOp(
symbol,
self.input_ports,
self.output_ports,

View File

@ -114,14 +114,14 @@ poly.print()
# CHECK: %example.y = msft.instance @example @PolyComputeForCoeff_62_42_6(%c23_i32) : (i32) -> i32
# CHECK: %example2.y = msft.instance @example2 @PolyComputeForCoeff_62_42_6(%example.y) : (i32) -> i32
# CHECK: %example2_1.y = msft.instance @example2_1 @PolyComputeForCoeff_1_2_3_4_5(%example.y) : (i32) -> i32
# CHECK: %CoolPolynomialCompute.y = hw.instance "CoolPolynomialCompute" @supercooldevice(x: %{{.+}}: i32) -> (y: i32)
# CHECK: hw.instance "M" @parameterized_extern<a: i64 = 8, b: i64 = 3>() -> ()
# CHECK: %CoolPolynomialCompute.y = msft.instance @CoolPolynomialCompute @supercooldevice(%{{.+}}) : (i32) -> i32
# CHECK: msft.instance @M @parameterized_extern() <a: i64 = 8, b: i64 = 3> : () -> ()
# CHECK: msft.output %example.y : i32
# CHECK: }
# CHECK: msft.module @PolyComputeForCoeff_62_42_6 {coefficients = {coeff = [62, 42, 6]}} (%x: i32) -> (y: i32)
# CHECK: msft.module @PolyComputeForCoeff_1_2_3_4_5 {coefficients = {coeff = [1, 2, 3, 4, 5]}} (%x: i32) -> (y: i32)
# CHECK: hw.module.extern @supercooldevice(%x: i32) -> (y: i32) attributes {verilogName = "supercooldevice"}
# CHECK: hw.module.extern @parameterized_extern<a: i64, b: i64>() attributes {verilogName = "parameterized_extern"}
# CHECK: msft.module.extern @supercooldevice(%x: i32) -> (y: i32) attributes {verilogName = "supercooldevice"}
# CHECK: msft.module.extern @parameterized_extern<a: i64, b: i64>() attributes {verilogName = "parameterized_extern"}
print("Generating rest...")
poly.generate()
@ -134,7 +134,7 @@ poly.print()
# CHECK: %example.y = hw.instance "example" sym @example @PolyComputeForCoeff_62_42_6(x: %c23_i32: i32) -> (y: i32)
# CHECK: %example2.y = hw.instance "example2" sym @example2 @PolyComputeForCoeff_62_42_6(x: %0: i32) -> (y: i32)
# CHECK: %example2_1.y = hw.instance "example2_1" sym @example2_1 @PolyComputeForCoeff_1_2_3_4_5(x: %1: i32) -> (y: i32)
# CHECK: %CoolPolynomialCompute.y = hw.instance "CoolPolynomialCompute" @supercooldevice(x: %c23_i32{{.*}}: i32) -> (y: i32)
# CHECK: %CoolPolynomialCompute.y = hw.instance "CoolPolynomialCompute" sym @CoolPolynomialCompute @supercooldevice(x: %c23_i32{{.*}}: i32) -> (y: i32)
# CHECK-LABEL: hw.module @PolyComputeForCoeff_62_42_6(%x: i32) -> (y: i32)
# CHECK: hw.constant 62
# CHECK: hw.constant 42

View File

@ -6,6 +6,12 @@
//
//===----------------------------------------------------------------------===//
// "Forward-declare" these HW attributes rather than including or duplicating
// them here. This lets us to refer to them in ODS, but delegates to HW in C++.
// These are used to represent parameters to MSFTModuleExternOp.
def ParamDeclAttr : Attr<CPred<"$_self.isa<hw::ParamDeclAttr>()">>;
def ParamDeclArrayAttr : TypedArrayAttrBase<ParamDeclAttr, "parameter array">;
def PrimitiveType : I32EnumAttr<"PrimitiveType",
"Type of device at physical location", [
I32EnumAttrCase<"M20K", 1>,

View File

@ -20,7 +20,8 @@ def InstanceOp : MSFTOp<"instance", [
let arguments = (ins SymbolNameAttr:$sym_name,
FlatSymbolRefAttr:$moduleName,
Variadic<AnyType>:$inputs);
Variadic<AnyType>:$inputs,
OptionalAttr<ParamDeclArrayAttr>:$parameters);
let results = (outs Variadic<AnyType>);
let extraClassDeclaration = [{
@ -44,7 +45,7 @@ def InstanceOp : MSFTOp<"instance", [
/// sym keyword for optional symbol simplifies parsing
let assemblyFormat = [{
$sym_name $moduleName `(` $inputs `)` attr-dict
$sym_name $moduleName `(` $inputs `)` custom<ParameterList>($parameters) attr-dict
`:` functional-type($inputs, results)
}];
}
@ -125,6 +126,36 @@ def MSFTModuleOp : MSFTOp<"module",
let parser = "return ::parse$cppClass(parser, result);";
}
def MSFTModuleExternOp : MSFTOp<"module.extern",
[Symbol, HasParent<"mlir::ModuleOp">]> {
let summary = "MSFT external Module";
let description = [{
Identical to `hw.module.extern`, and trivially lowers to that. This op
exists so that we can use `msft.instance` to refer to both `msft.module` and
`msft.module.extern`, rather than mixing `hw.instance` with `msft.instance`.
}];
let arguments = (ins StrArrayAttr:$argNames, StrArrayAttr:$resultNames,
ParamDeclArrayAttr:$parameters,
OptionalAttr<StrAttr>:$verilogName);
let regions = (region SizedRegion<0>:$body);
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins "StringAttr":$name, "const hw::ModulePortInfo &":$ports,
CArg<"StringRef", "StringRef()">:$verilogName,
CArg<"ArrayAttr", "{}">:$parameters,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>
];
let printer = "return ::print$cppClass(p, *this);";
let parser = "return ::parse$cppClass(parser, result);";
let verifier = "return ::verify$cppClass(*this);";
let extraClassDeclaration = [{
/// Decode information about the input and output ports on this module.
hw::ModulePortInfo getPorts();
}];
}
def OutputOp : MSFTOp<"output", [Terminator, HasParent<"MSFTModuleOp">,
NoSideEffect, ReturnLike]> {

View File

@ -10,6 +10,35 @@ import circt.support as support
from mlir.ir import *
def create_parameters(parameters: dict[str, _ir.Attribute], module: ModuleLike):
# Compute mapping from parameter name to index, and initialize array.
mod_param_decls = module.parameters
mod_param_decls_idxs = {
decl.name: idx for (idx, decl) in enumerate(mod_param_decls)
}
inst_param_array = [None] * len(module.parameters)
# Fill in all the parameters specified.
if isinstance(parameters, DictAttr):
parameters = {i.name: i.attr for i in parameters}
for (pname, pval) in parameters.items():
if pname not in mod_param_decls_idxs:
raise ValueError(
f"Could not find parameter '{pname}' in module parameter decls")
idx = mod_param_decls_idxs[pname]
param_decl = mod_param_decls[idx]
inst_param_array[idx] = hw.ParamDeclAttr.get(pname, param_decl.param_type,
pval)
# Fill in the defaults from the module param decl.
for (idx, pval) in enumerate(inst_param_array):
if pval is not None:
continue
inst_param_array[idx] = mod_param_decls[idx]
return inst_param_array
class InstanceBuilder(support.NamedValueOpView):
"""Helper class to incrementally construct an instance of a module."""
@ -26,29 +55,7 @@ class InstanceBuilder(support.NamedValueOpView):
self.module = module
instance_name = StringAttr.get(name)
module_name = FlatSymbolRefAttr.get(StringAttr(module.name).value)
mod_param_decls = module.parameters
mod_param_decls_idxs = {
decl.name: idx for (idx, decl) in enumerate(mod_param_decls)
}
inst_param_array = [None] * len(module.parameters)
# Fill in all the parameters specified.
if isinstance(parameters, DictAttr):
parameters = {i.name: i.attr for i in parameters}
for (pname, pval) in parameters.items():
if pname not in mod_param_decls_idxs:
raise ValueError(
f"Could not find parameter '{pname}' in module parameter decls")
idx = mod_param_decls_idxs[pname]
param_decl = mod_param_decls[idx]
inst_param_array[idx] = hw.ParamDeclAttr.get(pname, param_decl.param_type,
pval)
# Fill in the defaults from the module param decl.
for (idx, pval) in enumerate(inst_param_array):
if pval is not None:
continue
inst_param_array[idx] = mod_param_decls[idx]
inst_param_array = create_parameters(parameters, module)
if sym_name:
sym_name = StringAttr.get(sym_name)
pre_args = [instance_name, module_name]
@ -183,6 +190,12 @@ class ModuleLike:
def is_external(self):
return len(self.regions[0].blocks) == 0
@property
def parameters(self) -> list[ParamDeclAttr]:
return [
hw.ParamDeclAttr(a) for a in ArrayAttr(self.attributes["parameters"])
]
def create(self,
name: str,
parameters: Dict[str, object] = {},
@ -305,21 +318,10 @@ class HWModuleOp(ModuleLike):
self.body.blocks.append(*self.type.inputs)
return self.body.blocks[0]
@property
def parameters(self) -> list[ParamDeclAttr]:
return [
hw.ParamDeclAttr(a) for a in ArrayAttr(self.attributes["parameters"])
]
class HWModuleExternOp(ModuleLike):
"""Specialization for the HW module op class."""
@property
def parameters(self) -> list[ParamDeclAttr]:
return [
hw.ParamDeclAttr(a) for a in ArrayAttr(self.attributes["parameters"])
]
pass
class ConstantOp:

View File

@ -2,7 +2,7 @@
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
from circt.dialects import msft as _msft
from circt.dialects import hw, msft as _msft
import circt.dialects._hw_ops_ext as _hw_ext
import circt.support as support
@ -18,6 +18,7 @@ class InstanceBuilder(support.NamedValueOpView):
input_port_mapping,
*,
sym_name=None,
parameters=None,
loc=None,
ip=None):
self.module = module
@ -26,14 +27,22 @@ class InstanceBuilder(support.NamedValueOpView):
if sym_name:
sym_name = _ir.StringAttr.get(sym_name)
pre_args = [instance_name, module_name]
if parameters is not None:
parameters = _hw_ext.create_parameters(parameters, module)
else:
parameters = []
post_args = [_ir.ArrayAttr.get(parameters)]
results = module.type.results
super().__init__(_msft.InstanceOp,
results,
input_port_mapping,
pre_args, [],
loc=loc,
ip=ip)
super().__init__(
_msft.InstanceOp,
results,
input_port_mapping,
pre_args,
post_args,
loc=loc,
ip=ip,
)
def create_default_value(self, index, data_type, arg_name):
type = self.module.type.inputs[index]
@ -86,3 +95,28 @@ class MSFTModuleOp(_hw_ext.ModuleLike):
@property
def entry_block(self):
return self.regions[0].blocks[0]
@property
def parameters(self):
return [
hw.ParamDeclAttr.get(p.name, _ir.TypeAttr.get(p.attr.type), p.attr)
for p in _ir.DictAttr(self.attributes["parameters"])
]
class MSFTModuleExternOp(_hw_ext.ModuleLike):
def create(self,
name: str,
parameters=None,
results=None,
loc=None,
ip=None,
**kwargs):
return InstanceBuilder(self,
name,
kwargs,
sym_name=self.verilogName.value,
parameters=parameters,
loc=loc,
ip=ip)

View File

@ -11,12 +11,15 @@
//===----------------------------------------------------------------------===//
#include "circt/Dialect/MSFT/MSFTOps.h"
#include "circt/Dialect/HW/HWAttributes.h"
#include "circt/Dialect/HW/HWOps.h"
#include "circt/Dialect/HW/ModuleImplementation.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/DialectImplementation.h"
#include "mlir/IR/FunctionImplementation.h"
#include "mlir/IR/FunctionSupport.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/TypeSwitch.h"
using namespace circt;
@ -97,7 +100,8 @@ LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return emitError("Cannot find module definition '") << moduleName() << "'";
// It must be some sort of module.
if (!hw::isAnyModule(module) && !isa<MSFTModuleOp>(module))
if (!hw::isAnyModule(module) &&
!isa<MSFTModuleOp, MSFTModuleExternOp>(module))
return emitError("symbol reference '")
<< moduleName() << "' isn't a module";
return success();
@ -227,5 +231,319 @@ static void printMSFTModuleOp(OpAsmPrinter &p, MSFTModuleOp mod) {
/*printBlockTerminators=*/true);
}
/// Parse an parameter list if present. Same format as HW dialect.
/// module-parameter-list ::= `<` parameter-decl (`,` parameter-decl)* `>`
/// parameter-decl ::= identifier `:` type
/// parameter-decl ::= identifier `:` type `=` attribute
///
static ParseResult parseParameterList(OpAsmParser &parser,
SmallVector<Attribute> &parameters) {
return parser.parseCommaSeparatedList(
OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
std::string name;
Type type;
Attribute value;
if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
return failure();
// Parse the default value if present.
if (succeeded(parser.parseOptionalEqual())) {
if (parser.parseAttribute(value, type))
return failure();
}
auto &builder = parser.getBuilder();
parameters.push_back(hw::ParamDeclAttr::get(
builder.getContext(), builder.getStringAttr(name),
TypeAttr::get(type), value));
return success();
});
}
/// Shim to also use this for the InstanceOp custom parser.
static ParseResult parseParameterList(OpAsmParser &parser,
ArrayAttr &parameters) {
SmallVector<Attribute> parseParameters;
if (failed(parseParameterList(parser, parseParameters)))
return failure();
parameters = ArrayAttr::get(parser.getContext(), parseParameters);
return success();
}
/// Print a parameter list for a module or instance. Same format as HW dialect.
static void printParameterList(OpAsmPrinter &p, Operation *op,
ArrayAttr parameters) {
if (parameters.empty())
return;
p << '<';
llvm::interleaveComma(parameters, p, [&](Attribute param) {
auto paramAttr = param.cast<hw::ParamDeclAttr>();
p << paramAttr.getName().getValue() << ": " << paramAttr.getType();
if (auto value = paramAttr.getValue()) {
p << " = ";
p.printAttributeWithoutType(value);
}
});
p << '>';
}
/// Check parameter specified by `value` to see if it is valid within the scope
/// of the specified module `module`. If not, emit an error at the location of
/// `usingOp` and return failure, otherwise return success. If `usingOp` is
/// null, then no diagnostic is generated. Same format as HW dialect.
///
/// If `disallowParamRefs` is true, then parameter references are not allowed.
static LogicalResult checkParameterInContext(Attribute value, Operation *module,
Operation *usingOp,
bool disallowParamRefs) {
// Literals are always ok. Their types are already known to match
// expectations.
if (value.isa<IntegerAttr>() || value.isa<FloatAttr>() ||
value.isa<StringAttr>() || value.isa<hw::ParamVerbatimAttr>())
return success();
// Check both subexpressions of an expression.
if (auto expr = value.dyn_cast<hw::ParamExprAttr>()) {
for (auto op : expr.getOperands())
if (failed(
checkParameterInContext(op, module, usingOp, disallowParamRefs)))
return failure();
return success();
}
// Parameter references need more analysis to make sure they are valid within
// this module.
if (auto parameterRef = value.dyn_cast<hw::ParamDeclRefAttr>()) {
auto nameAttr = parameterRef.getName();
// Don't allow references to parameters from the default values of a
// parameter list.
if (disallowParamRefs) {
if (usingOp)
usingOp->emitOpError("parameter ")
<< nameAttr << " cannot be used as a default value for a parameter";
return failure();
}
// Find the corresponding attribute in the module.
for (auto param : module->getAttrOfType<ArrayAttr>("parameters")) {
auto paramAttr = param.cast<hw::ParamDeclAttr>();
if (paramAttr.getName() != nameAttr)
continue;
// If the types match then the reference is ok.
if (paramAttr.getType().getValue() == parameterRef.getType())
return success();
if (usingOp) {
auto diag = usingOp->emitOpError("parameter ")
<< nameAttr << " used with type " << parameterRef.getType()
<< "; should have type " << paramAttr.getType().getValue();
diag.attachNote(module->getLoc()) << "module declared here";
}
return failure();
}
if (usingOp) {
auto diag = usingOp->emitOpError("use of unknown parameter ") << nameAttr;
diag.attachNote(module->getLoc()) << "module declared here";
}
return failure();
}
if (usingOp)
usingOp->emitOpError("invalid parameter value ") << value;
return failure();
}
static ParseResult parseMSFTModuleExternOp(OpAsmParser &parser,
OperationState &result) {
using namespace mlir::function_like_impl;
auto loc = parser.getCurrentLocation();
SmallVector<OpAsmParser::OperandType, 4> entryArgs;
SmallVector<NamedAttrList, 4> argAttrs;
SmallVector<NamedAttrList, 4> resultAttrs;
SmallVector<Type, 4> argTypes;
SmallVector<Type, 4> resultTypes;
SmallVector<Attribute> parameters;
auto &builder = parser.getBuilder();
// Parse the name as a symbol.
StringAttr nameAttr;
if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
result.attributes))
return failure();
// Parse the function signature.
bool isVariadic = false;
SmallVector<Attribute> resultNames;
if (parseParameterList(parser, parameters) ||
hw::module_like_impl::parseModuleFunctionSignature(
parser, entryArgs, argTypes, argAttrs, isVariadic, resultTypes,
resultAttrs, resultNames) ||
// If function attributes are present, parse them.
parser.parseOptionalAttrDictWithKeyword(result.attributes))
return failure();
// Record the argument and result types as an attribute. This is necessary
// for external modules.
auto type = builder.getFunctionType(argTypes, resultTypes);
result.addAttribute(getTypeAttrName(), TypeAttr::get(type));
auto *context = result.getContext();
if (hasAttribute("resultNames", result.attributes) ||
hasAttribute("parameters", result.attributes)) {
parser.emitError(
loc, "explicit `resultNames` / `parameters` attributes not allowed");
return failure();
}
// Use the argument and result names if not already specified.
SmallVector<Attribute> argNames;
if (!entryArgs.empty()) {
for (auto &arg : entryArgs)
argNames.push_back(
hw::module_like_impl::getPortNameAttr(context, arg.name));
} else if (!argTypes.empty()) {
// The parser returns empty names in a special way.
argNames.assign(argTypes.size(), StringAttr::get(context, ""));
}
// An explicit `argNames` attribute overrides the MLIR names. This is how
// we represent port names that aren't valid MLIR identifiers. Result and
// parameter names are printed quoted when they aren't valid identifiers, so
// they don't need this affordance.
if (!hasAttribute("argNames", result.attributes))
result.addAttribute("argNames", ArrayAttr::get(context, argNames));
result.addAttribute("resultNames", ArrayAttr::get(context, resultNames));
result.addAttribute("parameters", ArrayAttr::get(context, parameters));
assert(argAttrs.size() == argTypes.size());
assert(resultAttrs.size() == resultTypes.size());
// Add the attributes to the function arguments.
addArgAndResultAttrs(builder, result, argAttrs, resultAttrs);
// Extern modules carry an empty region to work with HWModuleImplementation.h.
result.addRegion();
return success();
}
static void printMSFTModuleExternOp(OpAsmPrinter &p, MSFTModuleExternOp op) {
using namespace mlir::function_like_impl;
auto typeAttr = op->getAttrOfType<TypeAttr>(getTypeAttrName());
FunctionType fnType = typeAttr.getValue().cast<FunctionType>();
auto argTypes = fnType.getInputs();
auto resultTypes = fnType.getResults();
// Print the operation and the function name.
p << ' ';
p.printSymbolName(SymbolTable::getSymbolName(op).getValue());
// Print the parameter list if present.
printParameterList(p, op, op->getAttrOfType<ArrayAttr>("parameters"));
bool needArgNamesAttr = false;
hw::module_like_impl::printModuleSignature(
p, op, argTypes, /*isVariadic=*/false, resultTypes, needArgNamesAttr);
SmallVector<StringRef, 3> omittedAttrs;
if (!needArgNamesAttr)
omittedAttrs.push_back("argNames");
omittedAttrs.push_back("resultNames");
omittedAttrs.push_back("parameters");
printFunctionAttributes(p, op, argTypes.size(), resultTypes.size(),
omittedAttrs);
}
static LogicalResult verifyMSFTModuleExternOp(MSFTModuleExternOp module) {
using namespace mlir::function_like_impl;
auto typeAttr = module->getAttrOfType<TypeAttr>(getTypeAttrName());
auto moduleType = typeAttr.getValue().cast<FunctionType>();
auto argNames = module->getAttrOfType<ArrayAttr>("argNames");
auto resultNames = module->getAttrOfType<ArrayAttr>("resultNames");
if (argNames.size() != moduleType.getNumInputs())
return module->emitOpError("incorrect number of argument names");
if (resultNames.size() != moduleType.getNumResults())
return module->emitOpError("incorrect number of result names");
SmallPtrSet<Attribute, 4> paramNames;
// Check parameter default values are sensible.
for (auto param : module->getAttrOfType<ArrayAttr>("parameters")) {
auto paramAttr = param.cast<hw::ParamDeclAttr>();
// Check that we don't have any redundant parameter names. These are
// resolved by string name: reuse of the same name would cause ambiguities.
if (!paramNames.insert(paramAttr.getName()).second)
return module->emitOpError("parameter ")
<< paramAttr << " has the same name as a previous parameter";
// Default values are allowed to be missing, check them if present.
auto value = paramAttr.getValue();
if (!value)
continue;
if (value.getType() != paramAttr.getType().getValue())
return module->emitOpError("parameter ")
<< paramAttr << " should have type "
<< paramAttr.getType().getValue() << "; has type "
<< value.getType();
// Verify that this is a valid parameter value, disallowing parameter
// references. We could allow parameters to refer to each other in the
// future with lexical ordering if there is a need.
if (failed(checkParameterInContext(value, module, module,
/*disallowParamRefs=*/true)))
return failure();
}
return success();
}
hw::ModulePortInfo MSFTModuleExternOp::getPorts() {
using namespace mlir::function_like_impl;
SmallVector<hw::PortInfo> inputs, outputs;
auto typeAttr = getOperation()->getAttrOfType<TypeAttr>(getTypeAttrName());
auto moduleType = typeAttr.getValue().cast<FunctionType>();
auto argTypes = moduleType.getInputs();
auto resultTypes = moduleType.getResults();
auto argNames = getOperation()->getAttrOfType<ArrayAttr>("argNames");
for (unsigned i = 0, e = argTypes.size(); i < e; ++i) {
bool isInOut = false;
auto type = argTypes[i];
if (auto inout = type.dyn_cast<hw::InOutType>()) {
isInOut = true;
type = inout.getElementType();
}
auto direction =
isInOut ? hw::PortDirection::INOUT : hw::PortDirection::INPUT;
inputs.push_back({argNames[i].cast<StringAttr>(), direction, type, i});
}
auto resultNames = getOperation()->getAttrOfType<ArrayAttr>("resultNames");
for (unsigned i = 0, e = resultTypes.size(); i < e; ++i)
outputs.push_back({resultNames[i].cast<StringAttr>(),
hw::PortDirection::OUTPUT, resultTypes[i], i});
return hw::ModulePortInfo(inputs, outputs);
}
#define GET_OP_CLASSES
#include "circt/Dialect/MSFT/MSFT.cpp.inc"

View File

@ -62,7 +62,7 @@ InstanceOpLowering::matchAndRewrite(InstanceOp msftInst, OpAdaptor adaptor,
msftInst.getLoc(), referencedModule, msftInst.instanceNameAttr(),
SmallVector<Value>(adaptor.getOperands().begin(),
adaptor.getOperands().end()),
/*parameters=*/ArrayAttr{}, msftInst.sym_nameAttr());
msftInst.parameters().getValueOr(ArrayAttr()), msftInst.sym_nameAttr());
rewriter.replaceOp(msftInst, hwInst.getResults());
return success();
}
@ -99,6 +99,27 @@ ModuleOpLowering::matchAndRewrite(MSFTModuleOp mod, OpAdaptor adaptor,
hwmod.getBody().end());
return success();
}
namespace {
/// Lower MSFT's ModuleExternOp to HW's.
struct ModuleExternOpLowering : public OpConversionPattern<MSFTModuleExternOp> {
public:
using OpConversionPattern::OpConversionPattern;
LogicalResult
matchAndRewrite(MSFTModuleExternOp mod, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const final;
};
} // anonymous namespace
LogicalResult ModuleExternOpLowering::matchAndRewrite(
MSFTModuleExternOp mod, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const {
rewriter.replaceOpWithNewOp<hw::HWModuleExternOp>(
mod, mod.getNameAttr(), mod.getPorts(), mod.verilogName().getValueOr(""),
mod.parameters());
return success();
}
namespace {
/// Lower MSFT's OutputOp to HW's.
@ -123,7 +144,7 @@ struct LowerToHWPass : public LowerToHWBase<LowerToHWPass> {
void LowerToHWPass::runOnOperation() {
auto top = getOperation();
auto ctxt = &getContext();
auto *ctxt = &getContext();
// The `hw::InstanceOp` (which `msft::InstanceOp` lowers to) convenience
// builder gets its argNames and resultNames from the `hw::HWModuleOp`. So we
@ -132,12 +153,13 @@ void LowerToHWPass::runOnOperation() {
// Convert everything except instance ops first.
ConversionTarget target(*ctxt);
target.addIllegalOp<MSFTModuleOp, OutputOp>();
target.addIllegalOp<MSFTModuleOp, MSFTModuleExternOp, OutputOp>();
target.addLegalDialect<hw::HWDialect>();
target.addLegalDialect<sv::SVDialect>();
RewritePatternSet patterns(ctxt);
patterns.insert<ModuleOpLowering>(ctxt);
patterns.insert<ModuleExternOpLowering>(ctxt);
patterns.insert<OutputOpLowering>(ctxt);
if (failed(applyPartialConversion(top, target, std::move(patterns))))

View File

@ -7,6 +7,11 @@ hw.module @top () {
msft.instance @foo @Foo() : () -> (i32)
// CHECK: %foo.x = msft.instance @foo @Foo() : () -> i32
// HWLOW: %foo.x = hw.instance "foo" sym @foo @Foo() -> (x: i32)
%true = hw.constant true
%extern.out = msft.instance @extern @Extern(%true)<param: i1 = false> : (i1) -> i1
// CHECK: %extern.out = msft.instance @extern @Extern(%true) <param: i1 = false> : (i1) -> i1
// HWLOW: %extern.out = hw.instance "extern" sym @extern @Extern<param: i1 = false>(in: %true: i1) -> (out: i1)
}
// CHECK-LABEL: msft.module @B {WIDTH = 1 : i64} (%a: i4) -> (nameOfPortInSV: i4) {
@ -26,3 +31,7 @@ msft.module @Foo { "WIDTH" = 1 } () -> (x: i32) {
%c0 = hw.constant 0 : i32
msft.output %c0 : i32
}
// CHECK-LABEL: msft.module.extern @Extern<param: i1>(%in: i1) -> (out: i1)
// HWLOW-LABEL: hw.module.extern @Extern<param: i1>(%in: i1) -> (out: i1)
msft.module.extern @Extern<param: i1> (%in: i1) -> (out: i1)