aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwhitequark <whitequark@whitequark.org>2020-04-14 12:07:58 +0000
committerwhitequark <whitequark@whitequark.org>2020-04-14 12:07:58 +0000
commit102fb5424fa1e7be59786925bd3a10f5d46fa904 (patch)
tree8d1d8183a7720d78173987df924d9348bea34b15
parentf44b287f8e36684607447beb05556f1a5a34d625 (diff)
downloadyosys-102fb5424fa1e7be59786925bd3a10f5d46fa904.tar.gz
yosys-102fb5424fa1e7be59786925bd3a10f5d46fa904.tar.bz2
yosys-102fb5424fa1e7be59786925bd3a10f5d46fa904.zip
write_cxxrtl: enable separate compilation.
This commit makes it possible to use several cxxrtl-generated files in one application, as well as compiling cxxrtl-generated code as a separate compilation unit.
-rw-r--r--backends/cxxrtl/cxxrtl.cc90
1 files changed, 81 insertions, 9 deletions
diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl.cc
index 465882858..19c5d903a 100644
--- a/backends/cxxrtl/cxxrtl.cc
+++ b/backends/cxxrtl/cxxrtl.cc
@@ -357,13 +357,19 @@ struct FlowGraph {
};
struct CxxrtlWorker {
+ bool split_intf = false;
+ std::string intf_filename;
+ std::string design_ns = "cxxrtl_design";
+ std::ostream *impl_f = nullptr;
+ std::ostream *intf_f = nullptr;
+
bool elide_internal = false;
bool elide_public = false;
bool localize_internal = false;
bool localize_public = false;
bool run_splitnets = false;
- std::ostream &f;
+ std::ostringstream f;
std::string indent;
int temporary = 0;
@@ -377,8 +383,6 @@ struct CxxrtlWorker {
dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule;
pool<const RTLIL::Wire*> localized_wires;
- CxxrtlWorker(std::ostream &f) : f(f) {}
-
void inc_indent() {
indent += "\t";
}
@@ -1191,7 +1195,7 @@ struct CxxrtlWorker {
}
}
- void dump_module(RTLIL::Module *module)
+ void dump_module_intf(RTLIL::Module *module)
{
dump_attrs(module);
f << "struct " << mangle(module) << " : public module {\n";
@@ -1220,7 +1224,10 @@ struct CxxrtlWorker {
dec_indent();
f << "}; // struct " << mangle(module) << "\n";
f << "\n";
+ }
+ void dump_module_impl(RTLIL::Module *module)
+ {
f << "void " << mangle(module) << "::eval() {\n";
inc_indent();
for (auto wire : module->wires())
@@ -1323,18 +1330,49 @@ struct CxxrtlWorker {
}
log_assert(topo_design.sort());
- f << "#include <cxxrtl.h>\n";
+ if (split_intf) {
+ // The only thing more depraved than include guards, is mangling filenames to turn them into include guards.
+ std::string include_guard = design_ns + "_header";
+ std::transform(include_guard.begin(), include_guard.end(), include_guard.begin(), ::toupper);
+
+ f << "#ifndef " << include_guard << "\n";
+ f << "#define " << include_guard << "\n";
+ f << "\n";
+ f << "#include <backends/cxxrtl/cxxrtl.h>\n";
+ f << "\n";
+ f << "using namespace cxxrtl;\n";
+ f << "\n";
+ f << "namespace " << design_ns << " {\n";
+ f << "\n";
+ for (auto module : topo_design.sorted) {
+ if (!design->selected_module(module))
+ continue;
+ dump_module_intf(module);
+ }
+ f << "} // namespace " << design_ns << "\n";
+ f << "\n";
+ f << "#endif\n";
+ *intf_f << f.str(); f.str("");
+ }
+
+ if (split_intf)
+ f << "#include \"" << intf_filename << "\"\n";
+ else
+ f << "#include <backends/cxxrtl/cxxrtl.h>\n";
f << "\n";
f << "using namespace cxxrtl_yosys;\n";
f << "\n";
- f << "namespace cxxrtl_design {\n";
+ f << "namespace " << design_ns << " {\n";
f << "\n";
for (auto module : topo_design.sorted) {
if (!design->selected_module(module))
continue;
- dump_module(module);
+ if (!split_intf)
+ dump_module_intf(module);
+ dump_module_impl(module);
}
- f << "} // namespace cxxrtl_design\n";
+ f << "} // namespace " << design_ns << "\n";
+ *impl_f << f.str(); f.str("");
}
// Edge-type sync rules require us to emit edge detectors, which require coordination between
@@ -1618,6 +1656,16 @@ struct CxxrtlBackend : public Backend {
log("\n");
log("The following options are supported by this backend:\n");
log("\n");
+ log(" -header\n");
+ log(" generate separate interface (.h) and implementation (.cc) files.\n");
+ log(" if specified, the backend must be called with a filename, and filename\n");
+ log(" of the interface is derived from filename of the implementation.\n");
+ log(" otherwise, interface and implementation are generated together.\n");
+ log("\n");
+ log(" -namespace <ns-name>\n");
+ log(" place the generated code into namespace <ns-name>. if not specified,\n");
+ log(" \"cxxrtl_design\" is used.\n");
+ log("\n");
log(" -O <level>\n");
log(" set the optimization level. the default is -O%d. higher optimization\n", DEFAULT_OPT_LEVEL);
log(" levels dramatically decrease compile and run time, and highest level\n");
@@ -1645,6 +1693,7 @@ struct CxxrtlBackend : public Backend {
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
int opt_level = DEFAULT_OPT_LEVEL;
+ CxxrtlWorker worker;
log_header(design, "Executing CXXRTL backend.\n");
@@ -1659,11 +1708,18 @@ struct CxxrtlBackend : public Backend {
opt_level = std::stoi(args[argidx].substr(2));
continue;
}
+ if (args[argidx] == "-header") {
+ worker.split_intf = true;
+ continue;
+ }
+ if (args[argidx] == "-namespace" && argidx+1 < args.size()) {
+ worker.design_ns = args[++argidx];
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
- CxxrtlWorker worker(*f);
switch (opt_level) {
case 5:
worker.run_splitnets = true;
@@ -1680,6 +1736,22 @@ struct CxxrtlBackend : public Backend {
default:
log_cmd_error("Invalid optimization level %d.\n", opt_level);
}
+
+ std::ofstream intf_f;
+ if (worker.split_intf) {
+ if (filename == "<stdout>")
+ log_cmd_error("Option -header must be used with a filename.\n");
+
+ worker.intf_filename = filename.substr(0, filename.rfind('.')) + ".h";
+ intf_f.open(worker.intf_filename, std::ofstream::trunc);
+ if (intf_f.fail())
+ log_cmd_error("Can't open file `%s' for writing: %s\n",
+ worker.intf_filename.c_str(), strerror(errno));
+
+ worker.intf_f = &intf_f;
+ }
+ worker.impl_f = f;
+
worker.prepare_design(design);
worker.dump_design(design);
}