|
9 | 9 | #include "ImportVerilogInternals.h" |
10 | 10 | #include "slang/ast/Compilation.h" |
11 | 11 | #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" |
12 | 15 | #include "llvm/ADT/ScopeExit.h" |
13 | 16 |
|
14 | 17 | using namespace circt; |
@@ -194,6 +197,147 @@ struct PackageVisitor : public BaseVisitor { |
194 | 197 | }; |
195 | 198 | } // namespace |
196 | 199 |
|
| 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 | + |
197 | 341 | //===----------------------------------------------------------------------===// |
198 | 342 | // Module Conversion |
199 | 343 | //===----------------------------------------------------------------------===// |
@@ -289,6 +433,24 @@ struct ModuleVisitor : public BaseVisitor { |
289 | 433 | using slang::ast::MultiPortSymbol; |
290 | 434 | using slang::ast::PortSymbol; |
291 | 435 |
|
| 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 | + |
292 | 454 | auto *moduleLowering = context.convertModuleHeader(&instNode.body); |
293 | 455 | if (!moduleLowering) |
294 | 456 | return failure(); |
@@ -721,11 +883,34 @@ LogicalResult Context::convertCompilation() { |
721 | 883 | } |
722 | 884 | } |
723 | 885 |
|
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 | + |
727 | 911 | if (!convertModuleHeader(&inst->body)) |
728 | 912 | return failure(); |
| 913 | + } |
729 | 914 |
|
730 | 915 | // Convert all the root module definitions. |
731 | 916 | while (!moduleWorklist.empty()) { |
@@ -764,6 +949,52 @@ LogicalResult Context::convertCompilation() { |
764 | 949 | return success(); |
765 | 950 | } |
766 | 951 |
|
| 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 | + |
767 | 998 | /// Convert a module and its ports to an empty module op in the IR. Also adds |
768 | 999 | /// the op to the worklist of module bodies to be lowered. This acts like a |
769 | 1000 | /// module "declaration", allowing instances to already refer to a module even |
@@ -838,9 +1069,8 @@ Context::convertModuleHeader(const slang::ast::InstanceBodySymbol *module) { |
838 | 1069 | auto loc = convertLocation(module->location); |
839 | 1070 | OpBuilder::InsertionGuard g(builder); |
840 | 1071 |
|
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. |
844 | 1074 | auto kind = module->getDefinition().definitionKind; |
845 | 1075 | if (kind != slang::ast::DefinitionKind::Module && |
846 | 1076 | kind != slang::ast::DefinitionKind::Program) { |
|
0 commit comments