From 452d7fb3e022c496b04786ecf24fdb9be0827510 Mon Sep 17 00:00:00 2001 From: Emily Schmidt Date: Thu, 27 Jun 2024 13:16:57 +0100 Subject: [PATCH] tidy up generic functional backend, add generic scope class, tidy up c++ functional backend --- backends/functional/cxx.cc | 178 ++++++++++++------------------------- kernel/functionalir.cc | 44 +++++---- kernel/functionalir.h | 82 ++++++++++++----- 3 files changed, 149 insertions(+), 155 deletions(-) diff --git a/backends/functional/cxx.cc b/backends/functional/cxx.cc index 9571385ccb3..5f63a06153b 100644 --- a/backends/functional/cxx.cc +++ b/backends/functional/cxx.cc @@ -23,6 +23,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +const char illegal_characters[] = "!\"#%&'()*+,-./:;<=>?@[]\\^`{|}~ "; const char *reserved_keywords[] = { "alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit", "atomic_noexcept","auto","bitand","bitor","bool","break","case", @@ -41,45 +42,6 @@ const char *reserved_keywords[] = { nullptr }; -struct CxxScope { - pool used_names; - dict 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 CxxType { FunctionalIR::Sort sort; CxxType(FunctionalIR::Sort sort) : sort(sort) {} @@ -109,66 +71,30 @@ struct CxxWriter { struct CxxStruct { std::string name; dict types; - CxxScope scope; - bool generate_methods; - CxxStruct(std::string name, bool generate_methods = false) - : name(name), generate_methods(generate_methods) { + FunctionalTools::Scope scope; + CxxStruct(std::string name) + : name(name), scope(illegal_characters, reserved_keywords) { scope.reserve("out"); scope.reserve("dump"); } void insert(IdString name, CxxType type) { - scope.insert(name); + scope(name); types.insert({name, type}); } void print(CxxWriter &f) { - f.printf("struct %s {\n", name.c_str()); + f.printf("\tstruct %s {\n", name.c_str()); for (auto p : types) { - f.printf("\t%s %s;\n", p.second.to_string().c_str(), scope[p.first].c_str()); + f.printf("\t\t%s %s;\n", p.second.to_string().c_str(), scope(p.first).c_str()); } - f.printf("\n\ttemplate void dump(T &out) const {\n"); + f.printf("\n\t\ttemplate void visit(T &fn) {\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"); - - if (generate_methods) { - // Add size method - f.printf("\tint size() const {\n"); - f.printf("\t\treturn %d;\n", types.size()); - f.printf("\t}\n\n"); - - // Add get_input method - f.printf("\tstd::variant<%s> get_input(const int index) {\n", generate_variant_types().c_str()); - f.printf("\t\tswitch (index) {\n"); - int idx = 0; - for (auto p : types) { - f.printf("\t\t\tcase %d: return std::ref(%s);\n", idx, scope[p.first].c_str()); - idx++; - } - f.printf("\t\t\tdefault: throw std::out_of_range(\"Invalid input index\");\n"); - f.printf("\t\t}\n"); - f.printf("\t}\n"); + f.printf("\t\t\tfn(\"%s\", %s);\n", RTLIL::unescape_id(p.first).c_str(), scope(p.first).c_str()); } - - f.printf("};\n\n"); + f.printf("\t\t}\n"); + f.printf("\t};\n\n"); }; std::string operator[](IdString field) { - return scope[field]; - } - private: - std::string generate_variant_types() const { - std::set unique_types; - for (const auto& p : types) { - unique_types.insert("std::reference_wrapper<" + p.second.to_string() + ">"); - } - std::ostringstream oss; - for (auto it = unique_types.begin(); it != unique_types.end(); ++it) { - if (it != unique_types.begin()) { - oss << ", "; - } - oss << *it; - } - return oss.str(); + return scope(field); } }; @@ -255,58 +181,72 @@ template struct CxxPrintVisitor { std::string undriven(Node, int width) { return format("$const<%0>(0)", width); } }; -struct FunctionalCxxBackend : public Backend -{ - FunctionalCxxBackend() : Backend("functional_cxx", "convert design to C++ using the functional backend") {} +struct CxxModule { + FunctionalIR ir; + CxxStruct input_struct, output_struct, state_struct; + std::string module_name; - void help() override + explicit CxxModule(Module *module) : + ir(FunctionalIR::from_module(module)), + input_struct("Inputs"), + output_struct("Outputs"), + state_struct("State") { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - } - - void printCxx(std::ostream &stream, std::string, std::string const & name, Module *module) - { - auto ir = FunctionalIR::from_module(module); - CxxWriter f(stream); - - f.printf("#include \"sim.h\"\n"); - f.printf("#include \n"); - CxxStruct input_struct(name + "_Inputs", true); for (auto [name, sort] : ir.inputs()) input_struct.insert(name, sort); - CxxStruct output_struct(name + "_Outputs"); for (auto [name, sort] : ir.outputs()) output_struct.insert(name, sort); - CxxStruct state_struct(name + "_State"); for (auto [name, sort] : ir.state()) state_struct.insert(name, sort); - - dict node_names; - CxxScope locals; - + module_name = FunctionalTools::Scope(illegal_characters, reserved_keywords)(module->name); + } + void write_header(CxxWriter &f) { + f.printf("#include \"sim.h\"\n\n"); + } + void write_struct_def(CxxWriter &f) { + f.printf("struct %s {\n", module_name.c_str()); 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()); + f.printf("\tstatic void eval(Inputs const &, Outputs &, State const &, State &);\n"); + f.printf("};\n\n"); + } + void write_eval_def(CxxWriter &f) { + f.printf("void %s::eval(%s::Inputs const &input, %s::Outputs &output, %s::State const ¤t_state, %s::State &next_state)\n{\n", module_name.c_str(), module_name.c_str(), module_name.c_str(), module_name.c_str(), module_name.c_str()); + FunctionalTools::Scope locals(illegal_characters, reserved_keywords); locals.reserve("input"); locals.reserve("output"); locals.reserve("current_state"); locals.reserve("next_state"); - auto node_to_string = [&](FunctionalIR::Node n) { return node_names.at(n.id()); }; + auto node_name = [&](FunctionalIR::Node n) { return locals(n.id(), n.name()); }; for (auto node : ir) - { - 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()); - } + f.printf("\t%s %s = %s;\n", CxxType(node.sort()).to_string().c_str(), node_name(node).c_str(), node.visit(CxxPrintVisitor(node_name, 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()); + f.printf("\tnext_state.%s = %s;\n", state_struct[name].c_str(), node_name(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("\toutput.%s = %s;\n", output_struct[name].c_str(), node_name(ir.get_output_node(name)).c_str()); f.printf("}\n"); } +}; + +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"); + } + + void printCxx(std::ostream &stream, std::string, Module *module) + { + CxxWriter f(stream); + CxxModule mod(module); + mod.write_header(f); + mod.write_struct_def(f); + mod.write_eval_def(f); + } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { @@ -317,7 +257,7 @@ struct FunctionalCxxBackend : public Backend for (auto module : design->selected_modules()) { log("Dumping module `%s'.\n", module->name.c_str()); - printCxx(*f, filename, RTLIL::unescape_id(module->name), module); + printCxx(*f, filename, module); } } } FunctionalCxxBackend; diff --git a/kernel/functionalir.cc b/kernel/functionalir.cc index 27765ac26c5..6ae66d6802b 100644 --- a/kernel/functionalir.cc +++ b/kernel/functionalir.cc @@ -33,7 +33,7 @@ class CellSimplifier { } else { reduced_b_width = new_width; T lower_b = factory.slice(b, b_width, 0, new_width); - T overflow = factory.ugt(b, factory.constant(RTLIL::Const(y_width, b_width)), b_width); + T overflow = factory.unsigned_greater_than(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); } } @@ -102,17 +102,17 @@ class CellSimplifier { 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); + return extend(factory.equal(a, b, width), 1, y_width, false); else if(cellType.in({ID($ne), ID($nex)})) - return extend(factory.ne(a, b, width), 1, y_width, false); + return extend(factory.not_equal(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); + return extend(is_signed ? factory.signed_greater_than(b, a, width) : factory.unsigned_greater_than(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); + return extend(is_signed ? factory.signed_greater_equal(b, a, width) : factory.unsigned_greater_equal(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); + return extend(is_signed ? factory.signed_greater_than(a, b, width) : factory.unsigned_greater_than(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); + return extend(is_signed ? factory.signed_greater_equal(a, b, width) : factory.unsigned_greater_equal(a, b, width), 1, y_width, false); else log_abort(); }else if(cellType.in({ID($logic_or), ID($logic_and)})){ @@ -127,7 +127,7 @@ class CellSimplifier { 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); + return factory.unary_minus(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); @@ -161,7 +161,7 @@ class CellSimplifier { 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 shl = logical_shift_left(a, factory.unary_minus(b, b_width), width, b_width); T y = factory.mux(shr, shl, sign_b, width); return extend(y, width, y_width, false); } else { @@ -237,10 +237,10 @@ class FunctionalIRConstruction { if(results.size() == 0) return factory.undriven(0); T node = results[0]; - int size = results[0].size(); + int size = results[0].width(); for(size_t i = 1; i < results.size(); i++) { - node = factory.concat(node, size, results[i], results[i].size()); - size += results[i].size(); + node = factory.concat(node, size, results[i], results[i].width()); + size += results[i].width(); } return node; } @@ -405,6 +405,13 @@ void FunctionalIR::topological_sort() { if(scc) log_error("combinational loops, aborting\n"); } +IdString merge_name(IdString a, IdString b) { + if(a[0] == '$' && b[0] == '\\') + return b; + else + return a; +} + void FunctionalIR::forward_buf() { std::vector perm, alias; perm.clear(); @@ -416,10 +423,15 @@ void FunctionalIR::forward_buf() { { 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; - } + if(node.has_sparse_attr()) { + if(target_node.has_sparse_attr()) { + IdString id = merge_name(node.sparse_attr(), target_node.sparse_attr()); + target_node.sparse_attr() = id; + } else { + IdString id = node.sparse_attr(); + target_node.sparse_attr() = id; + } + } alias.push_back(target_index); } else diff --git a/kernel/functionalir.h b/kernel/functionalir.h index 7eca9d87f83..f49a659f37b 100644 --- a/kernel/functionalir.h +++ b/kernel/functionalir.h @@ -29,6 +29,58 @@ USING_YOSYS_NAMESPACE YOSYS_NAMESPACE_BEGIN +namespace FunctionalTools { + class Scope { + const char *_illegal_characters; + pool _used_names; + dict _by_id; + dict _by_name; + std::string allocate_name(IdString suggestion) { + std::string str = RTLIL::unescape_id(suggestion); + for(size_t i = 0; i < str.size(); i++) + if(strchr(_illegal_characters, str[i])) + str[i] = '_'; + if(_used_names.count(str) == 0) { + _used_names.insert(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); + return suffixed; + } + } + } + public: + Scope(const char *illegal_characters = "", const char **keywords = nullptr) { + _illegal_characters = illegal_characters; + if(keywords != nullptr) + for(const char **p = keywords; *p != nullptr; p++) + reserve(*p); + } + void reserve(std::string name) { + _used_names.insert(std::move(name)); + } + std::string operator()(int id, IdString suggestion) { + auto it = _by_id.find(id); + if(it != _by_id.end()) + return it->second; + std::string str = allocate_name(suggestion); + _by_id.insert({id, str}); + return str; + } + std::string operator()(IdString idstring) { + auto it = _by_name.find(idstring); + if(it != _by_name.end()) + return it->second; + std::string str = allocate_name(idstring); + _by_name.insert({idstring, str}); + return str; + } + }; +} + class FunctionalIR { enum class Fn { invalid, @@ -133,7 +185,7 @@ class FunctionalIR { friend class Factory; friend class FunctionalIR; Graph::Ref _ref; - Node(Graph::Ref ref) : _ref(ref) { } + explicit Node(Graph::Ref ref) : _ref(ref) { } operator Graph::Ref() { return _ref; } template struct PrintVisitor { NodePrinter np; @@ -225,7 +277,6 @@ class FunctionalIR { { return visit(PrintVisitor(np)); } - /* TODO: delete */ int size() const { return sort().width(); } }; class Factory { FunctionalIR &_ir; @@ -235,7 +286,7 @@ class FunctionalIR { Graph::Ref ref = _ir._graph.add(std::move(fn), {std::move(sort)}); for (auto arg : args) ref.append_arg(Graph::Ref(arg)); - return ref; + return Node(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()); } @@ -341,39 +392,30 @@ class FunctionalIR { 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]; } + Node operator[](int i) { return Node(_graph[i]); } void topological_sort(); void forward_buf(); dict inputs() const { return _inputs; } dict outputs() const { return _outputs; } dict 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}); } + Node get_output_node(IdString name) { return Node(_graph({name, false})); } + Node get_state_next_node(IdString name) { return Node(_graph({name, true})); } class Iterator { friend class FunctionalIR; - FunctionalIR &_ir; + FunctionalIR *_ir; int _index; - Iterator(FunctionalIR &ir, int index) : _ir(ir), _index(index) {} + Iterator(FunctionalIR *ir, int index) : _ir(ir), _index(index) {} public: - Node operator*() { return _ir._graph[_index]; } + Node operator*() { return Node(_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()); } + Iterator begin() { return Iterator(this, 0); } + Iterator end() { return Iterator(this, _graph.size()); } }; YOSYS_NAMESPACE_END