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:
parent
bdb59ffc8e
commit
99effb6789
10 changed files with 418 additions and 282 deletions
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue