Skip to content

Commit d834f6c

Browse files
committed
ImportVerilog: add step-1 SV interface import
1 parent 63f65d1 commit d834f6c

File tree

6 files changed

+375
-10
lines changed

6 files changed

+375
-10
lines changed

lib/Conversion/ImportVerilog/ImportVerilog.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,10 @@ LogicalResult ImportDriver::importVerilog(ModuleOp module) {
286286
return success();
287287

288288
// Traverse the parsed Verilog AST and map it to the equivalent CIRCT ops.
289-
mlirContext
290-
->loadDialect<moore::MooreDialect, hw::HWDialect, cf::ControlFlowDialect,
291-
func::FuncDialect, verif::VerifDialect, ltl::LTLDialect,
292-
debug::DebugDialect>();
289+
mlirContext->loadDialect<moore::MooreDialect, sv::SVDialect, hw::HWDialect,
290+
cf::ControlFlowDialect, func::FuncDialect,
291+
verif::VerifDialect, ltl::LTLDialect,
292+
debug::DebugDialect>();
293293
auto conversionTimer = ts.nest("Verilog to dialect mapping");
294294
Context context(options, *compilation, module, driver.sourceManager);
295295
if (failed(context.convertCompilation()))

lib/Conversion/ImportVerilog/ImportVerilogInternals.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "circt/Dialect/HW/HWOps.h"
1616
#include "circt/Dialect/LTL/LTLOps.h"
1717
#include "circt/Dialect/Moore/MooreOps.h"
18+
#include "circt/Dialect/SV/SVOps.h"
1819
#include "circt/Dialect/Verif/VerifOps.h"
1920
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
2021
#include "mlir/Dialect/Func/IR/FuncOps.h"
@@ -46,6 +47,14 @@ struct ModuleLowering {
4647
portsBySyntaxNode;
4748
};
4849

50+
/// Interface lowering information.
51+
struct InterfaceLowering {
52+
sv::InterfaceOp op;
53+
sv::InterfaceType type;
54+
DenseMap<const slang::ast::ValueSymbol *, mlir::FlatSymbolRefAttr> signalRefs;
55+
DenseMap<mlir::StringAttr, mlir::FlatSymbolRefAttr> signalRefsByName;
56+
};
57+
4958
/// Function lowering information.
5059
struct FunctionLowering {
5160
mlir::func::FuncOp op;
@@ -116,6 +125,8 @@ struct Context {
116125
ModuleLowering *
117126
convertModuleHeader(const slang::ast::InstanceBodySymbol *module);
118127
LogicalResult convertModuleBody(const slang::ast::InstanceBodySymbol *module);
128+
InterfaceLowering *
129+
convertInterface(const slang::ast::InstanceBodySymbol *interface);
119130
LogicalResult convertPackage(const slang::ast::PackageSymbol &package);
120131
FunctionLowering *
121132
declareFunction(const slang::ast::SubroutineSymbol &subroutine);
@@ -275,6 +286,10 @@ struct Context {
275286
DenseMap<const slang::ast::InstanceBodySymbol *,
276287
std::unique_ptr<ModuleLowering>>
277288
modules;
289+
/// How we have lowered interfaces to MLIR.
290+
DenseMap<const slang::ast::InstanceBodySymbol *,
291+
std::unique_ptr<InterfaceLowering>>
292+
interfaces;
278293
/// A list of modules for which the header has been created, but the body has
279294
/// not been converted yet.
280295
std::queue<const slang::ast::InstanceBodySymbol *> moduleWorklist;

lib/Conversion/ImportVerilog/Structure.cpp

Lines changed: 236 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#include "ImportVerilogInternals.h"
1010
#include "slang/ast/Compilation.h"
1111
#include "slang/ast/symbols/ClassSymbols.h"
12+
#include "slang/ast/symbols/CompilationUnitSymbols.h"
13+
#include "slang/ast/symbols/InstanceSymbols.h"
14+
#include "slang/ast/symbols/MemberSymbols.h"
1215
#include "llvm/ADT/ScopeExit.h"
1316

1417
using namespace circt;
@@ -194,6 +197,147 @@ struct PackageVisitor : public BaseVisitor {
194197
};
195198
} // namespace
196199

200+
//===----------------------------------------------------------------------===//
201+
// Interface Conversion
202+
//===----------------------------------------------------------------------===//
203+
204+
namespace {
205+
struct InterfaceVisitor : public BaseVisitor {
206+
using BaseVisitor::visit;
207+
208+
InterfaceLowering &lowering;
209+
210+
InterfaceVisitor(Context &context, Location loc, InterfaceLowering &lowering)
211+
: BaseVisitor(context, loc), lowering(lowering) {}
212+
213+
LogicalResult visit(const slang::ast::ParameterSymbol &) { return success(); }
214+
LogicalResult visit(const slang::ast::SpecparamSymbol &) { return success(); }
215+
216+
LogicalResult visit(const slang::ast::VariableSymbol &var) {
217+
return materializeSignal(var);
218+
}
219+
220+
LogicalResult visit(const slang::ast::NetSymbol &net) {
221+
return materializeSignal(net);
222+
}
223+
224+
LogicalResult visit(const slang::ast::PortSymbol &port) {
225+
auto *value = port.internalSymbol
226+
? port.internalSymbol->as_if<slang::ast::ValueSymbol>()
227+
: nullptr;
228+
if (!value)
229+
return mlir::emitError(loc)
230+
<< "unsupported interface port `" << port.name << "`";
231+
return materializeSignal(*value);
232+
}
233+
234+
LogicalResult visit(const slang::ast::ModportSymbol &modport) {
235+
SmallVector<Attribute> ports;
236+
for (auto &member :
237+
modport.membersOfType<slang::ast::ModportPortSymbol>()) {
238+
auto direction = convertDirection(member.direction);
239+
if (!direction)
240+
return mlir::emitError(loc) << "unsupported modport direction "
241+
<< slang::ast::toString(member.direction);
242+
243+
auto signal = lookupSignal(member);
244+
if (failed(signal))
245+
return failure();
246+
247+
auto directionAttr =
248+
sv::ModportDirectionAttr::get(builder.getContext(), *direction);
249+
ports.push_back(sv::ModportStructAttr::get(builder.getContext(),
250+
directionAttr, *signal));
251+
}
252+
253+
sv::InterfaceModportOp::create(
254+
context.builder, context.convertLocation(modport.location),
255+
builder.getStringAttr(modport.name), builder.getArrayAttr(ports));
256+
return success();
257+
}
258+
259+
template <typename T>
260+
LogicalResult visit(T &&node) {
261+
mlir::emitError(loc, "unsupported interface member: ")
262+
<< slang::ast::toString(node.kind);
263+
return failure();
264+
}
265+
266+
private:
267+
template <typename SymbolT>
268+
LogicalResult materializeSignal(const SymbolT &symbol) {
269+
if (lowering.signalRefs.contains(&symbol))
270+
return success();
271+
272+
auto type = context.convertType(*symbol.getDeclaredType());
273+
if (!type)
274+
return failure();
275+
if (auto intType = dyn_cast<moore::IntType>(type))
276+
type =
277+
IntegerType::get(type.getContext(), intType.getBitSize().value_or(1));
278+
279+
auto signal = sv::InterfaceSignalOp::create(
280+
builder, context.convertLocation(symbol.location), symbol.name, type);
281+
auto signalRef = mlir::FlatSymbolRefAttr::get(builder.getContext(),
282+
signal.getSymNameAttr());
283+
lowering.signalRefs.insert({&symbol, signalRef});
284+
lowering.signalRefsByName.insert({signal.getSymNameAttr(), signalRef});
285+
return success();
286+
}
287+
288+
std::optional<sv::ModportDirection>
289+
convertDirection(slang::ast::ArgumentDirection direction) {
290+
using slang::ast::ArgumentDirection;
291+
switch (direction) {
292+
case ArgumentDirection::In:
293+
return sv::ModportDirection::input;
294+
case ArgumentDirection::Out:
295+
return sv::ModportDirection::output;
296+
case ArgumentDirection::InOut:
297+
return sv::ModportDirection::inout;
298+
case ArgumentDirection::Ref:
299+
return std::nullopt;
300+
}
301+
llvm_unreachable("all directions handled");
302+
}
303+
304+
FailureOr<mlir::FlatSymbolRefAttr>
305+
lookupSignal(const slang::ast::ModportPortSymbol &member) {
306+
if (auto *value =
307+
member.internalSymbol
308+
? member.internalSymbol->as_if<slang::ast::ValueSymbol>()
309+
: nullptr) {
310+
if (failed(materializeSignal(*value)))
311+
return failure();
312+
return lowering.signalRefs.lookup(value);
313+
}
314+
315+
if (auto *port =
316+
member.internalSymbol
317+
? member.internalSymbol->as_if<slang::ast::PortSymbol>()
318+
: nullptr) {
319+
auto *value = port->internalSymbol
320+
? port->internalSymbol->as_if<slang::ast::ValueSymbol>()
321+
: nullptr;
322+
if (!value)
323+
return mlir::emitError(context.convertLocation(member.location))
324+
<< "unsupported modport member `" << member.name << "`";
325+
if (failed(materializeSignal(*value)))
326+
return failure();
327+
return lowering.signalRefs.lookup(value);
328+
}
329+
330+
auto name = builder.getStringAttr(member.name);
331+
if (auto signal = lowering.signalRefsByName.lookup(name))
332+
return signal;
333+
334+
return mlir::emitError(context.convertLocation(member.location))
335+
<< "modport member `" << member.name
336+
<< "` does not reference a lowered interface signal";
337+
}
338+
};
339+
} // namespace
340+
197341
//===----------------------------------------------------------------------===//
198342
// Module Conversion
199343
//===----------------------------------------------------------------------===//
@@ -289,6 +433,24 @@ struct ModuleVisitor : public BaseVisitor {
289433
using slang::ast::MultiPortSymbol;
290434
using slang::ast::PortSymbol;
291435

436+
if (instNode.body.getDefinition().definitionKind ==
437+
slang::ast::DefinitionKind::Interface) {
438+
auto *interfaceLowering = context.convertInterface(&instNode.body);
439+
if (!interfaceLowering)
440+
return failure();
441+
442+
if (!instNode.getPortConnections().empty()) {
443+
mlir::emitError(loc) << "unsupported interface instance connections";
444+
return failure();
445+
}
446+
447+
SmallString<64> instanceName(blockNamePrefix);
448+
instanceName += instNode.name;
449+
sv::InterfaceInstanceOp::create(builder, loc, interfaceLowering->type,
450+
instanceName, hw::InnerSymAttr{});
451+
return success();
452+
}
453+
292454
auto *moduleLowering = context.convertModuleHeader(&instNode.body);
293455
if (!moduleLowering)
294456
return failure();
@@ -721,11 +883,34 @@ LogicalResult Context::convertCompilation() {
721883
}
722884
}
723885

724-
// Prime the root definition worklist by adding all the top-level modules.
725-
SmallVector<const slang::ast::InstanceSymbol *> topInstances;
726-
for (auto *inst : root.topInstances)
886+
// Materialize interface definitions that may be referenced later, even if
887+
// they are not roots in the elaborated design.
888+
for (auto *symbol : compilation.getDefinitions()) {
889+
auto *definition = symbol->as_if<slang::ast::DefinitionSymbol>();
890+
if (!definition ||
891+
definition->definitionKind != slang::ast::DefinitionKind::Interface)
892+
continue;
893+
894+
auto &body = slang::ast::InstanceBodySymbol::fromDefinition(
895+
compilation, *definition, definition->location,
896+
slang::ast::InstanceFlags::None, nullptr, nullptr, nullptr);
897+
if (!convertInterface(&body))
898+
return failure();
899+
}
900+
901+
// Prime the root definition worklist by lowering top-level interfaces and
902+
// creating headers for top-level modules and programs.
903+
for (auto *inst : root.topInstances) {
904+
if (inst->body.getDefinition().definitionKind ==
905+
slang::ast::DefinitionKind::Interface) {
906+
if (!convertInterface(&inst->body))
907+
return failure();
908+
continue;
909+
}
910+
727911
if (!convertModuleHeader(&inst->body))
728912
return failure();
913+
}
729914

730915
// Convert all the root module definitions.
731916
while (!moduleWorklist.empty()) {
@@ -764,6 +949,52 @@ LogicalResult Context::convertCompilation() {
764949
return success();
765950
}
766951

952+
InterfaceLowering *
953+
Context::convertInterface(const slang::ast::InstanceBodySymbol *interface) {
954+
for (auto &entry : interfaces)
955+
if (entry.first && interface->hasSameType(*entry.first)) {
956+
interface = entry.first;
957+
break;
958+
}
959+
960+
auto &slot = interfaces[interface];
961+
if (slot)
962+
return slot.get();
963+
964+
auto loc = convertLocation(interface->location);
965+
if (interface->getDefinition().definitionKind !=
966+
slang::ast::DefinitionKind::Interface) {
967+
mlir::emitError(loc) << "expected interface definition but got "
968+
<< interface->getDefinition().getKindString();
969+
return {};
970+
}
971+
972+
slot = std::make_unique<InterfaceLowering>();
973+
auto &lowering = *slot;
974+
975+
OpBuilder::InsertionGuard guard(builder);
976+
auto it = orderedRootOps.upper_bound(interface->location);
977+
if (it == orderedRootOps.end())
978+
builder.setInsertionPointToEnd(intoModuleOp.getBody());
979+
else
980+
builder.setInsertionPoint(it->second);
981+
982+
auto interfaceOp = sv::InterfaceOp::create(builder, loc, interface->name);
983+
orderedRootOps.insert(it, {interface->location, interfaceOp});
984+
symbolTable.insert(interfaceOp);
985+
lowering.op = interfaceOp;
986+
lowering.type = interfaceOp.getInterfaceType();
987+
988+
builder.setInsertionPointToEnd(interfaceOp.getBodyBlock());
989+
for (auto &member : interface->members()) {
990+
auto memberLoc = convertLocation(member.location);
991+
if (failed(member.visit(InterfaceVisitor(*this, memberLoc, lowering))))
992+
return {};
993+
}
994+
995+
return &lowering;
996+
}
997+
767998
/// Convert a module and its ports to an empty module op in the IR. Also adds
768999
/// the op to the worklist of module bodies to be lowered. This acts like a
7691000
/// module "declaration", allowing instances to already refer to a module even
@@ -838,9 +1069,8 @@ Context::convertModuleHeader(const slang::ast::InstanceBodySymbol *module) {
8381069
auto loc = convertLocation(module->location);
8391070
OpBuilder::InsertionGuard g(builder);
8401071

841-
// We only support modules and programs for now. Extension to interfaces
842-
// should be trivial though, since they are essentially the same thing with
843-
// only minor differences in semantics.
1072+
// Modules and programs lower through `moore.module`. Interfaces use the
1073+
// dedicated `convertInterface` path instead.
8441074
auto kind = module->getDefinition().definitionKind;
8451075
if (kind != slang::ast::DefinitionKind::Module &&
8461076
kind != slang::ast::DefinitionKind::Program) {

test/Conversion/ImportVerilog/errors.sv

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,25 @@ module Foo;
200200
s[0] = b;
201201
end
202202
endmodule
203+
204+
// -----
205+
interface I;
206+
// expected-error @below {{unsupported interface member: ProceduralBlock}}
207+
initial begin
208+
end
209+
endinterface
210+
211+
module Top;
212+
I i();
213+
endmodule
214+
215+
// -----
216+
interface PortedIf
217+
(input logic clk);
218+
endinterface
219+
220+
module Foo
221+
(input logic clk);
222+
// expected-error @below {{unsupported interface instance connections}}
223+
PortedIf if0(clk);
224+
endmodule

0 commit comments

Comments
 (0)