mirror of https://github.com/YosysHQ/yosys.git
add new generic compute graph and rewrite c++ functional backend to use it
This commit is contained in:
parent
248d5f72d4
commit
6f9e21219b
2
Makefile
2
Makefile
|
@ -639,7 +639,7 @@ $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
|
|||
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
|
||||
OBJS += kernel/binding.o
|
||||
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o
|
||||
OBJS += kernel/drivertools.o
|
||||
OBJS += kernel/drivertools.o kernel/functionalir.o
|
||||
ifeq ($(ENABLE_ZLIB),1)
|
||||
OBJS += kernel/fstdata.o
|
||||
endif
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
OBJS += backends/functional/cxx.o
|
||||
OBJS += backends/functional/smtlib.o
|
||||
#OBJS += backends/functional/smtlib.o
|
||||
OBJS += backends/functional/test_generic.o
|
||||
|
|
|
@ -18,10 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/drivertools.h"
|
||||
#include "kernel/topo_scc.h"
|
||||
#include "kernel/functional.h"
|
||||
#include "kernel/graphtools.h"
|
||||
#include "kernel/functionalir.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
@ -84,38 +81,17 @@ struct CxxScope {
|
|||
};
|
||||
|
||||
struct CxxType {
|
||||
bool _is_memory;
|
||||
int _width;
|
||||
int _addr_width;
|
||||
public:
|
||||
CxxType() : _is_memory(false), _width(0), _addr_width(0) { }
|
||||
CxxType(int width) : _is_memory(false), _width(width), _addr_width(0) { }
|
||||
CxxType(int addr_width, int data_width) : _is_memory(true), _width(data_width), _addr_width(addr_width) { }
|
||||
static CxxType signal(int width) { return CxxType(width); }
|
||||
static CxxType memory(int addr_width, int data_width) { return CxxType(addr_width, data_width); }
|
||||
bool is_signal() const { return !_is_memory; }
|
||||
bool is_memory() const { return _is_memory; }
|
||||
int width() const { log_assert(is_signal()); return _width; }
|
||||
int addr_width() const { log_assert(is_memory()); return _addr_width; }
|
||||
int data_width() const { log_assert(is_memory()); return _width; }
|
||||
FunctionalIR::Sort sort;
|
||||
CxxType(FunctionalIR::Sort sort) : sort(sort) {}
|
||||
std::string to_string() const {
|
||||
if(_is_memory) {
|
||||
return stringf("Memory<%d, %d>", addr_width(), data_width());
|
||||
if(sort.is_memory()) {
|
||||
return stringf("Memory<%d, %d>", sort.addr_width(), sort.data_width());
|
||||
} else if(sort.is_signal()) {
|
||||
return stringf("Signal<%d>", sort.width());
|
||||
} else {
|
||||
return stringf("Signal<%d>", width());
|
||||
log_error("unknown sort");
|
||||
}
|
||||
}
|
||||
bool operator ==(CxxType const& other) const {
|
||||
if(_is_memory != other._is_memory) return false;
|
||||
if(_is_memory && _addr_width != other._addr_width) return false;
|
||||
return _width == other._width;
|
||||
}
|
||||
unsigned int hash() const {
|
||||
if(_is_memory)
|
||||
return mkhash(1, mkhash(_width, _addr_width));
|
||||
else
|
||||
return mkhash(0, _width);
|
||||
}
|
||||
};
|
||||
|
||||
struct CxxWriter {
|
||||
|
@ -135,9 +111,8 @@ struct CxxStruct {
|
|||
dict<IdString, CxxType> types;
|
||||
CxxScope scope;
|
||||
bool generate_methods;
|
||||
int count;
|
||||
CxxStruct(std::string name, bool generate_methods = false, int count = 0)
|
||||
: name(name), generate_methods(generate_methods), count(count) {
|
||||
CxxStruct(std::string name, bool generate_methods = false)
|
||||
: name(name), generate_methods(generate_methods) {
|
||||
scope.reserve("out");
|
||||
scope.reserve("dump");
|
||||
}
|
||||
|
@ -159,7 +134,7 @@ struct CxxStruct {
|
|||
if (generate_methods) {
|
||||
// Add size method
|
||||
f.printf("\tint size() const {\n");
|
||||
f.printf("\t\treturn %d;\n", count);
|
||||
f.printf("\t\treturn %d;\n", types.size());
|
||||
f.printf("\t}\n\n");
|
||||
|
||||
// Add get_input method
|
||||
|
@ -197,119 +172,87 @@ struct CxxStruct {
|
|||
}
|
||||
};
|
||||
|
||||
struct CxxFunction {
|
||||
IdString name;
|
||||
CxxType type;
|
||||
dict<IdString, Const> parameters;
|
||||
|
||||
CxxFunction(IdString name, CxxType type) : name(name), type(type) {}
|
||||
CxxFunction(IdString name, CxxType type, dict<IdString, Const> parameters) : name(name), type(type), parameters(parameters) {}
|
||||
|
||||
bool operator==(CxxFunction const &other) const {
|
||||
return name == other.name && parameters == other.parameters && type == other.type;
|
||||
struct CxxTemplate {
|
||||
vector<std::variant<std::string, int>> _v;
|
||||
public:
|
||||
CxxTemplate(std::string fmt) {
|
||||
std::string buf;
|
||||
for(auto it = fmt.begin(); it != fmt.end(); it++){
|
||||
if(*it == '%'){
|
||||
it++;
|
||||
log_assert(it != fmt.end());
|
||||
if(*it == '%')
|
||||
buf += *it;
|
||||
else {
|
||||
log_assert(*it >= '0' && *it <= '9');
|
||||
_v.emplace_back(std::move(buf));
|
||||
_v.emplace_back((int)(*it - '0'));
|
||||
}
|
||||
}else
|
||||
buf += *it;
|
||||
}
|
||||
if(!buf.empty())
|
||||
_v.emplace_back(std::move(buf));
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
return mkhash(name.hash(), mkhash(type.hash(), parameters.hash()));
|
||||
template<typename... Args> static std::string format(CxxTemplate fmt, Args&&... args) {
|
||||
vector<std::string> strs = {args...};
|
||||
std::string result;
|
||||
for(auto &v : fmt._v){
|
||||
if(std::string *s = std::get_if<std::string>(&v))
|
||||
result += *s;
|
||||
else if(int *i = std::get_if<int>(&v))
|
||||
result += strs[*i];
|
||||
else
|
||||
log_error("missing case");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
typedef ComputeGraph<CxxFunction, int, IdString, IdString> CxxComputeGraph;
|
||||
|
||||
class CxxComputeGraphFactory {
|
||||
CxxComputeGraph &graph;
|
||||
using T = CxxComputeGraph::Ref;
|
||||
static bool is_single_output(IdString type)
|
||||
{
|
||||
auto it = yosys_celltypes.cell_types.find(type);
|
||||
return it != yosys_celltypes.cell_types.end() && it->second.outputs.size() <= 1;
|
||||
}
|
||||
public:
|
||||
CxxComputeGraphFactory(CxxComputeGraph &g) : graph(g) {}
|
||||
T slice(T a, int in_width, int offset, int out_width) {
|
||||
log_assert(offset + out_width <= in_width);
|
||||
return graph.add(CxxFunction(ID($$slice), out_width, {{ID(offset), offset}}), 0, std::array<T, 1>{a});
|
||||
}
|
||||
T extend(T a, int in_width, int out_width, bool is_signed) {
|
||||
log_assert(in_width < out_width);
|
||||
if(is_signed)
|
||||
return graph.add(CxxFunction(ID($sign_extend), out_width, {{ID(WIDTH), out_width}}), 0, std::array<T, 1>{a});
|
||||
else
|
||||
return graph.add(CxxFunction(ID($zero_extend), out_width, {{ID(WIDTH), out_width}}), 0, std::array<T, 1>{a});
|
||||
}
|
||||
T concat(T a, int a_width, T b, int b_width) {
|
||||
return graph.add(CxxFunction(ID($$concat), a_width + b_width), 0, std::array<T, 2>{a, b});
|
||||
}
|
||||
T add(T a, T b, int width) { return graph.add(CxxFunction(ID($add), width), 0, std::array<T, 2>{a, b}); }
|
||||
T sub(T a, T b, int width) { return graph.add(CxxFunction(ID($sub), width), 0, std::array<T, 2>{a, b}); }
|
||||
T bitwise_and(T a, T b, int width) { return graph.add(CxxFunction(ID($and), width), 0, std::array<T, 2>{a, b}); }
|
||||
T bitwise_or(T a, T b, int width) { return graph.add(CxxFunction(ID($or), width), 0, std::array<T, 2>{a, b}); }
|
||||
T bitwise_xor(T a, T b, int width) { return graph.add(CxxFunction(ID($xor), width), 0, std::array<T, 2>{a, b}); }
|
||||
T bitwise_not(T a, int width) { return graph.add(CxxFunction(ID($not), width), 0, std::array<T, 1>{a}); }
|
||||
T neg(T a, int width) { return graph.add(CxxFunction(ID($neg), width), 0, std::array<T, 1>{a}); }
|
||||
T mux(T a, T b, T s, int width) { return graph.add(CxxFunction(ID($mux), width), 0, std::array<T, 3>{a, b, s}); }
|
||||
T pmux(T a, T b, T s, int width, int) { return graph.add(CxxFunction(ID($pmux), width), 0, std::array<T, 3>{a, b, s}); }
|
||||
T reduce_and(T a, int) { return graph.add(CxxFunction(ID($reduce_and), 1), 0, std::array<T, 1>{a}); }
|
||||
T reduce_or(T a, int) { return graph.add(CxxFunction(ID($reduce_or), 1), 0, std::array<T, 1>{a}); }
|
||||
T reduce_xor(T a, int) { return graph.add(CxxFunction(ID($reduce_xor), 1), 0, std::array<T, 1>{a}); }
|
||||
T eq(T a, T b, int) { return graph.add(CxxFunction(ID($eq), 1), 0, std::array<T, 2>{a, b}); }
|
||||
T ne(T a, T b, int) { return graph.add(CxxFunction(ID($ne), 1), 0, std::array<T, 2>{a, b}); }
|
||||
T gt(T a, T b, int) { return graph.add(CxxFunction(ID($gt), 1), 0, std::array<T, 2>{a, b}); }
|
||||
T ge(T a, T b, int) { return graph.add(CxxFunction(ID($ge), 1), 0, std::array<T, 2>{a, b}); }
|
||||
T ugt(T a, T b, int) { return graph.add(CxxFunction(ID($ugt), 1), 0, std::array<T, 2>{a, b}); }
|
||||
T uge(T a, T b, int) { return graph.add(CxxFunction(ID($uge), 1), 0, std::array<T, 2>{a, b}); }
|
||||
T logical_shift_left(T a, T b, int y_width, int) { return graph.add(CxxFunction(ID($shl), y_width, {{ID(WIDTH), y_width}}), 0, std::array<T, 2>{a, b}); }
|
||||
T logical_shift_right(T a, T b, int y_width, int) { return graph.add(CxxFunction(ID($shr), y_width, {{ID(WIDTH), y_width}}), 0, std::array<T, 2>{a, b}); }
|
||||
T arithmetic_shift_right(T a, T b, int y_width, int) { return graph.add(CxxFunction(ID($asr), y_width, {{ID(WIDTH), y_width}}), 0, std::array<T, 2>{a, b}); }
|
||||
|
||||
T constant(RTLIL::Const value) {
|
||||
return graph.add(CxxFunction(ID($$const), value.size(), {{ID(value), value}}), 0);
|
||||
}
|
||||
T input(IdString name, int width) { return graph.add(CxxFunction(ID($$input), width, {{name, {}}}), 0); }
|
||||
T state(IdString name, int width) { return graph.add(CxxFunction(ID($$state), width, {{name, {}}}), 0); }
|
||||
T state_memory(IdString name, int addr_width, int data_width) {
|
||||
return graph.add(CxxFunction(ID($$state), CxxType::memory(addr_width, data_width), {{name, {}}}), 0);
|
||||
}
|
||||
T cell_output(T cell, IdString type, IdString name, int width) {
|
||||
if (is_single_output(type))
|
||||
return cell;
|
||||
else
|
||||
return graph.add(CxxFunction(ID($$cell_output), width, {{name, {}}}), 0, std::array<T, 1>{cell});
|
||||
}
|
||||
T multiple(vector<T> args, int width) {
|
||||
return graph.add(CxxFunction(ID($$multiple), width), 0, args);
|
||||
}
|
||||
T undriven(int width) {
|
||||
return graph.add(CxxFunction(ID($$undriven), width), 0);
|
||||
}
|
||||
|
||||
T memory_read(T mem, T addr, int addr_width, int data_width) {
|
||||
return graph.add(CxxFunction(ID($memory_read), data_width), 0, std::array<T, 2>{mem, addr});
|
||||
}
|
||||
T memory_write(T mem, T addr, T data, int addr_width, int data_width) {
|
||||
return graph.add(CxxFunction(ID($memory_write), CxxType::memory(addr_width, data_width)), 0, std::array<T, 3>{mem, addr, data});
|
||||
}
|
||||
|
||||
T create_pending(int width) {
|
||||
return graph.add(CxxFunction(ID($$pending), width), 0);
|
||||
}
|
||||
void update_pending(T pending, T node) {
|
||||
log_assert(pending.function().name == ID($$pending));
|
||||
pending.set_function(CxxFunction(ID($$buf), pending.function().type));
|
||||
pending.append_arg(node);
|
||||
}
|
||||
void declare_output(T node, IdString name, int) {
|
||||
node.assign_key(name);
|
||||
}
|
||||
void declare_state(T node, IdString name, int) {
|
||||
node.assign_key(name);
|
||||
}
|
||||
void declare_state_memory(T node, IdString name, int, int) {
|
||||
node.assign_key(name);
|
||||
}
|
||||
void suggest_name(T node, IdString name) {
|
||||
node.sparse_attr() = name;
|
||||
template<class NodeNames> struct CxxPrintVisitor {
|
||||
using Node = FunctionalIR::Node;
|
||||
NodeNames np;
|
||||
CxxStruct &input_struct;
|
||||
CxxStruct &state_struct;
|
||||
CxxPrintVisitor(NodeNames np, CxxStruct &input_struct, CxxStruct &state_struct) : np(np), input_struct(input_struct), state_struct(state_struct) { }
|
||||
template<class T> std::string arg_to_string(T n) { return std::to_string(n); }
|
||||
template<> std::string arg_to_string(std::string n) { return n; }
|
||||
template<> std::string arg_to_string(Node n) { return np(n); }
|
||||
template<typename... Args> std::string format(std::string fmt, Args&&... args) {
|
||||
return CxxTemplate::format(fmt, arg_to_string(args)...);
|
||||
}
|
||||
std::string buf(Node, Node n) { return np(n); }
|
||||
std::string slice(Node, Node a, int, int offset, int out_width) { return format("slice<%2>(%0, %1)", a, offset, out_width); }
|
||||
std::string zero_extend(Node, Node a, int, int out_width) { return format("$zero_extend<%1>(%0)", a, out_width); }
|
||||
std::string sign_extend(Node, Node a, int, int out_width) { return format("$sign_extend<%1>(%0)", a, out_width); }
|
||||
std::string concat(Node, Node a, int, Node b, int) { return format("concat(%0, %1)", a, b); }
|
||||
std::string add(Node, Node a, Node b, int) { return format("$add(%0, %1)", a, b); }
|
||||
std::string sub(Node, Node a, Node b, int) { return format("$sub(%0, %1)", a, b); }
|
||||
std::string bitwise_and(Node, Node a, Node b, int) { return format("$and(%0, %1)", a, b); }
|
||||
std::string bitwise_or(Node, Node a, Node b, int) { return format("$or(%0, %1)", a, b); }
|
||||
std::string bitwise_xor(Node, Node a, Node b, int) { return format("$xor(%0, %1)", a, b); }
|
||||
std::string bitwise_not(Node, Node a, int) { return format("$not(%0)", a); }
|
||||
std::string unary_minus(Node, Node a, int) { return format("$neg(%0)", a); }
|
||||
std::string reduce_and(Node, Node a, int) { return format("$reduce_and(%0)", a); }
|
||||
std::string reduce_or(Node, Node a, int) { return format("$reduce_or(%0)", a); }
|
||||
std::string reduce_xor(Node, Node a, int) { return format("$reduce_xor(%0)", a); }
|
||||
std::string equal(Node, Node a, Node b, int) { return format("$eq(%0, %1)", a, b); }
|
||||
std::string not_equal(Node, Node a, Node b, int) { return format("$ne(%0, %1)", a, b); }
|
||||
std::string signed_greater_than(Node, Node a, Node b, int) { return format("$gt(%0, %1)", a, b); }
|
||||
std::string signed_greater_equal(Node, Node a, Node b, int) { return format("$ge(%0, %1)", a, b); }
|
||||
std::string unsigned_greater_than(Node, Node a, Node b, int) { return format("$ugt(%0, %1)", a, b); }
|
||||
std::string unsigned_greater_equal(Node, Node a, Node b, int) { return format("$uge(%0, %1)", a, b); }
|
||||
std::string logical_shift_left(Node, Node a, Node b, int, int) { return format("$shl<%2>(%0, %1)", a, b, a.width()); }
|
||||
std::string logical_shift_right(Node, Node a, Node b, int, int) { return format("$shr<%2>(%0, %1)", a, b, a.width()); }
|
||||
std::string arithmetic_shift_right(Node, Node a, Node b, int, int) { return format("$asr<%2>(%0, %1)", a, b, a.width()); }
|
||||
std::string mux(Node, Node a, Node b, Node s, int) { return format("$mux(%0, %1, %2)", a, b, s); }
|
||||
std::string pmux(Node, Node a, Node b, Node s, int, int) { return format("$pmux(%0, %1, %2)", a, b, s); }
|
||||
std::string constant(Node, RTLIL::Const value) { return format("$const<%0>(%1)", value.size(), value.as_int()); }
|
||||
std::string input(Node, IdString name) { return format("input.%0", input_struct[name]); }
|
||||
std::string state(Node, IdString name) { return format("current_state.%0", state_struct[name]); }
|
||||
std::string memory_read(Node, Node mem, Node addr, int, int) { return format("$memory_read(%0, %1)", mem, addr); }
|
||||
std::string memory_write(Node, Node mem, Node addr, Node data, int, int) { return format("$memory_write(%0, %1, %2)", mem, addr, data); }
|
||||
std::string undriven(Node, int width) { return format("$const<%0>(0)", width); }
|
||||
};
|
||||
|
||||
struct FunctionalCxxBackend : public Backend
|
||||
|
@ -322,88 +265,24 @@ struct FunctionalCxxBackend : public Backend
|
|||
log("\n");
|
||||
}
|
||||
|
||||
CxxComputeGraph calculate_compute_graph(RTLIL::Module *module)
|
||||
void printCxx(std::ostream &stream, std::string, std::string const & name, Module *module)
|
||||
{
|
||||
CxxComputeGraph compute_graph;
|
||||
CxxComputeGraphFactory factory(compute_graph);
|
||||
ComputeGraphConstruction<CxxComputeGraph::Ref, CxxComputeGraphFactory> construction(factory);
|
||||
construction.add_module(module);
|
||||
construction.process_queue();
|
||||
|
||||
// Perform topo sort and detect SCCs
|
||||
CxxComputeGraph::SccAdaptor compute_graph_scc(compute_graph);
|
||||
|
||||
bool scc = false;
|
||||
std::vector<int> perm;
|
||||
topo_sorted_sccs(compute_graph_scc, [&](int *begin, int *end) {
|
||||
perm.insert(perm.end(), begin, end);
|
||||
if (end > begin + 1)
|
||||
{
|
||||
log_warning("SCC:");
|
||||
for (int *i = begin; i != end; ++i)
|
||||
log(" %d(%s)(%s)", *i, compute_graph[*i].function().name.c_str(), compute_graph[*i].has_sparse_attr() ? compute_graph[*i].sparse_attr().c_str() : "");
|
||||
log("\n");
|
||||
scc = true;
|
||||
}
|
||||
}, /* sources_first */ true);
|
||||
compute_graph.permute(perm);
|
||||
if(scc) log_error("combinational loops, aborting\n");
|
||||
|
||||
// Forward $$buf
|
||||
std::vector<int> alias;
|
||||
perm.clear();
|
||||
|
||||
for (int i = 0; i < compute_graph.size(); ++i)
|
||||
{
|
||||
auto node = compute_graph[i];
|
||||
if (node.function().name == ID($$buf) && node.arg(0).index() < i)
|
||||
{
|
||||
int target_index = alias[node.arg(0).index()];
|
||||
auto target_node = compute_graph[perm[target_index]];
|
||||
if(!target_node.has_sparse_attr() && node.has_sparse_attr()){
|
||||
IdString id = node.sparse_attr();
|
||||
target_node.sparse_attr() = id;
|
||||
}
|
||||
alias.push_back(target_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
alias.push_back(GetSize(perm));
|
||||
perm.push_back(i);
|
||||
}
|
||||
}
|
||||
compute_graph.permute(perm, alias);
|
||||
return compute_graph;
|
||||
}
|
||||
|
||||
void printCxx(std::ostream &stream, std::string, std::string const & name, CxxComputeGraph &compute_graph)
|
||||
{
|
||||
dict<IdString, CxxType> inputs, state;
|
||||
auto ir = FunctionalIR::from_module(module);
|
||||
CxxWriter f(stream);
|
||||
|
||||
// Dump the compute graph
|
||||
for (int i = 0; i < compute_graph.size(); ++i)
|
||||
{
|
||||
auto ref = compute_graph[i];
|
||||
if(ref.function().name == ID($$input))
|
||||
inputs[ref.function().parameters.begin()->first] = ref.function().type;
|
||||
if(ref.function().name == ID($$state))
|
||||
state[ref.function().parameters.begin()->first] = ref.function().type;
|
||||
}
|
||||
f.printf("#include \"sim.h\"\n");
|
||||
f.printf("#include <variant>\n");
|
||||
CxxStruct input_struct(name + "_Inputs", true, inputs.size());
|
||||
for (auto const &input : inputs)
|
||||
input_struct.insert(input.first, input.second);
|
||||
CxxStruct input_struct(name + "_Inputs", true);
|
||||
for (auto [name, sort] : ir.inputs())
|
||||
input_struct.insert(name, sort);
|
||||
CxxStruct output_struct(name + "_Outputs");
|
||||
for (auto const &key : compute_graph.keys())
|
||||
if(state.count(key.first) == 0)
|
||||
output_struct.insert(key.first, compute_graph[key.second].function().type);
|
||||
for (auto [name, sort] : ir.outputs())
|
||||
output_struct.insert(name, sort);
|
||||
CxxStruct state_struct(name + "_State");
|
||||
for (auto const &state_var : state)
|
||||
state_struct.insert(state_var.first, state_var.second);
|
||||
for (auto [name, sort] : ir.state())
|
||||
state_struct.insert(name, sort);
|
||||
|
||||
idict<std::string> node_names;
|
||||
dict<int, std::string> node_names;
|
||||
CxxScope locals;
|
||||
|
||||
input_struct.print(f);
|
||||
|
@ -415,73 +294,17 @@ struct FunctionalCxxBackend : public Backend
|
|||
locals.reserve("output");
|
||||
locals.reserve("current_state");
|
||||
locals.reserve("next_state");
|
||||
for (int i = 0; i < compute_graph.size(); ++i)
|
||||
auto node_to_string = [&](FunctionalIR::Node n) { return node_names.at(n.id()); };
|
||||
for (auto node : ir)
|
||||
{
|
||||
auto ref = compute_graph[i];
|
||||
auto type = ref.function().type;
|
||||
std::string name;
|
||||
if(ref.has_sparse_attr())
|
||||
name = locals.insert(ref.sparse_attr());
|
||||
else
|
||||
name = locals.insert("\\n" + std::to_string(i));
|
||||
node_names(name);
|
||||
if(ref.function().name == ID($$input))
|
||||
f.printf("\t%s %s = input.%s;\n", type.to_string().c_str(), name.c_str(), input_struct[ref.function().parameters.begin()->first].c_str());
|
||||
else if(ref.function().name == ID($$state))
|
||||
f.printf("\t%s %s = current_state.%s;\n", type.to_string().c_str(), name.c_str(), state_struct[ref.function().parameters.begin()->first].c_str());
|
||||
else if(ref.function().name == ID($$buf))
|
||||
f.printf("\t%s %s = %s;\n", type.to_string().c_str(), name.c_str(), node_names[ref.arg(0).index()].c_str());
|
||||
else if(ref.function().name == ID($$cell_output))
|
||||
f.printf("\t%s %s = %s.%s;\n", type.to_string().c_str(), name.c_str(), node_names[ref.arg(0).index()].c_str(), RTLIL::unescape_id(ref.function().parameters.begin()->first).c_str());
|
||||
else if(ref.function().name == ID($$const)){
|
||||
auto c = ref.function().parameters.begin()->second;
|
||||
if(c.size() <= 32){
|
||||
f.printf("\t%s %s = $const<%d>(%#x);\n", type.to_string().c_str(), name.c_str(), type.width(), (uint32_t) c.as_int());
|
||||
}else{
|
||||
f.printf("\t%s %s = $const<%d>({%#x", type.to_string().c_str(), name.c_str(), type.width(), (uint32_t) c.as_int());
|
||||
while(c.size() > 32){
|
||||
c = c.extract(32, c.size() - 32);
|
||||
f.printf(", %#x", c.as_int());
|
||||
}
|
||||
f.printf("});\n");
|
||||
}
|
||||
}else if(ref.function().name == ID($$undriven))
|
||||
f.printf("\t%s %s; //undriven\n", type.to_string().c_str(), name.c_str());
|
||||
else if(ref.function().name == ID($$slice))
|
||||
f.printf("\t%s %s = slice<%d>(%s, %d);\n", type.to_string().c_str(), name.c_str(), type.width(), node_names[ref.arg(0).index()].c_str(), ref.function().parameters.at(ID(offset)).as_int());
|
||||
else if(ref.function().name == ID($$concat)){
|
||||
f.printf("\tauto %s = concat(", name.c_str());
|
||||
for (int i = 0, end = ref.size(); i != end; ++i){
|
||||
if(i > 0)
|
||||
f.printf(", ");
|
||||
f.printf("%s", node_names[ref.arg(i).index()].c_str());
|
||||
}
|
||||
f.printf(");\n");
|
||||
}else{
|
||||
f.printf("\t");
|
||||
f.printf("%s %s = %s", type.to_string().c_str(), name.c_str(), log_id(ref.function().name));
|
||||
if(ref.function().parameters.count(ID(WIDTH))){
|
||||
f.printf("<%d>", ref.function().parameters.at(ID(WIDTH)).as_int());
|
||||
}
|
||||
f.printf("(");
|
||||
for (int i = 0, end = ref.size(); i != end; ++i)
|
||||
f.printf("%s%s", i>0?", ":"", node_names[ref.arg(i).index()].c_str());
|
||||
f.printf("); //");
|
||||
for (auto const ¶m : ref.function().parameters)
|
||||
{
|
||||
if (param.second.empty())
|
||||
f.printf("[%s]", log_id(param.first));
|
||||
else
|
||||
f.printf("[%s=%s]", log_id(param.first), log_const(param.second));
|
||||
}
|
||||
f.printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const &key : compute_graph.keys())
|
||||
{
|
||||
f.printf("\t%s.%s = %s;\n", state.count(key.first) > 0 ? "next_state" : "output", state_struct[key.first].c_str(), node_names[key.second].c_str());
|
||||
std::string name = locals.insert(node.name());
|
||||
node_names.emplace(node.id(), name);
|
||||
f.printf("\t%s %s = %s;\n", CxxType(node.sort()).to_string().c_str(), name.c_str(), node.visit(CxxPrintVisitor(node_to_string, input_struct, state_struct)).c_str());
|
||||
}
|
||||
for (auto [name, sort] : ir.state())
|
||||
f.printf("\tnext_state.%s = %s;\n", state_struct[name].c_str(), node_to_string(ir.get_state_next_node(name)).c_str());
|
||||
for (auto [name, sort] : ir.outputs())
|
||||
f.printf("\toutput.%s = %s;\n", output_struct[name].c_str(), node_to_string(ir.get_output_node(name)).c_str());
|
||||
f.printf("}\n");
|
||||
}
|
||||
|
||||
|
@ -494,8 +317,7 @@ struct FunctionalCxxBackend : public Backend
|
|||
|
||||
for (auto module : design->selected_modules()) {
|
||||
log("Dumping module `%s'.\n", module->name.c_str());
|
||||
auto compute_graph = calculate_compute_graph(module);
|
||||
printCxx(*f, filename, RTLIL::unescape_id(module->name), compute_graph);
|
||||
printCxx(*f, filename, RTLIL::unescape_id(module->name), module);
|
||||
}
|
||||
}
|
||||
} FunctionalCxxBackend;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
|
||||
*
|
||||
* 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.h"
|
||||
#include "kernel/functionalir.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct FunctionalTestGeneric : public Pass
|
||||
{
|
||||
FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing Test Generic.\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
log("Dumping module `%s'.\n", module->name.c_str());
|
||||
auto fir = FunctionalIR::from_module(module);
|
||||
for(auto node : fir)
|
||||
std::cout << RTLIL::unescape_id(node.name()) << " = " << node.to_string([](auto n) { return RTLIL::unescape_id(n.name()); }) << "\n";
|
||||
for(auto [name, sort] : fir.outputs())
|
||||
std::cout << RTLIL::unescape_id(name) << " = " << RTLIL::unescape_id(fir.get_output_node(name).name()) << "\n";
|
||||
for(auto [name, sort] : fir.state())
|
||||
std::cout << RTLIL::unescape_id(name) << " = " << RTLIL::unescape_id(fir.get_state_next_node(name).name()) << "\n";
|
||||
}
|
||||
}
|
||||
} FunctionalCxxBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -123,6 +123,8 @@ public:
|
|||
Node &deref() const { this->check(); return this->graph_->nodes[this->index_]; }
|
||||
|
||||
public:
|
||||
Ref(BaseRef<ComputeGraph> ref) : Ref(ref.graph_, ref.index_) {}
|
||||
|
||||
void set_function(Fn const &function) const
|
||||
{
|
||||
deref().fn_index = this->graph_->functions(function);
|
||||
|
@ -224,7 +226,7 @@ public:
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
Ref add(Fn const &function, Attr const &attr, T const &args)
|
||||
Ref add(Fn const &function, Attr const &attr, T &&args)
|
||||
{
|
||||
Ref added = add(function, attr);
|
||||
for (auto arg : args)
|
||||
|
@ -233,7 +235,23 @@ public:
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
Ref add(Fn const &function, Attr &&attr, T const &args)
|
||||
Ref add(Fn const &function, Attr &&attr, T &&args)
|
||||
{
|
||||
Ref added = add(function, std::move(attr));
|
||||
for (auto arg : args)
|
||||
added.append_arg(arg);
|
||||
return added;
|
||||
}
|
||||
|
||||
Ref add(Fn const &function, Attr const &attr, std::initializer_list<Ref> args)
|
||||
{
|
||||
Ref added = add(function, attr);
|
||||
for (auto arg : args)
|
||||
added.append_arg(arg);
|
||||
return added;
|
||||
}
|
||||
|
||||
Ref add(Fn const &function, Attr &&attr, std::initializer_list<Ref> args)
|
||||
{
|
||||
Ref added = add(function, std::move(attr));
|
||||
for (auto arg : args)
|
||||
|
|
|
@ -17,15 +17,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef GRAPHTOOLS_H
|
||||
#define GRAPHTOOLS_H
|
||||
#include "kernel/functionalir.h"
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/drivertools.h"
|
||||
#include "kernel/functional.h"
|
||||
#include "kernel/mem.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
template <class T, class Factory>
|
||||
|
@ -196,7 +189,7 @@ public:
|
|||
};
|
||||
|
||||
template <class T, class Factory>
|
||||
class ComputeGraphConstruction {
|
||||
class FunctionalIRConstruction {
|
||||
std::deque<DriveSpec> queue;
|
||||
dict<DriveSpec, T> graph_nodes;
|
||||
idict<Cell *> cells;
|
||||
|
@ -218,7 +211,7 @@ class ComputeGraphConstruction {
|
|||
return it->second;
|
||||
}
|
||||
public:
|
||||
ComputeGraphConstruction(Factory &f) : factory(f), simplifier(f) {}
|
||||
FunctionalIRConstruction(Factory &f) : factory(f), simplifier(f) {}
|
||||
void add_module(Module *module)
|
||||
{
|
||||
driver_map.add(module);
|
||||
|
@ -238,8 +231,9 @@ public:
|
|||
memories[mem.cell] = &mem;
|
||||
}
|
||||
}
|
||||
T concatenate_read_results(Mem *mem, vector<T> results)
|
||||
T concatenate_read_results(Mem *, vector<T> results)
|
||||
{
|
||||
/* TODO: write code to check that this is ok to do */
|
||||
if(results.size() == 0)
|
||||
return factory.undriven(0);
|
||||
T node = results[0];
|
||||
|
@ -381,6 +375,60 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
FunctionalIR FunctionalIR::from_module(Module *module) {
|
||||
FunctionalIR ir;
|
||||
auto factory = ir.factory();
|
||||
FunctionalIRConstruction<FunctionalIR::Node, FunctionalIR::Factory> ctor(factory);
|
||||
ctor.add_module(module);
|
||||
ctor.process_queue();
|
||||
ir.topological_sort();
|
||||
ir.forward_buf();
|
||||
return ir;
|
||||
}
|
||||
|
||||
#endif
|
||||
void FunctionalIR::topological_sort() {
|
||||
Graph::SccAdaptor compute_graph_scc(_graph);
|
||||
bool scc = false;
|
||||
std::vector<int> perm;
|
||||
topo_sorted_sccs(compute_graph_scc, [&](int *begin, int *end) {
|
||||
perm.insert(perm.end(), begin, end);
|
||||
if (end > begin + 1)
|
||||
{
|
||||
log_warning("SCC:");
|
||||
for (int *i = begin; i != end; ++i)
|
||||
log(" %d", *i);
|
||||
log("\n");
|
||||
scc = true;
|
||||
}
|
||||
}, /* sources_first */ true);
|
||||
_graph.permute(perm);
|
||||
if(scc) log_error("combinational loops, aborting\n");
|
||||
}
|
||||
|
||||
void FunctionalIR::forward_buf() {
|
||||
std::vector<int> perm, alias;
|
||||
perm.clear();
|
||||
|
||||
for (int i = 0; i < _graph.size(); ++i)
|
||||
{
|
||||
auto node = _graph[i];
|
||||
if (node.function().fn() == Fn::buf && node.arg(0).index() < i)
|
||||
{
|
||||
int target_index = alias[node.arg(0).index()];
|
||||
auto target_node = _graph[perm[target_index]];
|
||||
if(!target_node.has_sparse_attr() && node.has_sparse_attr()){
|
||||
IdString id = node.sparse_attr();
|
||||
target_node.sparse_attr() = id;
|
||||
}
|
||||
alias.push_back(target_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
alias.push_back(GetSize(perm));
|
||||
perm.push_back(i);
|
||||
}
|
||||
}
|
||||
_graph.permute(perm, alias);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FUNCTIONALIR_H
|
||||
#define FUNCTIONALIR_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/functional.h"
|
||||
#include "kernel/drivertools.h"
|
||||
#include "kernel/mem.h"
|
||||
#include "kernel/topo_scc.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
class FunctionalIR {
|
||||
enum class Fn {
|
||||
invalid,
|
||||
buf,
|
||||
slice,
|
||||
zero_extend,
|
||||
sign_extend,
|
||||
concat,
|
||||
add,
|
||||
sub,
|
||||
bitwise_and,
|
||||
bitwise_or,
|
||||
bitwise_xor,
|
||||
bitwise_not,
|
||||
reduce_and,
|
||||
reduce_or,
|
||||
reduce_xor,
|
||||
unary_minus,
|
||||
equal,
|
||||
not_equal,
|
||||
signed_greater_than,
|
||||
signed_greater_equal,
|
||||
unsigned_greater_than,
|
||||
unsigned_greater_equal,
|
||||
logical_shift_left,
|
||||
logical_shift_right,
|
||||
arithmetic_shift_right,
|
||||
mux,
|
||||
pmux,
|
||||
constant,
|
||||
input,
|
||||
state,
|
||||
multiple,
|
||||
undriven,
|
||||
memory_read,
|
||||
memory_write
|
||||
};
|
||||
public:
|
||||
class Sort {
|
||||
std::variant<int, std::pair<int, int>> _v;
|
||||
public:
|
||||
explicit Sort(int width) : _v(width) { }
|
||||
Sort(int addr_width, int data_width) : _v(std::make_pair(addr_width, data_width)) { }
|
||||
bool is_signal() const { return _v.index() == 0; }
|
||||
bool is_memory() const { return _v.index() == 1; }
|
||||
int width() const { return std::get<0>(_v); }
|
||||
int addr_width() const { return std::get<1>(_v).first; }
|
||||
int data_width() const { return std::get<1>(_v).second; }
|
||||
bool operator==(Sort const& other) const { return _v == other._v; }
|
||||
unsigned int hash() const { return mkhash(_v); }
|
||||
};
|
||||
private:
|
||||
class NodeData {
|
||||
Fn _fn;
|
||||
std::variant<
|
||||
std::monostate,
|
||||
RTLIL::Const,
|
||||
IdString,
|
||||
int
|
||||
> _extra;
|
||||
public:
|
||||
NodeData() : _fn(Fn::invalid) {}
|
||||
NodeData(Fn fn) : _fn(fn) {}
|
||||
template<class T> NodeData(Fn fn, T &&extra) : _fn(fn), _extra(std::forward<T>(extra)) {}
|
||||
Fn fn() const { return _fn; }
|
||||
const RTLIL::Const &as_const() const { return std::get<RTLIL::Const>(_extra); }
|
||||
IdString as_idstring() const { return std::get<IdString>(_extra); }
|
||||
int as_int() const { return std::get<int>(_extra); }
|
||||
int hash() const {
|
||||
return mkhash((unsigned int) _fn, mkhash(_extra));
|
||||
}
|
||||
bool operator==(NodeData const &other) const {
|
||||
return _fn == other._fn && _extra == other._extra;
|
||||
}
|
||||
};
|
||||
struct Attr {
|
||||
Sort sort;
|
||||
};
|
||||
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);
|
||||
}
|
||||
public:
|
||||
class Factory;
|
||||
class Node {
|
||||
friend class Factory;
|
||||
friend class FunctionalIR;
|
||||
Graph::Ref _ref;
|
||||
Node(Graph::Ref ref) : _ref(ref) { }
|
||||
operator Graph::Ref() { return _ref; }
|
||||
template<class NodePrinter> struct PrintVisitor {
|
||||
NodePrinter np;
|
||||
PrintVisitor(NodePrinter np) : np(np) { }
|
||||
std::string buf(Node, Node n) { return "buf(" + np(n) + ")"; }
|
||||
std::string slice(Node, Node a, int, int offset, int out_width) { return "slice(" + np(a) + ", " + std::to_string(offset) + ", " + std::to_string(out_width) + ")"; }
|
||||
std::string zero_extend(Node, Node a, int, int out_width) { return "zero_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
|
||||
std::string sign_extend(Node, Node a, int, int out_width) { return "sign_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
|
||||
std::string concat(Node, Node a, int, Node b, int) { return "concat(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string add(Node, Node a, Node b, int) { return "add(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string sub(Node, Node a, Node b, int) { return "sub(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string bitwise_and(Node, Node a, Node b, int) { return "bitwise_and(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string bitwise_or(Node, Node a, Node b, int) { return "bitwise_or(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string bitwise_xor(Node, Node a, Node b, int) { return "bitwise_xor(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string bitwise_not(Node, Node a, int) { return "bitwise_not(" + np(a) + ")"; }
|
||||
std::string unary_minus(Node, Node a, int) { return "unary_minus(" + np(a) + ")"; }
|
||||
std::string reduce_and(Node, Node a, int) { return "reduce_and(" + np(a) + ")"; }
|
||||
std::string reduce_or(Node, Node a, int) { return "reduce_or(" + np(a) + ")"; }
|
||||
std::string reduce_xor(Node, Node a, int) { return "reduce_xor(" + np(a) + ")"; }
|
||||
std::string equal(Node, Node a, Node b, int) { return "equal(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string not_equal(Node, Node a, Node b, int) { return "not_equal(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string signed_greater_than(Node, Node a, Node b, int) { return "signed_greater_than(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string signed_greater_equal(Node, Node a, Node b, int) { return "signed_greater_equal(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string unsigned_greater_than(Node, Node a, Node b, int) { return "unsigned_greater_than(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string unsigned_greater_equal(Node, Node a, Node b, int) { return "unsigned_greater_equal(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string logical_shift_left(Node, Node a, Node b, int, int) { return "logical_shift_left(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string logical_shift_right(Node, Node a, Node b, int, int) { return "logical_shift_right(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string arithmetic_shift_right(Node, Node a, Node b, int, int) { return "arithmetic_shift_right(" + np(a) + ", " + np(b) + ")"; }
|
||||
std::string mux(Node, Node a, Node b, Node s, int) { return "mux(" + np(a) + ", " + np(b) + ", " + np(s) + ")"; }
|
||||
std::string pmux(Node, Node a, Node b, Node s, int, int) { return "pmux(" + np(a) + ", " + np(b) + ", " + np(s) + ")"; }
|
||||
std::string constant(Node, RTLIL::Const value) { return "constant(" + value.as_string() + ")"; }
|
||||
std::string input(Node, IdString name) { return "input(" + name.str() + ")"; }
|
||||
std::string state(Node, IdString name) { return "state(" + name.str() + ")"; }
|
||||
std::string memory_read(Node, Node mem, Node addr, int, int) { return "memory_read(" + np(mem) + ", " + np(addr) + ")"; }
|
||||
std::string memory_write(Node, Node mem, Node addr, Node data, int, int) { return "memory_write(" + np(mem) + ", " + np(addr) + ", " + np(data) + ")"; }
|
||||
std::string undriven(Node, int width) { return "undriven(" + std::to_string(width) + ")"; }
|
||||
};
|
||||
public:
|
||||
int id() const { return _ref.index(); }
|
||||
IdString name() const {
|
||||
if(_ref.has_sparse_attr())
|
||||
return _ref.sparse_attr();
|
||||
else
|
||||
return std::string("\\n") + std::to_string(id());
|
||||
}
|
||||
Sort sort() const { return _ref.attr().sort; }
|
||||
int width() const { return sort().width(); }
|
||||
Node arg(int n) const { return Node(_ref.arg(n)); }
|
||||
template<class Visitor> auto visit(Visitor v) const
|
||||
{
|
||||
switch(_ref.function().fn()) {
|
||||
case Fn::invalid: log_error("invalid node in visit"); break;
|
||||
case Fn::buf: return v.buf(*this, arg(0)); break;
|
||||
case Fn::slice: return v.slice(*this, arg(0), arg(0).width(), _ref.function().as_int(), sort().width()); break;
|
||||
case Fn::zero_extend: return v.zero_extend(*this, arg(0), arg(0).width(), width()); break;
|
||||
case Fn::sign_extend: return v.sign_extend(*this, arg(0), arg(0).width(), width()); break;
|
||||
case Fn::concat: return v.concat(*this, arg(0), arg(0).width(), arg(1), arg(1).width()); break;
|
||||
case Fn::add: return v.add(*this, arg(0), arg(1), sort().width()); break;
|
||||
case Fn::sub: return v.sub(*this, arg(0), arg(1), sort().width()); break;
|
||||
case Fn::bitwise_and: return v.bitwise_and(*this, arg(0), arg(1), sort().width()); break;
|
||||
case Fn::bitwise_or: return v.bitwise_or(*this, arg(0), arg(1), sort().width()); break;
|
||||
case Fn::bitwise_xor: return v.bitwise_xor(*this, arg(0), arg(1), sort().width()); break;
|
||||
case Fn::bitwise_not: return v.bitwise_not(*this, arg(0), sort().width()); break;
|
||||
case Fn::unary_minus: return v.bitwise_not(*this, arg(0), sort().width()); break;
|
||||
case Fn::reduce_and: return v.reduce_and(*this, arg(0), arg(0).width()); break;
|
||||
case Fn::reduce_or: return v.reduce_or(*this, arg(0), arg(0).width()); break;
|
||||
case Fn::reduce_xor: return v.reduce_xor(*this, arg(0), arg(0).width()); break;
|
||||
case Fn::equal: return v.equal(*this, arg(0), arg(1), arg(0).width()); break;
|
||||
case Fn::not_equal: return v.not_equal(*this, arg(0), arg(1), arg(0).width()); break;
|
||||
case Fn::signed_greater_than: return v.signed_greater_than(*this, arg(0), arg(1), arg(0).width()); break;
|
||||
case Fn::signed_greater_equal: return v.signed_greater_equal(*this, arg(0), arg(1), arg(0).width()); break;
|
||||
case Fn::unsigned_greater_than: return v.unsigned_greater_than(*this, arg(0), arg(1), arg(0).width()); break;
|
||||
case Fn::unsigned_greater_equal: return v.unsigned_greater_equal(*this, arg(0), arg(1), arg(0).width()); break;
|
||||
case Fn::logical_shift_left: return v.logical_shift_left(*this, arg(0), arg(1), arg(0).width(), arg(1).width()); break;
|
||||
case Fn::logical_shift_right: return v.logical_shift_right(*this, arg(0), arg(1), arg(0).width(), arg(1).width()); break;
|
||||
case Fn::arithmetic_shift_right: return v.arithmetic_shift_right(*this, arg(0), arg(1), arg(0).width(), arg(1).width()); break;
|
||||
case Fn::mux: return v.mux(*this, arg(0), arg(1), arg(2), arg(0).width()); break;
|
||||
case Fn::pmux: return v.pmux(*this, arg(0), arg(1), arg(2), arg(0).width(), arg(2).width()); break;
|
||||
case Fn::constant: return v.constant(*this, _ref.function().as_const()); break;
|
||||
case Fn::input: return v.input(*this, _ref.function().as_idstring()); break;
|
||||
case Fn::state: return v.state(*this, _ref.function().as_idstring()); break;
|
||||
case Fn::memory_read: return v.memory_read(*this, arg(0), arg(1), arg(1).width(), width()); break;
|
||||
case Fn::memory_write: return v.memory_write(*this, arg(0), arg(1), arg(2), arg(1).width(), arg(2).width()); break;
|
||||
case Fn::multiple: log_error("multiple in visit"); break;
|
||||
case Fn::undriven: return v.undriven(*this, width()); break;
|
||||
}
|
||||
}
|
||||
template<class NodePrinter> std::string to_string(NodePrinter np)
|
||||
{
|
||||
return visit(PrintVisitor(np));
|
||||
}
|
||||
/* TODO: delete */ int size() const { return sort().width(); }
|
||||
};
|
||||
class Factory {
|
||||
FunctionalIR &_ir;
|
||||
friend class FunctionalIR;
|
||||
explicit Factory(FunctionalIR &ir) : _ir(ir) {}
|
||||
Node add(NodeData &&fn, Sort &&sort, std::initializer_list<Node> args) {
|
||||
Graph::Ref ref = _ir._graph.add(std::move(fn), {std::move(sort)});
|
||||
for (auto arg : args)
|
||||
ref.append_arg(Graph::Ref(arg));
|
||||
return ref;
|
||||
}
|
||||
void check_basic_binary(Node const &a, Node const &b) { log_assert(a.sort().is_signal() && a.sort() == b.sort()); }
|
||||
void check_shift(Node const &a, Node const &b) { log_assert(a.sort().is_signal() && b.sort().is_signal()); }
|
||||
void check_unary(Node const &a) { log_assert(a.sort().is_signal()); }
|
||||
public:
|
||||
Node slice(Node a, int, int offset, int out_width) {
|
||||
log_assert(a.sort().is_signal() && offset + out_width <= a.sort().width());
|
||||
return add(NodeData(Fn::slice, offset), Sort(out_width), {a});
|
||||
}
|
||||
Node extend(Node a, int, int out_width, bool is_signed) {
|
||||
log_assert(a.sort().is_signal() && a.sort().width() < out_width);
|
||||
if(is_signed)
|
||||
return add(Fn::sign_extend, Sort(out_width), {a});
|
||||
else
|
||||
return add(Fn::zero_extend, Sort(out_width), {a});
|
||||
}
|
||||
Node concat(Node a, int, Node b, int) {
|
||||
log_assert(a.sort().is_signal() && b.sort().is_signal());
|
||||
return add(Fn::concat, Sort(a.sort().width() + b.sort().width()), {a, b});
|
||||
}
|
||||
Node add(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::add, a.sort(), {a, b}); }
|
||||
Node sub(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::sub, a.sort(), {a, b}); }
|
||||
Node bitwise_and(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::bitwise_and, a.sort(), {a, b}); }
|
||||
Node bitwise_or(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::bitwise_or, a.sort(), {a, b}); }
|
||||
Node bitwise_xor(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::bitwise_xor, a.sort(), {a, b}); }
|
||||
Node bitwise_not(Node a, int) { check_unary(a); return add(Fn::bitwise_not, a.sort(), {a}); }
|
||||
Node unary_minus(Node a, int) { check_unary(a); return add(Fn::unary_minus, a.sort(), {a}); }
|
||||
Node reduce_and(Node a, int) { check_unary(a); return add(Fn::reduce_and, Sort(1), {a}); }
|
||||
Node reduce_or(Node a, int) { check_unary(a); return add(Fn::reduce_or, Sort(1), {a}); }
|
||||
Node reduce_xor(Node a, int) { check_unary(a); return add(Fn::reduce_xor, Sort(1), {a}); }
|
||||
Node equal(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::equal, Sort(1), {a, b}); }
|
||||
Node not_equal(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::not_equal, Sort(1), {a, b}); }
|
||||
Node signed_greater_than(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::signed_greater_than, Sort(1), {a, b}); }
|
||||
Node signed_greater_equal(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::signed_greater_equal, Sort(1), {a, b}); }
|
||||
Node unsigned_greater_than(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::unsigned_greater_than, Sort(1), {a, b}); }
|
||||
Node unsigned_greater_equal(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::unsigned_greater_equal, Sort(1), {a, b}); }
|
||||
Node logical_shift_left(Node a, Node b, int, int) { check_shift(a, b); return add(Fn::logical_shift_left, a.sort(), {a, b}); }
|
||||
Node logical_shift_right(Node a, Node b, int, int) { check_shift(a, b); return add(Fn::logical_shift_right, a.sort(), {a, b}); }
|
||||
Node arithmetic_shift_right(Node a, Node b, int, int) { check_shift(a, b); return add(Fn::arithmetic_shift_right, a.sort(), {a, b}); }
|
||||
Node mux(Node a, Node b, Node s, int) {
|
||||
log_assert(a.sort().is_signal() && a.sort() == b.sort() && s.sort() == Sort(1));
|
||||
return add(Fn::mux, a.sort(), {a, b, s});
|
||||
}
|
||||
Node pmux(Node a, Node b, Node s, int, int) {
|
||||
log_assert(a.sort().is_signal() && b.sort().is_signal() && s.sort().is_signal() && a.sort().width() * s.sort().width() == b.sort().width());
|
||||
return add(Fn::pmux, a.sort(), {a, b, s});
|
||||
}
|
||||
Node memory_read(Node mem, Node addr, int, int) {
|
||||
log_assert(mem.sort().is_memory() && addr.sort().is_signal() && mem.sort().addr_width() == addr.sort().width());
|
||||
return add(Fn::memory_read, Sort(mem.sort().data_width()), {mem, addr});
|
||||
}
|
||||
Node memory_write(Node mem, Node addr, Node data, int, int) {
|
||||
log_assert(mem.sort().is_memory() && addr.sort().is_signal() && data.sort().is_signal() &&
|
||||
mem.sort().addr_width() == addr.sort().width() && mem.sort().data_width() == data.sort().width());
|
||||
return add(Fn::memory_write, mem.sort(), {mem, addr, data});
|
||||
}
|
||||
Node constant(RTLIL::Const value) {
|
||||
return add(NodeData(Fn::constant, std::move(value)), Sort(value.size()), {});
|
||||
}
|
||||
Node create_pending(int width) {
|
||||
return add(Fn::buf, Sort(width), {});
|
||||
}
|
||||
void update_pending(Node node, Node value) {
|
||||
log_assert(node._ref.function() == Fn::buf && node._ref.size() == 0 && node.sort() == value.sort());
|
||||
node._ref.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), {});
|
||||
}
|
||||
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), {});
|
||||
}
|
||||
Node cell_output(Node node, IdString, IdString, int) {
|
||||
return node;
|
||||
}
|
||||
Node multiple(vector<Node> args, int width) {
|
||||
auto node = add(Fn::multiple, Sort(width), {});
|
||||
for(const auto &arg : args)
|
||||
node._ref.append_arg(arg._ref);
|
||||
return node;
|
||||
}
|
||||
Node undriven(int width) {
|
||||
return add(Fn::undriven, Sort(width), {});
|
||||
}
|
||||
void declare_output(Node node, IdString name, int width) {
|
||||
_ir.add_output(name, Sort(width));
|
||||
node._ref.assign_key({name, false});
|
||||
}
|
||||
void declare_state(Node node, IdString name, int width) {
|
||||
_ir.add_state(name, Sort(width));
|
||||
node._ref.assign_key({name, true});
|
||||
}
|
||||
void declare_state_memory(Node node, IdString name, int addr_width, int data_width) {
|
||||
_ir.add_state(name, Sort(addr_width, data_width));
|
||||
node._ref.assign_key({name, true});
|
||||
}
|
||||
void suggest_name(Node node, IdString name) {
|
||||
node._ref.sparse_attr() = name;
|
||||
}
|
||||
|
||||
/* TODO delete this later*/
|
||||
Node eq(Node a, Node b, int) { return equal(a, b, 0); }
|
||||
Node ne(Node a, Node b, int) { return not_equal(a, b, 0); }
|
||||
Node gt(Node a, Node b, int) { return signed_greater_than(a, b, 0); }
|
||||
Node ge(Node a, Node b, int) { return signed_greater_equal(a, b, 0); }
|
||||
Node ugt(Node a, Node b, int) { return unsigned_greater_than(a, b, 0); }
|
||||
Node uge(Node a, Node b, int) { return unsigned_greater_equal(a, b, 0); }
|
||||
Node neg(Node a, int) { return unary_minus(a, 0); }
|
||||
};
|
||||
static FunctionalIR from_module(Module *module);
|
||||
Factory factory() { return Factory(*this); }
|
||||
int size() const { return _graph.size(); }
|
||||
Node operator[](int i) { return _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; }
|
||||
Node get_output_node(IdString name) { return _graph({name, false}); }
|
||||
Node get_state_next_node(IdString name) { return _graph({name, true}); }
|
||||
class Iterator {
|
||||
friend class FunctionalIR;
|
||||
FunctionalIR &_ir;
|
||||
int _index;
|
||||
Iterator(FunctionalIR &ir, int index) : _ir(ir), _index(index) {}
|
||||
public:
|
||||
Node operator*() { return _ir._graph[_index]; }
|
||||
Iterator &operator++() { _index++; return *this; }
|
||||
bool operator!=(Iterator const &other) const { return _index != other._index; }
|
||||
};
|
||||
Iterator begin() { return Iterator(*this, 0); }
|
||||
Iterator end() { return Iterator(*this, _graph.size()); }
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue