Skip to content

Commit

Permalink
Export blackboxes if not pre-loaded
Browse files Browse the repository at this point in the history
  • Loading branch information
povik committed Nov 15, 2024
1 parent c9030c9 commit a3147e3
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 1 deletion.
124 changes: 124 additions & 0 deletions src/blackboxes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
// Distributed under the terms of the ISC license, see LICENSE
//
#include "slang/ast/Compilation.h"
#include "slang/ast/symbols/CompilationUnitSymbols.h"
#include "slang/ast/symbols/InstanceSymbols.h"
#include "slang/ast/ASTVisitor.h"
#include "slang/syntax/AllSyntax.h"
#include "slang/syntax/SyntaxPrinter.h"
#include "slang/syntax/SyntaxTree.h"

#include "kernel/rtlil.h"

#include "slang_frontend.h"
#include "diag.h"

namespace slang_frontend {

Expand Down Expand Up @@ -200,4 +204,124 @@ bool is_decl_empty_module(const slang::syntax::SyntaxNode &syntax)
return true;
}

void export_blackbox_to_rtlil(ast::Compilation &comp, const ast::InstanceSymbol &inst, RTLIL::Design *target)
{
using namespace slang::ast;
using namespace slang::syntax;

RTLIL::IdString name = RTLIL::escape_id(std::string{inst.body.name});

if (target->module(name)) {
// Module already exists on the RLTIL side -- nothing to do
return;
}

for (auto instance : comp.getRoot().topInstances) {
if (!instance->name.compare(inst.name)) {
// A top module with the same name will be added later, nothing to do
return;
}
}

RTLIL::Module *mod = target->addModule(name);
transfer_attrs<ast::Symbol>((ast::Symbol&) inst.getDefinition(), mod);

inst.body.visit(ast::makeVisitor([&](auto&, const ast::PortSymbol &port) {
if (!port.getSyntax() ||
!port.getType().isFixedSize() ||
!port.internalSymbol ||
!port.internalSymbol->getDeclaredType()) {
inst.body.addDiag(diag::BboxExportPortWidths, port.location);
} else {
const DeclaredType *dt = port.internalSymbol->getDeclaredType();
const SyntaxNode &syntax = *dt->getTypeSyntax();
const SyntaxList<VariableDimensionSyntax> *dims = nullptr;
bool rejected = false;

if (syntax.kind == SyntaxKind::ImplicitType) {
auto &impl_type = syntax.as<ImplicitTypeSyntax>();
dims = &impl_type.dimensions;
} else if (IntegerTypeSyntax::isKind(syntax.kind)) {
auto &int_type = syntax.as<IntegerTypeSyntax>();
dims = &int_type.dimensions;
} else {
inst.body.addDiag(diag::BboxExportPortWidths, port.location);
rejected = true;
}

if (dims) {
for (auto dim : *dims) {
if (!dim->specifier ||
dim->specifier->kind != SyntaxKind::RangeDimensionSpecifier) {
inst.body.addDiag(diag::BboxExportPortWidths, port.location);
break;
}
auto &sel = dim->specifier->as<RangeDimensionSpecifierSyntax>().selector;
if (!RangeSelectSyntax::isKind(sel->kind) ||
!LiteralExpressionSyntax::isKind(sel->as<RangeSelectSyntax>().left->kind) ||
!LiteralExpressionSyntax::isKind(sel->as<RangeSelectSyntax>().right->kind)) {
inst.body.addDiag(diag::BboxExportPortWidths, port.location);
rejected = true;
break;
}
}
}

if (!rejected && dt->getDimensionSyntax()) {
for (auto dim : *dt->getDimensionSyntax()) {
if (!dim->specifier ||
dim->specifier->kind != SyntaxKind::RangeDimensionSpecifier) {
inst.body.addDiag(diag::BboxExportPortWidths, port.location);
break;
}
auto &sel = dim->specifier->as<RangeDimensionSpecifierSyntax>().selector;

if (RangeSelectSyntax::isKind(sel->kind)) {
auto &range = sel->as<RangeSelectSyntax>();
if (!LiteralExpressionSyntax::isKind(range.left->kind) ||
!LiteralExpressionSyntax::isKind(range.right->kind)) {
inst.body.addDiag(diag::BboxExportPortWidths, port.location);
break;
}
} else if (BitSelectSyntax::isKind(sel->kind)) {
auto &bit = sel->as<BitSelectSyntax>();
if (!LiteralExpressionSyntax::isKind(bit.expr->kind)) {
inst.body.addDiag(diag::BboxExportPortWidths, port.location);
break;
}
} else {
inst.body.addDiag(diag::BboxExportPortWidths, port.location);
break;
}
}
}
}

RTLIL::Wire *wire =
mod->addWire(RTLIL::escape_id(std::string{port.name}), port.getType().getBitstreamWidth());

switch (port.direction) {
case ast::ArgumentDirection::In:
wire->port_input = true;
break;
case ast::ArgumentDirection::Out:
wire->port_output = true;
break;
case ast::ArgumentDirection::InOut:
wire->port_input = true;
wire->port_output = true;
break;
default:
auto &diag = inst.body.addDiag(diag::UnsupportedPortDirection, port.location);
diag << ast::toString(port.direction);
break;
}

mod->ports.push_back(wire->name);
wire->port_id = mod->ports.size();
}, [&](auto&, const ast::ParameterSymbol &param) {
mod->avail_parameters(RTLIL::escape_id(std::string{param.name}));
}));
}

};
4 changes: 4 additions & 0 deletions src/diag.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ namespace diag {
slang::DiagCode NoParamsOnUnkBboxes(slang::DiagSubsystem::Netlist, 1037);
slang::DiagCode ConnNameRequiredOnUnkBboxes(slang::DiagSubsystem::Netlist, 1038);
slang::DiagCode BboxTypeParameter(slang::DiagSubsystem::Netlist, 1039);
slang::DiagCode BboxExportPortWidths(slang::DiagSubsystem::Netlist, 1040);

slang::DiagGroup unsynthesizable("unsynthesizable", {IffUnsupported, SignalSensitivityAmbiguous, GenericTimingUnsyn, BothEdgesUnsupported, ExpectingIfElseAload,
IfElseAloadPolarity, IfElseAloadMismatch});
Expand Down Expand Up @@ -150,6 +151,9 @@ namespace diag {

engine.setMessage(BboxTypeParameter, "blackbox cannot have a type parameter");
engine.setSeverity(BboxTypeParameter, slang::DiagnosticSeverity::Error);

engine.setMessage(BboxExportPortWidths, "cannot export a blackbox definition with non-constant port widths");
engine.setSeverity(BboxExportPortWidths, slang::DiagnosticSeverity::Error);
}
};
};
1 change: 1 addition & 0 deletions src/diag.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ extern slang::DiagCode BadInlinedPortConnection;
extern slang::DiagCode NoParamsOnUnkBboxes;
extern slang::DiagCode ConnNameRequiredOnUnkBboxes;
extern slang::DiagCode BboxTypeParameter;
extern slang::DiagCode BboxExportPortWidths;
void setup_messages(slang::DiagnosticEngine &engine);
};
};
3 changes: 2 additions & 1 deletion src/slang_frontend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ void transfer_attrs(T &from, RTLIL::AttrObject *to)
for (auto attr : global_compilation->getAttributes(from))
to->attributes[id(attr->name)] = convert_const(attr->getValue());
}

template void transfer_attrs<ast::Symbol>(ast::Symbol &from, RTLIL::AttrObject *to);

#define assert_nonstatic_free(signal) \
for (auto bit : (signal)) \
Expand Down Expand Up @@ -2521,6 +2521,7 @@ struct PopulateNetlist : public TimingPatternInterpretor, public ast::ASTVisitor
// no-op
}));
transfer_attrs(sym, cell);
export_blackbox_to_rtlil(netlist.compilation, sym, netlist.canvas->design);
return;
}

Expand Down
2 changes: 2 additions & 0 deletions src/slang_frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,12 @@ struct NetlistContext : RTLILBuilder {

// slang_frontend.cc
extern std::string hierpath_relative_to(const ast::Scope *relative_to, const ast::Scope *scope);
template<typename T> void transfer_attrs(T &from, RTLIL::AttrObject *to);

// blackboxes.cc
extern void import_blackboxes_from_rtlil(slang::SourceManager &mgr, ast::Compilation &target, RTLIL::Design *source);
extern bool is_decl_empty_module(const slang::syntax::SyntaxNode &syntax);
extern void export_blackbox_to_rtlil(ast::Compilation &comp, const ast::InstanceSymbol &inst, RTLIL::Design *target);

// abort_helpers.cc
[[noreturn]] void unimplemented_(const ast::Symbol &obj, const char *file, int line, const char *condition);
Expand Down
79 changes: 79 additions & 0 deletions tests/various/blackbox_scenarios.ys
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# scenario 1: pre-loaded blackbox, no blackbox definition in SV sources
# no support for parameters

design -reset
log -header Scenario 1
log -push
read_verilog <<EOF
(* blackbox *)
module foo(input wire [2:0] a, output wire b);
endmodule

(* blackbox *)
module bar(input wire [2:0] a, output wire b);
endmodule
EOF

read_slang --extern-modules <<EOF
module top();
wire c;
foo foo(.a(0), .b(c));
endmodule
EOF
hierarchy -check -top top
log -pop

# scenario 2: no pre-loaded blackbox, blackbox definition in SV sources
# parameters supported, but port widths must be fixed (non-parametric)

design -reset
log -header Scenario 2
log -push
design -reset
read_slang --dump-ast --top top <<EOF
(* blackbox *)
module foo (input wire [2:0] a, output wire b);
parameter ff = 2;
endmodule

module top();
wire c;
foo #(.ff(8)) foo(.a(0), .b(c));
endmodule
EOF
dump
hierarchy -check
log -pop

# scenario 3: pre-loaded blackbox, blackbox definition in SV sources
# parameters supported, port widths can be parametric
log -header Scenario 3
log -push
design -reset
read_verilog <<EOF
(* blackbox *)
module foo(a, b);
parameter WIDTH = 3;
input wire [WIDTH-1:0] a;
output wire [WIDTH-1:0] b;
endmodule
EOF

read_slang --extern-modules <<EOF
(* blackbox *)
module foo(a, b);
parameter WIDTH = 3;
input wire [WIDTH-1:0] a;
output wire [WIDTH-1:0] b;
endmodule

module top();
wire [4:0] c;
wire [2:0] d;
foo #(.WIDTH(5)) bar1(.a(0), .b(c));
foo #(.WIDTH(3)) bar2(.a(0), .b(d));
endmodule
EOF
hierarchy -check -top top
dump
log -pop

0 comments on commit a3147e3

Please sign in to comment.