Skip to content
Draft
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
7 changes: 6 additions & 1 deletion passes/hierarchy/hierarchy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "passes/hierarchy/util/positionals.h"
#include "passes/hierarchy/util/verilog.h"
#include "passes/hierarchy/util/generate.h"
#include "passes/hierarchy/util/ports.h"
#include <stdlib.h>
#include <stdio.h>
#include <set>
Expand Down Expand Up @@ -239,7 +240,11 @@ struct HierarchyPass : public Pass {
if ((flag_simcheck || flag_smtcheck) && top_mod == nullptr)
log_error("Design has no top module.\n");

expand_all_interfaces(design, top_mod, flag_check, flag_simcheck, flag_smtcheck, libdirs);
Hierarchy::ConnectAccumulator connect_acc;
expand_all_interfaces(design, top_mod, flag_check, flag_simcheck, flag_smtcheck, libdirs, &connect_acc);

log_header(design, "Resolving $connect directionality..\n");
Hierarchy::resolve_acc_connects(design, connect_acc);

if (top_mod != NULL) {
log_header(design, "Analyzing design hierarchy..\n");
Expand Down
1 change: 1 addition & 0 deletions passes/hierarchy/util/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ OBJS += passes/hierarchy/util/clean.o
OBJS += passes/hierarchy/util/generate.o
OBJS += passes/hierarchy/util/interfaces.o
OBJS += passes/hierarchy/util/misc.o
OBJS += passes/hierarchy/util/ports.o
OBJS += passes/hierarchy/util/positionals.o
OBJS += passes/hierarchy/util/top.o
OBJS += passes/hierarchy/util/verilog.o
8 changes: 5 additions & 3 deletions passes/hierarchy/util/interfaces.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,14 @@ void delete_marked_modules(Design* design) {
}
}

void expand_all_interfaces(Design* design, Module*& top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector<std::string> &libdirs) {
void expand_all_interfaces(Design* design, Module*& top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector<std::string> &libdirs, ConnectAccumulator* connect_acc) {
bool did_something = true;
while (did_something)
{
did_something = false;

for (auto module : used_modules(design, top_mod)) {
if (expand_module(design, module, flag_check, flag_simcheck, flag_smtcheck, libdirs))
if (expand_module(design, module, flag_check, flag_simcheck, flag_smtcheck, libdirs, connect_acc))
did_something = true;
}

Expand Down Expand Up @@ -246,7 +246,7 @@ struct CellArrays {
}
};

bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector<std::string> &libdirs)
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector<std::string> &libdirs, ConnectAccumulator* connect_acc)
{
bool did_something = false;
CellArrays cell_arrays;
Expand All @@ -264,6 +264,8 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
}
IFModExpander if_mod_expander(*design, *module);
for (auto cell : module->cells()) {
if (connect_acc)
connect_acc->collect(module, cell);
if (auto unarrayed_type = cell_arrays.trim_and_register(cell))
cell->type = *unarrayed_type;

Expand Down
19 changes: 17 additions & 2 deletions passes/hierarchy/util/interfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,23 @@
YOSYS_NAMESPACE_BEGIN
namespace Hierarchy {

void expand_all_interfaces(Design* design, Module*& top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector<std::string> &libdirs);
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector<std::string> &libdirs);
struct ConnectAccumulator {
dict<IdString, pool<IdString>> module_connect_cells;

// Collect a $connect cell during hierarchy traversal
void collect(Module* module, Cell* cell) {
if (cell->type == ID($connect) && !cell->has_keep_attr()) {
SigSpec sig_a = cell->getPort(ID::A);
SigSpec sig_b = cell->getPort(ID::B);
if (sig_a.size() > 0 && sig_b.size() > 0) {
module_connect_cells[module->name].insert(cell->name);
}
}
}
};

void expand_all_interfaces(Design* design, Module*& top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector<std::string> &libdirs, ConnectAccumulator* connect_acc);
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector<std::string> &libdirs, ConnectAccumulator* connect_acc);

// For expanding a module's interface connections
struct IFModExpander
Expand Down
283 changes: 283 additions & 0 deletions passes/hierarchy/util/ports.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <[email protected]>
* Copyright (C) 2018 Ruben Undheim <[email protected]>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/

#include "kernel/yosys_common.h"
#include "passes/hierarchy/util/ports.h"
#include "passes/hierarchy/util/interfaces.h"
#include "kernel/sigtools.h"

YOSYS_NAMESPACE_BEGIN
namespace Hierarchy {
enum class SigDirection {
UNKNOWN,
INPUT,
OUTPUT,
INOUT,
DRIVEN,
CONFLICT
};

static void build_driven_signals_index(Module *module, SigMap &sigmap, SigPool &driven_signals) {
for (auto cell : module->cells()) {
for (const auto& [port, sig] : cell->connections()) {
if (cell->output(port)) {
SigSpec mapped_sig = sigmap(sig);
driven_signals.add(mapped_sig);
}
}
}

for (auto &conn : module->connections()) {
if (conn.second.is_fully_const()) {
SigSpec lhs = sigmap(conn.first);
driven_signals.add(lhs);
}
}
}

static SigDirection get_signal_direction(const SigSpec &sig, SigMap &sigmap, const SigPool &driven_signals) {
if (sig.is_fully_const())
return SigDirection::DRIVEN;

bool has_input = false;
bool has_output = false;
bool has_driven = false;
bool has_unknown = false;

for (auto &chunk : sig.chunks()) {
if (chunk.is_wire()) {
Wire *w = chunk.wire;

if (w->port_input && w->port_output) {
has_input = true;
has_output = true;
} else if (w->port_input) {
has_input = true;
} else if (w->port_output) {
has_output = true;
} else {
SigSpec chunk_sig = sigmap(SigSpec(chunk));

if (driven_signals.check_any(chunk_sig)) {
has_driven = true;
} else {
has_unknown = true;
}
}
} else {
has_driven = true;
}
}

if (has_input && has_driven)
return SigDirection::CONFLICT;
if (has_input && has_output)
return SigDirection::INOUT;
if (has_output)
return SigDirection::OUTPUT;
if (has_driven)
return SigDirection::DRIVEN;
if (has_input)
return SigDirection::INPUT;
if (has_unknown)
return SigDirection::UNKNOWN;

return SigDirection::DRIVEN;
}

std::pair<Module*, bool> derive_blackbox_dynports(Module* module, Cell* cell, Design* design, std::set<Module*>& blackbox_derivatives) {
bool boxed_params = false;

if (!module->get_blackbox_attribute() || cell->parameters.empty()) {
return {module, boxed_params};
}

if (module->get_bool_attribute(ID::dynports)) {
IdString new_m_name = module->derive(design, cell->parameters, true);

if (new_m_name.empty())
return {nullptr, boxed_params};
if (new_m_name != module->name) {
module = design->module(new_m_name);
blackbox_derivatives.insert(module);
}
} else {
boxed_params = true;
}

return {module, boxed_params};
}

void check_and_adjust_ports(Module* module, std::set<Module*>& blackbox_derivatives, bool keep_portwidths, bool top_is_from_verific) {
Design* design = module->design;

for (auto cell : module->cells())
{
Module *m = design->module(cell->type);

if (m == nullptr)
continue;

auto [derived_m, boxed_params] = derive_blackbox_dynports(m, cell, design, blackbox_derivatives);
if (derived_m == nullptr)
continue;
m = derived_m;

for (const auto& [port, sig] : cell->connections())
{
Wire *w = m->wire(port);

if (w == nullptr || w->port_id == 0)
continue;

if (GetSize(sig) == 0)
continue;

SigSpec conn_sig = sig;

bool resize_widths = !keep_portwidths && GetSize(w) != GetSize(sig);
if (resize_widths && top_is_from_verific && boxed_params)
log_debug("Ignoring width mismatch on %s.%s.%s from verific\n",
log_id(module), log_id(cell), log_id(port)
);
else if (resize_widths) {
if (GetSize(w) < GetSize(sig))
{
int n = GetSize(sig) - GetSize(w);
if (!w->port_input && w->port_output)
{
RTLIL::SigSpec out = conn_sig.extract(0, GetSize(w));
out.extend_u0(GetSize(conn_sig), w->is_signed);
module->connect(conn_sig.extract(GetSize(w), n), out.extract(GetSize(w), n));
}
conn_sig.remove(GetSize(w), n);
}
else
{
int n = GetSize(w) - GetSize(sig);
if (w->port_input && !w->port_output)
conn_sig.extend_u0(GetSize(w), conn_sig.is_wire() && conn_sig.as_wire()->is_signed);
else
conn_sig.append(module->addWire(NEW_ID, n));
}

if (!sig.is_fully_const() || !w->port_input || w->port_output)
log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
log_id(port), GetSize(sig), GetSize(conn_sig));
cell->setPort(port, conn_sig);
}

if (w->port_output && !w->port_input && conn_sig.has_const())
log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
log_id(module), log_id(cell), log_id(port), log_id(cell->type), log_signal(conn_sig));
}
}
}

void resolve_acc_connects(Design* design, const ConnectAccumulator& connect_acc) {
std::vector<IdString> sorted_module_names;
for (const auto& [mod_name, cell_names] : connect_acc.module_connect_cells)
sorted_module_names.push_back(mod_name);
std::sort(sorted_module_names.begin(), sorted_module_names.end());

for (auto mod_name : sorted_module_names) {
Module* module = design->module(mod_name);
if (!module)
continue;

const pool<IdString>& cell_names = connect_acc.module_connect_cells.at(mod_name);
pool<IdString> remaining_cell_names = cell_names;
int iteration = 0;

while (true) {
iteration++;
pool<Cell*> cells_to_remove;
vector<SigSig> new_connections;
SigMap sigmap(module);
SigPool driven_signals;

build_driven_signals_index(module, sigmap, driven_signals);

for (auto cell_name : remaining_cell_names) {
Cell* cell = module->cell(cell_name);
if (!cell || cell->type != ID($connect) || cell->has_keep_attr())
continue;

SigSpec sig_a = cell->getPort(ID::A);
SigSpec sig_b = cell->getPort(ID::B);

if (sig_a.size() == 0 || sig_b.size() == 0)
continue;

SigDirection dir_a = get_signal_direction(sig_a, sigmap, driven_signals);
SigDirection dir_b = get_signal_direction(sig_b, sigmap, driven_signals);

if (dir_a == SigDirection::CONFLICT || dir_b == SigDirection::CONFLICT)
continue;

SigSpec driver, driven;
bool can_resolve = false;

if ((dir_a == SigDirection::OUTPUT || dir_a == SigDirection::DRIVEN) && dir_b == SigDirection::INPUT) {
driver = sig_a;
driven = sig_b;
can_resolve = true;
}
else if (dir_a == SigDirection::INPUT && (dir_b == SigDirection::OUTPUT || dir_b == SigDirection::DRIVEN)) {
driver = sig_b;
driven = sig_a;
can_resolve = true;
}
else if (dir_a == SigDirection::DRIVEN && dir_b == SigDirection::UNKNOWN) {
driver = sig_a;
driven = sig_b;
can_resolve = true;
}
else if (dir_a == SigDirection::UNKNOWN && dir_b == SigDirection::DRIVEN) {
driver = sig_b;
driven = sig_a;
can_resolve = true;
}

if (can_resolve) {
log_debug("Resolving $connect %s: %s <- %s\n", log_id(cell), log_signal(driven), log_signal(driver));
new_connections.push_back({driven, driver});
cells_to_remove.insert(cell);
}
}

for (auto &conn : new_connections)
module->connect(conn);

for (auto cell : cells_to_remove) {
remaining_cell_names.erase(cell->name);
module->remove(cell);
}

if (cells_to_remove.empty())
break;

log_debug("$connect res iteration %d: resolved %d cells\n", iteration, GetSize(cells_to_remove));
}
}
}
};

YOSYS_NAMESPACE_END
Loading
Loading