Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/Conversion/ImportVerilog/ImportVerilog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,10 @@ LogicalResult ImportDriver::importVerilog(ModuleOp module) {
return success();

// Traverse the parsed Verilog AST and map it to the equivalent CIRCT ops.
mlirContext
->loadDialect<moore::MooreDialect, hw::HWDialect, cf::ControlFlowDialect,
func::FuncDialect, verif::VerifDialect, ltl::LTLDialect,
debug::DebugDialect>();
mlirContext->loadDialect<moore::MooreDialect, sv::SVDialect, hw::HWDialect,
cf::ControlFlowDialect, func::FuncDialect,
verif::VerifDialect, ltl::LTLDialect,
debug::DebugDialect>();
auto conversionTimer = ts.nest("Verilog to dialect mapping");
Context context(options, *compilation, module, driver.sourceManager);
if (failed(context.convertCompilation()))
Expand Down
15 changes: 15 additions & 0 deletions lib/Conversion/ImportVerilog/ImportVerilogInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "circt/Dialect/HW/HWOps.h"
#include "circt/Dialect/LTL/LTLOps.h"
#include "circt/Dialect/Moore/MooreOps.h"
#include "circt/Dialect/SV/SVOps.h"
#include "circt/Dialect/Verif/VerifOps.h"
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
Expand Down Expand Up @@ -46,6 +47,14 @@ struct ModuleLowering {
portsBySyntaxNode;
};

/// Interface lowering information.
struct InterfaceLowering {
sv::InterfaceOp op;
sv::InterfaceType type;
DenseMap<const slang::ast::ValueSymbol *, mlir::FlatSymbolRefAttr> signalRefs;
DenseMap<mlir::StringAttr, mlir::FlatSymbolRefAttr> signalRefsByName;
};

/// Function lowering information.
struct FunctionLowering {
mlir::func::FuncOp op;
Expand Down Expand Up @@ -116,6 +125,8 @@ struct Context {
ModuleLowering *
convertModuleHeader(const slang::ast::InstanceBodySymbol *module);
LogicalResult convertModuleBody(const slang::ast::InstanceBodySymbol *module);
InterfaceLowering *
convertInterface(const slang::ast::InstanceBodySymbol *interface);
LogicalResult convertPackage(const slang::ast::PackageSymbol &package);
FunctionLowering *
declareFunction(const slang::ast::SubroutineSymbol &subroutine);
Expand Down Expand Up @@ -275,6 +286,10 @@ struct Context {
DenseMap<const slang::ast::InstanceBodySymbol *,
std::unique_ptr<ModuleLowering>>
modules;
/// How we have lowered interfaces to MLIR.
DenseMap<const slang::ast::InstanceBodySymbol *,
std::unique_ptr<InterfaceLowering>>
interfaces;
/// A list of modules for which the header has been created, but the body has
/// not been converted yet.
std::queue<const slang::ast::InstanceBodySymbol *> moduleWorklist;
Expand Down
242 changes: 236 additions & 6 deletions lib/Conversion/ImportVerilog/Structure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include "ImportVerilogInternals.h"
#include "slang/ast/Compilation.h"
#include "slang/ast/symbols/ClassSymbols.h"
#include "slang/ast/symbols/CompilationUnitSymbols.h"
#include "slang/ast/symbols/InstanceSymbols.h"
#include "slang/ast/symbols/MemberSymbols.h"
#include "llvm/ADT/ScopeExit.h"

using namespace circt;
Expand Down Expand Up @@ -194,6 +197,147 @@ struct PackageVisitor : public BaseVisitor {
};
} // namespace

//===----------------------------------------------------------------------===//
// Interface Conversion
//===----------------------------------------------------------------------===//

namespace {
struct InterfaceVisitor : public BaseVisitor {
using BaseVisitor::visit;

InterfaceLowering &lowering;

InterfaceVisitor(Context &context, Location loc, InterfaceLowering &lowering)
: BaseVisitor(context, loc), lowering(lowering) {}

LogicalResult visit(const slang::ast::ParameterSymbol &) { return success(); }
LogicalResult visit(const slang::ast::SpecparamSymbol &) { return success(); }

LogicalResult visit(const slang::ast::VariableSymbol &var) {
return materializeSignal(var);
}

LogicalResult visit(const slang::ast::NetSymbol &net) {
return materializeSignal(net);
}

LogicalResult visit(const slang::ast::PortSymbol &port) {
auto *value = port.internalSymbol
? port.internalSymbol->as_if<slang::ast::ValueSymbol>()
: nullptr;
if (!value)
return mlir::emitError(loc)
<< "unsupported interface port `" << port.name << "`";
return materializeSignal(*value);
}

LogicalResult visit(const slang::ast::ModportSymbol &modport) {
SmallVector<Attribute> ports;
for (auto &member :
modport.membersOfType<slang::ast::ModportPortSymbol>()) {
auto direction = convertDirection(member.direction);
if (!direction)
return mlir::emitError(loc) << "unsupported modport direction "
<< slang::ast::toString(member.direction);

auto signal = lookupSignal(member);
if (failed(signal))
return failure();

auto directionAttr =
sv::ModportDirectionAttr::get(builder.getContext(), *direction);
ports.push_back(sv::ModportStructAttr::get(builder.getContext(),
directionAttr, *signal));
}

sv::InterfaceModportOp::create(
context.builder, context.convertLocation(modport.location),
builder.getStringAttr(modport.name), builder.getArrayAttr(ports));
return success();
}

template <typename T>
LogicalResult visit(T &&node) {
mlir::emitError(loc, "unsupported interface member: ")
<< slang::ast::toString(node.kind);
return failure();
}

private:
template <typename SymbolT>
LogicalResult materializeSignal(const SymbolT &symbol) {
if (lowering.signalRefs.contains(&symbol))
return success();

auto type = context.convertType(*symbol.getDeclaredType());
if (!type)
return failure();
if (auto intType = dyn_cast<moore::IntType>(type))
type =
IntegerType::get(type.getContext(), intType.getBitSize().value_or(1));

auto signal = sv::InterfaceSignalOp::create(
builder, context.convertLocation(symbol.location), symbol.name, type);
auto signalRef = mlir::FlatSymbolRefAttr::get(builder.getContext(),
signal.getSymNameAttr());
lowering.signalRefs.insert({&symbol, signalRef});
lowering.signalRefsByName.insert({signal.getSymNameAttr(), signalRef});
return success();
}

std::optional<sv::ModportDirection>
convertDirection(slang::ast::ArgumentDirection direction) {
using slang::ast::ArgumentDirection;
switch (direction) {
case ArgumentDirection::In:
return sv::ModportDirection::input;
case ArgumentDirection::Out:
return sv::ModportDirection::output;
case ArgumentDirection::InOut:
return sv::ModportDirection::inout;
case ArgumentDirection::Ref:
return std::nullopt;
}
llvm_unreachable("all directions handled");
}

FailureOr<mlir::FlatSymbolRefAttr>
lookupSignal(const slang::ast::ModportPortSymbol &member) {
if (auto *value =
member.internalSymbol
? member.internalSymbol->as_if<slang::ast::ValueSymbol>()
: nullptr) {
if (failed(materializeSignal(*value)))
return failure();
return lowering.signalRefs.lookup(value);
}

if (auto *port =
member.internalSymbol
? member.internalSymbol->as_if<slang::ast::PortSymbol>()
: nullptr) {
auto *value = port->internalSymbol
? port->internalSymbol->as_if<slang::ast::ValueSymbol>()
: nullptr;
if (!value)
return mlir::emitError(context.convertLocation(member.location))
<< "unsupported modport member `" << member.name << "`";
if (failed(materializeSignal(*value)))
return failure();
return lowering.signalRefs.lookup(value);
}

auto name = builder.getStringAttr(member.name);
if (auto signal = lowering.signalRefsByName.lookup(name))
return signal;

return mlir::emitError(context.convertLocation(member.location))
<< "modport member `" << member.name
<< "` does not reference a lowered interface signal";
}
};
} // namespace

//===----------------------------------------------------------------------===//
// Module Conversion
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -289,6 +433,24 @@ struct ModuleVisitor : public BaseVisitor {
using slang::ast::MultiPortSymbol;
using slang::ast::PortSymbol;

if (instNode.body.getDefinition().definitionKind ==
slang::ast::DefinitionKind::Interface) {
auto *interfaceLowering = context.convertInterface(&instNode.body);
if (!interfaceLowering)
return failure();

if (!instNode.getPortConnections().empty()) {
mlir::emitError(loc) << "unsupported interface instance connections";
return failure();
}

SmallString<64> instanceName(blockNamePrefix);
instanceName += instNode.name;
sv::InterfaceInstanceOp::create(builder, loc, interfaceLowering->type,
instanceName, hw::InnerSymAttr{});
return success();
}

auto *moduleLowering = context.convertModuleHeader(&instNode.body);
if (!moduleLowering)
return failure();
Expand Down Expand Up @@ -721,11 +883,34 @@ LogicalResult Context::convertCompilation() {
}
}

// Prime the root definition worklist by adding all the top-level modules.
SmallVector<const slang::ast::InstanceSymbol *> topInstances;
for (auto *inst : root.topInstances)
// Materialize interface definitions that may be referenced later, even if
// they are not roots in the elaborated design.
for (auto *symbol : compilation.getDefinitions()) {
auto *definition = symbol->as_if<slang::ast::DefinitionSymbol>();
if (!definition ||
definition->definitionKind != slang::ast::DefinitionKind::Interface)
continue;

auto &body = slang::ast::InstanceBodySymbol::fromDefinition(
compilation, *definition, definition->location,
slang::ast::InstanceFlags::None, nullptr, nullptr, nullptr);
if (!convertInterface(&body))
return failure();
}

// Prime the root definition worklist by lowering top-level interfaces and
// creating headers for top-level modules and programs.
for (auto *inst : root.topInstances) {
if (inst->body.getDefinition().definitionKind ==
slang::ast::DefinitionKind::Interface) {
if (!convertInterface(&inst->body))
return failure();
continue;
}

if (!convertModuleHeader(&inst->body))
return failure();
}

// Convert all the root module definitions.
while (!moduleWorklist.empty()) {
Expand Down Expand Up @@ -764,6 +949,52 @@ LogicalResult Context::convertCompilation() {
return success();
}

InterfaceLowering *
Context::convertInterface(const slang::ast::InstanceBodySymbol *interface) {
for (auto &entry : interfaces)
if (entry.first && interface->hasSameType(*entry.first)) {
interface = entry.first;
break;
}

auto &slot = interfaces[interface];
if (slot)
return slot.get();

auto loc = convertLocation(interface->location);
if (interface->getDefinition().definitionKind !=
slang::ast::DefinitionKind::Interface) {
mlir::emitError(loc) << "expected interface definition but got "
<< interface->getDefinition().getKindString();
return {};
}

slot = std::make_unique<InterfaceLowering>();
auto &lowering = *slot;

OpBuilder::InsertionGuard guard(builder);
auto it = orderedRootOps.upper_bound(interface->location);
if (it == orderedRootOps.end())
builder.setInsertionPointToEnd(intoModuleOp.getBody());
else
builder.setInsertionPoint(it->second);

auto interfaceOp = sv::InterfaceOp::create(builder, loc, interface->name);
orderedRootOps.insert(it, {interface->location, interfaceOp});
symbolTable.insert(interfaceOp);
lowering.op = interfaceOp;
lowering.type = interfaceOp.getInterfaceType();

builder.setInsertionPointToEnd(interfaceOp.getBodyBlock());
for (auto &member : interface->members()) {
auto memberLoc = convertLocation(member.location);
if (failed(member.visit(InterfaceVisitor(*this, memberLoc, lowering))))
return {};
}

return &lowering;
}

/// Convert a module and its ports to an empty module op in the IR. Also adds
/// the op to the worklist of module bodies to be lowered. This acts like a
/// module "declaration", allowing instances to already refer to a module even
Expand Down Expand Up @@ -838,9 +1069,8 @@ Context::convertModuleHeader(const slang::ast::InstanceBodySymbol *module) {
auto loc = convertLocation(module->location);
OpBuilder::InsertionGuard g(builder);

// We only support modules and programs for now. Extension to interfaces
// should be trivial though, since they are essentially the same thing with
// only minor differences in semantics.
// Modules and programs lower through `moore.module`. Interfaces use the
// dedicated `convertInterface` path instead.
auto kind = module->getDefinition().definitionKind;
if (kind != slang::ast::DefinitionKind::Module &&
kind != slang::ast::DefinitionKind::Program) {
Expand Down
22 changes: 22 additions & 0 deletions test/Conversion/ImportVerilog/errors.sv
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,25 @@ module Foo;
s[0] = b;
end
endmodule

// -----
interface I;
// expected-error @below {{unsupported interface member: ProceduralBlock}}
initial begin
end
endinterface

module Top;
I i();
endmodule

// -----
interface PortedIf
(input logic clk);
endinterface

module Foo
(input logic clk);
// expected-error @below {{unsupported interface instance connections}}
PortedIf if0(clk);
endmodule
Loading
Loading