3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-23 09:05:32 +00:00

add support for initializing registers and memories to the functional backend

This commit is contained in:
Emily Schmidt 2024-07-24 17:37:17 +01:00
parent bdb59ffc8e
commit 99effb6789
10 changed files with 418 additions and 282 deletions

View file

@ -19,6 +19,8 @@
#include "kernel/functionalir.h"
#include <optional>
#include "ff.h"
#include "ffinit.h"
YOSYS_NAMESPACE_BEGIN
@ -70,7 +72,7 @@ struct PrintVisitor : FunctionalIR::DefaultVisitor<std::string> {
std::string slice(Node, Node a, int offset, int out_width) override { return "slice(" + np(a) + ", " + std::to_string(offset) + ", " + std::to_string(out_width) + ")"; }
std::string zero_extend(Node, Node a, int out_width) override { return "zero_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
std::string sign_extend(Node, Node a, int out_width) override { return "sign_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
std::string constant(Node, RTLIL::Const value) override { return "constant(" + value.as_string() + ")"; }
std::string constant(Node, RTLIL::Const const& value) override { return "constant(" + value.as_string() + ")"; }
std::string input(Node, IdString name) override { return "input(" + name.str() + ")"; }
std::string state(Node, IdString name) override { return "state(" + name.str() + ")"; }
std::string default_handler(Node self) override {
@ -407,6 +409,8 @@ class FunctionalIRConstruction {
CellSimplifier simplifier;
vector<Mem> memories_vector;
dict<Cell*, Mem*> memories;
SigMap sig_map; // TODO: this is only for FfInitVals, remove this once FfInitVals supports DriverMap
FfInitVals ff_initvals;
Node enqueue(DriveSpec const &spec)
{
@ -438,8 +442,11 @@ class FunctionalIRConstruction {
return it->second;
}
public:
FunctionalIRConstruction(FunctionalIR::Factory &f) : factory(f), simplifier(f) {}
void add_module(Module *module)
FunctionalIRConstruction(Module *module, FunctionalIR::Factory &f)
: factory(f)
, simplifier(f)
, sig_map(module)
, ff_initvals(&sig_map, module)
{
driver_map.add(module);
for (auto cell : module->cells()) {
@ -447,9 +454,12 @@ public:
queue.emplace_back(cell);
}
for (auto wire : module->wires()) {
if (wire->port_input)
factory.add_input(wire->name, wire->width);
if (wire->port_output) {
Node node = enqueue(DriveChunk(DriveChunkWire(wire, 0, wire->width)));
factory.declare_output(node, wire->name, wire->width);
factory.add_output(wire->name, wire->width);
Node value = enqueue(DriveChunk(DriveChunkWire(wire, 0, wire->width)));
factory.set_output(wire->name, value);
}
}
memories_vector = Mem::get_all_memories(module);
@ -487,9 +497,9 @@ public:
// - Since wr port j can only have priority over wr port i if j > i, if we do writes in
// ascending index order the result will obey the priorty relation.
vector<Node> read_results;
int addr_width = ceil_log2(mem->size);
int data_width = mem->width;
Node node = factory.state_memory(mem->cell->name, addr_width, data_width);
factory.add_state(mem->cell->name, FunctionalIR::Sort(ceil_log2(mem->size), mem->width));
factory.set_initial_state(mem->cell->name, MemContents(mem));
Node node = factory.get_current_state(mem->cell->name);
for (size_t i = 0; i < mem->wr_ports.size(); i++) {
const auto &wr = mem->wr_ports[i];
if (wr.clk_enable)
@ -513,7 +523,7 @@ public:
Node addr = enqueue(driver_map(DriveSpec(rd.addr)));
read_results.push_back(factory.memory_read(node, addr));
}
factory.declare_state_memory(node, mem->cell->name, addr_width, data_width);
factory.set_next_state(mem->cell->name, node);
return concatenate_read_results(mem, read_results);
}
void process_cell(Cell *cell)
@ -527,6 +537,17 @@ public:
}
Node node = handle_memory(mem);
factory.update_pending(cell_outputs.at({cell, ID(RD_DATA)}), node);
} else if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
FfData ff(&ff_initvals, cell);
if (!ff.has_gclk)
log_error("The design contains a %s flip-flop at %s. This is not supported by the functional backend. "
"Call async2sync or clk2fflogic to avoid this error.\n", log_id(cell->type), log_id(cell));
factory.add_state(ff.name, FunctionalIR::Sort(ff.width));
Node q_value = factory.get_current_state(ff.name);
factory.suggest_name(q_value, ff.name);
factory.update_pending(cell_outputs.at({cell, ID(Q)}), q_value);
factory.set_next_state(ff.name, enqueue(ff.sig_d));
factory.set_initial_state(ff.name, ff.val_init);
} else {
dict<IdString, Node> connections;
IdString output_name; // for the single output case
@ -572,7 +593,7 @@ public:
DriveChunkWire wire_chunk = chunk.wire();
if (wire_chunk.is_whole()) {
if (wire_chunk.wire->port_input) {
Node node = factory.input(wire_chunk.wire->name, wire_chunk.width);
Node node = factory.get_input(wire_chunk.wire->name);
factory.suggest_name(node, wire_chunk.wire->name);
factory.update_pending(pending, node);
} else {
@ -590,24 +611,8 @@ public:
DriveChunkPort port_chunk = chunk.port();
if (port_chunk.is_whole()) {
if (driver_map.celltypes.cell_output(port_chunk.cell->type, port_chunk.port)) {
if (port_chunk.cell->type.in(ID($dff), ID($ff)))
{
Cell *cell = port_chunk.cell;
Node node = factory.state(cell->name, port_chunk.width);
factory.suggest_name(node, port_chunk.cell->name);
factory.update_pending(pending, node);
for (auto const &conn : cell->connections()) {
if (driver_map.celltypes.cell_input(cell->type, conn.first)) {
Node node = enqueue(DriveChunkPort(cell, conn));
factory.declare_state(node, cell->name, port_chunk.width);
}
}
}
else
{
Node node = enqueue_cell(port_chunk.cell, port_chunk.port);
factory.update_pending(pending, node);
}
Node node = enqueue_cell(port_chunk.cell, port_chunk.port);
factory.update_pending(pending, node);
} else {
DriveSpec driver = driver_map(DriveSpec(port_chunk));
factory.update_pending(pending, enqueue(driver));
@ -641,8 +646,7 @@ public:
FunctionalIR FunctionalIR::from_module(Module *module) {
FunctionalIR ir;
auto factory = ir.factory();
FunctionalIRConstruction ctor(factory);
ctor.add_module(module);
FunctionalIRConstruction ctor(module, factory);
ctor.process_queue();
ir.topological_sort();
ir.forward_buf();

View file

@ -189,24 +189,11 @@ private:
// the bool is true for next state values
using Graph = ComputeGraph<NodeData, Attr, IdString, std::pair<IdString, bool>>;
Graph _graph;
dict<IdString, Sort> _inputs;
dict<IdString, Sort> _outputs;
dict<IdString, Sort> _state;
void add_input(IdString name, Sort sort) {
auto [it, found] = _inputs.emplace(name, std::move(sort));
if(found)
log_assert(it->second == sort);
}
void add_state(IdString name, Sort sort) {
auto [it, found] = _state.emplace(name, std::move(sort));
if(found)
log_assert(it->second == sort);
}
void add_output(IdString name, Sort sort) {
auto [it, found] = _outputs.emplace(name, std::move(sort));
if(found)
log_assert(it->second == sort);
}
dict<IdString, Sort> _input_sorts;
dict<IdString, Sort> _output_sorts;
dict<IdString, Sort> _state_sorts;
dict<IdString, RTLIL::Const> _initial_state_signal;
dict<IdString, MemContents> _initial_state_memory;
public:
class Factory;
// Node is an immutable reference to a FunctionalIR node
@ -306,7 +293,7 @@ public:
virtual T logical_shift_right(Node self, Node a, Node b) = 0;
virtual T arithmetic_shift_right(Node self, Node a, Node b) = 0;
virtual T mux(Node self, Node a, Node b, Node s) = 0;
virtual T constant(Node self, RTLIL::Const value) = 0;
virtual T constant(Node self, RTLIL::Const const & value) = 0;
virtual T input(Node self, IdString name) = 0;
virtual T state(Node self, IdString name) = 0;
virtual T memory_read(Node self, Node mem, Node addr) = 0;
@ -343,7 +330,7 @@ public:
T logical_shift_right(Node self, Node, Node) override { return default_handler(self); }
T arithmetic_shift_right(Node self, Node, Node) override { return default_handler(self); }
T mux(Node self, Node, Node, Node) override { return default_handler(self); }
T constant(Node self, RTLIL::Const) override { return default_handler(self); }
T constant(Node self, RTLIL::Const const &) override { return default_handler(self); }
T input(Node self, IdString) override { return default_handler(self); }
T state(Node self, IdString) override { return default_handler(self); }
T memory_read(Node self, Node, Node) override { return default_handler(self); }
@ -452,30 +439,41 @@ public:
log_assert(node._ref.function() == Fn::buf && node._ref.size() == 0);
log_assert(node.sort() == value.sort());
mutate(node).append_arg(value._ref);
}
Node input(IdString name, int width) {
_ir.add_input(name, Sort(width));
return add(NodeData(Fn::input, name), Sort(width), {});
}
Node state(IdString name, int width) {
_ir.add_state(name, Sort(width));
return add(NodeData(Fn::state, name), Sort(width), {});
void add_input(IdString name, int width) {
auto [it, inserted] = _ir._input_sorts.emplace(name, Sort(width));
if (!inserted) log_error("input `%s` was re-defined", name.c_str());
}
Node state_memory(IdString name, int addr_width, int data_width) {
_ir.add_state(name, Sort(addr_width, data_width));
return add(NodeData(Fn::state, name), Sort(addr_width, data_width), {});
void add_output(IdString name, int width) {
auto [it, inserted] = _ir._output_sorts.emplace(name, Sort(width));
if (!inserted) log_error("output `%s` was re-defined", name.c_str());
}
void declare_output(Node node, IdString name, int width) {
_ir.add_output(name, Sort(width));
mutate(node).assign_key({name, false});
void add_state(IdString name, Sort sort) {
auto [it, inserted] = _ir._state_sorts.emplace(name, sort);
if (!inserted) log_error("state `%s` was re-defined", name.c_str());
}
void declare_state(Node node, IdString name, int width) {
_ir.add_state(name, Sort(width));
mutate(node).assign_key({name, true});
Node get_input(IdString name) {
return add(NodeData(Fn::input, name), Sort(_ir._input_sorts.at(name)), {});
}
void declare_state_memory(Node node, IdString name, int addr_width, int data_width) {
_ir.add_state(name, Sort(addr_width, data_width));
mutate(node).assign_key({name, true});
Node get_current_state(IdString name) {
return add(NodeData(Fn::state, name), Sort(_ir._state_sorts.at(name)), {});
}
void set_output(IdString output, Node value) {
log_assert(_ir._output_sorts.at(output) == value.sort());
mutate(value).assign_key({output, false});
}
void set_initial_state(IdString state, RTLIL::Const value) {
Sort &sort = _ir._state_sorts.at(state);
value.extu(sort.width());
_ir._initial_state_signal.emplace(state, std::move(value));
}
void set_initial_state(IdString state, MemContents value) {
log_assert(Sort(value.addr_width(), value.data_width()) == _ir._state_sorts.at(state));
_ir._initial_state_memory.emplace(state, std::move(value));
}
void set_next_state(IdString state, Node value) {
log_assert(_ir._state_sorts.at(state) == value.sort());
mutate(value).assign_key({state, true});
}
void suggest_name(Node node, IdString name) {
mutate(node).sparse_attr() = name;
@ -487,9 +485,11 @@ public:
Node operator[](int i) { return Node(_graph[i]); }
void topological_sort();
void forward_buf();
dict<IdString, Sort> inputs() const { return _inputs; }
dict<IdString, Sort> outputs() const { return _outputs; }
dict<IdString, Sort> state() const { return _state; }
dict<IdString, Sort> inputs() const { return _input_sorts; }
dict<IdString, Sort> outputs() const { return _output_sorts; }
dict<IdString, Sort> state() const { return _state_sorts; }
RTLIL::Const const &get_initial_state_signal(IdString name) { return _initial_state_signal.at(name); }
MemContents const &get_initial_state_memory(IdString name) { return _initial_state_memory.at(name); }
Node get_output_node(IdString name) { return Node(_graph({name, false})); }
Node get_state_next_node(IdString name) { return Node(_graph({name, true})); }
class Iterator {