diff --git a/default.nix b/default.nix index 0edaf2c..c5c4305 100644 --- a/default.nix +++ b/default.nix @@ -1,6 +1,8 @@ { pkgs ? import {}}: rec { - nmigen = pkgs.callPackage ./nmigen.nix {}; + yosys = pkgs.callPackage ./yosys.nix {}; + symbiyosys = pkgs.symbiyosys.override { inherit yosys; }; + nmigen = pkgs.callPackage ./nmigen.nix { inherit yosys; }; jtagtap = pkgs.callPackage ./jtagtap.nix { inherit nmigen; }; minerva = pkgs.callPackage ./minerva.nix { inherit nmigen; inherit jtagtap; }; heavycomps = pkgs.callPackage ./heavycomps.nix { inherit nmigen; }; diff --git a/yosys.nix b/yosys.nix new file mode 100644 index 0000000..ccde0ab --- /dev/null +++ b/yosys.nix @@ -0,0 +1,78 @@ +{ stdenv, fetchFromGitHub +, pkgconfig, bison, flex +, tcl, readline, libffi, python3 +, protobuf +}: + +with builtins; + +stdenv.mkDerivation rec { + name = "yosys-${version}"; + version = "2019.03.25hx"; + + srcs = [ + (fetchFromGitHub { + owner = "yosyshq"; + repo = "yosys"; + rev = "ccfa2fe01cffcc4d23bc989e558bd33addfea58e"; + sha256 = "05raqky4l2na8nyim51g8fzv49mg5f3p64lpdr7slxg74s270zry"; + name = "yosys"; + }) + + # NOTE: the version of abc used here is synchronized with + # the one in the yosys Makefile of the version above; + # keep them the same for quality purposes. + (fetchFromGitHub { + owner = "berkeley-abc"; + repo = "abc"; + rev = "2ddc57d8760d94e86699be39a628178cff8154f8"; + sha256 = "0da7nnnnl9cq2r7s301xgdc8nlr6hqmqpvd9zn4b58m125sp0scl"; + name = "yosys-abc"; + }) + ]; + sourceRoot = "yosys"; + + enableParallelBuilding = true; + nativeBuildInputs = [ pkgconfig ]; + buildInputs = [ tcl readline libffi python3 bison flex protobuf ]; + + makeFlags = [ "ENABLE_PROTOBUF=1" ]; + + patchPhase = '' + patch -p1 < ${./yosys_726.patch} + substituteInPlace ../yosys-abc/Makefile \ + --replace 'CC := gcc' "" + substituteInPlace ./Makefile \ + --replace 'CXX = clang' "" \ + --replace 'ABCMKARGS = CC="$(CXX)"' 'ABCMKARGS =' \ + --replace 'echo UNKNOWN' 'echo ${substring 0 10 (elemAt srcs 0).rev}' + ''; + + preBuild = '' + chmod -R u+w ../yosys-abc + ln -s ../yosys-abc abc + make config-${if stdenv.cc.isClang or false then "clang" else "gcc"} + echo 'ABCREV := default' >> Makefile.conf + makeFlags="PREFIX=$out $makeFlags" + + # we have to do this ourselves for some reason... + (cd misc && ${protobuf}/bin/protoc --cpp_out ../backends/protobuf/ ./yosys.proto) + ''; + + meta = { + description = "Framework for RTL synthesis tools"; + longDescription = '' + Yosys is a framework for RTL synthesis tools. It currently has + extensive Verilog-2005 support and provides a basic set of + synthesis algorithms for various application domains. + Yosys can be adapted to perform any synthesis job by combining + the existing passes (algorithms) using synthesis scripts and + adding additional passes as needed by extending the yosys C++ + code base. + ''; + homepage = http://www.clifford.at/yosys/; + license = stdenv.lib.licenses.isc; + maintainers = with stdenv.lib.maintainers; [ shell thoughtpolice ]; + platforms = stdenv.lib.platforms.unix; + }; +} diff --git a/yosys_726.patch b/yosys_726.patch new file mode 100644 index 0000000..acb79cd --- /dev/null +++ b/yosys_726.patch @@ -0,0 +1,399 @@ +diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc +index 83d83f48..ef6f102c 100644 +--- a/backends/verilog/verilog_backend.cc ++++ b/backends/verilog/verilog_backend.cc +@@ -25,6 +25,7 @@ + #include "kernel/celltypes.h" + #include "kernel/log.h" + #include "kernel/sigtools.h" ++#include "kernel/modtools.h" + #include + #include + #include +@@ -33,15 +34,17 @@ + USING_YOSYS_NAMESPACE + PRIVATE_NAMESPACE_BEGIN + +-bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal, siminit; ++bool verbose, norename, noattr, attr2comment, noexpr, nodec, noinline, nohex, nostr, defparam, decimal, siminit; + int auto_name_counter, auto_name_offset, auto_name_digits; + std::map auto_name_map; + std::set reg_wires, reg_ct; ++std::map proc_consumed_wires; + std::string auto_prefix; + + RTLIL::Module *active_module; + dict active_initdata; + SigMap active_sigmap; ++ModIndex active_modindex; + + void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) + { +@@ -183,6 +186,72 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) + return true; + } + ++bool can_inline_cell_expr(RTLIL::Cell *cell) ++{ ++ static pool inlinable_cells = { ++ "$not", "$pos", "$neg", ++ "$and", "$or", "$xor", "$xnor", ++ "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", ++ "$shl", "$shr", "$sshl", "$sshr", ++ "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", ++ "$add", "$sub", "$mul", "$div", "$mod", "$pow", ++ "$logic_not", "$logic_and", "$logic_or", ++ "$mux" ++ }; ++ ++ if (noinline) ++ return false; ++ ++ if (!inlinable_cells.count(cell->type)) ++ return false; ++ ++ const RTLIL::SigSpec &output = cell->getPort("\\Y"); ++ if (!output.is_wire()) ++ return false; ++ ++ RTLIL::Wire *output_wire = output.as_wire(); ++ if (output_wire->port_id || (!noattr && output_wire->attributes.size())) ++ return false; ++ ++ pool ports = active_modindex.query_ports(output[0]); ++ if (proc_consumed_wires[output_wire] == 1 && ports.size() == 1) ++ return true; ++ ++ if (proc_consumed_wires[output_wire] == 0 && ports.size() == 2) ++ { ++ auto port1 = ports.pop(); ++ auto port2 = ports.pop(); ++ return port1.cell->getPort(port1.port).size() == ++ port2.cell->getPort(port2.port).size(); ++ } ++ ++ return false; ++} ++ ++bool can_inline_wire(RTLIL::Wire *wire) ++{ ++ if (noinline) ++ return false; ++ ++ RTLIL::SigSpec wire_spec = RTLIL::SigSpec(wire); ++ if (wire_spec.empty()) ++ return false; ++ ++ pool ports = active_modindex.query_ports(wire_spec[0]); ++ if (ports.size() > 2) ++ return false; ++ ++ for (auto &port_info : ports) ++ { ++ if (port_info.cell->name[0] == '$' && port_info.port == "\\Y") ++ { ++ if (can_inline_cell_expr(port_info.cell)) ++ return true; ++ } ++ } ++ return false; ++} ++ + void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false) + { + if (width < 0) +@@ -313,13 +382,29 @@ void dump_reg_init(std::ostream &f, SigSpec sig) + } + } + ++bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell, bool do_inline); ++ + void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false) + { + if (chunk.wire == NULL) { + dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal); + } else { + if (chunk.width == chunk.wire->width && chunk.offset == 0) { +- f << stringf("%s", id(chunk.wire->name).c_str()); ++ if (can_inline_wire(chunk.wire)) ++ { ++ f << "("; ++ pool ports = active_modindex.query_ports(SigSpec(chunk)[0]); ++ for (auto &port_info : ports) ++ { ++ if (port_info.port == "\\Y") ++ dump_cell_expr(f, "", port_info.cell, true); ++ } ++ f << ")"; ++ } ++ else ++ { ++ f << stringf("%s", id(chunk.wire->name).c_str()); ++ } + } else if (chunk.width == 1) { + if (chunk.wire->upto) + f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); +@@ -372,6 +457,9 @@ void dump_attributes(std::ostream &f, std::string indent, dictattributes); + #if 0 + if (wire->port_input && !wire->port_output) +@@ -464,30 +552,54 @@ no_special_reg_name: + } + } + +-void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) ++void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op, bool do_inline) + { +- f << stringf("%s" "assign ", indent.c_str()); +- dump_sigspec(f, cell->getPort("\\Y")); +- f << stringf(" = %s ", op.c_str()); ++ if (!do_inline) ++ { ++ f << stringf("%s" "assign ", indent.c_str()); ++ dump_sigspec(f, cell->getPort("\\Y")); ++ f << stringf(" = "); ++ } ++ f << stringf("%s ", op.c_str()); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", true); +- f << stringf(";\n"); ++ if (!do_inline) ++ { ++ f << stringf(";\n"); ++ } + } + +-void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) ++void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op, bool do_inline) + { +- f << stringf("%s" "assign ", indent.c_str()); +- dump_sigspec(f, cell->getPort("\\Y")); +- f << stringf(" = "); ++ if (!do_inline) ++ { ++ f << stringf("%s" "assign ", indent.c_str()); ++ dump_sigspec(f, cell->getPort("\\Y")); ++ f << stringf(" = "); ++ } ++ else ++ { ++ f << stringf("("); ++ } + dump_cell_expr_port(f, cell, "A", true); + f << stringf(" %s ", op.c_str()); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "B", true); +- f << stringf(";\n"); ++ if (!do_inline) ++ { ++ f << stringf(";\n"); ++ } ++ else ++ { ++ f << stringf(")"); ++ } + } + +-bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) ++bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell, bool do_inline) + { ++ if (can_inline_cell_expr(cell) && !do_inline) ++ return true; ++ + if (cell->type == "$_NOT_") { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); +@@ -658,9 +770,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) + } + + #define HANDLE_UNIOP(_type, _operator) \ +- if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; } ++ if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator, do_inline); return true; } + #define HANDLE_BINOP(_type, _operator) \ +- if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; } ++ if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator, do_inline); return true; } + + HANDLE_UNIOP("$not", "~") + HANDLE_UNIOP("$pos", "+") +@@ -756,16 +868,30 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) + + if (cell->type == "$mux") + { +- f << stringf("%s" "assign ", indent.c_str()); +- dump_sigspec(f, cell->getPort("\\Y")); +- f << stringf(" = "); ++ if (!do_inline) ++ { ++ f << stringf("%s" "assign ", indent.c_str()); ++ dump_sigspec(f, cell->getPort("\\Y")); ++ f << stringf(" = "); ++ } ++ else ++ { ++ f << stringf("("); ++ } + dump_sigspec(f, cell->getPort("\\S")); + f << stringf(" ? "); + dump_attributes(f, "", cell->attributes, ' '); + dump_sigspec(f, cell->getPort("\\B")); + f << stringf(" : "); + dump_sigspec(f, cell->getPort("\\A")); +- f << stringf(";\n"); ++ if (!do_inline) ++ { ++ f << stringf(";\n"); ++ } ++ else ++ { ++ f << stringf(")"); ++ } + return true; + } + +@@ -1243,7 +1369,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) + void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) + { + if (cell->type[0] == '$' && !noexpr) { +- if (dump_cell_expr(f, indent, cell)) ++ if (dump_cell_expr(f, indent, cell, false)) + return; + } + +@@ -1400,25 +1526,42 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw + void case_body_find_regs(RTLIL::CaseRule *cs) + { + for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) +- for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) +- case_body_find_regs(*it2); ++ { ++ for (auto &c : (*it)->signal.chunks()) ++ if (c.wire != NULL && c.offset == 0 && c.width == c.wire->width) ++ proc_consumed_wires[c.wire]++; ++ for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) ++ case_body_find_regs(*it2); ++ } + +- for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { ++ for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) ++ { + for (auto &c : it->first.chunks()) ++ { + if (c.wire != NULL) + reg_wires.insert(c.wire->name); ++ for (auto &c : it->second.chunks()) ++ if (c.wire != NULL && c.offset == 0 && c.width == c.wire->width) ++ proc_consumed_wires[c.wire]++; ++ } + } + } + +-void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false) ++void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool sweep = false) + { +- if (find_regs) { ++ if (sweep) { + case_body_find_regs(&proc->root_case); + for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) +- for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) { +- for (auto &c : it2->first.chunks()) +- if (c.wire != NULL) +- reg_wires.insert(c.wire->name); ++ { ++ for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) ++ { ++ for (auto &c : it2->first.chunks()) ++ if (c.wire != NULL) ++ reg_wires.insert(c.wire->name); ++ for (auto &c : it2->second.chunks()) ++ if (c.wire != NULL && c.offset == 0 && c.width == c.wire->width) ++ proc_consumed_wires[c.wire]++; ++ } + } + return; + } +@@ -1487,9 +1630,11 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo + void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) + { + reg_wires.clear(); ++ proc_consumed_wires.clear(); + reset_auto_counter(module); + active_module = module; + active_sigmap.set(module); ++ active_modindex = ModIndex(module); + active_initdata.clear(); + + for (auto wire : module->wires()) +@@ -1575,6 +1720,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) + + f << stringf("%s" "endmodule\n", indent.c_str()); + active_module = NULL; ++ active_modindex = ModIndex(); + active_sigmap.clear(); + active_initdata.clear(); + } +@@ -1605,7 +1751,12 @@ struct VerilogBackend : public Backend { + log("\n"); + log(" -noexpr\n"); + log(" without this option all internal cells are converted to Verilog\n"); +- log(" expressions.\n"); ++ log(" expressions. implies -noinline.\n"); ++ log("\n"); ++ log(" -noinline\n"); ++ log(" without this option all internal cells driving a wire connected to\n"); ++ log(" a single internal cell are inlined into that cell and the wire is\n"); ++ log(" omitted.\n"); + log("\n"); + log(" -siminit\n"); + log(" add initial statements with hierarchical refs to initialize FFs when\n"); +@@ -1662,6 +1813,7 @@ struct VerilogBackend : public Backend { + noattr = false; + attr2comment = false; + noexpr = false; ++ noinline = false; + nodec = false; + nohex = false; + nostr = false; +@@ -1723,7 +1875,11 @@ struct VerilogBackend : public Backend { + continue; + } + if (arg == "-noexpr") { +- noexpr = true; ++ noexpr = noinline = true; ++ continue; ++ } ++ if (arg == "-noinline") { ++ noinline = true; + continue; + } + if (arg == "-nodec") { +diff --git a/kernel/modtools.h b/kernel/modtools.h +index 409562eb..b198709d 100644 +--- a/kernel/modtools.h ++++ b/kernel/modtools.h +@@ -226,6 +226,10 @@ struct ModIndex : public RTLIL::Monitor + auto_reload_module = true; + } + ++ ModIndex() : module(NULL) ++ { ++ } ++ + ModIndex(RTLIL::Module *_m) : sigmap(_m), module(_m) + { + auto_reload_counter = 0; +@@ -235,7 +239,8 @@ struct ModIndex : public RTLIL::Monitor + + ~ModIndex() + { +- module->monitors.erase(this); ++ if (module) ++ module->monitors.erase(this); + } + + SigBitInfo *query(RTLIL::SigBit bit)