mirror of
https://github.com/YosysHQ/yosys
synced 2026-06-02 15:18:07 +00:00
rtlil, patch: update signorm index and driver fields when committing Cell from Patch to Design
This commit is contained in:
parent
b0eb50be1b
commit
5a6568edbe
3 changed files with 164 additions and 110 deletions
|
|
@ -2146,6 +2146,15 @@ private:
|
||||||
struct ConstructToken { explicit ConstructToken() = default; };
|
struct ConstructToken { explicit ConstructToken() = default; };
|
||||||
friend struct RTLIL::Module;
|
friend struct RTLIL::Module;
|
||||||
friend struct RTLIL::Patch;
|
friend struct RTLIL::Patch;
|
||||||
|
|
||||||
|
// Push existing port connections into signorm/bufnorm indices after module assignment.
|
||||||
|
// Assumes signals are already in normalized form.
|
||||||
|
void initIndex();
|
||||||
|
|
||||||
|
// Signorm index helpers (used by setPort/unsetPort/initIndex)
|
||||||
|
void signorm_index_remove(RTLIL::IdString portname, const RTLIL::SigSpec &old_signal, bool is_input);
|
||||||
|
void signorm_index_add(RTLIL::IdString portname, const RTLIL::SigSpec &new_signal, bool is_input);
|
||||||
|
bool bufnorm_handle_setPort(RTLIL::IdString portname, RTLIL::SigSpec &signal, dict<RTLIL::IdString, RTLIL::SigSpec>::iterator conn_it);
|
||||||
public:
|
public:
|
||||||
Hasher::hash_t hashidx_;
|
Hasher::hash_t hashidx_;
|
||||||
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||||
|
|
|
||||||
|
|
@ -1087,15 +1087,161 @@ static bool ignored_cell(const RTLIL::IdString& type)
|
||||||
return type == ID($specify2) || type == ID($specify3) || type == ID($specrule);
|
return type == ID($specify2) || type == ID($specify3) || type == ID($specrule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RTLIL::Cell::signorm_index_remove(IdString portname, const SigSpec &old_signal, bool is_input)
|
||||||
|
{
|
||||||
|
auto &index = *module->sig_norm_index;
|
||||||
|
index.dirty.insert(this);
|
||||||
|
if (is_input) {
|
||||||
|
int i = 0;
|
||||||
|
for (auto bit : old_signal) {
|
||||||
|
if (bit.is_wire()) {
|
||||||
|
auto found = index.fanout.find(bit);
|
||||||
|
log_assert(found != index.fanout.end());
|
||||||
|
int erased = found->second.erase(PortBit(this, portname, i));
|
||||||
|
log_assert(erased);
|
||||||
|
if (found->second.empty())
|
||||||
|
index.fanout.erase(found);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Wire *w = old_signal.as_wire();
|
||||||
|
log_assert(w->driverCell_ == this);
|
||||||
|
log_assert(w->driverPort_ == portname);
|
||||||
|
w->driverCell_ = nullptr;
|
||||||
|
w->driverPort_ = IdString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTLIL::Cell::signorm_index_add(IdString portname, const SigSpec &new_signal, bool is_input)
|
||||||
|
{
|
||||||
|
auto &index = *module->sig_norm_index;
|
||||||
|
index.dirty.insert(this);
|
||||||
|
if (is_input) {
|
||||||
|
int i = 0;
|
||||||
|
for (auto bit : new_signal) {
|
||||||
|
if (bit.is_wire())
|
||||||
|
index.fanout[bit].insert(PortBit(this, portname, i));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else if (GetSize(new_signal)) {
|
||||||
|
Wire *w = new_signal.as_wire();
|
||||||
|
log_assert(w->driverCell_ == nullptr);
|
||||||
|
log_assert(w->driverPort_.empty());
|
||||||
|
w->driverCell_ = this;
|
||||||
|
w->driverPort_ = portname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles the bufnorm part of setPort. Updates conn_it->second and returns true if the
|
||||||
|
// connection was stored (fast path or $connect cell). If false, caller must store signal.
|
||||||
|
bool RTLIL::Cell::bufnorm_handle_setPort(IdString portname, SigSpec &signal, dict<IdString, SigSpec>::iterator conn_it)
|
||||||
|
{
|
||||||
|
// Eagerly clear a driver that got disconnected by changing this port connection
|
||||||
|
if (conn_it->second.is_wire()) {
|
||||||
|
Wire *w = conn_it->second.as_wire();
|
||||||
|
if (w->driverCell_ == this && w->driverPort_ == portname) {
|
||||||
|
w->driverCell_ = nullptr;
|
||||||
|
w->driverPort_ = IdString();
|
||||||
|
module->buf_norm_wire_queue.insert(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = port_dir(portname);
|
||||||
|
// Fast path: connecting a full driverless wire to an output port — everything else
|
||||||
|
// goes through the bufnorm queues and is handled during the next bufNormalize call
|
||||||
|
if ((dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) && signal.is_wire()) {
|
||||||
|
Wire *w = signal.as_wire();
|
||||||
|
if (w->driverCell_ == nullptr &&
|
||||||
|
(w->port_input && !w->port_output) == (type == ID($input_port))) {
|
||||||
|
w->driverCell_ = this;
|
||||||
|
w->driverPort_ = portname;
|
||||||
|
conn_it->second = std::move(signal);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) {
|
||||||
|
module->buf_norm_cell_queue.insert(this);
|
||||||
|
module->buf_norm_cell_port_queue.emplace(this, portname);
|
||||||
|
} else {
|
||||||
|
for (auto &chunk : signal.chunks())
|
||||||
|
if (chunk.wire != nullptr && chunk.wire->driverCell_ == nullptr)
|
||||||
|
module->buf_norm_wire_queue.insert(chunk.wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == ID($connect)) {
|
||||||
|
for (auto &[port, sig] : connections_) {
|
||||||
|
for (auto &chunk : sig.chunks()) {
|
||||||
|
if (!chunk.wire) continue;
|
||||||
|
auto it = module->buf_norm_connect_index.find(chunk.wire);
|
||||||
|
if (it == module->buf_norm_connect_index.end()) continue;
|
||||||
|
it->second.erase(this);
|
||||||
|
if (it->second.empty())
|
||||||
|
module->buf_norm_connect_index.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn_it->second = std::move(signal);
|
||||||
|
for (auto &[port, sig] : connections_) {
|
||||||
|
for (auto &chunk : sig.chunks()) {
|
||||||
|
if (!chunk.wire) continue;
|
||||||
|
module->buf_norm_connect_index[chunk.wire].insert(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called after the cell's module pointer has been set to push all existing port connections
|
||||||
|
// into the signorm and bufnorm indices. Assumes signals are already in normalized form.
|
||||||
|
void RTLIL::Cell::initIndex()
|
||||||
|
{
|
||||||
|
log_assert(module != nullptr);
|
||||||
|
if (ignored_cell(type))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (module->sig_norm_index != nullptr) {
|
||||||
|
for (auto &[portname, signal] : connections_) {
|
||||||
|
bool is_input = port_dir(portname) == RTLIL::PD_INPUT;
|
||||||
|
signorm_index_add(portname, signal, is_input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module->design && module->design->flagBufferedNormalized) {
|
||||||
|
for (auto &[portname, signal] : connections_) {
|
||||||
|
auto dir = port_dir(portname);
|
||||||
|
if ((dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) && signal.is_wire()) {
|
||||||
|
Wire *w = signal.as_wire();
|
||||||
|
if (w->driverCell_ == nullptr &&
|
||||||
|
(w->port_input && !w->port_output) == (type == ID($input_port))) {
|
||||||
|
w->driverCell_ = this;
|
||||||
|
w->driverPort_ = portname;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) {
|
||||||
|
module->buf_norm_cell_queue.insert(this);
|
||||||
|
module->buf_norm_cell_port_queue.emplace(this, portname);
|
||||||
|
} else {
|
||||||
|
for (auto &chunk : signal.chunks())
|
||||||
|
if (chunk.wire != nullptr && chunk.wire->driverCell_ == nullptr)
|
||||||
|
module->buf_norm_wire_queue.insert(chunk.wire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal)
|
void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal)
|
||||||
{
|
{
|
||||||
bool is_input_port = false;
|
bool is_input = false;
|
||||||
if (module && module->sig_norm_index != nullptr && !ignored_cell(type)) {
|
if (module && module->sig_norm_index != nullptr && !ignored_cell(type)) {
|
||||||
module->sig_norm_index->sigmap.apply(signal);
|
module->sig_norm_index->sigmap.apply(signal);
|
||||||
auto dir = port_dir(portname);
|
auto dir = port_dir(portname);
|
||||||
|
|
||||||
if (dir == RTLIL::PD_INPUT) {
|
if (dir == RTLIL::PD_INPUT) {
|
||||||
is_input_port = true;
|
is_input = true;
|
||||||
} else {
|
} else {
|
||||||
Wire *wire = nullptr;
|
Wire *wire = nullptr;
|
||||||
if (signal.is_wire() && (wire = signal.as_wire())->driverCell_ != nullptr)
|
if (signal.is_wire() && (wire = signal.as_wire())->driverCell_ != nullptr)
|
||||||
|
|
@ -1128,111 +1274,17 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (module && module->sig_norm_index != nullptr && !ignored_cell(type)) {
|
if (module && module->sig_norm_index != nullptr && !ignored_cell(type)) {
|
||||||
module->sig_norm_index->dirty.insert(this);
|
if (!r.second)
|
||||||
if (!r.second) {
|
signorm_index_remove(portname, conn_it->second, is_input);
|
||||||
if (is_input_port) {
|
signorm_index_add(portname, signal, is_input);
|
||||||
auto &fanout = module->sig_norm_index->fanout;
|
|
||||||
int i = 0;
|
|
||||||
for (auto bit : conn_it->second) {
|
|
||||||
if (bit.is_wire()) {
|
|
||||||
auto found = fanout.find(bit);
|
|
||||||
log_assert(found != fanout.end());
|
|
||||||
int erased = found->second.erase(PortBit(this, portname, i));
|
|
||||||
log_assert(erased);
|
|
||||||
if (found->second.empty())
|
|
||||||
fanout.erase(found);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Wire *w = conn_it->second.as_wire();
|
|
||||||
log_assert(w->driverCell_ == this);
|
|
||||||
log_assert(w->driverPort_ == portname);
|
|
||||||
w->driverCell_ = nullptr;
|
|
||||||
w->driverPort_ = IdString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_input_port) {
|
|
||||||
auto &fanout = module->sig_norm_index->fanout;
|
|
||||||
int i = 0;
|
|
||||||
for (auto bit : signal) {
|
|
||||||
if (bit.is_wire())
|
|
||||||
fanout[bit].insert(PortBit(this, portname, i));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} else if (GetSize(signal)) {
|
|
||||||
Wire *w = signal.as_wire();
|
|
||||||
log_assert(w->driverCell_ == nullptr);
|
|
||||||
log_assert(w->driverPort_.empty());
|
|
||||||
w->driverCell_ = this;
|
|
||||||
w->driverPort_ = portname;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (module && module->design && module->design->flagBufferedNormalized)
|
if (module && module->design && module->design->flagBufferedNormalized) {
|
||||||
{
|
if (bufnorm_handle_setPort(portname, signal, conn_it))
|
||||||
// We eagerly clear a driver that got disconnected by changing this port connection
|
|
||||||
if (conn_it->second.is_wire()) {
|
|
||||||
Wire *w = conn_it->second.as_wire();
|
|
||||||
if (w->driverCell_ == this && w->driverPort_ == portname) {
|
|
||||||
w->driverCell_ = nullptr;
|
|
||||||
w->driverPort_ = IdString();
|
|
||||||
module->buf_norm_wire_queue.insert(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dir = port_dir(portname);
|
|
||||||
// This is a fast path that handles connecting a full driverless wire to an output port,
|
|
||||||
// everything else is goes through the bufnorm queues and is handled during the next
|
|
||||||
// bufNormalize call
|
|
||||||
if ((dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) && signal.is_wire()) {
|
|
||||||
Wire *w = signal.as_wire();
|
|
||||||
if (w->driverCell_ == nullptr && (
|
|
||||||
(w->port_input && !w->port_output) == (type == ID($input_port)))) {
|
|
||||||
w->driverCell_ = this;
|
|
||||||
w->driverPort_ = portname;
|
|
||||||
|
|
||||||
conn_it->second = std::move(signal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) {
|
|
||||||
module->buf_norm_cell_queue.insert(this);
|
|
||||||
module->buf_norm_cell_port_queue.emplace(this, portname);
|
|
||||||
} else {
|
|
||||||
for (auto &chunk : signal.chunks())
|
|
||||||
if (chunk.wire != nullptr && chunk.wire->driverCell_ == nullptr)
|
|
||||||
module->buf_norm_wire_queue.insert(chunk.wire);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == ID($connect)) {
|
|
||||||
for (auto &[port, sig] : connections_) {
|
|
||||||
for (auto &chunk : sig.chunks()) {
|
|
||||||
if (!chunk.wire)
|
|
||||||
continue;
|
|
||||||
auto it = module->buf_norm_connect_index.find(chunk.wire);
|
|
||||||
if (it == module->buf_norm_connect_index.end())
|
|
||||||
continue;
|
|
||||||
it->second.erase(this);
|
|
||||||
if (it->second.empty())
|
|
||||||
module->buf_norm_connect_index.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn_it->second = std::move(signal);
|
|
||||||
for (auto &[port, sig] : connections_) {
|
|
||||||
for (auto &chunk : sig.chunks()) {
|
|
||||||
if (!chunk.wire)
|
|
||||||
continue;
|
|
||||||
module->buf_norm_connect_index[chunk.wire].insert(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
conn_it->second = std::move(signal);
|
|
||||||
|
|
||||||
|
conn_it->second = std::move(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTLIL::Design::add(RTLIL::Module *module)
|
void RTLIL::Design::add(RTLIL::Module *module)
|
||||||
|
|
|
||||||
|
|
@ -118,24 +118,17 @@ void Patch::patch(Cell* old_cell, Cell* new_cell) {
|
||||||
auto dir = raw->port_dir(port_name);
|
auto dir = raw->port_dir(port_name);
|
||||||
log_assert(dir != PD_UNKNOWN);
|
log_assert(dir != PD_UNKNOWN);
|
||||||
if (dir == PD_OUTPUT || dir == PD_INOUT) {
|
if (dir == PD_OUTPUT || dir == PD_INOUT) {
|
||||||
SigSpec sig_to_fix = sig;
|
|
||||||
if (raw == new_cell) {
|
if (raw == new_cell) {
|
||||||
// RAUW
|
// RAUW
|
||||||
// TODO optimized implementation for signorm fanout transfer that avoids expensive(?) setPort?
|
|
||||||
auto yoink = old_cell->getPort(port_name);
|
auto yoink = old_cell->getPort(port_name);
|
||||||
log(">>>> RAUW %s to %s\n", port_name, log_signal(yoink));
|
log(">>>> RAUW %s to %s\n", port_name, log_signal(yoink));
|
||||||
new_cell->setPort(port_name, yoink);
|
new_cell->setPort(port_name, yoink);
|
||||||
old_cell->setPort(port_name, mod->addWire(NEW_ID, yoink.size()));
|
old_cell->setPort(port_name, mod->addWire(NEW_ID, yoink.size()));
|
||||||
sig_to_fix = yoink;
|
|
||||||
}
|
|
||||||
if (sig_to_fix.size()) {
|
|
||||||
auto* wire = sig_to_fix.as_wire();
|
|
||||||
wire->driverCell_ = raw;
|
|
||||||
wire->driverPort_ = port_name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
raw->module = mod;
|
raw->module = mod;
|
||||||
|
raw->initIndex();
|
||||||
raw->fixup_parameters();
|
raw->fixup_parameters();
|
||||||
}
|
}
|
||||||
log_module(mod, "");
|
log_module(mod, "");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue