mirror of https://github.com/YosysHQ/yosys.git
add initial version of functional C++ backend
This commit is contained in:
parent
dd5ec84a26
commit
63dea89fac
|
@ -0,0 +1 @@
|
|||
OBJS += backends/functional/cxx.o
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* 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/drivertools.h"
|
||||
#include "kernel/topo_scc.h"
|
||||
#include "kernel/functional.h"
|
||||
#include "kernel/graphtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
const char *reserved_keywords[] = {
|
||||
"alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit",
|
||||
"atomic_noexcept","auto","bitand","bitor","bool","break","case",
|
||||
"catch","char","char16_t","char32_t","char8_t","class","co_await",
|
||||
"co_return","co_yield","compl","concept","const","const_cast","consteval",
|
||||
"constexpr","constinit","continue","decltype","default","delete",
|
||||
"do","double","dynamic_cast","else","enum","explicit","export",
|
||||
"extern","false","float","for","friend","goto","if","inline",
|
||||
"int","long","mutable","namespace","new","noexcept","not","not_eq",
|
||||
"nullptr","operator","or","or_eq","private","protected","public",
|
||||
"reflexpr","register","reinterpret_cast","requires","return","short",
|
||||
"signed","sizeof","static","static_assert","static_cast","struct",
|
||||
"switch","synchronized","template","this","thread_local","throw",
|
||||
"true","try","typedef","typeid","typename","union","unsigned",
|
||||
"using","virtual","void","volatile","wchar_t","while","xor","xor_eq",
|
||||
nullptr
|
||||
};
|
||||
|
||||
struct CxxScope {
|
||||
pool<std::string> used_names;
|
||||
dict<IdString, std::string> name_map;
|
||||
|
||||
CxxScope() {
|
||||
for(const char **p = reserved_keywords; *p != nullptr; p++)
|
||||
reserve(*p);
|
||||
}
|
||||
void reserve(std::string name) {
|
||||
used_names.insert(name);
|
||||
}
|
||||
std::string insert(IdString id) {
|
||||
std::string str = RTLIL::unescape_id(id);
|
||||
for(size_t i = 0; i < str.size(); i++)
|
||||
if(strchr("!\"#%&'()*+,-./:;<=>?@[]\\^`{|}~ ", str[i]))
|
||||
str[i] = '_';
|
||||
if(used_names.count(str) == 0){
|
||||
used_names.insert(str);
|
||||
name_map.insert({id, str});
|
||||
return str;
|
||||
}
|
||||
for (int idx = 0 ; ; idx++){
|
||||
std::string suffixed = str + "_" + std::to_string(idx);
|
||||
if (used_names.count(suffixed) == 0) {
|
||||
used_names.insert(suffixed);
|
||||
if(name_map.count(id) == 0)
|
||||
name_map.insert({id, suffixed});
|
||||
return suffixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string operator[](IdString id) {
|
||||
if(name_map.count(id) > 0)
|
||||
return name_map[id];
|
||||
else
|
||||
return insert(id);
|
||||
}
|
||||
};
|
||||
|
||||
struct CxxWriter {
|
||||
std::ostream &f;
|
||||
CxxWriter(std::ostream &out) : f(out) {}
|
||||
void printf(const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
f << vstringf(fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
};
|
||||
|
||||
struct CxxStruct {
|
||||
std::string name;
|
||||
dict<IdString, std::string> types;
|
||||
CxxScope scope;
|
||||
CxxStruct(std::string name) : name(name) {
|
||||
scope.reserve("out");
|
||||
scope.reserve("dump");
|
||||
}
|
||||
void insert(IdString name, std::string type) {
|
||||
scope.insert(name);
|
||||
types.insert({name, type});
|
||||
}
|
||||
void print(CxxWriter &f) {
|
||||
f.printf("struct %s {\n", name.c_str());
|
||||
for (auto p : types) {
|
||||
f.printf("\t%s %s;\n", p.second.c_str(), scope[p.first].c_str());
|
||||
}
|
||||
f.printf("\n\ttemplate <typename T> void dump(T &out) {\n");
|
||||
for (auto p : types) {
|
||||
f.printf("\t\tout(\"%s\", %s);\n", RTLIL::unescape_id(p.first).c_str(), scope[p.first].c_str());
|
||||
}
|
||||
f.printf("\t}\n};\n\n");
|
||||
}
|
||||
std::string operator[](IdString field) {
|
||||
return scope[field];
|
||||
}
|
||||
};
|
||||
|
||||
struct CxxFunction {
|
||||
IdString name;
|
||||
int width;
|
||||
dict<IdString, Const> parameters;
|
||||
|
||||
CxxFunction(IdString name, int width) : name(name), width(width) {}
|
||||
CxxFunction(IdString name, int width, dict<IdString, Const> parameters) : name(name), width(width), parameters(parameters) {}
|
||||
|
||||
bool operator==(CxxFunction const &other) const {
|
||||
return name == other.name && parameters == other.parameters && width == other.width;
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
return mkhash(name.hash(), parameters.hash());
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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 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 create_pending(int width) {
|
||||
return graph.add(CxxFunction(ID($$pending), width), 0);
|
||||
}
|
||||
void update_pending(T pending, T node) {
|
||||
assert(pending.function().name == ID($$pending));
|
||||
pending.set_function(CxxFunction(ID($$buf), pending.function().width));
|
||||
pending.append_arg(node);
|
||||
}
|
||||
void declare_output(T node, IdString name) {
|
||||
node.assign_key(name);
|
||||
}
|
||||
void declare_state(T node, IdString name) {
|
||||
node.assign_key(name);
|
||||
}
|
||||
void suggest_name(T node, IdString name) {
|
||||
node.sparse_attr() = name;
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionalCxxBackend : public Backend
|
||||
{
|
||||
FunctionalCxxBackend() : Backend("functional_cxx", "convert design to C++ using the functional backend") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
}
|
||||
|
||||
CxxComputeGraph calculate_compute_graph(RTLIL::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())
|
||||
target_node.sparse_attr() = node.sparse_attr();
|
||||
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, int> inputs, state;
|
||||
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().width;
|
||||
if(ref.function().name == ID($$state))
|
||||
state[ref.function().parameters.begin()->first] = ref.function().width;
|
||||
}
|
||||
f.printf("#include \"sim.h\"\n");
|
||||
CxxStruct input_struct(name + "_Inputs");
|
||||
for (auto const &input : inputs)
|
||||
input_struct.insert(input.first, "Signal<" + std::to_string(input.second) + ">");
|
||||
CxxStruct output_struct(name + "_Outputs");
|
||||
for (auto const &key : compute_graph.keys())
|
||||
if(state.count(key.first) == 0)
|
||||
output_struct.insert(key.first, "Signal<" + std::to_string(compute_graph[key.second].function().width) + ">");
|
||||
CxxStruct state_struct(name + "_State");
|
||||
for (auto const &state_var : state)
|
||||
state_struct.insert(state_var.first, "Signal<" + std::to_string(state_var.second) + ">");
|
||||
|
||||
idict<std::string> node_names;
|
||||
CxxScope locals;
|
||||
|
||||
input_struct.print(f);
|
||||
output_struct.print(f);
|
||||
state_struct.print(f);
|
||||
|
||||
f.printf("void %s(%s_Inputs const &input, %s_Outputs &output, %s_State const ¤t_state, %s_State &next_state)\n{\n", name.c_str(), name.c_str(), name.c_str(), name.c_str(), name.c_str());
|
||||
locals.reserve("input");
|
||||
locals.reserve("output");
|
||||
locals.reserve("current_state");
|
||||
locals.reserve("next_state");
|
||||
for (int i = 0; i < compute_graph.size(); ++i)
|
||||
{
|
||||
auto ref = compute_graph[i];
|
||||
int width = ref.function().width;
|
||||
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("\tSignal<%d> %s = input.%s;\n", width, name.c_str(), input_struct[ref.function().parameters.begin()->first].c_str());
|
||||
else if(ref.function().name == ID($$state))
|
||||
f.printf("\tSignal<%d> %s = current_state.%s;\n", width, name.c_str(), state_struct[ref.function().parameters.begin()->first].c_str());
|
||||
else if(ref.function().name == ID($$buf))
|
||||
f.printf("\tSignal<%d> %s = %s;\n", width, name.c_str(), node_names[ref.arg(0).index()].c_str());
|
||||
else if(ref.function().name == ID($$cell_output))
|
||||
f.printf("\tSignal<%d> %s = %s.%s;\n", width, 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("\tSignal<%d> %s = $const<%d>(%#x);\n", width, name.c_str(), width, (uint32_t) c.as_int());
|
||||
}else{
|
||||
f.printf("\tSignal<%d> %s = $const<%d>({%#x", width, name.c_str(), 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("\tSignal<%d> %s; //undriven\n", width, name.c_str());
|
||||
else if(ref.function().name == ID($$slice))
|
||||
f.printf("\tSignal<%d> %s = slice<%d>(%s, %d);\n", width, name.c_str(), 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");
|
||||
if(ref.function().width > 0)
|
||||
f.printf("Signal<%d>", ref.function().width);
|
||||
else
|
||||
f.printf("%s_Outputs", log_id(ref.function().name));
|
||||
f.printf(" %s = %s", 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());
|
||||
}
|
||||
f.printf("}\n");
|
||||
}
|
||||
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing Functional C++ backend.\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(f, filename, args, argidx, design);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
} FunctionalCxxBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* 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 SIM_H
|
||||
#define SIM_H
|
||||
|
||||
#include <array>
|
||||
|
||||
template<size_t n>
|
||||
using Signal = std::array<bool, n>;
|
||||
|
||||
template<size_t n, size_t m>
|
||||
Signal<n> slice(Signal<m> const& a, size_t offset)
|
||||
{
|
||||
Signal<n> ret;
|
||||
|
||||
std::copy(a.begin() + offset, a.begin() + offset + n, ret.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $const(uint32_t val)
|
||||
{
|
||||
size_t i;
|
||||
Signal<n> ret;
|
||||
|
||||
for(i = 0; i < n; i++)
|
||||
if(i < 32)
|
||||
ret[i] = val & (1<<i);
|
||||
else
|
||||
ret[i] = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $const(std::initializer_list<uint32_t> vals)
|
||||
{
|
||||
size_t k, i;
|
||||
Signal<n> ret;
|
||||
|
||||
k = 0;
|
||||
for (auto val : vals) {
|
||||
for(i = 0; i < 32; i++)
|
||||
if(i + k < n)
|
||||
ret[i + k] = val & (1<<i);
|
||||
k += 32;
|
||||
}
|
||||
for(; k < n; k++)
|
||||
ret[k] = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
bool as_bool(Signal<n> sig)
|
||||
{
|
||||
for(int i = 0; i < n; i++)
|
||||
if(sig[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
uint32_t as_int(Signal<n> sig)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
for(int i = 0; i < n; i++)
|
||||
if(sig[i] && i < 32)
|
||||
ret |= 1<<i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $mux(Signal<n> const& a, Signal<n> const &b, Signal<1> const &s)
|
||||
{
|
||||
return s[0] ? b : a;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $not(Signal<n> const& a)
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret[i] = !a[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $neg(Signal<n> const& a)
|
||||
{
|
||||
Signal<n> ret;
|
||||
bool carry = true;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
int r = !a[i] + carry;
|
||||
ret[i] = (r & 1) != 0;
|
||||
carry = (r >> 1) != 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $reduce_or(Signal<n> const& a)
|
||||
{
|
||||
return { as_bool(a) };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $reduce_and(Signal<n> const& a)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
if(!a[i])
|
||||
return { false };
|
||||
return { true };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $reduce_bool(Signal<n> const& a)
|
||||
{
|
||||
return { as_bool(a) };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $logic_and(Signal<n> const& a, Signal<n> const& b)
|
||||
{
|
||||
return { as_bool(a) && as_bool(b) };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $logic_or(Signal<n> const& a, Signal<n> const& b)
|
||||
{
|
||||
return { as_bool(a) || as_bool(b) };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $logic_not(Signal<n> const& a)
|
||||
{
|
||||
return { !as_bool(a) };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $add(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
Signal<n> ret;
|
||||
size_t i;
|
||||
int x = 0;
|
||||
for(i = 0; i < n; i++){
|
||||
x += (int)a[i] + (int)b[i];
|
||||
ret[i] = x & 1;
|
||||
x >>= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
template<size_t n>
|
||||
Signal<n> $sub(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
Signal<n> ret;
|
||||
int x = 1;
|
||||
for(size_t i = 0; i < n; i++){
|
||||
x += (int)a[i] + (int)!b[i];
|
||||
ret[i] = x & 1;
|
||||
x >>= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $uge(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
for(size_t i = n; i-- != 0; )
|
||||
if(a[i] != b[i])
|
||||
return { a[i] };
|
||||
return { true };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $ugt(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
for(size_t i = n; i-- != 0; )
|
||||
if(a[i] != b[i])
|
||||
return { a[i] };
|
||||
return { false };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $ge(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
if(a[n-1] != b[n-1])
|
||||
return { b[n-1] };
|
||||
return $uge(a, b);
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $gt(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
if(a[n-1] != b[n-1])
|
||||
return { b[n-1] };
|
||||
return $ugt(a, b);
|
||||
}
|
||||
|
||||
template<size_t n> Signal<1> $ule(Signal<n> const& a, Signal<n> const &b) { return $uge(b, a); }
|
||||
template<size_t n> Signal<1> $ult(Signal<n> const& a, Signal<n> const &b) { return $ugt(b, a); }
|
||||
template<size_t n> Signal<1> $le(Signal<n> const& a, Signal<n> const &b) { return $ge(b, a); }
|
||||
template<size_t n> Signal<1> $lt(Signal<n> const& a, Signal<n> const &b) { return $gt(b, a); }
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $and(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret[i] = a[i] && b[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $or(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret[i] = a[i] || b[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<n> $xor(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret[i] = a[i] != b[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n, size_t na, size_t nb>
|
||||
Signal<n> $shl(Signal<na> const& a, Signal<nb> const &b)
|
||||
{
|
||||
if(nb >= sizeof(int) * 8 - 1)
|
||||
for(size_t i = sizeof(int) * 8 - 1; i < nb; i++)
|
||||
assert(!b[i]);
|
||||
size_t amount = as_int(b);
|
||||
Signal<n> ret = $const<n>(0);
|
||||
if(amount < n){
|
||||
if(amount + na > n)
|
||||
std::copy(a.begin(), a.begin() + (n - amount), ret.begin() + amount);
|
||||
else
|
||||
std::copy(a.begin(), a.end(), ret.begin() + amount);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n, size_t nb>
|
||||
Signal<n> $shr(Signal<n> const& a, Signal<nb> const &b)
|
||||
{
|
||||
if(nb >= sizeof(int) * 8 - 1)
|
||||
for(size_t i = sizeof(int) * 8 - 1; i < nb; i++)
|
||||
assert(!b[i]);
|
||||
size_t amount = as_int(b);
|
||||
Signal<n> ret;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if(i + amount < n)
|
||||
ret[i] = a[i + amount];
|
||||
else
|
||||
ret[i] = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n, size_t nb>
|
||||
Signal<n> $asr(Signal<n> const& a, Signal<nb> const &b)
|
||||
{
|
||||
if(nb >= sizeof(int) * 8 - 1)
|
||||
for(size_t i = sizeof(int) * 8 - 1; i < nb; i++)
|
||||
assert(!b[i]);
|
||||
size_t amount = as_int(b);
|
||||
Signal<n> ret;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if(i + amount < n)
|
||||
ret[i] = a[i + amount];
|
||||
else
|
||||
ret[i] = a[n - 1];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $eq(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
if(a[i] != b[i])
|
||||
return { false };
|
||||
return { true };
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
Signal<1> $ne(Signal<n> const& a, Signal<n> const &b)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
if(a[i] != b[i])
|
||||
return { true };
|
||||
return { false };
|
||||
}
|
||||
|
||||
template<size_t n, size_t ns>
|
||||
Signal<n> $pmux(Signal<n> const& a, Signal<n*ns> const &b, Signal<ns> const &s)
|
||||
{
|
||||
bool found;
|
||||
Signal<n> ret;
|
||||
|
||||
found = false;
|
||||
ret = a;
|
||||
for(size_t i = 0; i < ns; i++){
|
||||
if(s[i]){
|
||||
if(found)
|
||||
return $const<n>(0);
|
||||
found = true;
|
||||
ret = slice<n>(b, n * i);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n, size_t m>
|
||||
Signal<n+m> concat(Signal<n> const& a, Signal<m> const& b)
|
||||
{
|
||||
Signal<n + m> ret;
|
||||
std::copy(a.begin(), a.end(), ret.begin());
|
||||
std::copy(b.begin(), b.end(), ret.begin() + n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n, size_t m>
|
||||
Signal<n> $zero_extend(Signal<m> const& a)
|
||||
{
|
||||
assert(n >= m);
|
||||
Signal<n> ret;
|
||||
std::copy(a.begin(), a.end(), ret.begin());
|
||||
for(size_t i = m; i < n; i++)
|
||||
ret[i] = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t n, size_t m>
|
||||
Signal<n> $sign_extend(Signal<m> const& a)
|
||||
{
|
||||
assert(n >= m);
|
||||
Signal<n> ret;
|
||||
std::copy(a.begin(), a.end(), ret.begin());
|
||||
for(size_t i = m; i < n; i++)
|
||||
ret[i] = a[m-1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* 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 GRAPHTOOLS_H
|
||||
#define GRAPHTOOLS_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/drivertools.h"
|
||||
#include "kernel/functional.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
template <class T, class Factory>
|
||||
class CellSimplifier {
|
||||
Factory &factory;
|
||||
T reduce_shift_width(T b, int b_width, int y_width, int &reduced_b_width) {
|
||||
log_assert(y_width > 0);
|
||||
int new_width = sizeof(int) * 8 - __builtin_clz(y_width);
|
||||
if (b_width <= new_width) {
|
||||
reduced_b_width = b_width;
|
||||
return b;
|
||||
} else {
|
||||
reduced_b_width = new_width;
|
||||
T lower_b = factory.slice(b, b_width, 0, new_width);
|
||||
T overflow = factory.gt(b, factory.constant(RTLIL::Const(y_width, b_width)), b_width);
|
||||
return factory.mux(lower_b, factory.constant(RTLIL::Const(y_width, new_width)), overflow, new_width);
|
||||
}
|
||||
}
|
||||
public:
|
||||
T reduce_or(T a, int width) {
|
||||
if (width == 1)
|
||||
return a;
|
||||
return factory.reduce_or(a, width);
|
||||
}
|
||||
T extend(T a, int in_width, int out_width, bool is_signed) {
|
||||
if(in_width == out_width)
|
||||
return a;
|
||||
if(in_width > out_width)
|
||||
return factory.slice(a, in_width, 0, out_width);
|
||||
return factory.extend(a, in_width, out_width, is_signed);
|
||||
}
|
||||
T logical_shift_left(T a, T b, int y_width, int b_width) {
|
||||
int reduced_b_width;
|
||||
T reduced_b = reduce_shift_width(b, b_width, y_width, reduced_b_width);
|
||||
return factory.logical_shift_left(a, reduced_b, y_width, reduced_b_width);
|
||||
}
|
||||
T logical_shift_right(T a, T b, int y_width, int b_width) {
|
||||
int reduced_b_width;
|
||||
T reduced_b = reduce_shift_width(b, b_width, y_width, reduced_b_width);
|
||||
return factory.logical_shift_right(a, reduced_b, y_width, reduced_b_width);
|
||||
}
|
||||
T arithmetic_shift_right(T a, T b, int y_width, int b_width) {
|
||||
int reduced_b_width;
|
||||
T reduced_b = reduce_shift_width(b, b_width, y_width, reduced_b_width);
|
||||
return factory.arithmetic_shift_right(a, reduced_b, y_width, reduced_b_width);
|
||||
}
|
||||
CellSimplifier(Factory &f) : factory(f) {}
|
||||
T handle(IdString cellType, dict<IdString, Const> parameters, dict<IdString, T> inputs)
|
||||
{
|
||||
int a_width = parameters.at(ID(A_WIDTH), Const(-1)).as_int();
|
||||
int b_width = parameters.at(ID(B_WIDTH), Const(-1)).as_int();
|
||||
int y_width = parameters.at(ID(Y_WIDTH), Const(-1)).as_int();
|
||||
bool a_signed = parameters.at(ID(A_SIGNED), Const(0)).as_bool();
|
||||
bool b_signed = parameters.at(ID(B_SIGNED), Const(0)).as_bool();
|
||||
if(cellType.in({ID($add), ID($sub), ID($and), ID($or), ID($xor), ID($xnor)})){
|
||||
bool is_signed = a_signed && b_signed;
|
||||
T a = extend(inputs.at(ID(A)), a_width, y_width, is_signed);
|
||||
T b = extend(inputs.at(ID(B)), b_width, y_width, is_signed);
|
||||
if(cellType == ID($add))
|
||||
return factory.add(a, b, y_width);
|
||||
else if(cellType == ID($sub))
|
||||
return factory.sub(a, b, y_width);
|
||||
else if(cellType == ID($and))
|
||||
return factory.bitwise_and(a, b, y_width);
|
||||
else if(cellType == ID($or))
|
||||
return factory.bitwise_or(a, b, y_width);
|
||||
else if(cellType == ID($xor))
|
||||
return factory.bitwise_xor(a, b, y_width);
|
||||
else if(cellType == ID($xnor))
|
||||
return factory.bitwise_not(factory.bitwise_xor(a, b, y_width), y_width);
|
||||
else
|
||||
log_abort();
|
||||
}else if(cellType.in({ID($eq), ID($ne), ID($eqx), ID($nex), ID($le), ID($lt), ID($ge), ID($gt)})){
|
||||
bool is_signed = a_signed && b_signed;
|
||||
int width = max(a_width, b_width);
|
||||
T a = extend(inputs.at(ID(A)), a_width, width, is_signed);
|
||||
T b = extend(inputs.at(ID(B)), b_width, width, is_signed);
|
||||
if(cellType.in({ID($eq), ID($eqx)}))
|
||||
return extend(factory.eq(a, b, width), 1, y_width, false);
|
||||
if(cellType.in({ID($ne), ID($nex)}))
|
||||
return extend(factory.ne(a, b, width), 1, y_width, false);
|
||||
else if(cellType == ID($lt))
|
||||
return extend(is_signed ? factory.gt(b, a, width) : factory.ugt(b, a, width), 1, y_width, false);
|
||||
else if(cellType == ID($le))
|
||||
return extend(is_signed ? factory.ge(b, a, width) : factory.uge(b, a, width), 1, y_width, false);
|
||||
else if(cellType == ID($gt))
|
||||
return extend(is_signed ? factory.gt(a, b, width) : factory.ugt(a, b, width), 1, y_width, false);
|
||||
else if(cellType == ID($ge))
|
||||
return extend(is_signed ? factory.ge(a, b, width) : factory.uge(a, b, width), 1, y_width, false);
|
||||
else
|
||||
log_abort();
|
||||
}else if(cellType.in({ID($logic_or), ID($logic_and)})){
|
||||
T a = reduce_or(inputs.at(ID(A)), a_width);
|
||||
T b = reduce_or(inputs.at(ID(B)), b_width);
|
||||
T y = cellType == ID($logic_and) ? factory.bitwise_and(a, b, 1) : factory.bitwise_or(a, b, 1);
|
||||
return extend(y, 1, y_width, false);
|
||||
}else if(cellType == ID($not)){
|
||||
T a = extend(inputs.at(ID(A)), a_width, y_width, a_signed);
|
||||
return factory.bitwise_not(a, y_width);
|
||||
}else if(cellType == ID($pos)){
|
||||
return extend(inputs.at(ID(A)), a_width, y_width, a_signed);
|
||||
}else if(cellType == ID($neg)){
|
||||
T a = extend(inputs.at(ID(A)), a_width, y_width, a_signed);
|
||||
return factory.neg(a, y_width);
|
||||
}else if(cellType == ID($logic_not)){
|
||||
T a = reduce_or(inputs.at(ID(A)), a_width);
|
||||
T y = factory.bitwise_not(a, 1);
|
||||
return extend(y, 1, y_width, false);
|
||||
}else if(cellType.in({ID($reduce_or), ID($reduce_bool)})){
|
||||
T a = reduce_or(inputs.at(ID(A)), a_width);
|
||||
return extend(a, 1, y_width, false);
|
||||
}else if(cellType == ID($reduce_and)){
|
||||
T a = factory.reduce_and(inputs.at(ID(A)), a_width);
|
||||
return extend(a, 1, y_width, false);
|
||||
}else if(cellType.in({ID($reduce_xor), ID($reduce_xnor)})){
|
||||
T a = factory.reduce_xor(inputs.at(ID(A)), a_width);
|
||||
T y = cellType == ID($reduce_xnor) ? factory.bitwise_not(a, 1) : a;
|
||||
return extend(y, 1, y_width, false);
|
||||
}else if(cellType == ID($shl) || cellType == ID($sshl)){
|
||||
T a = extend(inputs.at(ID(A)), a_width, y_width, a_signed);
|
||||
T b = inputs.at(ID(B));
|
||||
return logical_shift_left(a, b, y_width, b_width);
|
||||
}else if(cellType == ID($shr) || cellType == ID($sshr)){
|
||||
int width = max(a_width, y_width);
|
||||
T a = extend(inputs.at(ID(A)), a_width, width, a_signed);
|
||||
T b = inputs.at(ID(B));
|
||||
T y = a_signed && cellType == ID($sshr) ?
|
||||
arithmetic_shift_right(a, b, width, b_width) :
|
||||
logical_shift_right(a, b, width, b_width);
|
||||
return extend(y, width, y_width, a_signed);
|
||||
}else if(cellType == ID($shiftx) || cellType == ID($shift)){
|
||||
int width = max(a_width, y_width);
|
||||
T a = extend(inputs.at(ID(A)), a_width, width, cellType == ID($shift) && a_signed);
|
||||
T b = inputs.at(ID(B));
|
||||
T shr = logical_shift_right(a, b, width, b_width);
|
||||
if(b_signed) {
|
||||
T sign_b = factory.slice(b, b_width, b_width - 1, 1);
|
||||
T shl = logical_shift_left(a, factory.neg(b, b_width), width, b_width);
|
||||
T y = factory.mux(shr, shl, sign_b, width);
|
||||
return extend(y, width, y_width, false);
|
||||
} else {
|
||||
return extend(shr, width, y_width, false);
|
||||
}
|
||||
}else if(cellType == ID($mux)){
|
||||
int width = parameters.at(ID(WIDTH)).as_int();
|
||||
return factory.mux(inputs.at(ID(A)), inputs.at(ID(B)), inputs.at(ID(S)), width);
|
||||
}else if(cellType == ID($pmux)){
|
||||
int width = parameters.at(ID(WIDTH)).as_int();
|
||||
int s_width = parameters.at(ID(S_WIDTH)).as_int();
|
||||
return factory.pmux(inputs.at(ID(A)), inputs.at(ID(B)), inputs.at(ID(S)), width, s_width);
|
||||
}else if(cellType == ID($concat)){
|
||||
T a = inputs.at(ID(A));
|
||||
T b = inputs.at(ID(B));
|
||||
return factory.concat(a, a_width, b, b_width);
|
||||
}else if(cellType == ID($slice)){
|
||||
int offset = parameters.at(ID(OFFSET)).as_int();
|
||||
T a = inputs.at(ID(A));
|
||||
return factory.slice(a, a_width, offset, y_width);
|
||||
}else{
|
||||
log_error("unhandled cell in CellSimplifier %s\n", cellType.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Factory>
|
||||
class ComputeGraphConstruction {
|
||||
std::deque<DriveSpec> queue;
|
||||
dict<DriveSpec, T> graph_nodes;
|
||||
idict<Cell *> cells;
|
||||
DriverMap driver_map;
|
||||
Factory& factory;
|
||||
CellSimplifier<T, Factory> simplifier;
|
||||
|
||||
T enqueue(DriveSpec const &spec)
|
||||
{
|
||||
auto it = graph_nodes.find(spec);
|
||||
if(it == graph_nodes.end()){
|
||||
auto node = factory.create_pending(spec.size());
|
||||
graph_nodes.insert({spec, node});
|
||||
queue.emplace_back(spec);
|
||||
return node;
|
||||
}else
|
||||
return it->second;
|
||||
}
|
||||
public:
|
||||
ComputeGraphConstruction(Factory &f) : factory(f), simplifier(f) {}
|
||||
void add_module(Module *module)
|
||||
{
|
||||
driver_map.add(module);
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type.in(ID($assert), ID($assume), ID($cover), ID($check)))
|
||||
enqueue(DriveBitMarker(cells(cell), 0));
|
||||
}
|
||||
for (auto wire : module->wires()) {
|
||||
if (wire->port_output) {
|
||||
T node = enqueue(DriveChunk(DriveChunkWire(wire, 0, wire->width)));
|
||||
factory.declare_output(node, wire->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
void process_queue()
|
||||
{
|
||||
for (; !queue.empty(); queue.pop_front()) {
|
||||
DriveSpec spec = queue.front();
|
||||
T pending = graph_nodes.at(spec);
|
||||
|
||||
if (spec.chunks().size() > 1) {
|
||||
auto chunks = spec.chunks();
|
||||
T node = enqueue(chunks[0]);
|
||||
int width = chunks[0].size();
|
||||
for(size_t i = 1; i < chunks.size(); i++) {
|
||||
node = factory.concat(node, width, enqueue(chunks[i]), chunks[i].size());
|
||||
width += chunks[i].size();
|
||||
}
|
||||
factory.update_pending(pending, node);
|
||||
} else if (spec.chunks().size() == 1) {
|
||||
DriveChunk chunk = spec.chunks()[0];
|
||||
if (chunk.is_wire()) {
|
||||
DriveChunkWire wire_chunk = chunk.wire();
|
||||
if (wire_chunk.is_whole()) {
|
||||
if (wire_chunk.wire->port_input) {
|
||||
T node = factory.input(wire_chunk.wire->name, wire_chunk.width);
|
||||
factory.suggest_name(node, wire_chunk.wire->name);
|
||||
factory.update_pending(pending, node);
|
||||
} else {
|
||||
DriveSpec driver = driver_map(DriveSpec(wire_chunk));
|
||||
T node = enqueue(driver);
|
||||
factory.suggest_name(node, wire_chunk.wire->name);
|
||||
factory.update_pending(pending, node);
|
||||
}
|
||||
} else {
|
||||
DriveChunkWire whole_wire(wire_chunk.wire, 0, wire_chunk.wire->width);
|
||||
T node = factory.slice(enqueue(whole_wire), wire_chunk.wire->width, wire_chunk.offset, wire_chunk.width);
|
||||
factory.update_pending(pending, node);
|
||||
}
|
||||
} else if (chunk.is_port()) {
|
||||
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;
|
||||
T 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)) {
|
||||
T node = enqueue(DriveChunkPort(cell, conn));
|
||||
factory.declare_state(node, cell->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
T cell = enqueue(DriveChunkMarker(cells(port_chunk.cell), 0, port_chunk.width));
|
||||
factory.suggest_name(cell, port_chunk.cell->name);
|
||||
T node = factory.cell_output(cell, port_chunk.cell->type, port_chunk.port, port_chunk.width);
|
||||
factory.suggest_name(node, port_chunk.cell->name.str() + "$" + port_chunk.port.str());
|
||||
factory.update_pending(pending, node);
|
||||
}
|
||||
} else {
|
||||
DriveSpec driver = driver_map(DriveSpec(port_chunk));
|
||||
factory.update_pending(pending, enqueue(driver));
|
||||
}
|
||||
} else {
|
||||
DriveChunkPort whole_port(port_chunk.cell, port_chunk.port, 0, GetSize(port_chunk.cell->connections().at(port_chunk.port)));
|
||||
T node = factory.slice(enqueue(whole_port), whole_port.width, port_chunk.offset, port_chunk.width);
|
||||
factory.update_pending(pending, node);
|
||||
}
|
||||
} else if (chunk.is_constant()) {
|
||||
T node = factory.constant(chunk.constant());
|
||||
factory.suggest_name(node, "$const" + std::to_string(chunk.size()) + "b" + chunk.constant().as_string());
|
||||
factory.update_pending(pending, node);
|
||||
} else if (chunk.is_multiple()) {
|
||||
vector<T> args;
|
||||
for (auto const &driver : chunk.multiple().multiple())
|
||||
args.push_back(enqueue(driver));
|
||||
T node = factory.multiple(args, chunk.size());
|
||||
factory.update_pending(pending, node);
|
||||
} else if (chunk.is_marker()) {
|
||||
Cell *cell = cells[chunk.marker().marker];
|
||||
dict<IdString, T> connections;
|
||||
for(auto const &conn : cell->connections()) {
|
||||
if(driver_map.celltypes.cell_input(cell->type, conn.first))
|
||||
connections.insert({ conn.first, enqueue(DriveChunkPort(cell, conn)) });
|
||||
}
|
||||
T node = simplifier.handle(cell->type, cell->parameters, connections);
|
||||
factory.update_pending(pending, node);
|
||||
} else if (chunk.is_none()) {
|
||||
T node = factory.undriven(chunk.size());
|
||||
factory.update_pending(pending, node);
|
||||
} else {
|
||||
log_error("unhandled drivespec: %s\n", log_signal(chunk));
|
||||
log_abort();
|
||||
}
|
||||
} else {
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue