From 583eb1addb82b033647aaee150ee02acd1d9e9fd Mon Sep 17 00:00:00 2001 From: George Rennie Date: Fri, 15 Nov 2024 15:15:42 +0100 Subject: [PATCH 001/181] pyosys: dereference cpp objects when constructing a tuple * This fixes a bug where when converting a tuple from python to c++, get_cpp_obj() was called (returning a pointer) without dereferencing the pointer to get the underlying object --- misc/py_wrap_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index b0ac1e82e..6715951fc 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -515,11 +515,11 @@ class TupleTranslator(PythonDictTranslator): if types[0].name.split(" ")[-1] in primitive_types: text += varname + "___tmp_0, " else: - text += varname + "___tmp_0.get_cpp_obj(), " + text += "*" + varname + "___tmp_0.get_cpp_obj(), " if types[1].name.split(" ")[-1] in primitive_types: text += varname + "___tmp_1);" else: - text += varname + "___tmp_1.get_cpp_obj());" + text += "*" + varname + "___tmp_1.get_cpp_obj());" return text #Generate c++ code to translate to a boost::python::tuple From 0a1c664f02cabd9d057060aa6fb3bf4602334843 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 25 Mar 2025 12:15:54 +1300 Subject: [PATCH 002/181] simplify: Skip AST_PRIMITIVE in AST_CELLARRAY Otherwise the `AST_PRIMITIVE` simplifies to the corresponding function and is no longer caught by the check for `AST_PRIMITIVE`s, raising an assertion error instead of an input error. Add bug4785.ys to tests/verilog to demonstrate. --- frontends/ast/simplify.cc | 3 ++- tests/verilog/bug4785.ys | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/verilog/bug4785.ys diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index d35756d4e..9c23c769f 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1758,7 +1758,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin break; if (type == AST_GENBLOCK) break; - if (type == AST_CELLARRAY && children[i]->type == AST_CELL) + if (type == AST_CELLARRAY && (children[i]->type == AST_CELL || children[i]->type == AST_PRIMITIVE)) continue; if (type == AST_BLOCK && !str.empty()) break; @@ -2741,6 +2741,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (new_cell->type == AST_PRIMITIVE) { input_error("Cell arrays of primitives are currently not supported.\n"); } else { + this->dumpAst(NULL, " "); log_assert(new_cell->children.at(0)->type == AST_CELLTYPE); new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str.c_str()); } diff --git a/tests/verilog/bug4785.ys b/tests/verilog/bug4785.ys new file mode 100644 index 000000000..174765983 --- /dev/null +++ b/tests/verilog/bug4785.ys @@ -0,0 +1,9 @@ +logger -expect error "Cell arrays of primitives are currently not supported" 1 +read_verilog < Date: Wed, 21 May 2025 16:09:39 +1200 Subject: [PATCH 003/181] functional.cc: Maintain port ordering Based on #4753. --- kernel/functional.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/functional.cc b/kernel/functional.cc index adf7bfb0c..178995de8 100644 --- a/kernel/functional.cc +++ b/kernel/functional.cc @@ -518,10 +518,12 @@ public: if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover), ID($check))) queue.emplace_back(cell); } - for (auto wire : module->wires()) { - if (wire->port_input) + for (auto port : module->ports) { + auto *wire = module->wire(port); + if (wire && wire->port_input) { factory.add_input(wire->name, ID($input), Sort(wire->width)); - if (wire->port_output) { + } + if (wire && wire->port_output) { auto &output = factory.add_output(wire->name, ID($output), Sort(wire->width)); output.set_value(enqueue(DriveChunk(DriveChunkWire(wire, 0, wire->width)))); } From 847558547b04c309a6ebe350d2ae2bd4f01da60a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Wed, 21 May 2025 16:21:27 +1200 Subject: [PATCH 004/181] functional.cc: Reverse port iteration --- kernel/functional.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/functional.cc b/kernel/functional.cc index 178995de8..4983703e3 100644 --- a/kernel/functional.cc +++ b/kernel/functional.cc @@ -518,8 +518,8 @@ public: if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover), ID($check))) queue.emplace_back(cell); } - for (auto port : module->ports) { - auto *wire = module->wire(port); + for (auto riter = module->ports.rbegin(); riter != module->ports.rend(); ++riter) { + auto *wire = module->wire(*riter); if (wire && wire->port_input) { factory.add_input(wire->name, ID($input), Sort(wire->width)); } From 6e3922e1c76a8ea2c689daab4d04198af92a387f Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 31 May 2025 09:57:43 +1200 Subject: [PATCH 005/181] functional.cc: Explicit unsorted-pool-as-LIFO --- kernel/functional.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/functional.cc b/kernel/functional.cc index 4983703e3..211527926 100644 --- a/kernel/functional.cc +++ b/kernel/functional.cc @@ -518,6 +518,7 @@ public: if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover), ID($check))) queue.emplace_back(cell); } + // we are relying here on unsorted pools iterating last-in-first-out for (auto riter = module->ports.rbegin(); riter != module->ports.rend(); ++riter) { auto *wire = module->wire(*riter); if (wire && wire->port_input) { From 45131f44251b151381f4bb38eed98d8a758be741 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 14 Jun 2025 10:54:06 +1200 Subject: [PATCH 006/181] chformal: Add -assert2cover option Also add to chformal tests. --- passes/cmds/chformal.cc | 13 +++++++++++++ tests/various/chformal_check.ys | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index e027103bb..572ed2153 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -115,6 +115,7 @@ struct ChformalPass : public Pass { log("\n"); #endif log(" -assert2assume\n"); + log(" -assert2cover\n"); log(" -assume2assert\n"); log(" -live2fair\n"); log(" -fair2live\n"); @@ -129,6 +130,7 @@ struct ChformalPass : public Pass { void execute(std::vector args, RTLIL::Design *design) override { bool assert2assume = false; + bool assert2cover = false; bool assume2assert = false; bool live2fair = false; bool fair2live = false; @@ -187,6 +189,11 @@ struct ChformalPass : public Pass { mode = 'c'; continue; } + if ((mode == 0 || mode == 'c') && args[argidx] == "-assert2cover") { + assert2cover = true; + mode = 'c'; + continue; + } if ((mode == 0 || mode == 'c') && args[argidx] == "-assume2assert") { assume2assert = true; mode = 'c'; @@ -218,6 +225,10 @@ struct ChformalPass : public Pass { constr_types.insert(ID($cover)); } + if (assert2assume && assert2cover) { + log_cmd_error("Cannot use both -assert2assume and -assert2cover.\n"); + } + if (mode == 0) log_cmd_error("Mode option is missing.\n"); @@ -381,6 +392,8 @@ struct ChformalPass : public Pass { IdString flavor = formal_flavor(cell); if (assert2assume && flavor == ID($assert)) set_formal_flavor(cell, ID($assume)); + if (assert2cover && flavor == ID($assert)) + set_formal_flavor(cell, ID($cover)); else if (assume2assert && flavor == ID($assume)) set_formal_flavor(cell, ID($assert)); else if (live2fair && flavor == ID($live)) diff --git a/tests/various/chformal_check.ys b/tests/various/chformal_check.ys index 951543fa5..2b2d292e4 100644 --- a/tests/various/chformal_check.ys +++ b/tests/various/chformal_check.ys @@ -36,6 +36,12 @@ select -assert-count 1 t:$assert design -load prep +chformal -assert2cover + +select -assert-count 1 t:$check r:FLAVOR=cover %i + +design -load prep + chformal -assert2assume async2sync chformal -lower @@ -66,3 +72,8 @@ design -copy-from gate -as gate top miter -equiv -flatten -make_assert gold gate miter sat -verify -prove-asserts -tempinduct miter + +design -load prep + +logger -expect error "Cannot use both" 1 +chformal -assert2assume -assert2cover From fa68299b251127eb8a7375790e8a2a5294a914d1 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:06:38 +1200 Subject: [PATCH 007/181] tests/verific: Add chformal tests --- tests/verific/chformal.ys | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/verific/chformal.ys diff --git a/tests/verific/chformal.ys b/tests/verific/chformal.ys new file mode 100644 index 000000000..0734f7bb7 --- /dev/null +++ b/tests/verific/chformal.ys @@ -0,0 +1,74 @@ +verific -formal < Date: Tue, 24 Jun 2025 12:01:12 +0200 Subject: [PATCH 008/181] dfflibmap: propagate negated next_state to output correctly --- passes/techmap/dfflibmap.cc | 18 +++- tests/techmap/dfflibmap_dffsr_not_next.lib | 28 +++++ tests/techmap/dfflibmap_formal.ys | 116 +++++++++++++++++++++ 3 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 tests/techmap/dfflibmap_dffsr_not_next.lib create mode 100644 tests/techmap/dfflibmap_formal.ys diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index d00fee83b..f0b0ef20b 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -392,9 +392,21 @@ static void find_cell_sr(std::vector cells, IdString cell_ty continue; if (!parse_next_state(cell, ff->find("next_state"), cell_next_pin, cell_next_pol, cell_enable_pin, cell_enable_pol)) continue; - if (!parse_pin(cell, ff->find("preset"), cell_set_pin, cell_set_pol) || cell_set_pol != setpol) + + if (!parse_pin(cell, ff->find("preset"), cell_set_pin, cell_set_pol)) continue; - if (!parse_pin(cell, ff->find("clear"), cell_clr_pin, cell_clr_pol) || cell_clr_pol != clrpol) + if (!parse_pin(cell, ff->find("clear"), cell_clr_pin, cell_clr_pol)) + continue; + if (!cell_next_pol) { + // next_state is negated + // we later propagate this inversion to the output, + // which requires the swap of set and reset + std::swap(cell_set_pin, cell_clr_pin); + std::swap(cell_set_pol, cell_clr_pol); + } + if (cell_set_pol != setpol) + continue; + if (cell_clr_pol != clrpol) continue; std::map this_cell_ports; @@ -432,12 +444,14 @@ static void find_cell_sr(std::vector cells, IdString cell_ty for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) value.erase(pos, 1); if (value == ff->args[0]) { + // next_state negation propagated to output this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; if (cell_next_pol) found_noninv_output = true; found_output = true; } else if (value == ff->args[1]) { + // next_state negation propagated to output this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; if (!cell_next_pol) found_noninv_output = true; diff --git a/tests/techmap/dfflibmap_dffsr_not_next.lib b/tests/techmap/dfflibmap_dffsr_not_next.lib new file mode 100644 index 000000000..579dedb10 --- /dev/null +++ b/tests/techmap/dfflibmap_dffsr_not_next.lib @@ -0,0 +1,28 @@ +library (test_not_next) { + cell (dffsr_not_next) { + area : 1.0; + pin (Q) { + direction : output; + function : "STATE"; + } + pin (CLK) { + clock : true; + direction : input; + } + pin (D) { + direction : input; + } + pin (RN) { + direction : input; + } + pin (SN) { + direction : input; + } + ff (STATE,STATEN) { + clear : "!SN"; + clocked_on : "CLK"; + next_state : "!D"; + preset : "!RN"; + } + } +} \ No newline at end of file diff --git a/tests/techmap/dfflibmap_formal.ys b/tests/techmap/dfflibmap_formal.ys new file mode 100644 index 000000000..11c90ea6c --- /dev/null +++ b/tests/techmap/dfflibmap_formal.ys @@ -0,0 +1,116 @@ +################################################################## + +read_verilog -sv -icells < Date: Tue, 24 Jun 2025 12:31:30 +0200 Subject: [PATCH 009/181] fixup! dfflibmap: propagate negated next_state to output correctly --- tests/techmap/dfflibmap_dffr_not_next.lib | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/techmap/dfflibmap_dffr_not_next.lib diff --git a/tests/techmap/dfflibmap_dffr_not_next.lib b/tests/techmap/dfflibmap_dffr_not_next.lib new file mode 100644 index 000000000..74004bea1 --- /dev/null +++ b/tests/techmap/dfflibmap_dffr_not_next.lib @@ -0,0 +1,33 @@ +library(test) { + cell (dffr_not_next) { + area : 6; + ff("IQ", "IQN") { + next_state : "!D"; + clocked_on : "CLK"; + clear : "CLEAR"; + preset : "PRESET"; + clear_preset_var1 : L; + clear_preset_var2 : L; + } + pin(D) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(CLEAR) { + direction : input; + } + pin(PRESET) { + direction : input; + } + pin(Q) { + direction: output; + function : "IQ"; + } + pin(QN) { + direction: output; + function : "IQN"; + } + } +} From 2b659626a3b5f4c3fe506e624f4d4aa55c835fc4 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 13 Jun 2025 14:25:22 +0200 Subject: [PATCH 010/181] rename: add -unescape --- backends/verilog/verilog_backend.cc | 106 ++++++++++++++++------------ backends/verilog/verilog_backend.h | 39 ++++++++++ passes/cmds/rename.cc | 75 ++++++++++++++++++++ tests/various/rename_unescape.ys | 41 +++++++++++ 4 files changed, 215 insertions(+), 46 deletions(-) create mode 100644 backends/verilog/verilog_backend.h create mode 100644 tests/various/rename_unescape.ys diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 997740a7c..1cef7be60 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -28,12 +28,71 @@ #include "kernel/ff.h" #include "kernel/mem.h" #include "kernel/fmt.h" +#include "backends/verilog/verilog_backend.h" #include #include #include #include USING_YOSYS_NAMESPACE + +using namespace VERILOG_BACKEND; + +const pool VERILOG_BACKEND::verilog_keywords() { + static const pool res = { + // IEEE 1800-2017 Annex B + "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", + "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", + "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", + "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", + "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", + "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", + "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", + "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", + "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", + "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", + "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", + "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", + "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", + "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", + "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", + "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", + "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", + "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", + "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", + "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", + "wildcard", "wire", "with", "within", "wor", "xnor", "xor", + }; + return res; +} + +bool VERILOG_BACKEND::char_is_verilog_escaped(char c) { + if ('0' <= c && c <= '9') + return false; + if ('a' <= c && c <= 'z') + return false; + if ('A' <= c && c <= 'Z') + return false; + if (c == '_') + return false; + + return true; +} + +bool VERILOG_BACKEND::id_is_verilog_escaped(const std::string &str) { + if ('0' <= str[0] && str[0] <= '9') + return true; + + for (int i = 0; str[i]; i++) + if (char_is_verilog_escaped(str[i])) + return true; + + if (verilog_keywords().count(str)) + return true; + + return false; +} + PRIVATE_NAMESPACE_BEGIN bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs, noparallelcase; @@ -105,7 +164,6 @@ std::string next_auto_id() std::string id(RTLIL::IdString internal_id, bool may_rename = true) { const char *str = internal_id.c_str(); - bool do_escape = false; if (may_rename && auto_name_map.count(internal_id) != 0) return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]); @@ -113,51 +171,7 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) if (*str == '\\') str++; - if ('0' <= *str && *str <= '9') - do_escape = true; - - for (int i = 0; str[i]; i++) - { - if ('0' <= str[i] && str[i] <= '9') - continue; - if ('a' <= str[i] && str[i] <= 'z') - continue; - if ('A' <= str[i] && str[i] <= 'Z') - continue; - if (str[i] == '_') - continue; - do_escape = true; - break; - } - - static const pool keywords = { - // IEEE 1800-2017 Annex B - "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", - "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", - "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", - "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", - "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", - "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", - "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", - "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", - "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", - "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", - "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", - "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", - "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", - "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", - "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", - "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", - "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", - "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", - "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", - "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", - "wildcard", "wire", "with", "within", "wor", "xnor", "xor", - }; - if (keywords.count(str)) - do_escape = true; - - if (do_escape) + if (id_is_verilog_escaped(str)) return "\\" + std::string(str) + " "; return std::string(str); } diff --git a/backends/verilog/verilog_backend.h b/backends/verilog/verilog_backend.h new file mode 100644 index 000000000..7e550a37c --- /dev/null +++ b/backends/verilog/verilog_backend.h @@ -0,0 +1,39 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * 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. + * + * --- + * + * A simple and straightforward Verilog backend. + * + */ + +#ifndef VERILOG_BACKEND_H +#define VERILOG_BACKEND_H + +#include + +YOSYS_NAMESPACE_BEGIN +namespace VERILOG_BACKEND { + + const pool verilog_keywords(); + bool char_is_verilog_escaped(char c); + bool id_is_verilog_escaped(const std::string &str); + +}; /* namespace VERILOG_BACKEND */ +YOSYS_NAMESPACE_END + +#endif /* VERILOG_BACKEND_H */ diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index fe8b4a444..02cab3221 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -20,6 +20,7 @@ #include "kernel/register.h" #include "kernel/rtlil.h" #include "kernel/log.h" +#include "backends/verilog/verilog_backend.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -186,6 +187,26 @@ static bool rename_witness(RTLIL::Design *design, dict &ca return has_witness_signals; } +static std::string renamed_unescaped(const std::string& str) +{ + std::string new_str = ""; + + if ('0' <= str[0] && str[0] <= '9') + new_str = '_' + new_str; + + for (char c : str) { + if (VERILOG_BACKEND::char_is_verilog_escaped(c)) + new_str += '_'; + else + new_str += c; + } + + if (VERILOG_BACKEND::verilog_keywords().count(str)) + new_str += "_"; + + return new_str; +} + struct RenamePass : public Pass { RenamePass() : Pass("rename", "rename object in the design") { } void help() override @@ -252,6 +273,12 @@ struct RenamePass : public Pass { log("can be used to change the random number generator seed from the default, but it\n"); log("must be non-zero.\n"); log("\n"); + log("\n"); + log(" rename -unescape [selection]\n"); + log("\n"); + log("Rename all selected public wires and cells that have to be escaped.\n"); + log("Replaces characters with underscores or adds additional underscores and numbers.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { @@ -265,6 +292,7 @@ struct RenamePass : public Pass { bool flag_top = false; bool flag_output = false; bool flag_scramble_name = false; + bool flag_unescape = false; bool got_mode = false; unsigned int seed = 1; @@ -312,6 +340,11 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-unescape" && !got_mode) { + flag_unescape = true; + got_mode = true; + continue; + } if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) { int pos = args[++argidx].find('%'); pattern_prefix = args[argidx].substr(0, pos); @@ -491,6 +524,48 @@ struct RenamePass : public Pass { module->rename(it.first, it.second); } } + else if (flag_unescape) + { + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + dict new_wire_names; + dict new_cell_names; + + for (auto wire : module->selected_wires()) { + auto name = wire->name.str(); + if (name[0] != '\\') + continue; + name = name.substr(1); + if (!VERILOG_BACKEND::id_is_verilog_escaped(name)) + continue; + new_wire_names[wire] = module->uniquify("\\" + renamed_unescaped(name)); + auto new_name = new_wire_names[wire].str().substr(1); + if (VERILOG_BACKEND::id_is_verilog_escaped(new_name)) + log_error("Failed to rename wire %s -> %s\n", name.c_str(), new_name.c_str()); + } + + for (auto cell : module->selected_cells()) { + auto name = cell->name.str(); + if (name[0] != '\\') + continue; + name = name.substr(1); + if (!VERILOG_BACKEND::id_is_verilog_escaped(name)) + continue; + new_cell_names[cell] = module->uniquify("\\" + renamed_unescaped(name)); + auto new_name = new_cell_names[cell].str().substr(1); + if (VERILOG_BACKEND::id_is_verilog_escaped(new_name)) + log_error("Failed to rename cell %s -> %s\n", name.c_str(), new_name.c_str()); + } + + for (auto &it : new_wire_names) + module->rename(it.first, it.second); + + for (auto &it : new_cell_names) + module->rename(it.first, it.second); + } + } else { if (argidx+2 != args.size()) diff --git a/tests/various/rename_unescape.ys b/tests/various/rename_unescape.ys new file mode 100644 index 000000000..546d97357 --- /dev/null +++ b/tests/various/rename_unescape.ys @@ -0,0 +1,41 @@ +read_verilog < Date: Thu, 26 Jun 2025 13:21:53 +0200 Subject: [PATCH 011/181] add linecoverage command to generate lcov report from selection --- passes/cmds/Makefile.inc | 1 + passes/cmds/linecoverage.cc | 148 ++++++++++++++++++++++++++++++++++++ tests/various/lcov.gold | 43 +++++++++++ tests/various/lcov.v | 55 ++++++++++++++ tests/various/lcov.ys | 4 + 5 files changed, 251 insertions(+) create mode 100644 passes/cmds/linecoverage.cc create mode 100644 tests/various/lcov.gold create mode 100644 tests/various/lcov.v create mode 100644 tests/various/lcov.ys diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 4ecaea7dd..9bf615a7e 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -56,3 +56,4 @@ OBJS += passes/cmds/setenv.o OBJS += passes/cmds/abstract.o OBJS += passes/cmds/test_select.o OBJS += passes/cmds/timeest.o +OBJS += passes/cmds/linecoverage.o diff --git a/passes/cmds/linecoverage.cc b/passes/cmds/linecoverage.cc new file mode 100644 index 000000000..01dc2973c --- /dev/null +++ b/passes/cmds/linecoverage.cc @@ -0,0 +1,148 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * 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/sigtools.h" + +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + + +static const std::regex src_re("(.*):(\\d+)\\.(\\d+)-(\\d+)\\.(\\d+)"); + +struct CoveragePass : public Pass { + CoveragePass() : Pass("linecoverage", "write coverage information to file") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" linecoverage [options] [selection]\n"); + log("\n"); + log("This command writes coverage information on the design based on the current\n"); + log("selection, where items in the selection are considered covered and items not in\n"); + log("the selection are considered uncovered. If the same source location is found\n"); + log("both on items inside and out of the selection, it is considered uncovered.\n"); + log("\n"); + log(" -lcov \n"); + log(" write coverage information in lcov format to this file\n"); + log("\n"); + } + + std::string extract_src_filename(std::string src) const + { + std::smatch m; + if (std::regex_match(src, m, src_re)) { + return m[1].str(); + }; + return ""; + } + + std::pair extract_src_lines(std::string src) const + { + std::smatch m; + if (std::regex_match(src, m, src_re)) { + return std::make_pair(stoi(m[2].str()), stoi(m[4].str())); + }; + return std::make_pair(0,0); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + std::string ofile; + + log_header(design, "Executing linecoverage pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-lcov" && argidx+1 < args.size()) { + ofile = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + std::ofstream fout; + if (!ofile.empty()) { + fout.open(ofile, std::ios::out | std::ios::trunc); + if (!fout.is_open()) + log_error("Could not open file \"%s\" with write access.\n", ofile.c_str()); + } + + std::map> uncovered_lines; + std::map> all_lines; + + for (auto module : design->modules()) + { + log_debug("Module %s:\n", log_id(module)); + for (auto wire: module->wires()) { + log_debug("%s\t%s\t%s\n", module->selected(wire) ? "*" : " ", wire->get_src_attribute().c_str(), log_id(wire->name)); + for (auto src: wire->get_strpool_attribute(ID::src)) { + auto filename = extract_src_filename(src); + if (filename.empty()) continue; + auto [begin, end] = extract_src_lines(src); + for (int l = begin; l <=end; l++) { + if (l == 0) continue; + all_lines[filename].insert(l); + if (!module->selected(wire)) + uncovered_lines[filename].insert(l); + } + } + } + for (auto cell: module->cells()) { + log_debug("%s\t%s\t%s\n", module->selected(cell) ? "*" : " ", cell->get_src_attribute().c_str(), log_id(cell->name)); + for (auto src: cell->get_strpool_attribute(ID::src)) { + auto filename = extract_src_filename(src); + if (filename.empty()) continue; + auto [begin, end] = extract_src_lines(src); + for (int l = begin; l <=end; l++) { + if (l == 0) continue; + all_lines[filename].insert(l); + if (!module->selected(cell)) + uncovered_lines[filename].insert(l); + } + } + } + log_debug("\n"); + } + + if(!ofile.empty()) { + for (const auto& file_entry : all_lines) { + fout << "SF:" << file_entry.first << "\n"; + for (int l : file_entry.second) { + fout << "DA:" << l << ","; + if (uncovered_lines.count(file_entry.first) && uncovered_lines[file_entry.first].count(l)) + fout << "0"; + else + fout << "1"; + fout << "\n"; + } + fout << "LF:" << file_entry.second.size() << "\n"; + fout << "LH:" << (uncovered_lines.count(file_entry.first) ? uncovered_lines[file_entry.first].size() : 0) << "\n"; + fout << "end_of_record\n"; + } + } + + } +} CoveragePass; + +PRIVATE_NAMESPACE_END diff --git a/tests/various/lcov.gold b/tests/various/lcov.gold new file mode 100644 index 000000000..564783557 --- /dev/null +++ b/tests/various/lcov.gold @@ -0,0 +1,43 @@ +SF:lcov.v +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,0 +DA:13,1 +DA:14,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:21,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:32,1 +DA:33,1 +DA:36,0 +DA:37,0 +DA:38,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:48,0 +DA:49,0 +DA:52,1 +DA:53,0 +LF:39 +LH:24 +end_of_record diff --git a/tests/various/lcov.v b/tests/various/lcov.v new file mode 100644 index 000000000..c481638cd --- /dev/null +++ b/tests/various/lcov.v @@ -0,0 +1,55 @@ +module top ( + input wire clk, + input wire rst, + input wire [7:0] a, + input wire [7:0] b, + input wire [3:0] c, + input wire en, + output wire [7:0] out1, + output wire [7:0] out2 +); + + // Shared intermediate signal + wire [7:0] ab_sum; + assign ab_sum = a + b; + + // Logic cone for out1 + wire [7:0] cone1_1, cone1_2; + assign cone1_1 = ab_sum ^ {4{c[1:0]}}; + assign cone1_2 = (a & b) | {4{c[3:2]}}; + + reg [7:0] reg1, reg2; // only reg1 feeds into out1, but both share a source location + always @(posedge clk or posedge rst) begin + if (rst) begin + reg1 <= 8'h00; + reg2 <= 8'hFF; + end else if (en) begin + reg1 <= cone1_1 + cone1_2; + reg2 <= cone1_2 - cone1_1; + end + end + + wire [7:0] cone1_3; + assign cone1_3 = reg1 & ~a[0]; + + // Logic cone for out2 + wire [7:0] cone2_1, cone2_2; + assign cone2_1 = (ab_sum << 1) | (a >> 2); + assign cone2_2 = (b ^ {4{c[2:0]}}) & 8'hAA; + + reg [7:0] reg3; + always @(posedge clk or posedge rst) begin + if (rst) + reg3 <= 8'h0F; + else + reg3 <= cone2_1 ^ cone2_2 ^ reg1[7:0]; + end + + wire [7:0] cone2_3; + assign cone2_3 = reg3 | (reg2 ^ 8'h55); + + // Outputs + assign out1 = cone1_3 | (reg1 ^ 8'hA5); + assign out2 = cone2_3 & (reg3 | 8'h5A); + +endmodule diff --git a/tests/various/lcov.ys b/tests/various/lcov.ys new file mode 100644 index 000000000..f1d233acc --- /dev/null +++ b/tests/various/lcov.ys @@ -0,0 +1,4 @@ +read_verilog lcov.v +prep -top top +linecoverage -lcov lcov.out o:\out1 %ci* +exec -expect-return 0 -- diff -q lcov.out lcov.gold From 8a4f4651436a9c37eb30d697917478df730381b2 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Tue, 1 Jul 2025 11:33:03 +0200 Subject: [PATCH 012/181] update test to use suggested selection for assertions --- tests/various/lcov.gold | 3 ++- tests/various/lcov.v | 4 ++++ tests/various/lcov.ys | 8 ++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/various/lcov.gold b/tests/various/lcov.gold index 564783557..5b160fae8 100644 --- a/tests/various/lcov.gold +++ b/tests/various/lcov.gold @@ -38,6 +38,7 @@ DA:48,0 DA:49,0 DA:52,1 DA:53,0 -LF:39 +DA:56,1 +LF:40 LH:24 end_of_record diff --git a/tests/various/lcov.v b/tests/various/lcov.v index c481638cd..09fccf70f 100644 --- a/tests/various/lcov.v +++ b/tests/various/lcov.v @@ -52,4 +52,8 @@ module top ( assign out1 = cone1_3 | (reg1 ^ 8'hA5); assign out2 = cone2_3 & (reg3 | 8'h5A); + always @(posedge clk) begin + assert (out1 == 8'h42); + end + endmodule diff --git a/tests/various/lcov.ys b/tests/various/lcov.ys index f1d233acc..8122611c0 100644 --- a/tests/various/lcov.ys +++ b/tests/various/lcov.ys @@ -1,4 +1,8 @@ -read_verilog lcov.v +read_verilog -formal lcov.v prep -top top -linecoverage -lcov lcov.out o:\out1 %ci* +async2sync +chformal -lower +select -set covered t:$assert %ci* +select -set irrelevant o:* %ci* %n +linecoverage -lcov lcov.out @covered @irrelevant %u exec -expect-return 0 -- diff -q lcov.out lcov.gold From 5feb1a1752a7469fd5a02ec8afdb68794e55ef8f Mon Sep 17 00:00:00 2001 From: Gary Wong Date: Thu, 3 Jul 2025 20:51:12 -0600 Subject: [PATCH 013/181] verilog: add support for SystemVerilog string literals. Differences are new escape sequences (including escaped newline continuations and hex escapes) and triple-quoted literals. --- docs/source/yosys_internals/verilog.rst | 3 + frontends/verilog/verilog_lexer.l | 168 ++++++++++++---- tests/verilog/bug5160.v | 5 - tests/verilog/string-literals.ys | 257 ++++++++++++++++++++++++ 4 files changed, 386 insertions(+), 47 deletions(-) delete mode 100644 tests/verilog/bug5160.v create mode 100644 tests/verilog/string-literals.ys diff --git a/docs/source/yosys_internals/verilog.rst b/docs/source/yosys_internals/verilog.rst index 0039aaab7..d67553aa9 100644 --- a/docs/source/yosys_internals/verilog.rst +++ b/docs/source/yosys_internals/verilog.rst @@ -381,3 +381,6 @@ from SystemVerilog: will process conditionals using these keywords by annotating their representation with the appropriate ``full_case`` and/or ``parallel_case`` attributes, which are described above.) + +- SystemVerilog string literals are supported (triple-quoted strings and + escape sequences such as line continuations and hex escapes). diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 40162b8d3..e2d7a2cd9 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -112,6 +112,129 @@ static bool isUserType(std::string &s) return false; } +static bool is_hex_dig(char c, int *val) +{ + if ('0' <= c && c <= '9') { + *val = c - '0'; + return true; + } else if ('a' <= c && c <= 'f') { + *val = c - 'a' + 0xA; + return true; + } else if ('A' <= c && c <= 'F') { + *val = c - 'A' + 0xA; + return true; + } else if (c == 'x' || c == 'X' || c == 'z' || c == 'Z' || c == '?') { + log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "'%c' not a valid digit in hex escape sequence.\n", c); + *val = 0; // not semantically valid in hex escape... + return true; // ...but still processed as part of hex token + } + + return false; +} + +static bool is_oct_dig(char c, int *val) +{ + if ('0' <= c && c <= '7') { + *val = c - '0'; + return true; + } else if (c == 'x' || c == 'X' || c == 'z' || c == 'Z' || c == '?') { + log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "'%c' not a valid digit in octal escape sequence.\n", c); + *val = 0; // not semantically valid in octal escape... + return true; // ...but still processed as part of octal token + } + + return false; +} + +static std::string *process_str(char *str, int len, bool triple) +{ + char *in, *out; // Overwrite input buffer: flex manual states "Actions + // are free to modify 'yytext' except for lengthening it". + + for (in = str, out = str; in < str + len; in++) + switch (*in) { + case '\n': + case '\r': + if (in + 1 < str + len && (in[1] ^ *in) == ('\n' ^ '\r')) + in++; + if (!triple) + log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "Multi-line string literals should be triple-quoted or escaped.\n"); + *out++ = '\n'; + break; + case '\\': + in++; + log_assert(in < str + len); + switch (*in) { + case 'a': + *out++ = '\a'; + break; + case 'f': + *out++ = '\f'; + break; + case 'n': + *out++ = '\n'; + break; + case 'r': /* not part of IEEE-1800 2023, but seems + like a good idea to support it anyway */ + *out++ = '\r'; + break; + case 't': + *out++ = '\t'; + break; + case 'v': + *out++ = '\v'; + break; + case 'x': + int val; + if (in + 1 < str + len && is_hex_dig(in[1], &val)) { + *out = val; + in++; + if (in + 1 < str + len && is_hex_dig(in[1], &val)) { + *out = *out * 0x10 + val; + in++; + } + out++; + } else + log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "ignoring invalid hex escape.\n"); + break; + case '\\': + *out++ = '\\'; + break; + case '"': + *out++ = '"'; + break; + case '\n': + case '\r': + if (in + 1 < str + len && (in[1] ^ *in) == ('\n' ^ '\r')) + in++; + break; + default: + if ('0' <= *in && *in <= '7') { + int val; + + *out = *in - '0'; + if (in + 1 < str + len && is_oct_dig(in[1], &val)) { + *out = *out * 010 + val; + in++; + if (in + 1 < str + len && is_oct_dig(in[1], &val)) { + if (*out >= 040) + log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "octal escape exceeds \\377\n"); + *out = *out * 010 + val; + in++; + } + } + out++; + } else + *out++ = *in; + } + break; + default: + *out++ = *in; + } + + return new std::string(str, out - str); +} + %} %option yylineno @@ -122,7 +245,6 @@ static bool isUserType(std::string &s) %option prefix="frontend_verilog_yy" %x COMMENT -%x STRING %x SYNOPSYS_TRANSLATE_OFF %x SYNOPSYS_FLAGS %x IMPORT_DPI @@ -335,47 +457,9 @@ TIME_SCALE_SUFFIX [munpf]?s return TOK_REALVAL; } -\" { BEGIN(STRING); } -([^\\"]|\\.)+ { yymore(); real_location = old_location; } -\" { - BEGIN(0); - char *yystr = strdup(yytext); - yystr[strlen(yytext) - 1] = 0; - int i = 0, j = 0; - while (yystr[i]) { - if (yystr[i] == '\\' && yystr[i + 1]) { - i++; - if (yystr[i] == 'a') - yystr[i] = '\a'; - else if (yystr[i] == 'f') - yystr[i] = '\f'; - else if (yystr[i] == 'n') - yystr[i] = '\n'; - else if (yystr[i] == 'r') - yystr[i] = '\r'; - else if (yystr[i] == 't') - yystr[i] = '\t'; - else if (yystr[i] == 'v') - yystr[i] = '\v'; - else if ('0' <= yystr[i] && yystr[i] <= '7') { - yystr[i] = yystr[i] - '0'; - if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { - yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; - i++; - } - if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { - yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; - i++; - } - } - } - yystr[j++] = yystr[i++]; - } - yystr[j] = 0; - yylval->string = new std::string(yystr, j); - free(yystr); - return TOK_STRING; -} +\"([^\\"]|\\.|\\\n)*\" { yylval->string = process_str(yytext + 1, yyleng - 2, false); return TOK_STRING; } + +\"{3}(\"{0,2}([^\\"]|\\.|\\\n))*\"{3} { yylval->string = process_str(yytext + 3, yyleng - 6, true); return TOK_STRING; } and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { yylval->string = new std::string(yytext); diff --git a/tests/verilog/bug5160.v b/tests/verilog/bug5160.v deleted file mode 100644 index 5b141a360..000000000 --- a/tests/verilog/bug5160.v +++ /dev/null @@ -1,5 +0,0 @@ -// Regression test for bug mentioned in #5160: -// https://github.com/YosysHQ/yosys/pull/5160#issuecomment-2983643084 -module top; - initial $display( "\\" ); -endmodule diff --git a/tests/verilog/string-literals.ys b/tests/verilog/string-literals.ys new file mode 100644 index 000000000..a0f0f0460 --- /dev/null +++ b/tests/verilog/string-literals.ys @@ -0,0 +1,257 @@ +# Test valid escape sequences yield correct results: +logger -expect-no-warnings +read_verilog << EOF +module top; + wire[7:0] sp = "\ "; + wire[7:0] spval = 32; + wire[7:0] ex = "\!"; + wire[7:0] exval = 33; + wire[7:0] dq = "\""; + wire[7:0] dqval = 34; + wire[7:0] ha = "\#"; + wire[7:0] haval = 35; + wire[7:0] do = "\$"; + wire[7:0] doval = 36; + wire[7:0] pc = "\%"; + wire[7:0] pcval = 37; + wire[7:0] am = "\&"; + wire[7:0] amval = 38; + wire[7:0] sq = "\'"; + wire[7:0] sqval = 39; + wire[7:0] op = "\("; + wire[7:0] opval = 40; + wire[7:0] cp = "\)"; + wire[7:0] cpval = 41; + wire[7:0] as = "\*"; + wire[7:0] asval = 42; + wire[7:0] pl = "\+"; + wire[7:0] plval = 43; + wire[7:0] co = "\,"; + wire[7:0] coval = 44; + wire[7:0] mi = "\-"; + wire[7:0] mival = 45; + wire[7:0] do = "\."; + wire[7:0] doval = 46; + wire[7:0] sl = "\/"; + wire[7:0] slval = 47; + + wire[7:0] dig0 = "\012"; + wire[7:0] dig0val = 10; + wire[7:0] dig8 = "\8"; // not octal, a literal '8' + wire[7:0] dig8val = 56; + wire[7:0] dig9 = "\9"; // not octal, a literal '9' + wire[7:0] dig9val = 57; + + wire[7:0] cl = "\:"; + wire[7:0] clval = 58; + wire[7:0] sc = "\;"; + wire[7:0] scval = 59; + wire[7:0] lt = "\<"; + wire[7:0] ltval = 60; + wire[7:0] eq = "\="; + wire[7:0] eqval = 61; + wire[7:0] gt = "\>"; + wire[7:0] gtval = 62; + wire[7:0] qu = "\?"; + wire[7:0] quval = 63; + wire[7:0] at = "\@"; + wire[7:0] atval = 64; + + wire[7:0] A = "\A"; + wire[7:0] Aval = 65; // etc. etc. + + wire[7:0] os = "\["; + wire[7:0] osval = 91; + wire[7:0] bs = "\\"; + wire[7:0] bsval = 92; + wire[7:0] cs = "\]"; + wire[7:0] csval = 93; + wire[7:0] ca = "\^"; + wire[7:0] caval = 94; + wire[7:0] us = "\_"; + wire[7:0] usval = 95; + wire[7:0] bq = "\`"; + wire[7:0] bqval = 96; + + wire[7:0] a = "\a"; // alert, ASCII BEL=7 + wire[7:0] aval = 7; + wire[7:0] b = "\b"; + wire[7:0] bval = 98; + wire[7:0] c = "\c"; + wire[7:0] cval = 99; + wire[7:0] d = "\d"; + wire[7:0] dval = 100; + wire[7:0] e = "\e"; + wire[7:0] eval = 101; + wire[7:0] f = "\f"; // form feed, ASCII FF=12 + wire[7:0] fval = 12; + wire[7:0] g = "\g"; + wire[7:0] gval = 103; + wire[7:0] h = "\h"; + wire[7:0] hval = 104; + wire[7:0] i = "\i"; + wire[7:0] ival = 105; + wire[7:0] j = "\j"; + wire[7:0] jval = 106; + wire[7:0] k = "\k"; + wire[7:0] kval = 107; + wire[7:0] l = "\l"; + wire[7:0] lval = 108; + wire[7:0] m = "\m"; + wire[7:0] mval = 109; + wire[7:0] n = "\n"; // new line, ASCII LF=10 + wire[7:0] nval = 10; + wire[7:0] o = "\o"; + wire[7:0] oval = 111; + wire[7:0] p = "\p"; + wire[7:0] pval = 112; + wire[7:0] q = "\q"; + wire[7:0] qval = 113; + wire[7:0] r = "\r"; // carriage return, ASCII CR=13, not IEEE 1800-2023 + wire[7:0] rval = 13; + wire[7:0] s = "\s"; + wire[7:0] sval = 115; + wire[7:0] t = "\t"; // tab, ASCII HT=9 + wire[7:0] tval = 9; + wire[7:0] u = "\u"; + wire[7:0] uval = 117; + wire[7:0] v = "\v"; // vertical tab, ASCII VT=11 + wire[7:0] vval = 11; + wire[7:0] w = "\w"; + wire[7:0] wval = 119; + wire[7:0] x = "\x2A"; // hex escape + wire[7:0] xval = 42; + wire[7:0] y = "\y"; + wire[7:0] yval = 121; + wire[7:0] z = "\z"; + wire[7:0] zval = 122; + + wire[7:0] ob = "\{"; + wire[7:0] obval = 123; + wire[7:0] vb = "\|"; + wire[7:0] vbval = 124; + wire[7:0] cb = "\}"; + wire[7:0] cbval = 125; + wire[7:0] ti = "\~"; + wire[7:0] tival = 126; +endmodule +EOF +sat -prove sp spval -prove ex exval -prove dq dqval -prove ha haval -prove do doval -prove pc pcval -prove am amval -prove sq sqval -prove op opval -prove cp cpval -prove as asval -prove pl plval -prove co coval -prove mi mival -prove do doval -prove sl slval -verify +sat -prove dig0 dig0val -prove dig8 dig8val -prove dig9 dig9val -verify +sat -prove cl clval -prove sc scval -prove lt ltval -prove eq eqval -prove gt gtval -prove qu quval -prove at atval -prove A Aval -verify +sat -prove os osval -prove bs bsval -prove cs csval -prove ca caval -prove us usval -prove bq bqval -verify +sat -prove a aval -prove b bval -prove c cval -prove d dval -prove e eval -prove f fval -prove g gval -prove h hval -prove i ival -prove j jval -prove k kval -prove l lval -prove m mval -prove n nval -prove o oval -prove p pval -prove q qval -prove r rval -prove s sval -prove t tval -prove u uval -prove v vval -prove w wval -prove x xval -prove y yval -prove z zval -verify +sat -prove ob obval -prove vb vbval -prove cb cbval -prove ti tival -verify +logger -check-expected +design -reset + +# Test octal escape out of range. +logger -expect warning "octal escape exceeds \\377" 1 +read_verilog << EOF +module top; + wire[7:0] x = "\400"; +endmodule +EOF +logger -check-expected +design -reset + +# Test invalid octal digit. +logger -expect warning "'\?' not a valid digit in octal escape sequence" 1 +read_verilog << EOF +module top; + wire[7:0] x = "\0?"; +endmodule +EOF +logger -check-expected +design -reset + +# Test invalid hex digit. +logger -expect warning "'X' not a valid digit in hex escape sequence" 1 +read_verilog << EOF +module top; + wire[7:0] x = "\x0X"; +endmodule +EOF +logger -check-expected +design -reset + +# Test hex escape with no hex digits at all. +logger -expect warning "ignoring invalid hex escape" 1 +read_verilog << EOF +module top; + wire[7:0] x = "\xy"; +endmodule +EOF +logger -check-expected +design -reset + +# Test hex escape interrupted by end of string. +logger -expect warning "ignoring invalid hex escape" 1 +read_verilog << EOF +module top; + wire[7:0] x = "\x"; +endmodule +EOF +logger -check-expected +design -reset + +# Test multi-line string. +logger -expect warning "Multi-line string literals should be triple-quoted or escaped" 1 +read_verilog << EOF +module top; + wire[31:0] x = "A +BC"; + wire[31:0] xval = 32'h410A4243; +endmodule +EOF +logger -check-expected +design -reset + +# Test multi-line triple-quoted string. +logger -expect-no-warnings +read_verilog << EOF +module top; + wire[31:0] x = """A +BC"""; + wire[31:0] xval = 32'h410A4243; +endmodule +EOF +logger -check-expected +sat -prove x xval -verify +design -reset + +# Test escaped multi-line string. +logger -expect-no-warnings +read_verilog << EOF +module top; + wire[31:0] x = "AB\ +CD"; + wire[31:0] xval = 32'h41424344; +endmodule +EOF +logger -check-expected +sat -prove x xval -verify +design -reset + +# Test octal escape with surrounding data. +logger -expect-no-warnings +read_verilog << EOF +module top; + wire[31:0] x = "AB\234C"; + wire[31:0] xval = 32'h41429C43; +endmodule +EOF +logger -check-expected +sat -prove x xval -verify +design -reset + +# Test hex escape with surrounding data. +logger -expect-no-warnings +read_verilog << EOF +module top; + wire[31:0] x = "A\xBCDE"; + wire[31:0] xval = 32'h41BC4445; +endmodule +EOF +logger -check-expected +sat -prove x xval -verify From 22a44e4333e3495982e472794e105b355adf7721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 3 Jul 2025 11:23:30 +0200 Subject: [PATCH 014/181] Start `opt_hier` --- passes/opt/Makefile.inc | 1 + passes/opt/opt.cc | 11 + passes/opt/opt_hier.cc | 470 +++++++++++++++++++++++++++++++++++ techlibs/common/synth.cc | 26 +- tests/opt/opt_hier.tcl | 34 +++ tests/opt/opt_hier_simple1.v | 8 + tests/opt/opt_hier_simple2.v | 7 + tests/opt/opt_hier_test1.v | 22 ++ tests/opt/run-test.sh | 2 +- 9 files changed, 575 insertions(+), 6 deletions(-) create mode 100644 passes/opt/opt_hier.cc create mode 100644 tests/opt/opt_hier.tcl create mode 100644 tests/opt/opt_hier_simple1.v create mode 100644 tests/opt/opt_hier_simple2.v create mode 100644 tests/opt/opt_hier_test1.v diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 08d8191c7..426d9a79a 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -11,6 +11,7 @@ OBJS += passes/opt/opt_dff.o OBJS += passes/opt/opt_share.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_expr.o +OBJS += passes/opt/opt_hier.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index dc88563c2..146c21cce 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -46,6 +46,7 @@ struct OptPass : public Pass { log(" opt_merge [-share_all]\n"); log(" opt_share (-full only)\n"); log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n"); + log(" opt_hier (-hier only)\n"); log(" opt_clean [-purge]\n"); log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n"); log(" while \n"); @@ -56,6 +57,7 @@ struct OptPass : public Pass { log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n"); log(" opt_merge [-share_all]\n"); log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n"); + log(" opt_hier (-hier only)\n"); log(" opt_clean [-purge]\n"); log(" while \n"); log("\n"); @@ -74,6 +76,7 @@ struct OptPass : public Pass { bool opt_share = false; bool fast_mode = false; bool noff_mode = false; + bool hier_mode = false; log_header(design, "Executing OPT pass (performing simple optimizations).\n"); log_push(); @@ -141,6 +144,10 @@ struct OptPass : public Pass { noff_mode = true; continue; } + if (args[argidx] == "-hier") { + hier_mode = true; + continue; + } break; } extra_args(args, argidx, design); @@ -155,6 +162,8 @@ struct OptPass : public Pass { Pass::call(design, "opt_dff" + opt_dff_args); if (design->scratchpad_get_bool("opt.did_something") == false) break; + if (hier_mode) + Pass::call(design, "opt_hier"); Pass::call(design, "opt_clean" + opt_clean_args); log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n"); } @@ -173,6 +182,8 @@ struct OptPass : public Pass { Pass::call(design, "opt_share"); if (!noff_mode) Pass::call(design, "opt_dff" + opt_dff_args); + if (hier_mode) + Pass::call(design, "opt_hier"); Pass::call(design, "opt_clean" + opt_clean_args); Pass::call(design, "opt_expr" + opt_expr_args); if (design->scratchpad_get_bool("opt.did_something") == false) diff --git a/passes/opt/opt_hier.cc b/passes/opt/opt_hier.cc new file mode 100644 index 000000000..a8df78dc1 --- /dev/null +++ b/passes/opt/opt_hier.cc @@ -0,0 +1,470 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) Martin Povišer + * + * 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/register.h" +#include "kernel/rtlil.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +// Used to propagate information out of a module +struct ModuleIndex { + Module *module; + SigMap sigmap; + SigPool used; + dict constant_outputs; + std::vector tie_together_outputs; + + ModuleIndex(Module *module) + : module(module), sigmap(module) + { + if (module->get_blackbox_attribute()) { + for (auto wire : module->wires()) { + for (auto bit : SigSpec(wire)) + used.add(sigmap(bit)); + } + return; + } + + auto count_usage = [&](const SigSpec &signal) { + for (auto bit : signal) + used.add(sigmap(bit)); + }; + for (auto wire : module->wires()) { + if (wire->port_output) { + SigSpec wire1 = wire; + count_usage(wire1); + } + } + for (auto [_, process] : module->processes) + process->rewrite_sigspecs(count_usage); + for (auto cell : module->cells()) { + bool known = cell->known(); + for (auto &conn : cell->connections()) { + if (!known || cell->input(conn.first)) + count_usage(conn.second); + } + } + + + dict classes; + for (auto &pair : module->connections_) { + for (int i = 0; i < pair.first.size(); i++) { + if (pair.first[i].wire + && pair.first[i].wire->port_output + && !pair.first[i].wire->port_input) { + if (!pair.second[i].wire) { + constant_outputs[pair.first[i]] = pair.second[i]; + } else { + classes[pair.second[i]].append(pair.first[i]); + } + } + } + } + + for (auto [key, new_class] : classes) { + if (new_class.size() > 1) { + new_class.sort_and_unify(); + tie_together_outputs.push_back(new_class); + } + } + } + + bool apply_changes(ModuleIndex &parent, Cell *instantiation) { + log_assert(instantiation->module == parent.module); + + if (module->get_blackbox_attribute()) { + // no propagating out of blackboxes + return false; + } + + bool changed = false; + for (auto &[port_name, value] : instantiation->connections_) { + Wire *port = module->wire(port_name); + if (!port || (!port->port_input && !port->port_output) || port->width != value.size()) { + log_error("Port %s connected on instance %s not found in module %s" + " or width is not matching\n", + log_id(port_name), log_id(instantiation), log_id(module)); + } + + if (port->port_input && port->port_output) { + // ignore bidirectional: hard to come up with sound handling + continue; + } + + int nunused = 0, nconstants = 0; + // disconnect unused inputs + if (port->port_input) { + for (int i = 0; i < port->width; i++) { + if (value[i].is_wire() && !used.check(sigmap(SigBit(port, i)))) { + value[i] = RTLIL::Sx; + nunused++; + } + } + } + + // propagate constants + if (port->port_output) { + SigSpec port_new_const; + + for (int i = 0; i < port->width; i++) { + SigBit port_bit(port, i); + if (value[i].is_wire() && constant_outputs.count(port_bit) && parent.used.check(value[i])) { + port_new_const.append(port_bit); + nconstants++; + } + } + + for (auto chunk : port_new_const.chunks()) { + RTLIL::SigSpec rhs = chunk; + rhs.replace(constant_outputs); + log_assert(rhs.is_fully_const()); + parent.module->connect(value.extract(chunk.offset, chunk.width), rhs); + SigSpec dummy = parent.module->addWire(NEW_ID_SUFFIX("const_output"), chunk.width); + for (int i = 0; i < chunk.width; i++) + value[chunk.offset + i] = dummy[i]; + } + } + + if (nunused > 0) { + log("Disconnected %d input bits of instance '%s' of '%s' in '%s'\n", + nunused, log_id(instantiation), log_id(instantiation->type), log_id(parent.module)); + changed = true; + } + if (nconstants > 0) { + log("Substituting constant for %d output bits of instance '%s' of '%s' in '%s'\n", + nconstants, log_id(instantiation), log_id(instantiation->type), log_id(parent.module)); + changed = true; + } + } + + // propagate tie-togethers + int ntie_togethers = 0; + SigSpec severed_port_bits; + for (auto class_ : tie_together_outputs) { + // filtered class represented by bits on the two sides of boundary + SigSpec new_tie; + + for (auto port_bit : class_) { + if (instantiation->connections_.count(port_bit.wire->name)) { + SigBit bit = instantiation->connections_.at(port_bit.wire->name)[port_bit.offset]; + if (parent.used.check(bit)) { + if (!new_tie.empty()) { + severed_port_bits.append(port_bit); + ntie_togethers++; + } + new_tie.append(bit); + } + } + } + + if (new_tie.size() > 1) + parent.module->connect(new_tie.extract_end(1), SigSpec(new_tie[0]).repeat(new_tie.size() - 1)); + } + + severed_port_bits.sort_and_unify(); + for (auto chunk : severed_port_bits.chunks()) { + SigSpec &value = instantiation->connections_.at(chunk.wire->name); + SigSpec dummy = parent.module->addWire(NEW_ID_SUFFIX("tie_together"), chunk.width); + for (int i = 0; i < chunk.width; i++) + value[chunk.offset + i] = dummy[i]; + } + + if (ntie_togethers > 0) { + log("Replacing %d output bits with tie-togethers on instance '%s' of '%s' in '%s'\n", + ntie_togethers, log_id(instantiation), log_id(instantiation->type), log_id(parent.module)); + changed = true; + } + + return changed; + } +}; + +// Used to propagate information into a module +struct UsageData { + Module *module; + SigPool used_outputs; + // Values are constant nets. We're not using `dict` + // since we want to use this with `SigSpec::replace()` + dict constant_inputs; + std::vector tie_together_inputs; + + SigSpec all_inputs; + SigSpec all_outputs; + + UsageData(Module *module) + : module(module) + { + SigSpec all_inputs; + + for (auto port_name : module->ports) { + Wire *port = module->wire(port_name); + log_assert(port); + + if (port->port_input && port->port_output) { + // ignore bidirectional: hard to come up with sound handling + continue; + } + + if (port->port_input) { + for (int i = 0; i < port->width; i++) { + constant_inputs[SigBit(port, i)] = RTLIL::Sx; + } + all_inputs.append(port); + } else { + all_outputs.append(port); + } + } + + tie_together_inputs.push_back(all_inputs); + } + + void refine_used_outputs(Wire *port, SigSpec connection, ModuleIndex &index) { + for (int i = 0; i < port->width; i++) { + if (connection[i].is_wire() && index.used.check(index.sigmap(connection[i]))) { + used_outputs.add(SigBit(port, i)); + } + } + } + + void refine_input_constants(Wire *port, SigSpec connection) { + for (int i = 0; i < port->width; i++) { + SigBit port_bit(port, i); + // is connnected constant incompatible with candidate constant? + if (connection[i] != RTLIL::Sx + && constant_inputs.count(port_bit) + && constant_inputs.at(port_bit) != connection[i]) { + // we can go Sx -> S1/S0, otherwise erase the candidate constant + if (constant_inputs.at(port_bit) == RTLIL::Sx && !connection[i].is_wire()) { + constant_inputs[port_bit] = connection[i]; + } else { + constant_inputs.erase(port_bit); + } + } + } + } + + void refine_tie_togethers(const dict &inputs) { + std::vector new_tie_togethers; + + for (auto &class_ : tie_together_inputs) { + dict new_classes; + + for (auto bit : class_) { + SigBit connected_bit = inputs.count(bit) ? inputs.at(bit) : RTLIL::Sx; + new_classes[connected_bit].append(bit); + } + + for (auto [key, new_class] : new_classes) { + if (new_class.size() > 1) + new_tie_togethers.push_back(new_class); + } + } + + new_tie_togethers.swap(tie_together_inputs); + } + + // inspect the given instantiation and refine usage data accordingly + void refine(Cell *instance, ModuleIndex &index) { + dict inputs; + + for (auto &[port_name, value] : instance->connections_) { + Wire *port = module->wire(port_name); + if (!port || (!port->port_input && !port->port_output) || port->width != value.size()) { + log_error("Port %s connected on instance %s not found in module %s" + " or width is not matching\n", + log_id(port_name), log_id(instance), log_id(module)); + } + + if (port->port_input && port->port_output) { + // ignore bidirectional: hard to come up with sound handling + continue; + } + + if (port->port_output) { + refine_used_outputs(port, value, index); + } else { + refine_input_constants(port, value); + for (int i = 0; i < port->width; i++) + inputs[SigBit(port, i)] = value[i]; + } + } + + refine_tie_togethers(inputs); + } + + bool apply_changes() { + bool did_something = false; + + if (module->get_blackbox_attribute()) { + // no propagating into blackboxes + return false; + } + + // Disconnect unused outputs + for (auto &pair : module->connections_) { + for (int i = 0; i < pair.first.size(); i++) { + // If an output is constant there's no benefit to disconnecting + // so consider it "used" + if (pair.first[i].wire + && pair.first[i].wire->port_output + && !pair.second[i].wire) + used_outputs.add(pair.first[i]); + } + } + + SigSpec disconnect_outputs; + for (auto bit : all_outputs) { + if (!used_outputs.check(bit)) + disconnect_outputs.append(bit); + } + + dict replacement_map; + for (auto chunk : disconnect_outputs.chunks()) { + Wire *repl_wire = module->addWire(module->uniquify(std::string("$") + chunk.wire->name.str()), chunk.size()); + for (int i = 0; i < repl_wire->width; i++) + replacement_map[SigSpec(chunk)[i]] = SigBit(repl_wire, i); + } + auto disconnect_rewrite = [&](SigSpec &signal) { + signal.replace(replacement_map); + }; + module->rewrite_sigspecs(disconnect_rewrite); + for (auto chunk : disconnect_outputs.chunks()) { + log("Disconnected unused output terminal '%s' in module '%s'\n", log_signal(chunk), log_id(module)); + did_something = true; + module->connect(chunk, SigSpec(RTLIL::Sx, chunk.size())); + } + + // Connect constant inputs + SigPool applied_constants; + auto constant_rewrite = [&](SigSpec &signal) { + for (auto bit : signal) { + if (constant_inputs.count(bit)) + applied_constants.add(bit); + } + signal.replace(constant_inputs); + }; + module->rewrite_sigspecs(constant_rewrite); + SigSpec applied_constants2 = applied_constants.export_all(); + applied_constants2.sort_and_unify(); + for (auto chunk : applied_constants2.chunks()) { + SigSpec const_ = chunk; + const_.replace(constant_inputs); + log("Substituting constant %s for input terminal '%s' in module '%s'\n", + log_signal(const_), log_signal(chunk), log_id(module)); + } + + // Propagate tied-together inputs + dict ties; + for (auto group : tie_together_inputs) { + for (int i = 1; i < group.size(); i++) + ties[group[i]] = group[0]; + } + SigPool applied_ties; + auto ties_rewrite = [&](SigSpec &signal) { + for (auto bit : signal) { + if (ties.count(bit)) { + applied_ties.add(bit); + } + } + signal.replace(ties); + }; + module->rewrite_sigspecs(ties_rewrite); + if (applied_ties.size()) { + log("Replacing %zu input terminal bits with tie-togethers in module '%s'\n", + applied_ties.size(), log_id(module)); + } + return did_something; + } +}; + +struct OptHierPass : Pass { + OptHierPass() : Pass("opt_hier", "perform cross-boundary optimization") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_hier [selection]\n"); + log("\n"); + log("This pass considers the design hierarchy and propagates unused signals, constant\n"); + log("signals, and tied-together signals across module boundaries to facilitate\n"); + log("optimization. Only the selected modules are affected.\n"); + log("\n"); + log("Note this pass changes port semantics on modules which are not the top.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *d) override + { + log_header(d, "Executing OPT_HIER pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, d); + + if (!d->top_module()) + log_cmd_error("Top module needs to be selected for opt_hier\n"); + + dict indices; + for (auto module : d->modules()) { + log_debug("Building index for %s\n", log_id(module)); + indices.emplace(module->name, ModuleIndex(module)); + } + + dict usage_datas; + for (auto module : d->selected_modules(RTLIL::SELECT_WHOLE_ONLY, RTLIL::SB_UNBOXED_CMDERR)) { + if (module->get_bool_attribute(ID::top)) + continue; + + log_debug("Starting usage data for %s\n", log_id(module)); + usage_datas.emplace(module->name, UsageData(module)); + } + + for (auto module : d->modules()) { + for (auto cell : module->cells()) { + if (usage_datas.count(cell->type)) { + log_debug("Account for instance %s of %s in %s\n", log_id(cell), log_id(cell->type), log_id(module)); + usage_datas.at(cell->type).refine(cell, indices.at(module->name)); + } + } + } + + bool did_something = false; + for (auto module : d->selected_modules(RTLIL::SELECT_WHOLE_ONLY, RTLIL::SB_UNBOXED_CMDERR)) { + if (usage_datas.count(module->name)) { + log_debug("Applying usage data changes to %s\n", log_id(module)); + did_something |= usage_datas.at(module->name).apply_changes(); + } + + ModuleIndex &parent_index = indices.at(module->name); + for (auto cell : module->cells()) { + if (indices.count(cell->type)) { + log_debug("Applying changes to instance %s of %s in %s\n", log_id(cell), log_id(cell->type), log_id(module)); + did_something |= indices.at(cell->type).apply_changes(parent_index, cell); + } + } + } + if (did_something) + d->scratchpad_set_bool("opt.did_something", true); + } +} OptHierPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 74a484d59..e3a82ba48 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -47,6 +47,11 @@ struct SynthPass : public ScriptPass { log(" flatten the design before synthesis. this will pass '-auto-top' to\n"); log(" 'hierarchy' if no top module is specified.\n"); log("\n"); + log(" -hieropt\n"); + log(" enable hierarchical optimization. this option is useful when `-flatten'\n"); + log(" is not used, or when selected modules are marked with 'keep_hierarchy'\n."); + log(" to prevent their dissolution (EXPERIMENTAL).\n"); + log("\n"); log(" -encfile \n"); log(" passed to 'fsm_recode' via 'fsm'\n"); log("\n"); @@ -99,7 +104,7 @@ struct SynthPass : public ScriptPass { } string top_module, fsm_opts, memory_opts, abc; - bool autotop, flatten, noalumacc, nofsm, noabc, noshare, flowmap, booth; + bool autotop, flatten, noalumacc, nofsm, noabc, noshare, flowmap, booth, hieropt; int lut; std::vector techmap_maps; @@ -118,6 +123,7 @@ struct SynthPass : public ScriptPass { noshare = false; flowmap = false; booth = false; + hieropt = false; abc = "abc"; techmap_maps.clear(); } @@ -201,6 +207,10 @@ struct SynthPass : public ScriptPass { techmap_maps.push_back(args[++argidx]); continue; } + if (args[argidx] == "-hieropt") { + hieropt = true; + continue; + } break; } extra_args(args, argidx, design); @@ -223,6 +233,12 @@ struct SynthPass : public ScriptPass { void script() override { + std::string hieropt_flag; + if (help_mode) + hieropt_flag = " [-hier]"; + else + hieropt_flag = hieropt ? " -hier" : ""; + if (check_label("begin")) { if (help_mode) { run("hierarchy -check [-top | -auto-top]"); @@ -247,7 +263,7 @@ struct SynthPass : public ScriptPass { run("opt -nodffe -nosdff"); if (!nofsm || help_mode) run("fsm" + fsm_opts, " (unless -nofsm)"); - run("opt"); + run("opt" + hieropt_flag); run("wreduce"); run("peepopt"); run("opt_clean"); @@ -261,13 +277,13 @@ struct SynthPass : public ScriptPass { run("alumacc", " (unless -noalumacc)"); if (!noshare) run("share", " (unless -noshare)"); - run("opt"); + run("opt" + hieropt_flag); run("memory -nomap" + memory_opts); run("opt_clean"); } if (check_label("fine")) { - run("opt -fast -full"); + run("opt -fast -full" + hieropt_flag); run("memory_map"); run("opt -full"); if (help_mode) { @@ -291,7 +307,7 @@ struct SynthPass : public ScriptPass { } else if (flowmap) { run(stringf("flowmap -maxlut %d", lut)); } - run("opt -fast"); + run("opt -fast" + hieropt_flag); if ((!noabc && !flowmap) || help_mode) { #ifdef YOSYS_ENABLE_ABC diff --git a/tests/opt/opt_hier.tcl b/tests/opt/opt_hier.tcl new file mode 100644 index 000000000..65d8f9809 --- /dev/null +++ b/tests/opt/opt_hier.tcl @@ -0,0 +1,34 @@ +yosys -import + +# per each opt_hier_*.v source file, confirm flattening and hieropt+flattening +# are combinationally equivalent +foreach fn [glob opt_hier_*.v] { + log -header "Test $fn" + log -push + design -reset + + read_verilog $fn + hierarchy -auto-top + prep -top top + design -save start + flatten + design -save gold + design -load start + opt -hier + # check any instances marked `should_get_optimized_out` were + # indeed optimized out + select -assert-none a:should_get_optimized_out + dump + flatten + design -save gate + + design -reset + design -copy-from gold -as gold A:top + design -copy-from gate -as gate A:top + yosys rename -hide + equiv_make gold gate equiv + equiv_induct equiv + equiv_status -assert equiv + + log -pop +} diff --git a/tests/opt/opt_hier_simple1.v b/tests/opt/opt_hier_simple1.v new file mode 100644 index 000000000..17935a960 --- /dev/null +++ b/tests/opt/opt_hier_simple1.v @@ -0,0 +1,8 @@ +module m(input a, output y1, output y2); + assign y1 = a; + assign y2 = a; +endmodule + +module top(input a, output y2, output y1); + m inst(.a(a), .y1(y1), .y2(y2)); +endmodule diff --git a/tests/opt/opt_hier_simple2.v b/tests/opt/opt_hier_simple2.v new file mode 100644 index 000000000..9e724216b --- /dev/null +++ b/tests/opt/opt_hier_simple2.v @@ -0,0 +1,7 @@ +module m(input [3:0] i, output [3:0] y); + assign y = i + 1; +endmodule + +module top(output [3:0] y); + m inst(.i(4), .y(y)); +endmodule diff --git a/tests/opt/opt_hier_test1.v b/tests/opt/opt_hier_test1.v new file mode 100644 index 000000000..69de6b92f --- /dev/null +++ b/tests/opt/opt_hier_test1.v @@ -0,0 +1,22 @@ +(* blackbox *) +module bb(output y); +endmodule + +// all instances of `m` tie together a[1], a[2] +// this can be used to conclude y[0]=0 +module m(input [3:0] a, output [1:0] y, output x); + assign y[0] = a[1] != a[2]; + assign x = a[0] ^ a[3]; + (* should_get_optimized_out *) + bb bb1(.y(y[1])); +endmodule + +module top(input j, output z, output [2:0] x); + wire [1:0] y1; + wire [1:0] y2; + wire [1:0] y3; + m inst1(.a(0), .y(y1), .x(x[0])); + m inst2(.a(15), .y(y2), .x(x[1])); + m inst3(.a({1'b1, j, j, 1'b0}), .y(y3), .x(x[2])); + assign z = (&y1) ^ (&y2) ^ (&y3); +endmodule diff --git a/tests/opt/run-test.sh b/tests/opt/run-test.sh index 006c731e3..1d1d9b7a6 100755 --- a/tests/opt/run-test.sh +++ b/tests/opt/run-test.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -eu source ../gen-tests-makefile.sh -generate_mk --yosys-scripts +generate_mk --yosys-scripts --tcl-scripts From 62067cd6cb6ddb1ba7d5aeed44160b27c909b5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 4 Jul 2025 17:57:00 +0200 Subject: [PATCH 015/181] Update docs after addition of new pass --- docs/source/code_examples/macro_commands/opt.ys | 1 + docs/source/using_yosys/synthesis/opt.rst | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/docs/source/code_examples/macro_commands/opt.ys b/docs/source/code_examples/macro_commands/opt.ys index cb883bc58..ebd938836 100644 --- a/docs/source/code_examples/macro_commands/opt.ys +++ b/docs/source/code_examples/macro_commands/opt.ys @@ -9,6 +9,7 @@ do opt_merge opt_share (-full only) opt_dff (except when called with -noff) + opt_hier (-hier only) opt_clean opt_expr while diff --git a/docs/source/using_yosys/synthesis/opt.rst b/docs/source/using_yosys/synthesis/opt.rst index 743b24997..43b558739 100644 --- a/docs/source/using_yosys/synthesis/opt.rst +++ b/docs/source/using_yosys/synthesis/opt.rst @@ -192,6 +192,13 @@ control inputs. Called with ``-nodffe`` and ``-nosdff``, this pass is used to prepare a design for :doc:`/using_yosys/synthesis/fsm`. +Hierarchical optimization - `opt_hier` pass +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This pass considers the design hierarchy and propagates unused signals, constant +signals, and tied-together signals across module boundaries to facilitate +optimization by other passes. + Removing unused cells and wires - `opt_clean` pass ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 658c7dd424c5c93387415ed51e8d922a11df76c5 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 7 Jul 2025 16:16:57 +0200 Subject: [PATCH 016/181] rename: fix help --- passes/cmds/rename.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 02cab3221..e0586ec7e 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -276,7 +276,7 @@ struct RenamePass : public Pass { log("\n"); log(" rename -unescape [selection]\n"); log("\n"); - log("Rename all selected public wires and cells that have to be escaped.\n"); + log("Rename all selected public wires and cells that have to be escaped in Verilog.\n"); log("Replaces characters with underscores or adds additional underscores and numbers.\n"); log("\n"); } From e60cf3e2facf8a2d7e27068bc99775183356b8d0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 00:25:06 +0000 Subject: [PATCH 017/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b7928f33a..e14be4ad2 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+0 +YOSYS_VER := 0.55+3 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From ad80e2bd39917eaae922fbeac2ed9aaab0180f94 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 8 Jul 2025 13:39:03 +0200 Subject: [PATCH 018/181] libparse: install headers for use in plugins --- Makefile | 1 + passes/techmap/libparse.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Makefile b/Makefile index e14be4ad2..0379adf57 100644 --- a/Makefile +++ b/Makefile @@ -627,6 +627,7 @@ endif $(eval $(call add_include_file,libs/sha1/sha1.h)) $(eval $(call add_include_file,libs/json11/json11.hpp)) $(eval $(call add_include_file,passes/fsm/fsmdata.h)) +$(eval $(call add_include_file,passes/techmap/libparse.h)) $(eval $(call add_include_file,frontends/ast/ast.h)) $(eval $(call add_include_file,frontends/ast/ast_binding.h)) $(eval $(call add_include_file,frontends/blif/blifparse.h)) diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 949adbdcf..8a2ab22e0 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -26,6 +26,11 @@ #include #include +/** + * This file is likely to change in the near future. + * Rely on it in your plugins at your own peril + */ + namespace Yosys { struct LibertyAst From 478b6a2b3fbab0fd4097b841914cbe8bb9f67268 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Mon, 16 Jun 2025 13:40:07 +0100 Subject: [PATCH 019/181] kernel: treat zero width constant as zero --- kernel/rtlil.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 189db0648..8a0080dbf 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -384,7 +384,7 @@ bool RTLIL::Const::convertible_to_int(bool is_signed) const { auto size = get_min_size(is_signed); - if (size <= 0) + if (size < 0) return false; // If it fits in 31 bits it is definitely convertible @@ -5509,6 +5509,9 @@ bool RTLIL::SigSpec::convertible_to_int(bool is_signed) const if (!is_fully_const()) return false; + if (empty()) + return true; + return RTLIL::Const(chunks_[0].data).convertible_to_int(is_signed); } @@ -5520,6 +5523,9 @@ std::optional RTLIL::SigSpec::try_as_int(bool is_signed) const if (!is_fully_const()) return std::nullopt; + if (empty()) + return 0; + return RTLIL::Const(chunks_[0].data).try_as_int(is_signed); } @@ -5529,7 +5535,10 @@ int RTLIL::SigSpec::as_int_saturating(bool is_signed) const pack(); log_assert(is_fully_const() && GetSize(chunks_) <= 1); - log_assert(!empty()); + + if (empty()) + return 0; + return RTLIL::Const(chunks_[0].data).as_int_saturating(is_signed); } From 743df9f0f943c62835ebd21d31bea68a0395edac Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 8 Jul 2025 23:53:38 +0000 Subject: [PATCH 020/181] Fix space leak in `SatGen::importSigSpecWorker()` by avoiding `log_id()`. Calling `log_id()` leaks a copy of the ID into `log_id_cache` until the end of the pass, which causes exorbitant memory usage. See issue #5210. --- kernel/satgen.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/satgen.h b/kernel/satgen.h index 8a89ff9db..2c8cbda13 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -101,7 +101,9 @@ struct SatGen else vec.push_back(bit == (undef_mode ? RTLIL::State::Sx : RTLIL::State::S1) ? ez->CONST_TRUE : ez->CONST_FALSE); } else { - std::string name = pf + (bit.wire->width == 1 ? stringf("%s", log_id(bit.wire)) : stringf("%s [%d]", log_id(bit.wire->name), bit.offset)); + std::string wire_name = RTLIL::unescape_id(bit.wire->name); + std::string name = pf + + (bit.wire->width == 1 ? wire_name : stringf("%s [%d]", wire_name.c_str(), bit.offset)); vec.push_back(ez->frozen_literal(name)); imported_signals[pf][bit] = vec.back(); } From 7566af4a4b588640ed54438aa571fa9e94870846 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:25:57 +0000 Subject: [PATCH 021/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e14be4ad2..59becefac 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+3 +YOSYS_VER := 0.55+8 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From e47f5369fd897dab4c3c8da2143986dd7d040411 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Wed, 9 Jul 2025 15:58:35 +0200 Subject: [PATCH 022/181] verificsva: check -L value is small enough for code to work --- frontends/verific/verific.cc | 3 +++ frontends/verific/verificsva.cc | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index d83db3410..241b2db30 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -4124,6 +4124,9 @@ struct VerificPass : public Pass { if (argidx > GetSize(args) && args[argidx].compare(0, 1, "-") == 0) cmd_error(args, argidx, "unknown option"); + if ((unsigned long)verific_sva_fsm_limit >= sizeof(1ull)*8) + log_cmd_error("-L %d: limit too large; maximum allowed value is %zu.\n", verific_sva_fsm_limit, sizeof(1ull)*8-1); + std::set top_mod_names; if (mode_all) diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 7652f8130..6e87bd267 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -479,14 +479,14 @@ struct SvaFsm GetSize(dnode.ctrl), verific_sva_fsm_limit); } - for (int i = 0; i < (1 << GetSize(dnode.ctrl)); i++) + for (unsigned long long i = 0; i < (1ull << GetSize(dnode.ctrl)); i++) { Const ctrl_val(i, GetSize(dnode.ctrl)); pool ctrl_bits; - for (int i = 0; i < GetSize(dnode.ctrl); i++) - if (ctrl_val[i] == State::S1) - ctrl_bits.insert(dnode.ctrl[i]); + for (int j = 0; j < GetSize(dnode.ctrl); j++) + if (ctrl_val[j] == State::S1) + ctrl_bits.insert(dnode.ctrl[j]); vector new_state; bool accept = false, cond = false; From f34c4f2e26f0e18df7eb3393ec755b5e6c288b00 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 9 Jul 2025 16:20:27 +0200 Subject: [PATCH 023/181] log: deduplicate unescape_id from log_id --- kernel/log.cc | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/kernel/log.cc b/kernel/log.cc index fabbe09fd..679a55562 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -664,15 +664,9 @@ const char *log_const(const RTLIL::Const &value, bool autoint) const char *log_id(const RTLIL::IdString &str) { - log_id_cache.push_back(strdup(str.c_str())); - const char *p = log_id_cache.back(); - if (p[0] != '\\') - return p; - if (p[1] == '$' || p[1] == '\\' || p[1] == 0) - return p; - if (p[1] >= '0' && p[1] <= '9') - return p; - return p+1; + std::string unescaped = RTLIL::unescape_id(str); + log_id_cache.push_back(strdup(unescaped.c_str())); + return log_id_cache.back(); } const char *log_str(const char *str) From 7fe817c52f51591eaeb780e3022fe8f2a4114abc Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 10 Jul 2025 18:54:43 +0200 Subject: [PATCH 024/181] dfflibmap: test negated state next_state with mixed polarities --- tests/techmap/dfflibmap.ys | 16 ++++++++++ tests/techmap/dfflibmap_dffsr_mixedpol.lib | 35 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/techmap/dfflibmap_dffsr_mixedpol.lib diff --git a/tests/techmap/dfflibmap.ys b/tests/techmap/dfflibmap.ys index 77488d60f..822d87a36 100644 --- a/tests/techmap/dfflibmap.ys +++ b/tests/techmap/dfflibmap.ys @@ -81,3 +81,19 @@ clean select -assert-count 0 t:dffn select -assert-count 5 t:dffsr select -assert-count 1 t:dffe + +design -load orig +dfflibmap -liberty dfflibmap.lib -liberty dfflibmap_dffsr_mixedpol.lib -dont_use dffsr +clean +# We have one more _NOT_ than with the regular dffsr +select -assert-count 6 t:$_NOT_ +select -assert-count 1 t:dffn +select -assert-count 4 t:dffsr_mixedpol +select -assert-count 1 t:dffe +# The additional NOT is on ff2. +# Originally, ff2.R is an active high "set". +# dffsr_mixedpol has functionally swapped labels due to the next_state inversion, +# so we use its CLEAR port for the "set", +# but we have to invert it because the CLEAR pin is active low. +# ff2.CLEAR = !R +select -assert-count 1 c:ff2 %x:+[CLEAR] %ci t:$_NOT_ %i diff --git a/tests/techmap/dfflibmap_dffsr_mixedpol.lib b/tests/techmap/dfflibmap_dffsr_mixedpol.lib new file mode 100644 index 000000000..8c6a2f509 --- /dev/null +++ b/tests/techmap/dfflibmap_dffsr_mixedpol.lib @@ -0,0 +1,35 @@ +library(test) { + cell (dffsr_mixedpol) { + area : 6; + ff("IQ", "IQN") { + // look here + next_state : "!D"; + clocked_on : "CLK"; + // look here + clear : "!CLEAR"; + preset : "PRESET"; + clear_preset_var1 : L; + clear_preset_var2 : L; + } + pin(D) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(CLEAR) { + direction : input; + } + pin(PRESET) { + direction : input; + } + pin(Q) { + direction: output; + function : "IQ"; + } + pin(QN) { + direction: output; + function : "IQN"; + } + } +} From 6ee01308f22bdbbad3ab6717e98d78917b84c43a Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 11 Jul 2025 00:33:01 +0200 Subject: [PATCH 025/181] dfflibmap: show dffe inference is broken by space ANDs --- tests/techmap/dfflibmap.lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/techmap/dfflibmap.lib b/tests/techmap/dfflibmap.lib index d0cd472c3..54a44a296 100644 --- a/tests/techmap/dfflibmap.lib +++ b/tests/techmap/dfflibmap.lib @@ -55,7 +55,7 @@ library(test) { cell (dffe) { area : 6; ff("IQ", "IQN") { - next_state : "(D&EN) | (IQ&!EN)"; + next_state : "(D EN) | (IQ !EN)"; clocked_on : "!CLK"; } pin(D) { From e57a2b944239eb5214c66e0fa897cae397c907dc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 00:25:36 +0000 Subject: [PATCH 026/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3fbd8762a..d9f3b937c 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+8 +YOSYS_VER := 0.55+23 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 4b1a8a3b667d133fc583ba40e20192f3f4e520da Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 11 Jul 2025 18:27:19 +0200 Subject: [PATCH 027/181] libparse: add LibertyExpression::str for testing --- passes/techmap/dfflibmap.cc | 1 + passes/techmap/libparse.cc | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index f0b0ef20b..ae9498a9c 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -117,6 +117,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std // the next_state variable isn't just a pin name; perhaps this is an enable? auto helper = LibertyExpression::Lexer(expr); auto tree = LibertyExpression::parse(helper); + log_debug("liberty expression:\n%s\n", tree.str().c_str()); if (tree.kind == LibertyExpression::Kind::EMPTY) { if (!warned_cells.count(cell_name)) { diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 85ed35ea1..0df7af347 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -306,6 +306,47 @@ bool LibertyExpression::eval(dict& values) { } return false; } + +std::string LibertyExpression::str(int indent) +{ + std::string prefix; + prefix = std::string(indent, ' '); + switch (kind) { + case AND: + prefix += "(and "; + break; + case OR: + prefix += "(or "; + break; + case NOT: + prefix += "(not "; + break; + case XOR: + prefix += "(xor "; + break; + case PIN: + prefix += "(pin \"" + name + "\""; + break; + case EMPTY: + prefix += "("; + break; + default: + log_assert(false); + } + size_t add_indent = prefix.length(); + bool first = true; + for (auto child : children) { + if (!first) { + prefix += "\n" + child.str(indent + add_indent); + } else { + prefix += child.str(0); + } + first = false; + } + prefix += ")"; + return prefix; +} + #endif int LibertyParser::lexer(std::string &str) From c6e1d461faa0e845fa2306ca9015aed0d37e83e6 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 11 Jul 2025 23:09:30 +0200 Subject: [PATCH 028/181] libparse: support space ANDs --- passes/techmap/dfflibmap.cc | 5 +---- passes/techmap/libparse.cc | 43 ++++++++++++++++++++++++------------- passes/techmap/libparse.h | 3 +++ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index ae9498a9c..ade155791 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -92,9 +92,6 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std auto expr = attr->value; auto cell_name = cell->args[0]; - for (size_t pos = expr.find_first_of("\" \t"); pos != std::string::npos; pos = expr.find_first_of("\" \t")) - expr.erase(pos, 1); - // if this isn't an enable flop, the next_state variable is usually just the input pin name. if (expr[expr.size()-1] == '\'') { data_name = expr.substr(0, expr.size()-1); @@ -117,7 +114,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std // the next_state variable isn't just a pin name; perhaps this is an enable? auto helper = LibertyExpression::Lexer(expr); auto tree = LibertyExpression::parse(helper); - log_debug("liberty expression:\n%s\n", tree.str().c_str()); + // log_debug("liberty expression:\n%s\n", tree.str().c_str()); if (tree.kind == LibertyExpression::Kind::EMPTY) { if (!warned_cells.count(cell_name)) { diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 0df7af347..957a529b9 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -163,6 +163,12 @@ void LibertyAst::dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string i } #ifndef FILTERLIB + +// binary operators excluding ' ' +bool LibertyExpression::is_nice_binop(char c) { + return c == '*' || c == '&' || c == '^' || c == '+' || c == '|'; +} + // https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { if (s.empty()) @@ -201,15 +207,8 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { while (true) { if (s.empty()) break; - - c = s.peek(); - while (isspace(c)) { - if (s.empty()) - return lhs; - s.next(); - c = s.peek(); - } + c = s.peek(); if (c == '\'') { // postfix NOT if (min_prio > 7) @@ -235,11 +234,27 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { lhs = std::move(n); continue; - } else if (c == '&' || c == '*') { // infix AND - // technically space should be considered infix AND. it seems rare in practice. + } else if (c == '&' || c == '*' || isspace(c)) { // infix AND if (min_prio > 3) break; - s.next(); + + if (isspace(c)) { + // Rewind past this space and any further spaces + while (isspace(c) && !s.empty()) { + if (s.empty()) + return lhs; + s.next(); + c = s.peek(); + } + if (is_nice_binop(c)) { + // We found a real binop, so this space wasn't an AND + // and we just discard it as meaningless whitespace + continue; + } + } else { + // Rewind past this op + s.next(); + } auto rhs = parse(s, 4); auto n = LibertyExpression{}; @@ -310,7 +325,6 @@ bool LibertyExpression::eval(dict& values) { std::string LibertyExpression::str(int indent) { std::string prefix; - prefix = std::string(indent, ' '); switch (kind) { case AND: prefix += "(and "; @@ -337,10 +351,9 @@ std::string LibertyExpression::str(int indent) bool first = true; for (auto child : children) { if (!first) { - prefix += "\n" + child.str(indent + add_indent); - } else { - prefix += child.str(0); + prefix += "\n" + std::string(indent + add_indent, ' '); } + prefix += child.str(indent + add_indent); first = false; } prefix += ")"; diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 8a2ab22e0..ee0f3db42 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -93,6 +93,9 @@ namespace Yosys static LibertyExpression parse(Lexer &s, int min_prio = 0); void get_pin_names(pool& names); bool eval(dict& values); + std::string str(int indent = 0); + private: + static bool is_nice_binop(char c); }; class LibertyInputStream { From bf1f236998954bbfd5d3695201c225e7a0ce6800 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 11 Jul 2025 23:12:58 +0200 Subject: [PATCH 029/181] dfflibmap: add back tab and quote filters for good vibes --- passes/techmap/dfflibmap.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index ade155791..409ac7865 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -92,6 +92,9 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std auto expr = attr->value; auto cell_name = cell->args[0]; + for (size_t pos = expr.find_first_of("\"\t"); pos != std::string::npos; pos = expr.find_first_of("\"\t")) + expr.erase(pos, 1); + // if this isn't an enable flop, the next_state variable is usually just the input pin name. if (expr[expr.size()-1] == '\'') { data_name = expr.substr(0, expr.size()-1); From 62c270a38b4ca22a8b93d0453df46472ebfe2035 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 15 Jul 2025 01:28:07 +0000 Subject: [PATCH 030/181] Allow installing headers and `yosys-config` without the rest. This is useful to be able to build a plugin like `yosys-slang` without having to build the entirety of Yosys, which can save a lot of time. --- Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d9f3b937c..e50d085a1 100644 --- a/Makefile +++ b/Makefile @@ -981,6 +981,12 @@ unit-test: libyosys.so clean-unit-test: @$(MAKE) -C $(UNITESTPATH) clean +install-dev: $(PROGRAM_PREFIX)yosys-config share + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) + $(INSTALL_SUDO) cp $(PROGRAM_PREFIX)yosys-config $(DESTDIR)$(BINDIR) + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR) + $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/. + install: $(TARGETS) $(EXTRA_TARGETS) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) $(INSTALL_SUDO) cp $(filter-out libyosys.so,$(TARGETS)) $(DESTDIR)$(BINDIR) @@ -1223,5 +1229,5 @@ echo-cxx: FORCE: -.PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc +.PHONY: all top-all abc test install-dev install install-abc docs clean mrproper qtcreator coverage vcxsrc .PHONY: config-clean config-clang config-gcc config-gcc-static config-gprof config-sudo From e960428587b65c56db60ac80c24aab684f5b475f Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 15 Jul 2025 12:21:01 +0200 Subject: [PATCH 031/181] unit tests: fix run failure detection --- tests/unit/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 4c2848ea2..48635eb0d 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -24,7 +24,7 @@ $(OBJTEST)/%.o: $(basename $(subst $(OBJTEST),.,%)).cc .PHONY: prepare run-tests clean run-tests: $(TESTS) - $(subst Test ,Test; ,$^) + $(subst Test ,Test&& ,$^) prepare: mkdir -p $(addprefix $(BINTEST)/,$(TESTDIRS)) From 21e68ec9be2aa5d2827f5fe0a5ac4004fbf3f58e Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 15 Jul 2025 12:53:13 +0200 Subject: [PATCH 032/181] libparse: fix space ANDs --- passes/techmap/libparse.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 957a529b9..c6f87b60b 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -240,7 +240,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { if (isspace(c)) { // Rewind past this space and any further spaces - while (isspace(c) && !s.empty()) { + while (isspace(c)) { if (s.empty()) return lhs; s.next(); @@ -257,6 +257,8 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { } auto rhs = parse(s, 4); + if (rhs.kind == EMPTY) + continue; auto n = LibertyExpression{}; n.kind = Kind::AND; n.children.push_back(lhs); From c7a3abbcc4c09d4b6681f5b467dbe8cf62f6ebda Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 15 Jul 2025 12:53:30 +0200 Subject: [PATCH 033/181] libparse: LibertyExpression unit test --- tests/unit/techmap/libparseTest.cc | 88 ++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tests/unit/techmap/libparseTest.cc diff --git a/tests/unit/techmap/libparseTest.cc b/tests/unit/techmap/libparseTest.cc new file mode 100644 index 000000000..b58e55bf2 --- /dev/null +++ b/tests/unit/techmap/libparseTest.cc @@ -0,0 +1,88 @@ +#include +#include "passes/techmap/libparse.h" + +YOSYS_NAMESPACE_BEGIN + +namespace RTLIL { + + class TechmapLibparseTest : public testing::Test { + protected: + TechmapLibparseTest() { + if (log_files.empty()) log_files.emplace_back(stdout); + } + void checkAll(std::initializer_list expressions, std::string expected) { + for (const auto& e : expressions) { + auto helper = LibertyExpression::Lexer(e); + auto tree_s = LibertyExpression::parse(helper).str(); + EXPECT_EQ(tree_s, expected); + } + } + }; + TEST_F(TechmapLibparseTest, LibertyExpressionSpace) + { + checkAll({ + "x", + "x ", + " x", + " x ", + " x ", + }, "(pin \"x\")"); + + checkAll({ + "x y", + " x y ", + "x (y)", + " x (y) ", + "(x) y", + " (x) y ", + + "x & y", + "x&y", + "x &y", + "x& y", + " x&y ", + "x & (y)", + "x&(y)", + "x &(y)", + "x& (y)", + " x&(y) ", + "(x) & y", + "(x)&y", + "(x) &y", + "(x)& y", + " (x)&y ", + }, "(and (pin \"x\")\n" + " (pin \"y\"))" + ); + + checkAll({ + "x ^ y", + "x^y", + "x ^y", + "x^ y", + " x^y ", + }, "(xor (pin \"x\")\n" + " (pin \"y\"))" + ); + checkAll({ + "x !y", + " x !y ", + "x & !y", + "x&!y", + "x &!y", + "x& !y", + " x&!y ", + "x y'", + " x y' ", + "x & y'", + "x&y'", + "x &y'", + "x& y'", + " x&y' ", + }, "(and (pin \"x\")\n" + " (not (pin \"y\")))" + ); + } +} + +YOSYS_NAMESPACE_END From 381381c997cc0f4a4a7986180aabd46d24e48086 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Tue, 15 Jul 2025 14:14:07 +0100 Subject: [PATCH 034/181] write_firrtl: clear used names cache each pass --- backends/firrtl/firrtl.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index ceb805dcb..e4254f85a 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -1223,6 +1223,7 @@ struct FirrtlBackend : public Backend { Pass::call(design, "demuxmap"); Pass::call(design, "bwmuxmap"); + used_names.clear(); namecache.clear(); autoid_counter = 0; @@ -1262,6 +1263,7 @@ struct FirrtlBackend : public Backend { } } + used_names.clear(); namecache.clear(); autoid_counter = 0; } From af0b263557c09133da695d752456f53c199fd968 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 00:25:58 +0000 Subject: [PATCH 035/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e50d085a1..2c89b1f36 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+23 +YOSYS_VER := 0.55+36 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From fb6974dcd7883e6dbfddcb0e27d871db10d32933 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Wed, 16 Jul 2025 13:40:07 +0200 Subject: [PATCH 036/181] print summary of line coverage to log --- passes/cmds/linecoverage.cc | 14 +++++++++----- tests/various/lcov.gold | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/passes/cmds/linecoverage.cc b/passes/cmds/linecoverage.cc index 01dc2973c..d00042026 100644 --- a/passes/cmds/linecoverage.cc +++ b/passes/cmds/linecoverage.cc @@ -36,7 +36,7 @@ struct CoveragePass : public Pass { log("\n"); log(" linecoverage [options] [selection]\n"); log("\n"); - log("This command writes coverage information on the design based on the current\n"); + log("This command prints coverage information on the design based on the current\n"); log("selection, where items in the selection are considered covered and items not in\n"); log("the selection are considered uncovered. If the same source location is found\n"); log("both on items inside and out of the selection, it is considered uncovered.\n"); @@ -125,8 +125,12 @@ struct CoveragePass : public Pass { log_debug("\n"); } - if(!ofile.empty()) { - for (const auto& file_entry : all_lines) { + for (const auto& file_entry : all_lines) { + int lines_found = file_entry.second.size(); + int lines_hit = file_entry.second.size() - (uncovered_lines.count(file_entry.first) ? uncovered_lines[file_entry.first].size() : 0); + log("File %s: %d/%d lines covered\n", file_entry.first.c_str(), lines_hit, lines_found); + + if(!ofile.empty()) { fout << "SF:" << file_entry.first << "\n"; for (int l : file_entry.second) { fout << "DA:" << l << ","; @@ -136,8 +140,8 @@ struct CoveragePass : public Pass { fout << "1"; fout << "\n"; } - fout << "LF:" << file_entry.second.size() << "\n"; - fout << "LH:" << (uncovered_lines.count(file_entry.first) ? uncovered_lines[file_entry.first].size() : 0) << "\n"; + fout << "LF:" << lines_found << "\n"; + fout << "LH:" << lines_hit << "\n"; fout << "end_of_record\n"; } } diff --git a/tests/various/lcov.gold b/tests/various/lcov.gold index 5b160fae8..b9a87ed94 100644 --- a/tests/various/lcov.gold +++ b/tests/various/lcov.gold @@ -40,5 +40,5 @@ DA:52,1 DA:53,0 DA:56,1 LF:40 -LH:24 +LH:16 end_of_record From beb71a6c476400ace6dea0628b4e345150c62eee Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Wed, 16 Jul 2025 17:11:19 +0200 Subject: [PATCH 037/181] update short help --- passes/cmds/linecoverage.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/linecoverage.cc b/passes/cmds/linecoverage.cc index d00042026..9a88dec7f 100644 --- a/passes/cmds/linecoverage.cc +++ b/passes/cmds/linecoverage.cc @@ -29,7 +29,7 @@ PRIVATE_NAMESPACE_BEGIN static const std::regex src_re("(.*):(\\d+)\\.(\\d+)-(\\d+)\\.(\\d+)"); struct CoveragePass : public Pass { - CoveragePass() : Pass("linecoverage", "write coverage information to file") { } + CoveragePass() : Pass("linecoverage", "report coverage information") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From 415b7d3f6510a492371fd9a171259db665949a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 17 Jul 2025 12:02:44 +0200 Subject: [PATCH 038/181] Drop experimental label off `synth -hieropt` --- techlibs/common/synth.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index e3a82ba48..8b4561c34 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -50,7 +50,7 @@ struct SynthPass : public ScriptPass { log(" -hieropt\n"); log(" enable hierarchical optimization. this option is useful when `-flatten'\n"); log(" is not used, or when selected modules are marked with 'keep_hierarchy'\n."); - log(" to prevent their dissolution (EXPERIMENTAL).\n"); + log(" to prevent their dissolution.\n"); log("\n"); log(" -encfile \n"); log(" passed to 'fsm_recode' via 'fsm'\n"); From cf9720ecabb75fabed3ae5db12e365312e10e13b Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 15 Jul 2025 02:55:45 +0000 Subject: [PATCH 039/181] Make const lookup methods take a lookup path that never rehashes the table. This avoids the need to cast away `const` and makes these methods thread-compatible. --- kernel/hashlib.h | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 4144d701d..d56a10aa5 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -451,16 +451,21 @@ class dict { return 1; } - int do_lookup(const K &key, Hasher::hash_t &hash) const + int do_lookup(const K &key, Hasher::hash_t &hash) { if (hashtable.empty()) return -1; if (entries.size() * hashtable_size_trigger > hashtable.size()) { - ((dict*)this)->do_rehash(); + do_rehash(); hash = do_hash(key); } + return do_lookup_internal(key, hash); + } + + int do_lookup_internal(const K &key, Hasher::hash_t hash) const + { int index = hashtable[hash]; while (index >= 0 && !ops.cmp(entries[index].udata.first, key)) { @@ -471,6 +476,14 @@ class dict { return index; } + int do_lookup_no_rehash(const K &key, Hasher::hash_t hash) const + { + if (hashtable.empty()) + return -1; + + return do_lookup_internal(key, hash); + } + int do_insert(const K &key, Hasher::hash_t &hash) { if (hashtable.empty()) { @@ -694,14 +707,14 @@ public: int count(const K &key) const { Hasher::hash_t hash = do_hash(key); - int i = do_lookup(key, hash); + int i = do_lookup_no_rehash(key, hash); return i < 0 ? 0 : 1; } int count(const K &key, const_iterator it) const { Hasher::hash_t hash = do_hash(key); - int i = do_lookup(key, hash); + int i = do_lookup_no_rehash(key, hash); return i < 0 || i > it.index ? 0 : 1; } @@ -717,7 +730,7 @@ public: const_iterator find(const K &key) const { Hasher::hash_t hash = do_hash(key); - int i = do_lookup(key, hash); + int i = do_lookup_no_rehash(key, hash); if (i < 0) return end(); return const_iterator(this, i); @@ -735,7 +748,7 @@ public: const T& at(const K &key) const { Hasher::hash_t hash = do_hash(key); - int i = do_lookup(key, hash); + int i = do_lookup_no_rehash(key, hash); if (i < 0) throw std::out_of_range("dict::at()"); return entries[i].udata.second; @@ -744,7 +757,7 @@ public: const T& at(const K &key, const T &defval) const { Hasher::hash_t hash = do_hash(key); - int i = do_lookup(key, hash); + int i = do_lookup_no_rehash(key, hash); if (i < 0) return defval; return entries[i].udata.second; From 4bf7c23e2bfb63b4c93461a30dad08eab2cacb57 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 15 Jul 2025 03:02:35 +0000 Subject: [PATCH 040/181] Make `pool` const lookup methods take a lookup path that never rehashes the table. This avoids the need to cast away `const` and makes these methods thread-compatible. --- kernel/hashlib.h | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/kernel/hashlib.h b/kernel/hashlib.h index d56a10aa5..52ff96028 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -919,16 +919,21 @@ protected: return 1; } - int do_lookup(const K &key, Hasher::hash_t &hash) const + int do_lookup(const K &key, Hasher::hash_t &hash) { if (hashtable.empty()) return -1; if (entries.size() * hashtable_size_trigger > hashtable.size()) { - ((pool*)this)->do_rehash(); + do_rehash(); hash = do_hash(key); } + return do_lookup_internal(key, hash); + } + + int do_lookup_internal(const K &key, Hasher::hash_t hash) const + { int index = hashtable[hash]; while (index >= 0 && !ops.cmp(entries[index].udata, key)) { @@ -939,6 +944,14 @@ protected: return index; } + int do_lookup_no_rehash(const K &key, Hasher::hash_t hash) const + { + if (hashtable.empty()) + return -1; + + return do_lookup_internal(key, hash); + } + int do_insert(const K &value, Hasher::hash_t &hash) { if (hashtable.empty()) { @@ -1100,14 +1113,14 @@ public: int count(const K &key) const { Hasher::hash_t hash = do_hash(key); - int i = do_lookup(key, hash); + int i = do_lookup_no_rehash(key, hash); return i < 0 ? 0 : 1; } int count(const K &key, const_iterator it) const { Hasher::hash_t hash = do_hash(key); - int i = do_lookup(key, hash); + int i = do_lookup_no_rehash(key, hash); return i < 0 || i > it.index ? 0 : 1; } @@ -1123,7 +1136,7 @@ public: const_iterator find(const K &key) const { Hasher::hash_t hash = do_hash(key); - int i = do_lookup(key, hash); + int i = do_lookup_no_rehash(key, hash); if (i < 0) return end(); return const_iterator(this, i); @@ -1235,7 +1248,7 @@ public: int at(const K &key) const { Hasher::hash_t hash = database.do_hash(key); - int i = database.do_lookup(key, hash); + int i = database.do_lookup_no_rehash(key, hash); if (i < 0) throw std::out_of_range("idict::at()"); return i + offset; @@ -1244,7 +1257,7 @@ public: int at(const K &key, int defval) const { Hasher::hash_t hash = database.do_hash(key); - int i = database.do_lookup(key, hash); + int i = database.do_lookup_no_rehash(key, hash); if (i < 0) return defval; return i + offset; @@ -1253,7 +1266,7 @@ public: int count(const K &key) const { Hasher::hash_t hash = database.do_hash(key); - int i = database.do_lookup(key, hash); + int i = database.do_lookup_no_rehash(key, hash); return i < 0 ? 0 : 1; } From aa1daa702346a1e8907c587f36d4417d783aaabc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 00:25:48 +0000 Subject: [PATCH 041/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2c89b1f36..b91d83737 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+36 +YOSYS_VER := 0.55+46 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From fe3adfd960d97ea7ef762efd082346494a9d8c35 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:32:09 +1200 Subject: [PATCH 042/181] Docs: Remove unused write_cell_rst function The `help -write-rst-cells-manual` approach was made redundant by `help -dump-cells-json`. --- Makefile | 6 ---- kernel/register.cc | 74 ---------------------------------------------- 2 files changed, 80 deletions(-) diff --git a/Makefile b/Makefile index b91d83737..0959dbea9 100644 --- a/Makefile +++ b/Makefile @@ -1041,12 +1041,6 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' $(Q) $(RSYNC_CP) temp/docs/source/cmd docs/source $(Q) rm -rf temp -docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) - $(Q) mkdir -p docs/source/cell - $(Q) mkdir -p temp/docs/source/cell - $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-cells-manual' - $(Q) $(RSYNC_CP) temp/docs/source/cell docs/source - $(Q) rm -rf temp docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' diff --git a/kernel/register.cc b/kernel/register.cc index af1823b5b..b38f9f4b2 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -798,64 +798,6 @@ struct HelpPass : public Pass { } fclose(f); } - void write_cell_rst(Yosys::SimHelper cell, Yosys::CellType ct) - { - // open - FILE *f = fopen(stringf("docs/source/cell/%s.rst", cell.filesafe_name().c_str()).c_str(), "wt"); - - // make header - string title_line; - if (cell.title.length()) - title_line = stringf("%s - %s", cell.name.c_str(), cell.title.c_str()); - else title_line = cell.name; - string underline = "\n"; - underline.insert(0, title_line.length(), '='); - fprintf(f, "%s\n", title_line.c_str()); - fprintf(f, "%s\n", underline.c_str()); - - // help text, with cell def for links - fprintf(f, ".. cell:def:: %s\n", cell.name.c_str()); - if (cell.title.length()) - fprintf(f, " :title: %s\n\n", cell.title.c_str()); - else - fprintf(f, " :title: %s\n\n", cell.name.c_str()); - std::stringstream ss; - ss << cell.desc; - for (std::string line; std::getline(ss, line, '\n');) { - fprintf(f, " %s\n", line.c_str()); - } - - // properties - fprintf(f, "\nProperties"); - fprintf(f, "\n----------\n\n"); - dict prop_dict = { - {"is_evaluable", ct.is_evaluable}, - {"is_combinatorial", ct.is_combinatorial}, - {"is_synthesizable", ct.is_synthesizable}, - }; - for (auto &it : prop_dict) { - fprintf(f, "- %s: %s\n", it.first.c_str(), it.second ? "true" : "false"); - } - - // source code - fprintf(f, "\nSimulation model (Verilog)"); - fprintf(f, "\n--------------------------\n\n"); - fprintf(f, ".. code-block:: verilog\n"); - fprintf(f, " :caption: %s\n\n", cell.source.c_str()); - std::stringstream ss2; - ss2 << cell.code; - for (std::string line; std::getline(ss2, line, '\n');) { - fprintf(f, " %s\n", line.c_str()); - } - - // footer - fprintf(f, "\n.. note::\n\n"); - fprintf(f, " This page was auto-generated from the output of\n"); - fprintf(f, " ``help %s``.\n", cell.name.c_str()); - - // close - fclose(f); - } bool dump_cells_json(PrettyJson &json) { // init json json.begin_object(); @@ -993,22 +935,6 @@ struct HelpPass : public Pass { write_cmd_rst(it.first, it.second->short_help, buf.str()); } } - // this option is also undocumented as it is for internal use only - else if (args[1] == "-write-rst-cells-manual") { - bool raise_error = false; - for (auto &it : yosys_celltypes.cell_types) { - auto name = it.first.str(); - if (cell_help_messages.contains(name)) { - write_cell_rst(cell_help_messages.get(name), it.second); - } else { - log("ERROR: Missing cell help for cell '%s'.\n", name.c_str()); - raise_error |= true; - } - } - if (raise_error) { - log_error("One or more cells defined in celltypes.h are missing help documentation.\n"); - } - } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); if (pass_register.at(args[1])->experimental_flag) { From 2c79aeb7ade1bb7c5b7b174c35c5d7d4c4c41d17 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:32:09 +1200 Subject: [PATCH 043/181] json.h: Fix array template Using `{begin|end}_object` inserts curly braces instead of square brackets, which can result in reordering (and may be syntactically incorrect?). --- kernel/json.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/json.h b/kernel/json.h index c9aa0e045..e8905c8e1 100644 --- a/kernel/json.h +++ b/kernel/json.h @@ -90,10 +90,10 @@ public: template void array(const T &&values) { - begin_object(); + begin_array(); for (auto &item : values) value(item); - end_object(); + end_array(); } }; From 28ecb910528290d56844aff769e02fcfb6857771 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:32:51 +1200 Subject: [PATCH 044/181] register: Add pass_usages and default help Experimental new formatting for describing passes that can be rendered into the standard help format, as well as being more amenable to smarter formatting for web documentation. --- kernel/register.cc | 45 +++++++++++++++++++++++++++++++++++++++++---- kernel/register.h | 20 +++++++++++++++++++- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/kernel/register.cc b/kernel/register.cc index b38f9f4b2..119204db3 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -29,6 +29,30 @@ YOSYS_NAMESPACE_BEGIN +#define MAX_LINE_LEN 80 +void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) { + if (pass_str.empty()) + return; + std::string indent_str(indent*4, ' '); + std::istringstream iss(pass_str); + if (leading_newline) + log("\n"); + for (std::string line; std::getline(iss, line);) { + log("%s", indent_str.c_str()); + auto curr_len = indent_str.length(); + std::istringstream lss(line); + for (std::string word; std::getline(lss, word, ' ');) { + if (curr_len + word.length() >= MAX_LINE_LEN) { + curr_len = 0; + log("\n%s", indent_str.c_str()); + } + log("%s ", word.c_str()); + curr_len += word.length() + 1; + } + log("\n"); + } +} + #define MAX_REG_COUNT 1000 bool echo_mode = false; @@ -41,7 +65,7 @@ std::map backend_register; std::vector Frontend::next_args; -Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help) +Pass::Pass(std::string name, std::string short_help, const vector usages) : pass_name(name), short_help(short_help), pass_usages(usages) { next_queued_pass = first_queued_pass; first_queued_pass = this; @@ -116,9 +140,22 @@ void Pass::post_execute(Pass::pre_post_exec_state_t state) void Pass::help() { - log("\n"); - log("No help message for command `%s'.\n", pass_name.c_str()); - log("\n"); + if (HasUsages()) { + for (auto usage : pass_usages) { + log_pass_str(usage.signature, 1, true); + log_pass_str(usage.description, 0, true); + for (auto option : usage.options) { + log_pass_str(option.keyword, 1, true); + log_pass_str(option.description, 2, false); + } + log_pass_str(usage.postscript, 0, true); + } + log("\n"); + } else { + log("\n"); + log("No help message for command `%s'.\n", pass_name.c_str()); + log("\n"); + } } void Pass::clear_flags() diff --git a/kernel/register.h b/kernel/register.h index f4e2127e1..74c2a1e4a 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -25,10 +25,24 @@ YOSYS_NAMESPACE_BEGIN +struct PassOption { + string keyword; + string description; +}; + +struct PassUsageBlock { + string signature = ""; + string description = ""; + vector options = {}; + string postscript = ""; +}; + struct Pass { std::string pass_name, short_help; - Pass(std::string name, std::string short_help = "** document me **"); + const vector pass_usages; + Pass(std::string name, std::string short_help = "** document me **", + const vector usages = {}); // Prefer overriding 'Pass::on_shutdown()' if possible virtual ~Pass(); @@ -44,6 +58,10 @@ struct Pass experimental_flag = true; } + bool HasUsages() { + return !pass_usages.empty(); + } + struct pre_post_exec_state_t { Pass *parent_pass; int64_t begin_ns; From 804e18e0c67e0cc3913ebd7692c39135c916d890 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:32:51 +1200 Subject: [PATCH 045/181] Docs: WIP dump_cmds_json --- Makefile | 3 + kernel/register.cc | 153 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0959dbea9..6dc16fdb2 100644 --- a/Makefile +++ b/Makefile @@ -1042,6 +1042,9 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) $(RSYNC_CP) temp/docs/source/cmd docs/source $(Q) rm -rf temp +docs/source/generated/cmds.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) + $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cmds-json $@' + docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' diff --git a/kernel/register.cc b/kernel/register.cc index 119204db3..08c0b45a6 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -835,6 +835,145 @@ struct HelpPass : public Pass { } fclose(f); } + bool dump_cmds_json(PrettyJson &json) { + // init json + json.begin_object(); + json.entry("version", "Yosys command reference"); + json.entry("generator", yosys_version_str); + + bool raise_error = false; + + json.name("cmds"); json.begin_object(); + // iterate over commands + for (auto &it : pass_register) { + auto name = it.first; + auto pass = it.second; + auto title = pass->short_help; + vector usages; + auto experimental_flag = pass->experimental_flag; + + if (pass->HasUsages()) { + for (auto usage : pass->pass_usages) + usages.push_back(usage); + } else { + enum PassUsageState { + PUState_signature, + PUState_description, + PUState_options, + PUState_postscript, + }; + + // dump command help + std::ostringstream buf; + log_streams.push_back(&buf); + pass->help(); + log_streams.pop_back(); + std::stringstream ss; + ss << buf.str(); + + // parse command help + int blank_count = 0; + size_t def_strip_count = 0; + auto current_state = PUState_postscript; + auto catch_verific = false; + for (string line; std::getline(ss, line, '\n');) { + // find position of first non space character + std::size_t first_pos = line.find_first_not_of(" \t"); + std::size_t last_pos = line.find_last_not_of(" \t"); + if (first_pos == std::string::npos) { + // skip empty lines + blank_count += 1; + continue; + } + + // strip leading and trailing whitespace + std::string stripped_line = line.substr(first_pos, last_pos - first_pos +1); + bool IsDefinition = stripped_line[0] == '-'; + IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; + bool IsDedent = def_strip_count && first_pos < def_strip_count; + bool IsIndent = first_pos == 2 || first_pos == 4 || first_pos == 8; + + // line looks like a signature + bool IsSignature = stripped_line.find(name) == 0; + + // start new usage block if it's a signature and we left the current signature + // or if we are adding new options after we left the options + bool NewUsage = (IsSignature && current_state != PUState_signature) + || (IsDefinition && current_state == PUState_postscript); + + if (NewUsage) { + current_state = PUState_signature; + usages.push_back({}); + def_strip_count = first_pos; + catch_verific = false; + } else if (IsDedent) { + def_strip_count = first_pos; + if (current_state == PUState_signature) + current_state = PUState_description; + else + current_state = PUState_postscript; + } + + if (IsDefinition && IsIndent && !catch_verific) { + current_state = PUState_options; + usages.back().options.push_back(PassOption({stripped_line, ""})); + def_strip_count = first_pos; + } else { + string *desc_str; + if (current_state == PUState_signature) { + desc_str = &(usages.back().signature); + blank_count += 1; + } else if (current_state == PUState_description) { + desc_str = &(usages.back().description); + } else if (current_state == PUState_options) { + desc_str = &(usages.back().options.back().description); + } else if (current_state == PUState_postscript) { + desc_str = &(usages.back().postscript); + } else { + log_abort(); + } + if (desc_str->empty()) + *desc_str = stripped_line; + else if (catch_verific) + *desc_str += (IsIndent ? "\n" : " ") + stripped_line; + else + *desc_str += (blank_count > 0 ? "\n" : " ") + stripped_line; + if (stripped_line.compare("Command file parser supports following commands in file:") == 0) + catch_verific = true; + } + + blank_count = 0; + } + } + + // write to json + json.name(name.c_str()); json.begin_object(); + json.entry("title", title); + json.name("usages"); json.begin_array(); + for (auto usage : usages) { + json.begin_object(); + json.entry("signature", usage.signature); + json.entry("description", usage.description); + json.name("options"); json.begin_array(); + for (auto option : usage.options) { + json.begin_array(); + json.value(option.keyword); + json.value(option.description); + json.end_array(); + } + json.end_array(); + json.entry("postscript", usage.postscript); + json.end_object(); + } + json.end_array(); + json.entry("experimental_flag", experimental_flag); + json.end_object(); + } + json.end_object(); + + json.end_object(); + return raise_error; + } bool dump_cells_json(PrettyJson &json) { // init json json.begin_object(); @@ -1007,7 +1146,17 @@ struct HelpPass : public Pass { log("No such command or cell type: %s\n", args[1].c_str()); return; } else if (args.size() == 3) { - if (args[1] == "-dump-cells-json") { + // this option is undocumented as it is for internal use only + if (args[1] == "-dump-cmds-json") { + PrettyJson json; + if (!json.write_to_file(args[2])) + log_error("Can't open file `%s' for writing: %s\n", args[2].c_str(), strerror(errno)); + if (dump_cmds_json(json)) { + log_abort(); + } + } + // this option is undocumented as it is for internal use only + else if (args[1] == "-dump-cells-json") { PrettyJson json; if (!json.write_to_file(args[2])) log_error("Can't open file `%s' for writing: %s\n", args[2].c_str(), strerror(errno)); @@ -1015,6 +1164,8 @@ struct HelpPass : public Pass { log_error("One or more cells defined in celltypes.h are missing help documentation.\n"); } } + else + log("Unknown help command: `%s %s'\n", args[1].c_str(), args[2].c_str()); return; } From 5ce097ed3d61669d329a9cf1a3c1a0607fdd41e3 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:33:31 +1200 Subject: [PATCH 046/181] Docs: Test new pass help with chformal --- passes/cmds/chformal.cc | 105 +++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index 572ed2153..c925f59bf 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -70,63 +70,60 @@ static bool is_triggered_check_cell(RTLIL::Cell * cell) } struct ChformalPass : public Pass { - ChformalPass() : Pass("chformal", "change formal constraints of the design") { } - void help() override + ChformalPass() : Pass("chformal", "change formal constraints of the design", { { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" chformal [types] [mode] [options] [selection]\n"); - log("\n"); - log("Make changes to the formal constraints of the design. The [types] options\n"); - log("the type of constraint to operate on. If none of the following options are\n"); - log("given, the command will operate on all constraint types:\n"); - log("\n"); - log(" -assert $assert cells, representing assert(...) constraints\n"); - log(" -assume $assume cells, representing assume(...) constraints\n"); - log(" -live $live cells, representing assert(s_eventually ...)\n"); - log(" -fair $fair cells, representing assume(s_eventually ...)\n"); - log(" -cover $cover cells, representing cover() statements\n"); - log("\n"); - log(" Additionally chformal will operate on $check cells corresponding to the\n"); - log(" selected constraint types.\n"); - log("\n"); - log("Exactly one of the following modes must be specified:\n"); - log("\n"); - log(" -remove\n"); - log(" remove the cells and thus constraints from the design\n"); - log("\n"); - log(" -early\n"); - log(" bypass FFs that only delay the activation of a constraint. When inputs\n"); - log(" of the bypassed FFs do not remain stable between clock edges, this may\n"); - log(" result in unexpected behavior.\n"); - log("\n"); - log(" -delay \n"); - log(" delay activation of the constraint by clock cycles\n"); - log("\n"); - log(" -skip \n"); - log(" ignore activation of the constraint in the first clock cycles\n"); - log("\n"); - log(" -coverenable\n"); - log(" add cover statements for the enable signals of the constraints\n"); - log("\n"); + .signature = "chformal [types] [mode] [options] [selection]", + .description = "Make changes to the formal constraints of the design. The [types] options the type of " + "constraint to operate on. If none of the following options are given, the command " + "will operate on all constraint types:", + .options = { + {"-assert $assert cells, representing assert(...) constraints" + "\n-assume $assume cells, representing assume(...) constraints" + "\n-live $live cells, representing assert(s_eventually ...)" + "\n-fair $fair cells, representing assume(s_eventually ...)" + "\n-cover $cover cells, representing cover() statements", ""}, + {"Additionally chformal will operate on $check cells corresponding to the selected constraint " + "types.", ""}, + } + }, + { + .description = "Exactly one of the following modes must be specified:", + .options = { + {"-remove", + "remove the cells and thus constraints from the design"}, + {"-early", + "bypass FFs that only delay the activation of a constraint. When inputs " + "of the bypassed FFs do not remain stable between clock edges, this may " + "result in unexpected behavior." + }, + {"-delay ", + "delay activation of the constraint by clock cycles" + }, + {"-skip ", + "ignore activation of the constraint in the first clock cycles" + }, + {"-coverenable", + "add cover statements for the enable signals of the constraints" #ifdef YOSYS_ENABLE_VERIFIC - log(" Note: For the Verific frontend it is currently not guaranteed that a\n"); - log(" reachable SVA statement corresponds to an active enable signal.\n"); - log("\n"); + "\n\nNote: For the Verific frontend it is currently not guaranteed that a " + "reachable SVA statement corresponds to an active enable signal." #endif - log(" -assert2assume\n"); - log(" -assert2cover\n"); - log(" -assume2assert\n"); - log(" -live2fair\n"); - log(" -fair2live\n"); - log(" change the roles of cells as indicated. these options can be combined\n"); - log("\n"); - log(" -lower\n"); - log(" convert each $check cell into an $assert, $assume, $live, $fair or\n"); - log(" $cover cell. If the $check cell contains a message, also produce a\n"); - log(" $print cell.\n"); - log("\n"); - } + }, + {"-assert2assume" + "\n-assert2cover" + "\n-assume2assert" + "\n-live2fair" + "\n-fair2live", + "change the roles of cells as indicated. these options can be combined" + }, + {"-lower", + "convert each $check cell into an $assert, $assume, $live, $fair or " + "$cover cell. If the $check cell contains a message, also produce a " + "$print cell." + }, + } + }, + }) { } void execute(std::vector args, RTLIL::Design *design) override { bool assert2assume = false; From 714790c70b4a918b8a3aa2fbb272c57fa2fd2706 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:33:31 +1200 Subject: [PATCH 047/181] Docs: Proto doc_string approach for cmd help Add `doc_string` field to `Pass` constructor Add `docs/util/newcmdref.py` to contain command domain Update `docs/util/cmdref.py` with `cmd:usage` and `cmd:optiongroup` for describing commands. Functional, but WIP. --- docs/source/conf.py | 2 + docs/util/cmdref.py | 131 ++++++++++++- docs/util/newcmdref.py | 427 +++++++++++++++++++++++++++++++++++++++++ kernel/register.cc | 112 +++++++---- kernel/register.h | 6 + 5 files changed, 635 insertions(+), 43 deletions(-) create mode 100644 docs/util/newcmdref.py diff --git a/docs/source/conf.py b/docs/source/conf.py index 05dcb7d5f..490fc523e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -112,6 +112,8 @@ extensions.append('util.cmdref') extensions.append('sphinx.ext.autodoc') extensions.append('util.cellref') cells_json = Path(__file__).parent / 'generated' / 'cells.json' +extensions.append('util.newcmdref') +cmds_json = Path(__file__).parent / 'generated' / 'cmds.json' from sphinx.application import Sphinx def setup(app: Sphinx) -> None: diff --git a/docs/util/cmdref.py b/docs/util/cmdref.py index a31b08e0d..203d9369e 100644 --- a/docs/util/cmdref.py +++ b/docs/util/cmdref.py @@ -4,10 +4,12 @@ from __future__ import annotations import re from typing import cast +import warnings from docutils import nodes -from docutils.nodes import Node, Element, system_message +from docutils.nodes import Node, Element from docutils.parsers.rst import directives +from docutils.parsers.rst.roles import GenericRole from docutils.parsers.rst.states import Inliner from sphinx.application import Sphinx from sphinx.domains import Domain, Index @@ -17,7 +19,7 @@ from sphinx.roles import XRefRole from sphinx.directives import ObjectDescription from sphinx.directives.code import container_wrapper from sphinx.util.nodes import make_refnode -from sphinx.util.docfields import Field +from sphinx.util.docfields import Field, GroupedField from sphinx import addnodes class TocNode(ObjectDescription): @@ -63,10 +65,15 @@ class CommandNode(TocNode): name = 'cmd' required_arguments = 1 - option_spec = { + option_spec = TocNode.option_spec.copy() + option_spec.update({ 'title': directives.unchanged, 'tags': directives.unchanged - } + }) + + doc_field_types = [ + GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')), + ] def handle_signature(self, sig, signode: addnodes.desc_signature): signode['fullname'] = sig @@ -93,6 +100,120 @@ class CommandNode(TocNode): idx, 0)) +class CommandUsageNode(TocNode): + """A custom node that describes command usages""" + + name = 'cmdusage' + + option_spec = TocNode.option_spec + option_spec.update({ + 'usage': directives.unchanged, + }) + + doc_field_types = [ + GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')), + ] + + def handle_signature(self, sig: str, signode: addnodes.desc_signature): + try: + cmd, use = sig.split('::') + except ValueError: + cmd, use = sig, '' + signode['fullname'] = sig + usage = self.options.get('usage', use or sig) + if usage: + signode['tocname'] = usage + signode += addnodes.desc_name(text=usage) + return signode['fullname'] + + def add_target_and_index( + self, + name: str, + sig: str, + signode: addnodes.desc_signature + ) -> None: + idx = ".".join(name.split("::")) + signode['ids'].append(idx) + if 'noindex' not in self.options: + tocname: str = signode.get('tocname', name) + objs = self.env.domaindata[self.domain]['objects'] + # (name, sig, typ, docname, anchor, prio) + objs.append((name, + tocname, + type(self).name, + self.env.docname, + idx, + 1)) + +class CommandOptionGroupNode(TocNode): + """A custom node that describes a group of related options""" + + name = 'cmdoptiongroup' + + option_spec = TocNode.option_spec + + doc_field_types = [ + Field('opt', ('option',), label='', rolename='option') + ] + + def handle_signature(self, sig: str, signode: addnodes.desc_signature): + try: + cmd, name = sig.split('::') + except ValueError: + cmd, name = '', sig + signode['fullname'] = sig + signode['tocname'] = name + signode += addnodes.desc_name(text=name) + return signode['fullname'] + + def add_target_and_index( + self, + name: str, + sig: str, + signode: addnodes.desc_signature + ) -> None: + idx = ".".join(name.split("::")) + signode['ids'].append(idx) + if 'noindex' not in self.options: + tocname: str = signode.get('tocname', name) + objs = self.env.domaindata[self.domain]['objects'] + # (name, sig, typ, docname, anchor, prio) + objs.append((name, + tocname, + type(self).name, + self.env.docname, + idx, + 1)) + + def transform_content(self, contentnode: addnodes.desc_content) -> None: + """hack `:option -thing: desc` into a proper option list""" + newchildren = [] + for node in contentnode: + newnode = node + if isinstance(node, nodes.field_list): + newnode = nodes.option_list() + for field in node: + is_option = False + option_list_item = nodes.option_list_item() + for child in field: + if isinstance(child, nodes.field_name): + option_group = nodes.option_group() + option_list_item += option_group + option = nodes.option() + option_group += option + name, text = child.rawsource.split(' ', 1) + is_option = name == 'option' + option += nodes.option_string(text=text) + if not is_option: warnings.warn(f'unexpected option \'{name}\' in {field.source}') + elif isinstance(child, nodes.field_body): + description = nodes.description() + description += child.children + option_list_item += description + if is_option: + newnode += option_list_item + newchildren.append(newnode) + contentnode.children = newchildren + class PropNode(TocNode): name = 'prop' fieldname = 'props' @@ -517,6 +638,8 @@ class CommandDomain(Domain): directives = { 'def': CommandNode, + 'usage': CommandUsageNode, + 'optiongroup': CommandOptionGroupNode, } indices = { diff --git a/docs/util/newcmdref.py b/docs/util/newcmdref.py new file mode 100644 index 000000000..4b5995065 --- /dev/null +++ b/docs/util/newcmdref.py @@ -0,0 +1,427 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +from dataclasses import dataclass +import json +from pathlib import Path, PosixPath, WindowsPath +import re + +from typing import Any +from sphinx.application import Sphinx +from sphinx.ext import autodoc +from sphinx.ext.autodoc import Documenter +from sphinx.util import logging + +logger = logging.getLogger(__name__) + +# cmd signature +cmd_ext_sig_re = re.compile( + r'''^ ([\w$._]+?) # module name + (?:\.([\w_]+))? # optional: thing name + (::[\w_]+)? # attribute + \s* $ # and nothing more + ''', re.VERBOSE) + +@dataclass +class YosysCmdUsage: + signature: str + description: str + options: list[tuple[str,str]] + postscript: str + +class YosysCmd: + name: str + title: str + content: list[str] + usages: list[YosysCmdUsage] + experimental_flag: bool + + def __init__( + self, + name:str = "", title:str = "", + content: list[str] = [], + usages: list[dict[str]] = [], + experimental_flag: bool = False + ) -> None: + self.name = name + self.title = title + self.content = content + self.usages = [YosysCmdUsage(**u) for u in usages] + self.experimental_flag = experimental_flag + + @property + def source_file(self) -> str: + return "" + + @property + def source_line(self) -> int: + return 0 + +class YosysCmdGroupDocumenter(Documenter): + objtype = 'cmdgroup' + priority = 10 + object: tuple[str, list[str]] + lib_key = 'groups' + + option_spec = { + 'caption': autodoc.annotation_option, + 'members': autodoc.members_option, + 'source': autodoc.bool_option, + 'linenos': autodoc.bool_option, + } + + __cmd_lib: dict[str, list[str] | dict[str]] | None = None + @property + def cmd_lib(self) -> dict[str, list[str] | dict[str]]: + if not self.__cmd_lib: + self.__cmd_lib = {} + cmds_obj: dict[str, dict[str, list[str] | dict[str]]] + try: + with open(self.config.cmds_json, "r") as f: + cmds_obj = json.loads(f.read()) + except FileNotFoundError: + logger.warning( + f"unable to find cmd lib at {self.config.cmds_json}", + type = 'cmdref', + subtype = 'cmd_lib' + ) + else: + for (name, obj) in cmds_obj.get(self.lib_key, {}).items(): + self.__cmd_lib[name] = obj + return self.__cmd_lib + + @classmethod + def can_document_member( + cls, + member: Any, + membername: str, + isattr: bool, + parent: Any + ) -> bool: + return False + + def parse_name(self) -> bool: + if not self.options.caption: + self.content_indent = '' + self.fullname = self.modname = self.name + return True + + def import_object(self, raiseerror: bool = False) -> bool: + # get cmd + try: + self.object = (self.modname, self.cmd_lib[self.modname]) + except KeyError: + if raiseerror: + raise + return False + + self.real_modname = self.modname + return True + + def get_sourcename(self) -> str: + return self.env.doc2path(self.env.docname) + + def format_name(self) -> str: + return self.options.caption or '' + + def format_signature(self, **kwargs: Any) -> str: + return self.modname + + def add_directive_header(self, sig: str) -> None: + domain = getattr(self, 'domain', 'cmd') + directive = getattr(self, 'directivetype', 'group') + name = self.format_name() + sourcename = self.get_sourcename() + cmd_list = self.object + + # cmd definition + self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) + self.add_line(f' :caption: {name}', sourcename) + + if self.options.noindex: + self.add_line(' :noindex:', sourcename) + + def add_content(self, more_content: Any | None) -> None: + # groups have no native content + # add additional content (e.g. from document), if present + if more_content: + for line, src in zip(more_content.data, more_content.items): + self.add_line(line, src[0], src[1]) + + def filter_members( + self, + members: list[tuple[str, Any]], + want_all: bool + ) -> list[tuple[str, Any, bool]]: + return [(x[0], x[1], False) for x in members] + + def get_object_members( + self, + want_all: bool + ) -> tuple[bool, list[tuple[str, Any]]]: + ret: list[tuple[str, str]] = [] + + if want_all: + for member in self.object[1]: + ret.append((member, self.modname)) + else: + memberlist = self.options.members or [] + for name in memberlist: + if name in self.object: + ret.append((name, self.modname)) + else: + logger.warning(('unknown module mentioned in :members: option: ' + f'group {self.modname}, module {name}'), + type='cmdref') + + return False, ret + + def document_members(self, all_members: bool = False) -> None: + want_all = (all_members or + self.options.inherited_members or + self.options.members is autodoc.ALL) + # find out which members are documentable + members_check_module, members = self.get_object_members(want_all) + + # document non-skipped members + memberdocumenters: list[tuple[Documenter, bool]] = [] + for (mname, member, isattr) in self.filter_members(members, want_all): + classes = [cls for cls in self.documenters.values() + if cls.can_document_member(member, mname, isattr, self)] + if not classes: + # don't know how to document this member + continue + # prefer the documenter with the highest priority + classes.sort(key=lambda cls: cls.priority) + # give explicitly separated module name, so that members + # of inner classes can be documented + full_mname = self.format_signature() + '::' + mname + documenter = classes[-1](self.directive, full_mname, self.indent) + memberdocumenters.append((documenter, isattr)) + + member_order = self.options.member_order or self.config.autodoc_member_order + memberdocumenters = self.sort_members(memberdocumenters, member_order) + + for documenter, isattr in memberdocumenters: + documenter.generate( + all_members=True, real_modname=self.real_modname, + check_module=members_check_module and not isattr) + + def generate( + self, + more_content: Any | None = None, + real_modname: str | None = None, + check_module: bool = False, + all_members: bool = False + ) -> None: + if not self.parse_name(): + # need a cmd lib to import from + logger.warning( + f"don't know which cmd lib to import for autodocumenting {self.name}", + type = 'cmdref' + ) + return + + if not self.import_object(): + logger.warning( + f"unable to load {self.name} with {type(self)}", + type = 'cmdref' + ) + return + + # check __module__ of object (for members not given explicitly) + # if check_module: + # if not self.check_module(): + # return + + sourcename = self.get_sourcename() + self.add_line('', sourcename) + + # format the object's signature, if any + try: + sig = self.format_signature() + except Exception as exc: + logger.warning(('error while formatting signature for %s: %s'), + self.fullname, exc, type='cmdref') + return + + # generate the directive header and options, if applicable + self.add_directive_header(sig) + self.add_line('', sourcename) + + # e.g. the module directive doesn't have content + self.indent += self.content_indent + + # add all content (from docstrings, attribute docs etc.) + self.add_content(more_content) + + # document members, if possible + self.document_members(all_members) + +class YosysCmdDocumenter(YosysCmdGroupDocumenter): + objtype = 'cmd' + priority = 15 + object: YosysCmd + lib_key = 'cmds' + + @classmethod + def can_document_member( + cls, + member: Any, + membername: str, + isattr: bool, + parent: Any + ) -> bool: + if membername.startswith('$'): + return False + return isinstance(parent, YosysCmdGroupDocumenter) + + def parse_name(self) -> bool: + try: + matched = cmd_ext_sig_re.match(self.name) + modname, thing, attribute = matched.groups() + except AttributeError: + logger.warning(('invalid signature for auto%s (%r)') % (self.objtype, self.name), + type='cmdref') + return False + + self.modname = modname + self.attribute = attribute or '' + self.fullname = ((self.modname) + (thing or '')) + + return True + + def import_object(self, raiseerror: bool = False) -> bool: + if super().import_object(raiseerror): + self.object = YosysCmd(self.modname, **self.object[1]) + return True + return False + + def get_sourcename(self) -> str: + return self.object.source_file + + def format_name(self) -> str: + return self.object.name + + def format_signature(self, **kwargs: Any) -> str: + return self.fullname + self.attribute + + def add_directive_header(self, sig: str) -> None: + domain = getattr(self, 'domain', self.objtype) + directive = getattr(self, 'directivetype', 'def') + source_name = self.object.source_file + source_line = self.object.source_line + + # cmd definition + self.add_line(f'.. {domain}:{directive}:: {sig}', source_name, source_line) + + if self.options.noindex: + self.add_line(' :noindex:', source_name) + + def add_content(self, more_content: Any | None) -> None: + # set sourcename and add content from attribute documentation + domain = getattr(self, 'domain', self.objtype) + source_name = self.object.source_file + + for usage in self.object.usages: + self.add_line('', source_name) + if usage.signature: + self.add_line(f' .. {domain}:usage:: {self.name}::{usage.signature}', source_name) + self.add_line('', source_name) + for line in usage.description.splitlines(): + self.add_line(f' {line}', source_name) + self.add_line('', source_name) + if usage.options: + self.add_line(f' .. {domain}:optiongroup:: {self.name}::something', source_name) + self.add_line('', source_name) + for opt, desc in usage.options: + self.add_line(f' :option {opt}: {desc}', source_name) + self.add_line('', source_name) + for line in usage.postscript.splitlines(): + self.add_line(f' {line}', source_name) + self.add_line('', source_name) + + for line in self.object.content: + if line.startswith('..') and ':: ' in line: + line = line.replace(':: ', f':: {self.name}::', 1) + self.add_line(line, source_name) + + # add additional content (e.g. from document), if present + if more_content: + for line, src in zip(more_content.data, more_content.items): + self.add_line(line, src[0], src[1]) + + # fields + self.add_line('\n', source_name) + field_attrs = ["properties", ] + for field in field_attrs: + attr = getattr(self.object, field, []) + for val in attr: + self.add_line(f':{field} {val}:', source_name) + + def get_object_members( + self, + want_all: bool + ) -> tuple[bool, list[tuple[str, Any]]]: + + return False, [] + +class YosysCmdUsageDocumenter(YosysCmdDocumenter): + objtype = 'cmdusage' + priority = 20 + object: YosysCmdUsage + parent: YosysCmd + + def add_directive_header(self, sig: str) -> None: + domain = getattr(self, 'domain', 'cmd') + directive = getattr(self, 'directivetype', 'usage') + name = self.format_name() + sourcename = self.parent.source_file + cmd = self.parent + + # cmd definition + self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) + if self.object.signature: + self.add_line(f' :usage: {self.object.signature}', sourcename) + else: + self.add_line(f' :noindex:', sourcename) + # for usage in self.object.signature.splitlines(): + # self.add_line(f' :usage: {usage}', sourcename) + + # if self.options.linenos: + # self.add_line(f' :source: {cmd.source.split(":")[0]}', sourcename) + # else: + # self.add_line(f' :source: {cmd.source}', sourcename) + # self.add_line(f' :language: verilog', sourcename) + + if self.options.noindex: + self.add_line(' :noindex:', sourcename) + + def add_content(self, more_content: Any | None) -> None: + # set sourcename and add content from attribute documentation + sourcename = self.parent.source_file + startline = self.parent.source_line + + for line in self.object.description.splitlines(): + self.add_line(line, sourcename) + + # add additional content (e.g. from document), if present + if more_content: + for line, src in zip(more_content.data, more_content.items): + self.add_line(line, src[0], src[1]) + + def get_object_members( + self, + want_all: bool + ) -> tuple[bool, list[tuple[str, Any]]]: + return False, [] + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_config_value('cmds_json', False, 'html', [Path, PosixPath, WindowsPath]) + app.setup_extension('sphinx.ext.autodoc') + app.add_autodocumenter(YosysCmdGroupDocumenter) + app.add_autodocumenter(YosysCmdDocumenter) + return { + 'version': '1', + 'parallel_read_safe': True, + } diff --git a/kernel/register.cc b/kernel/register.cc index 08c0b45a6..8e93e9b42 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -29,30 +29,6 @@ YOSYS_NAMESPACE_BEGIN -#define MAX_LINE_LEN 80 -void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) { - if (pass_str.empty()) - return; - std::string indent_str(indent*4, ' '); - std::istringstream iss(pass_str); - if (leading_newline) - log("\n"); - for (std::string line; std::getline(iss, line);) { - log("%s", indent_str.c_str()); - auto curr_len = indent_str.length(); - std::istringstream lss(line); - for (std::string word; std::getline(lss, word, ' ');) { - if (curr_len + word.length() >= MAX_LINE_LEN) { - curr_len = 0; - log("\n%s", indent_str.c_str()); - } - log("%s ", word.c_str()); - curr_len += word.length() + 1; - } - log("\n"); - } -} - #define MAX_REG_COUNT 1000 bool echo_mode = false; @@ -65,7 +41,7 @@ std::map backend_register; std::vector Frontend::next_args; -Pass::Pass(std::string name, std::string short_help, const vector usages) : pass_name(name), short_help(short_help), pass_usages(usages) +Pass::Pass(std::string name, std::string short_help, const vector doc_string, const vector usages) : pass_name(name), short_help(short_help), doc_string(doc_string), pass_usages(usages) { next_queued_pass = first_queued_pass; first_queued_pass = this; @@ -138,6 +114,37 @@ void Pass::post_execute(Pass::pre_post_exec_state_t state) current_pass->runtime_ns -= time_ns; } +#define MAX_LINE_LEN 80 +void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) { + if (pass_str.empty()) + return; + std::istringstream iss(pass_str); + if (leading_newline) + log("\n"); + for (std::string line; std::getline(iss, line);) { + log("%s", indent_str.c_str()); + auto curr_len = indent_str.length(); + std::istringstream lss(line); + for (std::string word; std::getline(lss, word, ' ');) { + while (word[0] == '`' && word.back() == '`') + word = word.substr(1, word.length()-2); + if (curr_len + word.length() >= MAX_LINE_LEN-1) { + curr_len = 0; + log("\n%s", indent_str.c_str()); + } + if (word.length()) { + log("%s ", word.c_str()); + curr_len += word.length() + 1; + } + } + log("\n"); + } +} +void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) { + std::string indent_str(indent*4, ' '); + log_pass_str(pass_str, indent_str, leading_newline); +} + void Pass::help() { if (HasUsages()) { @@ -151,6 +158,28 @@ void Pass::help() log_pass_str(usage.postscript, 0, true); } log("\n"); + } else if (HasDocstring()) { + log("\n"); + auto print_empty = true; + for (auto doc_line : doc_string) { + if (doc_line.find("..") == 0 && doc_line.find(":: ") != std::string::npos) { + auto command_pos = doc_line.find(":: "); + auto command_str = doc_line.substr(0, command_pos); + if (command_str.compare(".. cmd:usage") == 0) { + log_pass_str(doc_line.substr(command_pos+3), 1); + } else { + print_empty = false; + } + } else if (doc_line.length()) { + std::size_t first_pos = doc_line.find_first_not_of(" \t"); + auto indent_str = doc_line.substr(0, first_pos); + log_pass_str(doc_line, indent_str); + print_empty = true; + } else if (print_empty) { + log("\n"); + } + } + log("\n"); } else { log("\n"); log("No help message for command `%s'.\n", pass_name.c_str()); @@ -852,7 +881,7 @@ struct HelpPass : public Pass { vector usages; auto experimental_flag = pass->experimental_flag; - if (pass->HasUsages()) { + if (pass->HasUsages() || pass->HasDocstring()) { for (auto usage : pass->pass_usages) usages.push_back(usage); } else { @@ -949,23 +978,28 @@ struct HelpPass : public Pass { // write to json json.name(name.c_str()); json.begin_object(); json.entry("title", title); - json.name("usages"); json.begin_array(); - for (auto usage : usages) { - json.begin_object(); - json.entry("signature", usage.signature); - json.entry("description", usage.description); - json.name("options"); json.begin_array(); - for (auto option : usage.options) { - json.begin_array(); - json.value(option.keyword); - json.value(option.description); + if (pass->HasDocstring()) { + json.entry("content", pass->doc_string); + } + if (usages.size()) { + json.name("usages"); json.begin_array(); + for (auto usage : usages) { + json.begin_object(); + json.entry("signature", usage.signature); + json.entry("description", usage.description); + json.name("options"); json.begin_array(); + for (auto option : usage.options) { + json.begin_array(); + json.value(option.keyword); + json.value(option.description); + json.end_array(); + } json.end_array(); + json.entry("postscript", usage.postscript); + json.end_object(); } json.end_array(); - json.entry("postscript", usage.postscript); - json.end_object(); } - json.end_array(); json.entry("experimental_flag", experimental_flag); json.end_object(); } diff --git a/kernel/register.h b/kernel/register.h index 74c2a1e4a..e7d3250a3 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -40,8 +40,10 @@ struct PassUsageBlock { struct Pass { std::string pass_name, short_help; + const vector doc_string; const vector pass_usages; Pass(std::string name, std::string short_help = "** document me **", + const vector doc_string = {}, const vector usages = {}); // Prefer overriding 'Pass::on_shutdown()' if possible virtual ~Pass(); @@ -62,6 +64,10 @@ struct Pass return !pass_usages.empty(); } + bool HasDocstring() { + return !doc_string.empty(); + } + struct pre_post_exec_state_t { Pass *parent_pass; int64_t begin_ns; From f5ace20bf6492fe0932a50b3a5c8537e83f69dac Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:33:31 +1200 Subject: [PATCH 048/181] Docs: Improve autoref Fix `help $cell` type references, as well as actually implement the fallback to yoscrypt. --- docs/source/conf.py | 6 +++++- docs/util/cmdref.py | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 490fc523e..813befa47 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -43,8 +43,12 @@ html_static_path = ['_static', "_images"] # default to no highlight highlight_language = 'none' -# default single quotes to attempt auto reference, or fallback to code +# default single quotes to attempt auto reference, or fallback to yoscrypt default_role = 'autoref' +rst_prolog = """ +.. role:: yoscrypt(code) + :language: yoscrypt +""" extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex'] diff --git a/docs/util/cmdref.py b/docs/util/cmdref.py index 203d9369e..da27860b3 100644 --- a/docs/util/cmdref.py +++ b/docs/util/cmdref.py @@ -715,10 +715,18 @@ class CellDomain(CommandDomain): def autoref(name, rawtext: str, text: str, lineno, inliner: Inliner, options=None, content=None): - role = 'cell:ref' if text[0] == '$' else 'cmd:ref' - if text.startswith("help ") and text.count(' ') == 1: - _, cmd = text.split(' ', 1) - text = f'{text} <{cmd}>' + words = text.split(' ') + if len(words) == 2 and words[0] == "help": + IsLinkable = True + thing = words[1] + else: + IsLinkable = len(words) == 1 and words[0][0] != '-' + thing = words[0] + if IsLinkable: + role = 'cell:ref' if thing[0] == '$' else 'cmd:ref' + text = f'{text} <{thing}>' + else: + role = 'yoscrypt' return inliner.interpreted(rawtext, text, role, lineno) def setup(app: Sphinx): From 3718f916f3ec53e6a7a34e6729bad6ad91ba5b0f Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:33:31 +1200 Subject: [PATCH 049/181] Makefile: Add cmds.json to docs/prep --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6dc16fdb2..f8b8a0c91 100644 --- a/Makefile +++ b/Makefile @@ -1095,7 +1095,7 @@ docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep -docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage docs/gen/functional_ir +docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/source/generated/cmds.json docs/gen docs/usage docs/gen/functional_ir DOC_TARGET ?= html docs: docs/prep From 3bef122a3f1830fdd88fadc48b2b568a94517cf5 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:11 +1200 Subject: [PATCH 050/181] WIP docs: Proto log_help Define `PrettyHelp` class with methods for declaring different parts of help message. Currently able to produce standard help messages as expected. Updates chformal to use (only) the new help_v2. Currently makes use of a global static to track the current help context, allowing register.h to live in blissful ignorance and instead rely on help_v2 implementations calling `auto *help = PrettyHelp::get_current();` and `return true;` to minimise impact on rebuilds (i.e. not requiring every source file to be recompiled). --- Makefile | 1 + kernel/log_help.cc | 116 +++++++++++++++++++++++++++++++++++++++ kernel/log_help.h | 51 +++++++++++++++++ kernel/register.cc | 118 +++++++--------------------------------- kernel/register.h | 27 +-------- passes/cmds/chformal.cc | 105 +++++++++++++++++------------------ 6 files changed, 242 insertions(+), 176 deletions(-) create mode 100644 kernel/log_help.cc create mode 100644 kernel/log_help.h diff --git a/Makefile b/Makefile index f8b8a0c91..2120dbdea 100644 --- a/Makefile +++ b/Makefile @@ -634,6 +634,7 @@ $(eval $(call add_include_file,frontends/blif/blifparse.h)) $(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 kernel/io.o kernel/gzip.o +OBJS += kernel/log_help.o OBJS += kernel/binding.o kernel/tclapi.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 kernel/sexpr.o OBJS += kernel/drivertools.o kernel/functional.o diff --git a/kernel/log_help.cc b/kernel/log_help.cc new file mode 100644 index 000000000..fe9829af1 --- /dev/null +++ b/kernel/log_help.cc @@ -0,0 +1,116 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2025 Krystine Dawn + * + * 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/log_help.h" + +USING_YOSYS_NAMESPACE + +#define MAX_LINE_LEN 80 +void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) { + if (pass_str.empty()) + return; + std::istringstream iss(pass_str); + if (leading_newline) + log("\n"); + for (std::string line; std::getline(iss, line);) { + log("%s", indent_str.c_str()); + auto curr_len = indent_str.length(); + std::istringstream lss(line); + for (std::string word; std::getline(lss, word, ' ');) { + while (word[0] == '`' && word.back() == '`') + word = word.substr(1, word.length()-2); + if (curr_len + word.length() >= MAX_LINE_LEN-1) { + curr_len = 0; + log("\n%s", indent_str.c_str()); + } + if (word.length()) { + log("%s ", word.c_str()); + curr_len += word.length() + 1; + } + } + log("\n"); + } +} +void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) { + std::string indent_str(indent*4, ' '); + log_pass_str(pass_str, indent_str, leading_newline); +} + +PrettyHelp *current_help = nullptr; + +PrettyHelp::PrettyHelp() +{ + prior = current_help; + current_help = this; +} + +PrettyHelp::~PrettyHelp() +{ + current_help = prior; +} + +PrettyHelp *PrettyHelp::get_current() +{ + if (current_help != nullptr) + return current_help; + else + return new PrettyHelp(); +} + +bool PrettyHelp::has_content() +{ + return false; +} + +void PrettyHelp::usage(const string &usage) +{ + log_pass_str(usage, current_indent+1, true); + log("\n"); +} + +void PrettyHelp::option(const string &option, const string &description) +{ + log_pass_str(option, current_indent); + if (description.length()) { + log_pass_str(description, current_indent+1); + log("\n"); + } +} + +void PrettyHelp::codeblock(const string &code, const string &) +{ + log("%s\n", code.c_str()); +} + +void PrettyHelp::paragraph(const string &text) +{ + log_pass_str(text, current_indent); + log("\n"); +} + +void PrettyHelp::optiongroup(const string &) +{ + current_indent += 1; +} + +void PrettyHelp::endgroup() +{ + current_indent -= 1; + log_assert(current_indent >= 0); +} diff --git a/kernel/log_help.h b/kernel/log_help.h new file mode 100644 index 000000000..bc62b20a6 --- /dev/null +++ b/kernel/log_help.h @@ -0,0 +1,51 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2025 Krystine Dawn + * + * 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 LOG_HELP_H +#define LOG_HELP_H + +#include "kernel/yosys_common.h" +#include "kernel/json.h" + +YOSYS_NAMESPACE_BEGIN + +class PrettyHelp +{ + PrettyHelp *prior = nullptr; + int current_indent = 0; +public: + PrettyHelp(); + ~PrettyHelp(); + + static PrettyHelp *get_current(); + + bool has_content(); + + void usage(const string &usage); + void option(const string &option, const string &description = ""); + void codeblock(const string &code, const string &language = "none"); + void paragraph(const string &text); + + void optiongroup(const string &group = ""); + void endgroup(); +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/register.cc b/kernel/register.cc index 8e93e9b42..2f203fb6d 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -21,6 +21,7 @@ #include "kernel/satgen.h" #include "kernel/json.h" #include "kernel/gzip.h" +#include "kernel/log_help.h" #include #include @@ -41,7 +42,7 @@ std::map backend_register; std::vector Frontend::next_args; -Pass::Pass(std::string name, std::string short_help, const vector doc_string, const vector usages) : pass_name(name), short_help(short_help), doc_string(doc_string), pass_usages(usages) +Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help) { next_queued_pass = first_queued_pass; first_queued_pass = this; @@ -114,79 +115,20 @@ void Pass::post_execute(Pass::pre_post_exec_state_t state) current_pass->runtime_ns -= time_ns; } -#define MAX_LINE_LEN 80 -void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) { - if (pass_str.empty()) - return; - std::istringstream iss(pass_str); - if (leading_newline) - log("\n"); - for (std::string line; std::getline(iss, line);) { - log("%s", indent_str.c_str()); - auto curr_len = indent_str.length(); - std::istringstream lss(line); - for (std::string word; std::getline(lss, word, ' ');) { - while (word[0] == '`' && word.back() == '`') - word = word.substr(1, word.length()-2); - if (curr_len + word.length() >= MAX_LINE_LEN-1) { - curr_len = 0; - log("\n%s", indent_str.c_str()); - } - if (word.length()) { - log("%s ", word.c_str()); - curr_len += word.length() + 1; - } - } - log("\n"); - } -} -void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) { - std::string indent_str(indent*4, ' '); - log_pass_str(pass_str, indent_str, leading_newline); -} - void Pass::help() { - if (HasUsages()) { - for (auto usage : pass_usages) { - log_pass_str(usage.signature, 1, true); - log_pass_str(usage.description, 0, true); - for (auto option : usage.options) { - log_pass_str(option.keyword, 1, true); - log_pass_str(option.description, 2, false); - } - log_pass_str(usage.postscript, 0, true); - } - log("\n"); - } else if (HasDocstring()) { - log("\n"); - auto print_empty = true; - for (auto doc_line : doc_string) { - if (doc_line.find("..") == 0 && doc_line.find(":: ") != std::string::npos) { - auto command_pos = doc_line.find(":: "); - auto command_str = doc_line.substr(0, command_pos); - if (command_str.compare(".. cmd:usage") == 0) { - log_pass_str(doc_line.substr(command_pos+3), 1); - } else { - print_empty = false; - } - } else if (doc_line.length()) { - std::size_t first_pos = doc_line.find_first_not_of(" \t"); - auto indent_str = doc_line.substr(0, first_pos); - log_pass_str(doc_line, indent_str); - print_empty = true; - } else if (print_empty) { - log("\n"); - } - } - log("\n"); - } else { + if (!help_v2()) { log("\n"); log("No help message for command `%s'.\n", pass_name.c_str()); log("\n"); } } +bool Pass::help_v2() +{ + return false; +} + void Pass::clear_flags() { } @@ -878,13 +820,12 @@ struct HelpPass : public Pass { auto name = it.first; auto pass = it.second; auto title = pass->short_help; - vector usages; auto experimental_flag = pass->experimental_flag; - if (pass->HasUsages() || pass->HasDocstring()) { - for (auto usage : pass->pass_usages) - usages.push_back(usage); - } else { + auto cmd_help = PrettyHelp(); + auto has_pretty_help = pass->help_v2(); + + if (!has_pretty_help) { enum PassUsageState { PUState_signature, PUState_description, @@ -932,7 +873,7 @@ struct HelpPass : public Pass { if (NewUsage) { current_state = PUState_signature; - usages.push_back({}); + // usages.push_back({}); def_strip_count = first_pos; catch_verific = false; } else if (IsDedent) { @@ -945,19 +886,19 @@ struct HelpPass : public Pass { if (IsDefinition && IsIndent && !catch_verific) { current_state = PUState_options; - usages.back().options.push_back(PassOption({stripped_line, ""})); + // usages.back().options.push_back(PassOption({stripped_line, ""})); def_strip_count = first_pos; } else { string *desc_str; if (current_state == PUState_signature) { - desc_str = &(usages.back().signature); + // desc_str = &(usages.back().signature); blank_count += 1; } else if (current_state == PUState_description) { - desc_str = &(usages.back().description); + // desc_str = &(usages.back().description); } else if (current_state == PUState_options) { - desc_str = &(usages.back().options.back().description); + // desc_str = &(usages.back().options.back().description); } else if (current_state == PUState_postscript) { - desc_str = &(usages.back().postscript); + // desc_str = &(usages.back().postscript); } else { log_abort(); } @@ -978,28 +919,7 @@ struct HelpPass : public Pass { // write to json json.name(name.c_str()); json.begin_object(); json.entry("title", title); - if (pass->HasDocstring()) { - json.entry("content", pass->doc_string); - } - if (usages.size()) { - json.name("usages"); json.begin_array(); - for (auto usage : usages) { - json.begin_object(); - json.entry("signature", usage.signature); - json.entry("description", usage.description); - json.name("options"); json.begin_array(); - for (auto option : usage.options) { - json.begin_array(); - json.value(option.keyword); - json.value(option.description); - json.end_array(); - } - json.end_array(); - json.entry("postscript", usage.postscript); - json.end_object(); - } - json.end_array(); - } + // json.entry("content", cmd_help); json.entry("experimental_flag", experimental_flag); json.end_object(); } diff --git a/kernel/register.h b/kernel/register.h index e7d3250a3..fdd361ba6 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -25,30 +25,15 @@ YOSYS_NAMESPACE_BEGIN -struct PassOption { - string keyword; - string description; -}; - -struct PassUsageBlock { - string signature = ""; - string description = ""; - vector options = {}; - string postscript = ""; -}; - struct Pass { std::string pass_name, short_help; - const vector doc_string; - const vector pass_usages; - Pass(std::string name, std::string short_help = "** document me **", - const vector doc_string = {}, - const vector usages = {}); + Pass(std::string name, std::string short_help = "** document me **"); // Prefer overriding 'Pass::on_shutdown()' if possible virtual ~Pass(); virtual void help(); + virtual bool help_v2(); virtual void clear_flags(); virtual void execute(std::vector args, RTLIL::Design *design) = 0; @@ -60,14 +45,6 @@ struct Pass experimental_flag = true; } - bool HasUsages() { - return !pass_usages.empty(); - } - - bool HasDocstring() { - return !doc_string.empty(); - } - struct pre_post_exec_state_t { Pass *parent_pass; int64_t begin_ns; diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index c925f59bf..9eb5b0048 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -70,60 +71,60 @@ static bool is_triggered_check_cell(RTLIL::Cell * cell) } struct ChformalPass : public Pass { - ChformalPass() : Pass("chformal", "change formal constraints of the design", { - { - .signature = "chformal [types] [mode] [options] [selection]", - .description = "Make changes to the formal constraints of the design. The [types] options the type of " - "constraint to operate on. If none of the following options are given, the command " - "will operate on all constraint types:", - .options = { - {"-assert $assert cells, representing assert(...) constraints" - "\n-assume $assume cells, representing assume(...) constraints" - "\n-live $live cells, representing assert(s_eventually ...)" - "\n-fair $fair cells, representing assume(s_eventually ...)" - "\n-cover $cover cells, representing cover() statements", ""}, - {"Additionally chformal will operate on $check cells corresponding to the selected constraint " - "types.", ""}, - } - }, - { - .description = "Exactly one of the following modes must be specified:", - .options = { - {"-remove", - "remove the cells and thus constraints from the design"}, - {"-early", - "bypass FFs that only delay the activation of a constraint. When inputs " - "of the bypassed FFs do not remain stable between clock edges, this may " - "result in unexpected behavior." - }, - {"-delay ", - "delay activation of the constraint by clock cycles" - }, - {"-skip ", - "ignore activation of the constraint in the first clock cycles" - }, - {"-coverenable", - "add cover statements for the enable signals of the constraints" + ChformalPass() : Pass("chformal", "change formal constraints of the design") {} + + bool help_v2() override { + auto *help = PrettyHelp::get_current(); + help->usage("chformal [types] [mode] [options] [selection]"); + help->paragraph( + "Make changes to the formal constraints of the design. The [types] options " + "the type of constraint to operate on. If none of the following options are " + "given, the command will operate on all constraint types:" + ); + + help->optiongroup("[types]"); + help->option("-assert", "`$assert` cells, representing ``assert(...)`` constraints"); + help->option("-assume", "`$assume` cells, representing ``assume(...)`` constraints"); + help->option("-live", "`$live` cells, representing ``assert(s_eventually ...)``"); + help->option("-fair", "`$fair` cells, representing ``assume(s_eventually ...)``"); + help->option("-cover", "`$cover` cells, representing ``cover()`` statements"); + help->paragraph( + "Additionally chformal will operate on `$check` cells corresponding to the " + "selected constraint types." + ); + help->endgroup(); + + help->paragraph("Exactly one of the following modes must be specified:"); + + help->optiongroup("[mode]"); + help->option("-remove", "remove the cells and thus constraints from the design"); + help->option("-early", + "bypass FFs that only delay the activation of a constraint. When inputs " + "of the bypassed FFs do not remain stable between clock edges, this may " + "result in unexpected behavior." + ); + help->option("-delay ", "delay activation of the constraint by clock cycles"); + help->option("-skip ", "ignore activation of the constraint in the first clock cycles"); + help->option("-coverenable", + "add cover statements for the enable signals of the constraints" #ifdef YOSYS_ENABLE_VERIFIC - "\n\nNote: For the Verific frontend it is currently not guaranteed that a " - "reachable SVA statement corresponds to an active enable signal." + "\n\n" + "Note: For the Verific frontend it is currently not guaranteed that a " + "reachable SVA statement corresponds to an active enable signal." #endif - }, - {"-assert2assume" - "\n-assert2cover" - "\n-assume2assert" - "\n-live2fair" - "\n-fair2live", - "change the roles of cells as indicated. these options can be combined" - }, - {"-lower", - "convert each $check cell into an $assert, $assume, $live, $fair or " - "$cover cell. If the $check cell contains a message, also produce a " - "$print cell." - }, - } - }, - }) { } + ); + help->option("-assert2assume"); + help->option("-assert2cover"); + help->option("-assume2assert"); + help->option("-live2fair"); + help->option("-fair2live", "change the roles of cells as indicated. these options can be combined"); + help->option("-lower", + "convert each $check cell into an $assert, $assume, $live, $fair or " + "$cover cell. If the $check cell contains a message, also produce a " + "$print cell." + ); + return true; + } void execute(std::vector args, RTLIL::Design *design) override { bool assert2assume = false; From 10fea26fa9c37f594ca508312878a537c1601665 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:11 +1200 Subject: [PATCH 051/181] log_help: Options can have content Refactor `PrettyHelp::endgroup()` -> `PrettyHelp::close(int levels = 1)`. --- kernel/log_help.cc | 19 +++++++++++++------ kernel/log_help.h | 7 ++++--- passes/cmds/chformal.cc | 16 ++++++++++------ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index fe9829af1..a6c6ad778 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -84,13 +84,14 @@ void PrettyHelp::usage(const string &usage) log("\n"); } -void PrettyHelp::option(const string &option, const string &description) +void PrettyHelp::option(const string &text, const string &description) { - log_pass_str(option, current_indent); + open_option(text); if (description.length()) { - log_pass_str(description, current_indent+1); + log_pass_str(description, current_indent); log("\n"); } + close(1); } void PrettyHelp::codeblock(const string &code, const string &) @@ -104,13 +105,19 @@ void PrettyHelp::paragraph(const string &text) log("\n"); } -void PrettyHelp::optiongroup(const string &) +void PrettyHelp::open_optiongroup(const string &) { current_indent += 1; } -void PrettyHelp::endgroup() +void PrettyHelp::open_option(const string &text) { - current_indent -= 1; + log_pass_str(text, current_indent); + current_indent += 1; +} + +void PrettyHelp::close(int levels) +{ + current_indent -= levels; log_assert(current_indent >= 0); } diff --git a/kernel/log_help.h b/kernel/log_help.h index bc62b20a6..850c1516d 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -38,12 +38,13 @@ public: bool has_content(); void usage(const string &usage); - void option(const string &option, const string &description = ""); + void option(const string &text, const string &description = ""); void codeblock(const string &code, const string &language = "none"); void paragraph(const string &text); - void optiongroup(const string &group = ""); - void endgroup(); + void open_optiongroup(const string &group = ""); + void open_option(const string &text); + void close(int levels = 1); }; YOSYS_NAMESPACE_END diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index 9eb5b0048..1d44df51f 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -82,7 +82,7 @@ struct ChformalPass : public Pass { "given, the command will operate on all constraint types:" ); - help->optiongroup("[types]"); + help->open_optiongroup("[types]"); help->option("-assert", "`$assert` cells, representing ``assert(...)`` constraints"); help->option("-assume", "`$assume` cells, representing ``assume(...)`` constraints"); help->option("-live", "`$live` cells, representing ``assert(s_eventually ...)``"); @@ -92,11 +92,11 @@ struct ChformalPass : public Pass { "Additionally chformal will operate on `$check` cells corresponding to the " "selected constraint types." ); - help->endgroup(); + help->close(); help->paragraph("Exactly one of the following modes must be specified:"); - help->optiongroup("[mode]"); + help->open_optiongroup("[mode]"); help->option("-remove", "remove the cells and thus constraints from the design"); help->option("-early", "bypass FFs that only delay the activation of a constraint. When inputs " @@ -105,14 +105,17 @@ struct ChformalPass : public Pass { ); help->option("-delay ", "delay activation of the constraint by clock cycles"); help->option("-skip ", "ignore activation of the constraint in the first clock cycles"); - help->option("-coverenable", + help->open_option("-coverenable"); + help->paragraph( "add cover statements for the enable signals of the constraints" + ); #ifdef YOSYS_ENABLE_VERIFIC - "\n\n" + help->paragraph( "Note: For the Verific frontend it is currently not guaranteed that a " "reachable SVA statement corresponds to an active enable signal." -#endif ); +#endif + help->close(); help->option("-assert2assume"); help->option("-assert2cover"); help->option("-assume2assert"); @@ -123,6 +126,7 @@ struct ChformalPass : public Pass { "$cover cell. If the $check cell contains a message, also produce a " "$print cell." ); + help->close(); return true; } void execute(std::vector args, RTLIL::Design *design) override From ae3514adfdd5776cdd09b5056ee67468fb37e883 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:12 +1200 Subject: [PATCH 052/181] log_help: Include source_location Use `std::experimental::source_location` because clang support is `??` Add `ENABLE_SOURCE_LOCATION` make variable and corresponding `YOSYS_ENABLE_SOURCE_LOCATION` define. Dummy out the struct if disabled and check for null instead of using `#ifdef` blocks everywhere. --- Makefile | 3 +++ kernel/log_help.cc | 18 ++++++++++++------ kernel/log_help.h | 45 +++++++++++++++++++++++++++++++++++++++------ 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 2120dbdea..c4e62cdb4 100644 --- a/Makefile +++ b/Makefile @@ -532,6 +532,9 @@ LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VE endif endif +ifeq ($(ENABLE_SOURCE_LOCATION),1) +CXXFLAGS += -DYOSYS_ENABLE_SOURCE_LOCATION +endif ifeq ($(ENABLE_COVER),1) CXXFLAGS += -DYOSYS_ENABLE_COVER diff --git a/kernel/log_help.cc b/kernel/log_help.cc index a6c6ad778..2b3fb5dfc 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -78,13 +78,15 @@ bool PrettyHelp::has_content() return false; } -void PrettyHelp::usage(const string &usage) +void PrettyHelp::usage(const string &usage, + const source_location location) { log_pass_str(usage, current_indent+1, true); log("\n"); } -void PrettyHelp::option(const string &text, const string &description) +void PrettyHelp::option(const string &text, const string &description, + const source_location location) { open_option(text); if (description.length()) { @@ -94,23 +96,27 @@ void PrettyHelp::option(const string &text, const string &description) close(1); } -void PrettyHelp::codeblock(const string &code, const string &) +void PrettyHelp::codeblock(const string &code, const string &, + const source_location location) { log("%s\n", code.c_str()); } -void PrettyHelp::paragraph(const string &text) +void PrettyHelp::paragraph(const string &text, + const source_location location) { log_pass_str(text, current_indent); log("\n"); } -void PrettyHelp::open_optiongroup(const string &) +void PrettyHelp::open_optiongroup(const string &, + const source_location location) { current_indent += 1; } -void PrettyHelp::open_option(const string &text) +void PrettyHelp::open_option(const string &text, + const source_location location) { log_pass_str(text, current_indent); current_indent += 1; diff --git a/kernel/log_help.h b/kernel/log_help.h index 850c1516d..ea88c72e0 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -23,6 +23,19 @@ #include "kernel/yosys_common.h" #include "kernel/json.h" +#ifdef YOSYS_ENABLE_SOURCE_LOCATION +#include +using std::experimental::source_location; +#else +struct source_location { // dummy placeholder + int line() const { return 0; } + int column() const { return 0; } + const char* file_name() const { return nullptr; } + const char* function_name() const { return nullptr; } + static const source_location current(...) { return source_location(); } +}; +#endif + YOSYS_NAMESPACE_BEGIN class PrettyHelp @@ -37,13 +50,33 @@ public: bool has_content(); - void usage(const string &usage); - void option(const string &text, const string &description = ""); - void codeblock(const string &code, const string &language = "none"); - void paragraph(const string &text); + void usage( + const string &usage, + const source_location location = source_location::current() + ); + void option( + const string &text, + const string &description = "", + const source_location location = source_location::current() + ); + void codeblock( + const string &code, + const string &language = "none", + const source_location location = source_location::current() + ); + void paragraph( + const string &text, + const source_location location = source_location::current() + ); - void open_optiongroup(const string &group = ""); - void open_option(const string &text); + void open_optiongroup( + const string &group = "", + const source_location location = source_location::current() + ); + void open_option( + const string &text, + const source_location location = source_location::current() + ); void close(int levels = 1); }; From d4498acea7ab9a467207ace291c40d9c3f540c2e Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:12 +1200 Subject: [PATCH 053/181] log_help: Json dumpable Current modes are `LOG` and `LISTING`, which `log()` and store for conversion to json respectively. Add `ContentListing` listing struct to (recursively) contain help data for conversion to a json object to be exported and used elsewhere (e.g. the docs). Rather than formatting as rst we can just export with type information and do the conversion at the destination (i.e. in the python code which loads the domain for autodoc). Implement `PrettyHelp::has_content()`. Provide `PrettyHelp::get_content()` which returns a read-only list of the current content. `PrettyHelp` constructor takes optional `Mode` enum to define format of help content. Updates `PrettyHelp` methods to use a switch case for checking current mode, calling `log_abort()` in the default case (i.e. unsupported mode). --- kernel/log_help.cc | 136 +++++++++++++++++++++++++++++++++++++-------- kernel/log_help.h | 50 +++++++++++++++-- kernel/register.cc | 7 ++- 3 files changed, 164 insertions(+), 29 deletions(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 2b3fb5dfc..137a75a5e 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -21,6 +21,18 @@ USING_YOSYS_NAMESPACE +Json ContentListing::to_json() { + Json::object object; + object["type"] = type; + if (body.length()) object["body"] = body; + if (source_file != nullptr) object["source_file"] = source_file; + if (source_line != 0) object["source_line"] = source_line; + Json::array content_array; + for (auto child : content) content_array.push_back(child->to_json()); + object["content"] = content_array; + return object; +} + #define MAX_LINE_LEN 80 void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) { if (pass_str.empty()) @@ -54,15 +66,22 @@ void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newlin PrettyHelp *current_help = nullptr; -PrettyHelp::PrettyHelp() +PrettyHelp::PrettyHelp(Mode mode) { - prior = current_help; + _prior = current_help; + _mode = mode; + _root_listing = ContentListing({ + .type = "root" + }); + _root_listing.type = "root"; + _current_listing = &_root_listing; + current_help = this; } PrettyHelp::~PrettyHelp() { - current_help = prior; + current_help = _prior; } PrettyHelp *PrettyHelp::get_current() @@ -73,16 +92,21 @@ PrettyHelp *PrettyHelp::get_current() return new PrettyHelp(); } -bool PrettyHelp::has_content() -{ - return false; -} - void PrettyHelp::usage(const string &usage, const source_location location) { - log_pass_str(usage, current_indent+1, true); - log("\n"); + switch (_mode) + { + case LOG: + log_pass_str(usage, _current_indent+1, true); + log("\n"); + break; + case LISTING: + _current_listing->add_content("usage", usage, location); + break; + default: + log_abort(); + } } void PrettyHelp::option(const string &text, const string &description, @@ -90,40 +114,108 @@ void PrettyHelp::option(const string &text, const string &description, { open_option(text); if (description.length()) { - log_pass_str(description, current_indent); - log("\n"); + switch (_mode) + { + case LOG: + log_pass_str(description, _current_indent); + log("\n"); + break; + case LISTING: + _current_listing->add_content("text", description, location); + break; + default: + log_abort(); + } } close(1); } -void PrettyHelp::codeblock(const string &code, const string &, +void PrettyHelp::codeblock(const string &code, const string &language, const source_location location) { - log("%s\n", code.c_str()); + switch (_mode) + { + case LOG: + log("%s\n", code.c_str()); + break; + case LISTING: + _current_listing->add_content("code", code, location); + break; + default: + log_abort(); + } } void PrettyHelp::paragraph(const string &text, const source_location location) { - log_pass_str(text, current_indent); - log("\n"); + switch (_mode) + { + case LOG: + log_pass_str(text, _current_indent); + log("\n"); + break; + case LISTING: + _current_listing->add_content("text", text, location); + break; + default: + log_abort(); + } } -void PrettyHelp::open_optiongroup(const string &, +void PrettyHelp::open_optiongroup(const string &name, const source_location location) { - current_indent += 1; + switch (_mode) + { + case LOG: + break; + case LISTING: + _current_listing->add_content("optiongroup", name, location); + _current_listing = _current_listing->content.back(); + break; + default: + log_abort(); + } + _current_indent += 1; } void PrettyHelp::open_option(const string &text, const source_location location) { - log_pass_str(text, current_indent); - current_indent += 1; + switch (_mode) + { + case LOG: + log_pass_str(text, _current_indent); + break; + case LISTING: + _current_listing->add_content("option", text, location); + _current_listing = _current_listing->content.back(); + break; + default: + log_abort(); + } + _current_indent += 1; } void PrettyHelp::close(int levels) { - current_indent -= levels; - log_assert(current_indent >= 0); + + switch (_mode) + { + case LOG: + _current_indent -= levels; + log_assert(_current_indent >= 0); + break; + case LISTING: + for (int i=0; i= 0); + _current_listing = _current_listing->parent; + log_assert(_current_listing != nullptr); + } + break; + default: + log_abort(); + } } diff --git a/kernel/log_help.h b/kernel/log_help.h index ea88c72e0..dd06e88e5 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -38,17 +38,57 @@ struct source_location { // dummy placeholder YOSYS_NAMESPACE_BEGIN +struct ContentListing { + string type = "root"; + string body = ""; + const char* source_file = nullptr; + int source_line = 0; + vector content = {}; + ContentListing *parent = nullptr; + + void add_content(ContentListing *new_content) { + new_content->parent = this; + content.push_back(new_content); + } + + void add_content(string type, string body, source_location location) { + auto new_content = new ContentListing(); + new_content->type = type; + new_content->body = body; + new_content->source_file = location.file_name(); + new_content->source_line = location.line(); + add_content(new_content); + } + + Json to_json(); +}; + class PrettyHelp { - PrettyHelp *prior = nullptr; - int current_indent = 0; public: - PrettyHelp(); + enum Mode { + LOG, + LISTING, + }; + +private: + PrettyHelp *_prior; + Mode _mode; + + int _current_indent = 0; + ContentListing _root_listing; + ContentListing *_current_listing; +public: + PrettyHelp(Mode mode = LOG); ~PrettyHelp(); static PrettyHelp *get_current(); - bool has_content(); + bool has_content() { return _root_listing.content.size();} + const vector get_content() { + const vector content = _root_listing.content; + return content; + } void usage( const string &usage, @@ -70,7 +110,7 @@ public: ); void open_optiongroup( - const string &group = "", + const string &name = "", const source_location location = source_location::current() ); void open_option( diff --git a/kernel/register.cc b/kernel/register.cc index 2f203fb6d..c9c98acc6 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -822,7 +822,7 @@ struct HelpPass : public Pass { auto title = pass->short_help; auto experimental_flag = pass->experimental_flag; - auto cmd_help = PrettyHelp(); + auto cmd_help = PrettyHelp(PrettyHelp::Mode::LISTING); auto has_pretty_help = pass->help_v2(); if (!has_pretty_help) { @@ -919,7 +919,10 @@ struct HelpPass : public Pass { // write to json json.name(name.c_str()); json.begin_object(); json.entry("title", title); - // json.entry("content", cmd_help); + json.name("content"); json.begin_array(); + for (auto content : cmd_help.get_content()) + json.value(content->to_json()); + json.end_array(); json.entry("experimental_flag", experimental_flag); json.end_object(); } From 7d0bcd8b4888ffdd2df0437a9200d9cbdbfe4a44 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:12 +1200 Subject: [PATCH 054/181] log_help: {add,push,pop}_content helpers --- kernel/log_help.cc | 17 +++++++---------- kernel/log_help.h | 12 ++++++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 137a75a5e..7c9b88f06 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -102,7 +102,7 @@ void PrettyHelp::usage(const string &usage, log("\n"); break; case LISTING: - _current_listing->add_content("usage", usage, location); + add_content("usage", usage, location); break; default: log_abort(); @@ -121,7 +121,7 @@ void PrettyHelp::option(const string &text, const string &description, log("\n"); break; case LISTING: - _current_listing->add_content("text", description, location); + add_content("text", description, location); break; default: log_abort(); @@ -139,7 +139,7 @@ void PrettyHelp::codeblock(const string &code, const string &language, log("%s\n", code.c_str()); break; case LISTING: - _current_listing->add_content("code", code, location); + add_content("code", code, location); break; default: log_abort(); @@ -156,7 +156,7 @@ void PrettyHelp::paragraph(const string &text, log("\n"); break; case LISTING: - _current_listing->add_content("text", text, location); + add_content("text", text, location); break; default: log_abort(); @@ -171,8 +171,7 @@ void PrettyHelp::open_optiongroup(const string &name, case LOG: break; case LISTING: - _current_listing->add_content("optiongroup", name, location); - _current_listing = _current_listing->content.back(); + push_content("optiongroup", name, location); break; default: log_abort(); @@ -189,8 +188,7 @@ void PrettyHelp::open_option(const string &text, log_pass_str(text, _current_indent); break; case LISTING: - _current_listing->add_content("option", text, location); - _current_listing = _current_listing->content.back(); + push_content("option", text, location); break; default: log_abort(); @@ -211,8 +209,7 @@ void PrettyHelp::close(int levels) for (int i=0; i= 0); - _current_listing = _current_listing->parent; - log_assert(_current_listing != nullptr); + pop_content(); } break; default: diff --git a/kernel/log_help.h b/kernel/log_help.h index dd06e88e5..e4ddc52d6 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -78,6 +78,18 @@ private: int _current_indent = 0; ContentListing _root_listing; ContentListing *_current_listing; + + void add_content(string type, string body, source_location location) { + _current_listing->add_content(type, body, location); + } + void push_content(string type, string body, source_location location) { + add_content(type, body, location); + _current_listing = _current_listing->content.back(); + } + void pop_content() { + _current_listing = _current_listing->parent; + log_assert(_current_listing != nullptr); + } public: PrettyHelp(Mode mode = LOG); ~PrettyHelp(); From fe2be07bc81a7b255ef3d87c9ddd78e5ba672746 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:12 +1200 Subject: [PATCH 055/181] Docs: Fix dump_cmds_json for PrettyHelp --- kernel/register.cc | 92 +++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/kernel/register.cc b/kernel/register.cc index c9c98acc6..fff0a1e84 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -827,12 +827,16 @@ struct HelpPass : public Pass { if (!has_pretty_help) { enum PassUsageState { + PUState_none, PUState_signature, PUState_description, PUState_options, - PUState_postscript, + PUState_optionbody, }; + source_location null_source; + string current_buffer = ""; + // dump command help std::ostringstream buf; log_streams.push_back(&buf); @@ -842,17 +846,31 @@ struct HelpPass : public Pass { ss << buf.str(); // parse command help - int blank_count = 0; size_t def_strip_count = 0; - auto current_state = PUState_postscript; + auto current_state = PUState_none; auto catch_verific = false; for (string line; std::getline(ss, line, '\n');) { // find position of first non space character std::size_t first_pos = line.find_first_not_of(" \t"); std::size_t last_pos = line.find_last_not_of(" \t"); if (first_pos == std::string::npos) { + switch (current_state) + { + case PUState_signature: + cmd_help.usage(current_buffer, null_source); + current_state = PUState_none; + break; + case PUState_none: + if (!current_buffer.empty()) cmd_help.codeblock(current_buffer, "none", null_source); + break; + case PUState_optionbody: + if (!current_buffer.empty()) cmd_help.codeblock(current_buffer, "none", null_source); + break; + default: + break; + } // skip empty lines - blank_count += 1; + current_buffer = ""; continue; } @@ -861,58 +879,58 @@ struct HelpPass : public Pass { bool IsDefinition = stripped_line[0] == '-'; IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; bool IsDedent = def_strip_count && first_pos < def_strip_count; - bool IsIndent = first_pos == 2 || first_pos == 4 || first_pos == 8; + bool IsIndent = def_strip_count < first_pos; // line looks like a signature bool IsSignature = stripped_line.find(name) == 0; - // start new usage block if it's a signature and we left the current signature - // or if we are adding new options after we left the options - bool NewUsage = (IsSignature && current_state != PUState_signature) - || (IsDefinition && current_state == PUState_postscript); - - if (NewUsage) { + if (IsSignature) { + if (current_state == PUState_options || current_state == PUState_optionbody) { + cmd_help.close(2); + } + if (current_state == PUState_signature) { + cmd_help.usage(current_buffer, null_source); + current_buffer = ""; + } current_state = PUState_signature; - // usages.push_back({}); def_strip_count = first_pos; catch_verific = false; } else if (IsDedent) { def_strip_count = first_pos; - if (current_state == PUState_signature) - current_state = PUState_description; + if (current_state == PUState_optionbody) { + current_state = PUState_options; + if (!current_buffer.empty()) { + cmd_help.codeblock(current_buffer, "none", null_source); + } + } else - current_state = PUState_postscript; + current_state = PUState_none; } - if (IsDefinition && IsIndent && !catch_verific) { + if (IsDefinition && !catch_verific && current_state != PUState_signature) { + if (current_state == PUState_options || current_state == PUState_optionbody) { + cmd_help.close(1); + } else { + cmd_help.open_optiongroup("", null_source); + } current_state = PUState_options; - // usages.back().options.push_back(PassOption({stripped_line, ""})); + cmd_help.open_option(stripped_line, null_source); def_strip_count = first_pos; } else { - string *desc_str; - if (current_state == PUState_signature) { - // desc_str = &(usages.back().signature); - blank_count += 1; - } else if (current_state == PUState_description) { - // desc_str = &(usages.back().description); - } else if (current_state == PUState_options) { - // desc_str = &(usages.back().options.back().description); - } else if (current_state == PUState_postscript) { - // desc_str = &(usages.back().postscript); - } else { - log_abort(); + if (current_state == PUState_options) { + current_state = PUState_optionbody; } - if (desc_str->empty()) - *desc_str = stripped_line; - else if (catch_verific) - *desc_str += (IsIndent ? "\n" : " ") + stripped_line; - else - *desc_str += (blank_count > 0 ? "\n" : " ") + stripped_line; + if (current_buffer.empty()) + current_buffer = stripped_line; + else if (current_state == PUState_signature && IsIndent) + current_buffer += stripped_line; + else if (current_state == PUState_none) { + current_buffer += "\n" + line; + } else + current_buffer += "\n" + stripped_line; if (stripped_line.compare("Command file parser supports following commands in file:") == 0) catch_verific = true; } - - blank_count = 0; } } From a19f0103ff634941758ca8acb37c198c02de8fec Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:12 +1200 Subject: [PATCH 056/181] Docs: Update cmdref domain Compatible with `dump_cmds_json`. --- docs/util/cmdref.py | 5 +- docs/util/newcmdref.py | 126 +++++++++++++++-------------------------- 2 files changed, 50 insertions(+), 81 deletions(-) diff --git a/docs/util/cmdref.py b/docs/util/cmdref.py index da27860b3..89982d75f 100644 --- a/docs/util/cmdref.py +++ b/docs/util/cmdref.py @@ -162,8 +162,9 @@ class CommandOptionGroupNode(TocNode): except ValueError: cmd, name = '', sig signode['fullname'] = sig - signode['tocname'] = name - signode += addnodes.desc_name(text=name) + if name: + signode['tocname'] = name + signode += addnodes.desc_name(text=name) return signode['fullname'] def add_target_and_index( diff --git a/docs/util/newcmdref.py b/docs/util/newcmdref.py index 4b5995065..0a4874714 100644 --- a/docs/util/newcmdref.py +++ b/docs/util/newcmdref.py @@ -22,31 +22,42 @@ cmd_ext_sig_re = re.compile( \s* $ # and nothing more ''', re.VERBOSE) -@dataclass -class YosysCmdUsage: - signature: str - description: str - options: list[tuple[str,str]] - postscript: str +class YosysCmdContentListing: + type: str + body: str + source_file: str + source_line: int + content: list[YosysCmdContentListing] + + def __init__( + self, + type: str = "", + body: str = "", + source_file: str = "unknown", + source_line: int = 0, + content: list[dict[str]] = [], + ): + self.type = type + self.body = body + self.source_file = source_file + self.source_line = source_line + self.content = [YosysCmdContentListing(**c) for c in content] class YosysCmd: name: str title: str - content: list[str] - usages: list[YosysCmdUsage] + content: list[YosysCmdContentListing] experimental_flag: bool def __init__( self, name:str = "", title:str = "", - content: list[str] = [], - usages: list[dict[str]] = [], + content: list[dict[str]] = [], experimental_flag: bool = False ) -> None: self.name = name self.title = title - self.content = content - self.usages = [YosysCmdUsage(**u) for u in usages] + self.content = [YosysCmdContentListing(**c) for c in content] self.experimental_flag = experimental_flag @property @@ -314,6 +325,8 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): # cmd definition self.add_line(f'.. {domain}:{directive}:: {sig}', source_name, source_line) + if self.object.title: + self.add_line(f' :title: {self.object.title}', source_name, source_line) if self.options.noindex: self.add_line(' :noindex:', source_name) @@ -323,28 +336,33 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): domain = getattr(self, 'domain', self.objtype) source_name = self.object.source_file - for usage in self.object.usages: - self.add_line('', source_name) - if usage.signature: - self.add_line(f' .. {domain}:usage:: {self.name}::{usage.signature}', source_name) + def render(content_list: YosysCmdContentListing, indent: int=0): + content_source = content_list.source_file or source_name + indent_str = ' '*indent + if content_list.type in ['usage', 'optiongroup']: + if content_list.body: + self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::{content_list.body}', content_source) + else: + self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::', content_source) + self.add_line(f'{indent_str} :noindex:', source_name) self.add_line('', source_name) - for line in usage.description.splitlines(): - self.add_line(f' {line}', source_name) + elif content_list.type in ['option']: + self.add_line(f'{indent_str}:{content_list.type} {content_list.body}:', content_source) + elif content_list.type == 'text': + self.add_line(f'{indent_str}{content_list.body}', content_source) self.add_line('', source_name) - if usage.options: - self.add_line(f' .. {domain}:optiongroup:: {self.name}::something', source_name) + elif content_list.type == 'code': + self.add_line(f'{indent_str}::', source_name) self.add_line('', source_name) - for opt, desc in usage.options: - self.add_line(f' :option {opt}: {desc}', source_name) - self.add_line('', source_name) - for line in usage.postscript.splitlines(): - self.add_line(f' {line}', source_name) + self.add_line(f'{indent_str} {content_list.body}', content_source) self.add_line('', source_name) + else: + logger.warning(f"unknown content type '{content_list.type}'") + for content in content_list.content: + render(content, indent+1) - for line in self.object.content: - if line.startswith('..') and ':: ' in line: - line = line.replace(':: ', f':: {self.name}::', 1) - self.add_line(line, source_name) + for content in self.object.content: + render(content) # add additional content (e.g. from document), if present if more_content: @@ -366,56 +384,6 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): return False, [] -class YosysCmdUsageDocumenter(YosysCmdDocumenter): - objtype = 'cmdusage' - priority = 20 - object: YosysCmdUsage - parent: YosysCmd - - def add_directive_header(self, sig: str) -> None: - domain = getattr(self, 'domain', 'cmd') - directive = getattr(self, 'directivetype', 'usage') - name = self.format_name() - sourcename = self.parent.source_file - cmd = self.parent - - # cmd definition - self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) - if self.object.signature: - self.add_line(f' :usage: {self.object.signature}', sourcename) - else: - self.add_line(f' :noindex:', sourcename) - # for usage in self.object.signature.splitlines(): - # self.add_line(f' :usage: {usage}', sourcename) - - # if self.options.linenos: - # self.add_line(f' :source: {cmd.source.split(":")[0]}', sourcename) - # else: - # self.add_line(f' :source: {cmd.source}', sourcename) - # self.add_line(f' :language: verilog', sourcename) - - if self.options.noindex: - self.add_line(' :noindex:', sourcename) - - def add_content(self, more_content: Any | None) -> None: - # set sourcename and add content from attribute documentation - sourcename = self.parent.source_file - startline = self.parent.source_line - - for line in self.object.description.splitlines(): - self.add_line(line, sourcename) - - # add additional content (e.g. from document), if present - if more_content: - for line, src in zip(more_content.data, more_content.items): - self.add_line(line, src[0], src[1]) - - def get_object_members( - self, - want_all: bool - ) -> tuple[bool, list[tuple[str, Any]]]: - return False, [] - def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value('cmds_json', False, 'html', [Path, PosixPath, WindowsPath]) app.setup_extension('sphinx.ext.autodoc') From cb502e75057ab417dabadf02535038fc989c2302 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:12 +1200 Subject: [PATCH 057/181] cmdref: Combine consecutive code blocks Formatting is nicer when there is only one code block instead of multiple in a row, especially in pdf. --- kernel/register.cc | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/kernel/register.cc b/kernel/register.cc index fff0a1e84..ae52c12c9 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -829,7 +829,6 @@ struct HelpPass : public Pass { enum PassUsageState { PUState_none, PUState_signature, - PUState_description, PUState_options, PUState_optionbody, }; @@ -849,6 +848,7 @@ struct HelpPass : public Pass { size_t def_strip_count = 0; auto current_state = PUState_none; auto catch_verific = false; + auto blank_lines = 0; for (string line; std::getline(ss, line, '\n');) { // find position of first non space character std::size_t first_pos = line.find_first_not_of(" \t"); @@ -859,18 +859,16 @@ struct HelpPass : public Pass { case PUState_signature: cmd_help.usage(current_buffer, null_source); current_state = PUState_none; + current_buffer = ""; break; case PUState_none: - if (!current_buffer.empty()) cmd_help.codeblock(current_buffer, "none", null_source); - break; case PUState_optionbody: - if (!current_buffer.empty()) cmd_help.codeblock(current_buffer, "none", null_source); + blank_lines += 1; break; default: break; } // skip empty lines - current_buffer = ""; continue; } @@ -886,11 +884,15 @@ struct HelpPass : public Pass { if (IsSignature) { if (current_state == PUState_options || current_state == PUState_optionbody) { + cmd_help.codeblock(current_buffer, "none", null_source); + current_buffer = ""; cmd_help.close(2); - } - if (current_state == PUState_signature) { + } else if (current_state == PUState_signature) { cmd_help.usage(current_buffer, null_source); current_buffer = ""; + } else if (current_state == PUState_none && !current_buffer.empty()) { + cmd_help.codeblock(current_buffer, "none", null_source); + current_buffer = ""; } current_state = PUState_signature; def_strip_count = first_pos; @@ -901,6 +903,7 @@ struct HelpPass : public Pass { current_state = PUState_options; if (!current_buffer.empty()) { cmd_help.codeblock(current_buffer, "none", null_source); + current_buffer = ""; } } else @@ -908,6 +911,10 @@ struct HelpPass : public Pass { } if (IsDefinition && !catch_verific && current_state != PUState_signature) { + if (!current_buffer.empty()) { + cmd_help.codeblock(current_buffer, "none", null_source); + current_buffer = ""; + } if (current_state == PUState_options || current_state == PUState_optionbody) { cmd_help.close(1); } else { @@ -925,12 +932,13 @@ struct HelpPass : public Pass { else if (current_state == PUState_signature && IsIndent) current_buffer += stripped_line; else if (current_state == PUState_none) { - current_buffer += "\n" + line; + current_buffer += (blank_lines > 0 ? "\n\n" : "\n") + line; } else - current_buffer += "\n" + stripped_line; + current_buffer += (blank_lines > 0 ? "\n\n" : "\n") + stripped_line; if (stripped_line.compare("Command file parser supports following commands in file:") == 0) catch_verific = true; } + blank_lines = 0; } } From bab867f3477c921dc508e0b2311f8df157d98528 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:13 +1200 Subject: [PATCH 058/181] Makefile: Drop cmds rst from docs/prep --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c4e62cdb4..5690410a7 100644 --- a/Makefile +++ b/Makefile @@ -1099,7 +1099,7 @@ docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep -docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/source/generated/cmds.json docs/gen docs/usage docs/gen/functional_ir +docs/prep: docs/source/generated/cells.json docs/source/generated/cmds.json docs/gen docs/usage docs/gen/functional_ir DOC_TARGET ?= html docs: docs/prep From 3691bb674caa2cc8c7dbdd441aa95a27b1e99e75 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:13 +1200 Subject: [PATCH 059/181] log_help: Better location tracking Assign root location in call to `PrettyHelp::get_current()`. Set default `source_file` to `"unknown"`, since that appears to be the default value rather than `nullptr`. --- kernel/log_help.cc | 13 +++++++------ kernel/log_help.h | 10 ++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 7c9b88f06..ada51be60 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -25,7 +25,7 @@ Json ContentListing::to_json() { Json::object object; object["type"] = type; if (body.length()) object["body"] = body; - if (source_file != nullptr) object["source_file"] = source_file; + if (strcmp(source_file, "unknown") != 0) object["source_file"] = source_file; if (source_line != 0) object["source_line"] = source_line; Json::array content_array; for (auto child : content) content_array.push_back(child->to_json()); @@ -84,12 +84,13 @@ PrettyHelp::~PrettyHelp() current_help = _prior; } -PrettyHelp *PrettyHelp::get_current() +PrettyHelp *PrettyHelp::get_current(source_location location) { - if (current_help != nullptr) - return current_help; - else - return new PrettyHelp(); + if (current_help == nullptr) + new PrettyHelp(); + current_help->_root_listing.source_file = location.file_name(); + current_help->_root_listing.source_line = location.line(); + return current_help; } void PrettyHelp::usage(const string &usage, diff --git a/kernel/log_help.h b/kernel/log_help.h index e4ddc52d6..fec888548 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -30,8 +30,8 @@ using std::experimental::source_location; struct source_location { // dummy placeholder int line() const { return 0; } int column() const { return 0; } - const char* file_name() const { return nullptr; } - const char* function_name() const { return nullptr; } + const char* file_name() const { return "unknown"; } + const char* function_name() const { return "unknown"; } static const source_location current(...) { return source_location(); } }; #endif @@ -41,7 +41,7 @@ YOSYS_NAMESPACE_BEGIN struct ContentListing { string type = "root"; string body = ""; - const char* source_file = nullptr; + const char* source_file = "unknown"; int source_line = 0; vector content = {}; ContentListing *parent = nullptr; @@ -94,7 +94,7 @@ public: PrettyHelp(Mode mode = LOG); ~PrettyHelp(); - static PrettyHelp *get_current(); + static PrettyHelp *get_current(source_location location = source_location::current()); bool has_content() { return _root_listing.content.size();} const vector get_content() { @@ -102,6 +102,8 @@ public: return content; } + const char* source_file() const { return _root_listing.source_file; } + void usage( const string &usage, const source_location location = source_location::current() From 00b6d96aeeb983c3a72bb922d313d6853b2ede20 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:13 +1200 Subject: [PATCH 060/181] docs: Working cmdref groups Also adds note on source location if available. --- docs/util/cmdref.py | 45 +++++-------------------------- docs/util/newcmdref.py | 61 +++++++++++++++++++----------------------- kernel/register.cc | 1 + 3 files changed, 35 insertions(+), 72 deletions(-) diff --git a/docs/util/cmdref.py b/docs/util/cmdref.py index 89982d75f..e471ea46d 100644 --- a/docs/util/cmdref.py +++ b/docs/util/cmdref.py @@ -115,12 +115,11 @@ class CommandUsageNode(TocNode): ] def handle_signature(self, sig: str, signode: addnodes.desc_signature): - try: - cmd, use = sig.split('::') - except ValueError: - cmd, use = sig, '' - signode['fullname'] = sig - usage = self.options.get('usage', use or sig) + parts = sig.split('::') + if len(parts) > 2: parts.pop(0) + use = parts[-1] + signode['fullname'] = '::'.join(parts) + usage = self.options.get('usage', use) if usage: signode['tocname'] = usage signode += addnodes.desc_name(text=usage) @@ -145,46 +144,16 @@ class CommandUsageNode(TocNode): idx, 1)) -class CommandOptionGroupNode(TocNode): +class CommandOptionGroupNode(CommandUsageNode): """A custom node that describes a group of related options""" name = 'cmdoptiongroup' - option_spec = TocNode.option_spec + option_spec = CommandUsageNode.option_spec doc_field_types = [ Field('opt', ('option',), label='', rolename='option') ] - - def handle_signature(self, sig: str, signode: addnodes.desc_signature): - try: - cmd, name = sig.split('::') - except ValueError: - cmd, name = '', sig - signode['fullname'] = sig - if name: - signode['tocname'] = name - signode += addnodes.desc_name(text=name) - return signode['fullname'] - - def add_target_and_index( - self, - name: str, - sig: str, - signode: addnodes.desc_signature - ) -> None: - idx = ".".join(name.split("::")) - signode['ids'].append(idx) - if 'noindex' not in self.options: - tocname: str = signode.get('tocname', name) - objs = self.env.domaindata[self.domain]['objects'] - # (name, sig, typ, docname, anchor, prio) - objs.append((name, - tocname, - type(self).name, - self.env.docname, - idx, - 1)) def transform_content(self, contentnode: addnodes.desc_content) -> None: """hack `:option -thing: desc` into a proper option list""" diff --git a/docs/util/newcmdref.py b/docs/util/newcmdref.py index 0a4874714..d4b6aff82 100644 --- a/docs/util/newcmdref.py +++ b/docs/util/newcmdref.py @@ -16,7 +16,8 @@ logger = logging.getLogger(__name__) # cmd signature cmd_ext_sig_re = re.compile( - r'''^ ([\w$._]+?) # module name + r'''^ ([\w/]+::)? # optional group + ([\w$._]+?) # module name (?:\.([\w_]+))? # optional: thing name (::[\w_]+)? # attribute \s* $ # and nothing more @@ -47,23 +48,22 @@ class YosysCmd: name: str title: str content: list[YosysCmdContentListing] + source_file: str experimental_flag: bool def __init__( self, name:str = "", title:str = "", content: list[dict[str]] = [], + source_file:str = 'unknown', experimental_flag: bool = False ) -> None: self.name = name self.title = title self.content = [YosysCmdContentListing(**c) for c in content] + self.source_file = source_file self.experimental_flag = experimental_flag - @property - def source_file(self) -> str: - return "" - @property def source_line(self) -> int: return 0 @@ -86,7 +86,7 @@ class YosysCmdGroupDocumenter(Documenter): def cmd_lib(self) -> dict[str, list[str] | dict[str]]: if not self.__cmd_lib: self.__cmd_lib = {} - cmds_obj: dict[str, dict[str, list[str] | dict[str]]] + cmds_obj: dict[str, dict[str, dict[str]]] try: with open(self.config.cmds_json, "r") as f: cmds_obj = json.loads(f.read()) @@ -96,8 +96,19 @@ class YosysCmdGroupDocumenter(Documenter): type = 'cmdref', subtype = 'cmd_lib' ) - else: - for (name, obj) in cmds_obj.get(self.lib_key, {}).items(): + cmds_obj = {} + for (name, obj) in cmds_obj.get('cmds', {}).items(): + if self.lib_key == 'groups': + source_file: str = obj.get('source_file', 'unknown') + if source_file == 'unknown': + source_group = 'unknown' + else: + source_group = str(Path(source_file).parent) + try: + self.__cmd_lib[source_group].append(name) + except KeyError: + self.__cmd_lib[source_group] = [name,] + else: self.__cmd_lib[name] = obj return self.__cmd_lib @@ -139,25 +150,10 @@ class YosysCmdGroupDocumenter(Documenter): return self.modname def add_directive_header(self, sig: str) -> None: - domain = getattr(self, 'domain', 'cmd') - directive = getattr(self, 'directivetype', 'group') - name = self.format_name() - sourcename = self.get_sourcename() - cmd_list = self.object - - # cmd definition - self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) - self.add_line(f' :caption: {name}', sourcename) - - if self.options.noindex: - self.add_line(' :noindex:', sourcename) + pass def add_content(self, more_content: Any | None) -> None: - # groups have no native content - # add additional content (e.g. from document), if present - if more_content: - for line, src in zip(more_content.data, more_content.items): - self.add_line(line, src[0], src[1]) + pass def filter_members( self, @@ -290,13 +286,14 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): def parse_name(self) -> bool: try: matched = cmd_ext_sig_re.match(self.name) - modname, thing, attribute = matched.groups() + group, modname, thing, attribute = matched.groups() except AttributeError: logger.warning(('invalid signature for auto%s (%r)') % (self.objtype, self.name), type='cmdref') return False self.modname = modname + self.groupname = group or '' self.attribute = attribute or '' self.fullname = ((self.modname) + (thing or '')) @@ -364,19 +361,15 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): for content in self.object.content: render(content) + if self.get_sourcename() != 'unknown': + self.add_line('\n', source_name) + self.add_line(f'.. note:: Help text automatically generated from :file:`{source_name}`', source_name) + # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) - # fields - self.add_line('\n', source_name) - field_attrs = ["properties", ] - for field in field_attrs: - attr = getattr(self.object, field, []) - for val in attr: - self.add_line(f':{field} {val}:', source_name) - def get_object_members( self, want_all: bool diff --git a/kernel/register.cc b/kernel/register.cc index ae52c12c9..e00678c81 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -949,6 +949,7 @@ struct HelpPass : public Pass { for (auto content : cmd_help.get_content()) json.value(content->to_json()); json.end_array(); + json.entry("source_file", cmd_help.source_file()); json.entry("experimental_flag", experimental_flag); json.end_object(); } From 8d1b9b1c1feecdb7d373e504d96be9d4bf7add55 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:13 +1200 Subject: [PATCH 061/181] dump_cmds_json: Fix auto formatting - Command list for script passes - Check buffer after loop - Close options properly - Ignore mismatched sigs e.g. `fsm_detect` on `fsm` - Require double blank line before new signature --- kernel/register.cc | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/kernel/register.cc b/kernel/register.cc index e00678c81..1f0768417 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -848,7 +848,7 @@ struct HelpPass : public Pass { size_t def_strip_count = 0; auto current_state = PUState_none; auto catch_verific = false; - auto blank_lines = 0; + auto blank_lines = 2; for (string line; std::getline(ss, line, '\n');) { // find position of first non space character std::size_t first_pos = line.find_first_not_of(" \t"); @@ -880,9 +880,9 @@ struct HelpPass : public Pass { bool IsIndent = def_strip_count < first_pos; // line looks like a signature - bool IsSignature = stripped_line.find(name) == 0; + bool IsSignature = stripped_line.find(name) == 0 && (stripped_line.length() == name.length() || stripped_line.at(name.size()) == ' '); - if (IsSignature) { + if (IsSignature && first_pos <= 4 && (blank_lines >= 2 || current_state == PUState_signature)) { if (current_state == PUState_options || current_state == PUState_optionbody) { cmd_help.codeblock(current_buffer, "none", null_source); current_buffer = ""; @@ -900,14 +900,20 @@ struct HelpPass : public Pass { } else if (IsDedent) { def_strip_count = first_pos; if (current_state == PUState_optionbody) { - current_state = PUState_options; if (!current_buffer.empty()) { cmd_help.codeblock(current_buffer, "none", null_source); current_buffer = ""; } - } - else + if (IsIndent) { + current_state = PUState_options; + cmd_help.close(1); + } else { + current_state = PUState_none; + cmd_help.close(2); + } + } else { current_state = PUState_none; + } } if (IsDefinition && !catch_verific && current_state != PUState_signature) { @@ -940,6 +946,11 @@ struct HelpPass : public Pass { } blank_lines = 0; } + + if (!current_buffer.empty()) { + cmd_help.codeblock(current_buffer, "none", null_source); + current_buffer = ""; + } } // write to json From 929c437b264dd2b305176cd992a2467090fb9bf4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:13 +1200 Subject: [PATCH 062/181] Docs: Group commands Removes group parsing from command ref domain, instead relying on a 'groups' object in the cmds.json file. `docs/source/cmd` is no longer ignored or cleaned. --- docs/.gitignore | 1 - docs/Makefile | 2 +- docs/source/cmd/index_backends.rst | 5 ++++ docs/source/cmd/index_formal.rst | 5 ++++ docs/source/cmd/index_frontends.rst | 5 ++++ docs/source/cmd/index_kernel.rst | 5 ++++ docs/source/cmd/index_other.rst | 7 +++++ docs/source/cmd/index_passes_cmds.rst | 5 ++++ docs/source/cmd/index_passes_equiv.rst | 5 ++++ docs/source/cmd/index_passes_fsm.rst | 5 ++++ docs/source/cmd/index_passes_hierarchy.rst | 5 ++++ docs/source/cmd/index_passes_memory.rst | 5 ++++ docs/source/cmd/index_passes_opt.rst | 5 ++++ docs/source/cmd/index_passes_pmgen.rst | 5 ++++ docs/source/cmd/index_passes_proc.rst | 5 ++++ docs/source/cmd/index_passes_sat.rst | 5 ++++ docs/source/cmd/index_passes_techmap.rst | 5 ++++ docs/source/cmd/index_passes_tests.rst | 5 ++++ docs/source/cmd/index_techlibs.rst | 5 ++++ docs/source/cmd_ref.rst | 16 +++++++---- docs/util/newcmdref.py | 33 +++++++++------------- 21 files changed, 113 insertions(+), 26 deletions(-) create mode 100644 docs/source/cmd/index_backends.rst create mode 100644 docs/source/cmd/index_formal.rst create mode 100644 docs/source/cmd/index_frontends.rst create mode 100644 docs/source/cmd/index_kernel.rst create mode 100644 docs/source/cmd/index_other.rst create mode 100644 docs/source/cmd/index_passes_cmds.rst create mode 100644 docs/source/cmd/index_passes_equiv.rst create mode 100644 docs/source/cmd/index_passes_fsm.rst create mode 100644 docs/source/cmd/index_passes_hierarchy.rst create mode 100644 docs/source/cmd/index_passes_memory.rst create mode 100644 docs/source/cmd/index_passes_opt.rst create mode 100644 docs/source/cmd/index_passes_pmgen.rst create mode 100644 docs/source/cmd/index_passes_proc.rst create mode 100644 docs/source/cmd/index_passes_sat.rst create mode 100644 docs/source/cmd/index_passes_techmap.rst create mode 100644 docs/source/cmd/index_passes_tests.rst create mode 100644 docs/source/cmd/index_techlibs.rst diff --git a/docs/.gitignore b/docs/.gitignore index 65bbcdeae..09bb59048 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,5 +1,4 @@ /build/ -/source/cmd /source/generated /source/_images/**/*.log /source/_images/**/*.aux diff --git a/docs/Makefile b/docs/Makefile index a8874bb83..fb3e03b79 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -47,7 +47,7 @@ help: .PHONY: clean clean: clean-examples rm -rf $(BUILDDIR)/* - rm -rf source/cmd util/__pycache__ + rm -rf util/__pycache__ rm -rf source/generated $(MAKE) -C source/_images clean diff --git a/docs/source/cmd/index_backends.rst b/docs/source/cmd/index_backends.rst new file mode 100644 index 000000000..fada942d8 --- /dev/null +++ b/docs/source/cmd/index_backends.rst @@ -0,0 +1,5 @@ +backends +------------------ + +.. autocmdgroup:: backends + :members: diff --git a/docs/source/cmd/index_formal.rst b/docs/source/cmd/index_formal.rst new file mode 100644 index 000000000..53701a437 --- /dev/null +++ b/docs/source/cmd/index_formal.rst @@ -0,0 +1,5 @@ +formal +------------------ + +.. autocmdgroup:: formal + :members: diff --git a/docs/source/cmd/index_frontends.rst b/docs/source/cmd/index_frontends.rst new file mode 100644 index 000000000..cf046e2db --- /dev/null +++ b/docs/source/cmd/index_frontends.rst @@ -0,0 +1,5 @@ +frontends +------------------ + +.. autocmdgroup:: frontends + :members: diff --git a/docs/source/cmd/index_kernel.rst b/docs/source/cmd/index_kernel.rst new file mode 100644 index 000000000..c3f323e16 --- /dev/null +++ b/docs/source/cmd/index_kernel.rst @@ -0,0 +1,5 @@ +kernel +------------------ + +.. autocmdgroup:: kernel + :members: diff --git a/docs/source/cmd/index_other.rst b/docs/source/cmd/index_other.rst new file mode 100644 index 000000000..abd41bcdc --- /dev/null +++ b/docs/source/cmd/index_other.rst @@ -0,0 +1,7 @@ +Other commands +============== + +Unknown source location + +.. autocmdgroup:: unknown + :members: diff --git a/docs/source/cmd/index_passes_cmds.rst b/docs/source/cmd/index_passes_cmds.rst new file mode 100644 index 000000000..6d2c5026d --- /dev/null +++ b/docs/source/cmd/index_passes_cmds.rst @@ -0,0 +1,5 @@ +passes/cmds +------------------ + +.. autocmdgroup:: passes/cmds + :members: diff --git a/docs/source/cmd/index_passes_equiv.rst b/docs/source/cmd/index_passes_equiv.rst new file mode 100644 index 000000000..5b0bc2741 --- /dev/null +++ b/docs/source/cmd/index_passes_equiv.rst @@ -0,0 +1,5 @@ +passes/equiv +------------------ + +.. autocmdgroup:: passes/equiv + :members: diff --git a/docs/source/cmd/index_passes_fsm.rst b/docs/source/cmd/index_passes_fsm.rst new file mode 100644 index 000000000..ddb2a9168 --- /dev/null +++ b/docs/source/cmd/index_passes_fsm.rst @@ -0,0 +1,5 @@ +passes/fsm +------------------ + +.. autocmdgroup:: passes/fsm + :members: diff --git a/docs/source/cmd/index_passes_hierarchy.rst b/docs/source/cmd/index_passes_hierarchy.rst new file mode 100644 index 000000000..1d9f237c7 --- /dev/null +++ b/docs/source/cmd/index_passes_hierarchy.rst @@ -0,0 +1,5 @@ +passes/hierarchy +------------------ + +.. autocmdgroup:: passes/hierarchy + :members: diff --git a/docs/source/cmd/index_passes_memory.rst b/docs/source/cmd/index_passes_memory.rst new file mode 100644 index 000000000..74c044135 --- /dev/null +++ b/docs/source/cmd/index_passes_memory.rst @@ -0,0 +1,5 @@ +passes/memory +------------------ + +.. autocmdgroup:: passes/memory + :members: diff --git a/docs/source/cmd/index_passes_opt.rst b/docs/source/cmd/index_passes_opt.rst new file mode 100644 index 000000000..66d78b74a --- /dev/null +++ b/docs/source/cmd/index_passes_opt.rst @@ -0,0 +1,5 @@ +passes/opt +------------------ + +.. autocmdgroup:: passes/opt + :members: diff --git a/docs/source/cmd/index_passes_pmgen.rst b/docs/source/cmd/index_passes_pmgen.rst new file mode 100644 index 000000000..aac8c1b47 --- /dev/null +++ b/docs/source/cmd/index_passes_pmgen.rst @@ -0,0 +1,5 @@ +passes/pmgen +------------------ + +.. autocmdgroup:: passes/pmgen + :members: diff --git a/docs/source/cmd/index_passes_proc.rst b/docs/source/cmd/index_passes_proc.rst new file mode 100644 index 000000000..05081f29a --- /dev/null +++ b/docs/source/cmd/index_passes_proc.rst @@ -0,0 +1,5 @@ +passes/proc +------------------ + +.. autocmdgroup:: passes/proc + :members: diff --git a/docs/source/cmd/index_passes_sat.rst b/docs/source/cmd/index_passes_sat.rst new file mode 100644 index 000000000..af041b040 --- /dev/null +++ b/docs/source/cmd/index_passes_sat.rst @@ -0,0 +1,5 @@ +passes/sat +------------------ + +.. autocmdgroup:: passes/sat + :members: diff --git a/docs/source/cmd/index_passes_techmap.rst b/docs/source/cmd/index_passes_techmap.rst new file mode 100644 index 000000000..fa9b54575 --- /dev/null +++ b/docs/source/cmd/index_passes_techmap.rst @@ -0,0 +1,5 @@ +passes/techmap +------------------ + +.. autocmdgroup:: passes/techmap + :members: diff --git a/docs/source/cmd/index_passes_tests.rst b/docs/source/cmd/index_passes_tests.rst new file mode 100644 index 000000000..72453f55f --- /dev/null +++ b/docs/source/cmd/index_passes_tests.rst @@ -0,0 +1,5 @@ +passes/tests +------------------ + +.. autocmdgroup:: passes/tests + :members: diff --git a/docs/source/cmd/index_techlibs.rst b/docs/source/cmd/index_techlibs.rst new file mode 100644 index 000000000..aeeb17c90 --- /dev/null +++ b/docs/source/cmd/index_techlibs.rst @@ -0,0 +1,5 @@ +techlibs +------------------ + +.. autocmdgroup:: techlibs + :members: diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index acf2d1d41..bc0707d67 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -8,9 +8,15 @@ Command line reference :start-at: Usage .. toctree:: - :caption: Command reference - :maxdepth: 1 - :glob: + :caption: Command reference + :maxdepth: 2 + :glob: - /appendix/env_vars - /cmd/* + /appendix/env_vars + /cmd/index_backends + /cmd/index_frontends + /cmd/index_kernel + /cmd/index_formal + /cmd/index_passes* + /cmd/index_techlibs + /cmd/index_other diff --git a/docs/util/newcmdref.py b/docs/util/newcmdref.py index d4b6aff82..6db7493af 100644 --- a/docs/util/newcmdref.py +++ b/docs/util/newcmdref.py @@ -48,25 +48,30 @@ class YosysCmd: name: str title: str content: list[YosysCmdContentListing] + group: str source_file: str + source_line: int + source_func: str experimental_flag: bool def __init__( self, name:str = "", title:str = "", content: list[dict[str]] = [], - source_file:str = 'unknown', + group: str = 'unknown', + source_file: str = "", + source_line: int = 0, + source_func: str = "", experimental_flag: bool = False ) -> None: self.name = name self.title = title self.content = [YosysCmdContentListing(**c) for c in content] + self.group = group self.source_file = source_file + self.source_line = source_line + self.source_func = source_func self.experimental_flag = experimental_flag - - @property - def source_line(self) -> int: - return 0 class YosysCmdGroupDocumenter(Documenter): objtype = 'cmdgroup' @@ -97,19 +102,8 @@ class YosysCmdGroupDocumenter(Documenter): subtype = 'cmd_lib' ) cmds_obj = {} - for (name, obj) in cmds_obj.get('cmds', {}).items(): - if self.lib_key == 'groups': - source_file: str = obj.get('source_file', 'unknown') - if source_file == 'unknown': - source_group = 'unknown' - else: - source_group = str(Path(source_file).parent) - try: - self.__cmd_lib[source_group].append(name) - except KeyError: - self.__cmd_lib[source_group] = [name,] - else: - self.__cmd_lib[name] = obj + for (name, obj) in cmds_obj.get(self.lib_key, {}).items(): + self.__cmd_lib[name] = obj return self.__cmd_lib @classmethod @@ -332,6 +326,7 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): # set sourcename and add content from attribute documentation domain = getattr(self, 'domain', self.objtype) source_name = self.object.source_file + source_line = self.object.source_line def render(content_list: YosysCmdContentListing, indent: int=0): content_source = content_list.source_file or source_name @@ -363,7 +358,7 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): if self.get_sourcename() != 'unknown': self.add_line('\n', source_name) - self.add_line(f'.. note:: Help text automatically generated from :file:`{source_name}`', source_name) + self.add_line(f'.. note:: Help text automatically generated from :file:`{source_name}:{source_line}`', source_name) # add additional content (e.g. from document), if present if more_content: From 6fdefee35ba7b1a63ab3f83e891f6f317ccd0fc3 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:31 +1200 Subject: [PATCH 063/181] Move source_location to register.h Revert `PrettyHelp::get_current()` for no args since we can use `Pass::location` instead. --- kernel/log_help.cc | 4 +--- kernel/log_help.h | 17 +---------------- kernel/register.cc | 11 ++++++----- kernel/register.h | 26 ++++++++++++++++++++++---- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index ada51be60..82967b0b6 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -84,12 +84,10 @@ PrettyHelp::~PrettyHelp() current_help = _prior; } -PrettyHelp *PrettyHelp::get_current(source_location location) +PrettyHelp *PrettyHelp::get_current() { if (current_help == nullptr) new PrettyHelp(); - current_help->_root_listing.source_file = location.file_name(); - current_help->_root_listing.source_line = location.line(); return current_help; } diff --git a/kernel/log_help.h b/kernel/log_help.h index fec888548..b460bce62 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -23,19 +23,6 @@ #include "kernel/yosys_common.h" #include "kernel/json.h" -#ifdef YOSYS_ENABLE_SOURCE_LOCATION -#include -using std::experimental::source_location; -#else -struct source_location { // dummy placeholder - int line() const { return 0; } - int column() const { return 0; } - const char* file_name() const { return "unknown"; } - const char* function_name() const { return "unknown"; } - static const source_location current(...) { return source_location(); } -}; -#endif - YOSYS_NAMESPACE_BEGIN struct ContentListing { @@ -94,7 +81,7 @@ public: PrettyHelp(Mode mode = LOG); ~PrettyHelp(); - static PrettyHelp *get_current(source_location location = source_location::current()); + static PrettyHelp *get_current(); bool has_content() { return _root_listing.content.size();} const vector get_content() { @@ -102,8 +89,6 @@ public: return content; } - const char* source_file() const { return _root_listing.source_file; } - void usage( const string &usage, const source_location location = source_location::current() diff --git a/kernel/register.cc b/kernel/register.cc index 1f0768417..cc4c87019 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -42,7 +42,8 @@ std::map backend_register; std::vector Frontend::next_args; -Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help) +Pass::Pass(std::string name, std::string short_help, source_location location) : + pass_name(name), short_help(short_help), location(location) { next_queued_pass = first_queued_pass; first_queued_pass = this; @@ -389,8 +390,8 @@ void ScriptPass::help_script() script(); } -Frontend::Frontend(std::string name, std::string short_help) : - Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help), +Frontend::Frontend(std::string name, std::string short_help, source_location location) : + Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help, location), frontend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name) { } @@ -535,8 +536,8 @@ void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string } } -Backend::Backend(std::string name, std::string short_help) : - Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help), +Backend::Backend(std::string name, std::string short_help, source_location location) : + Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help, location), backend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name) { } diff --git a/kernel/register.h b/kernel/register.h index fdd361ba6..82ff98781 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -23,12 +23,27 @@ #include "kernel/yosys_common.h" #include "kernel/yosys.h" +#ifdef YOSYS_ENABLE_SOURCE_LOCATION +#include +using std::experimental::source_location; +#else +struct source_location { // dummy placeholder + int line() const { return 0; } + int column() const { return 0; } + const char* file_name() const { return "unknown"; } + const char* function_name() const { return "unknown"; } + static const source_location current(...) { return source_location(); } +}; +#endif + YOSYS_NAMESPACE_BEGIN struct Pass { std::string pass_name, short_help; - Pass(std::string name, std::string short_help = "** document me **"); + source_location location; + Pass(std::string name, std::string short_help = "** document me **", + source_location location = source_location::current()); // Prefer overriding 'Pass::on_shutdown()' if possible virtual ~Pass(); @@ -82,7 +97,8 @@ struct ScriptPass : Pass RTLIL::Design *active_design; std::string active_run_from, active_run_to; - ScriptPass(std::string name, std::string short_help = "** document me **") : Pass(name, short_help) { } + ScriptPass(std::string name, std::string short_help = "** document me **", source_location location = source_location::current()) : + Pass(name, short_help, location) { } virtual void script() = 0; @@ -100,7 +116,8 @@ struct Frontend : Pass static std::string last_here_document; std::string frontend_name; - Frontend(std::string name, std::string short_help = "** document me **"); + Frontend(std::string name, std::string short_help = "** document me **", + source_location location = source_location::current()); void run_register() override; ~Frontend() override; void execute(std::vector args, RTLIL::Design *design) override final; @@ -116,7 +133,8 @@ struct Frontend : Pass struct Backend : Pass { std::string backend_name; - Backend(std::string name, std::string short_help = "** document me **"); + Backend(std::string name, std::string short_help = "** document me **", + source_location location = source_location::current()); void run_register() override; ~Backend() override; void execute(std::vector args, RTLIL::Design *design) override final; From 1c627f4a1b421a4410d023fc84387aef4f71144c Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:31 +1200 Subject: [PATCH 064/181] log_help: Add manual group support Sets `chformal` group to "formal" for testing purposes --- kernel/log_help.h | 4 ++++ passes/cmds/chformal.cc | 1 + 2 files changed, 5 insertions(+) diff --git a/kernel/log_help.h b/kernel/log_help.h index b460bce62..730d20c27 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -53,6 +53,7 @@ struct ContentListing { class PrettyHelp { public: + string group = "unknown"; enum Mode { LOG, LISTING, @@ -89,6 +90,9 @@ public: return content; } + void set_group(const string g) { group = g; } + bool has_group() { return group.compare("unknown") != 0; } + void usage( const string &usage, const source_location location = source_location::current() diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index 1d44df51f..c233390cd 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -75,6 +75,7 @@ struct ChformalPass : public Pass { bool help_v2() override { auto *help = PrettyHelp::get_current(); + help->set_group("formal"); help->usage("chformal [types] [mode] [options] [selection]"); help->paragraph( "Make changes to the formal constraints of the design. The [types] options " From 7b625591c871b7fc234d95f3e03f807659cad3db Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:32 +1200 Subject: [PATCH 065/181] dump_cmds_json: Output groups Also output `pass->location.{file_name,line,function_name}()`. If no group is given (i.e. it is "unknown"), attempt to automatically apply a group. If we have source locations, this is based on path to the source file. If we do not have source locations, try to match on the pass name instead. --- kernel/register.cc | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/kernel/register.cc b/kernel/register.cc index cc4c87019..93107c21d 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -27,6 +27,7 @@ #include #include #include +#include YOSYS_NAMESPACE_BEGIN @@ -814,6 +815,7 @@ struct HelpPass : public Pass { json.entry("generator", yosys_version_str); bool raise_error = false; + std::map> groups; json.name("cmds"); json.begin_object(); // iterate over commands @@ -954,6 +956,42 @@ struct HelpPass : public Pass { } } + // attempt auto group + if (!cmd_help.has_group()) { + string source_file = pass->location.file_name(); + bool has_source = source_file.compare("unknown") != 0; + if (source_file.find("backends/") == 0 || (!has_source && name.find("read_") == 0)) + cmd_help.group = "backends"; + else if (source_file.find("frontends/") == 0 || (!has_source && name.find("write_") == 0)) + cmd_help.group = "frontends"; + else if (source_file.find("techlibs/") == 0 || (!has_source && name.find("synth_") == 0)) + cmd_help.group = "techlibs"; + else if (has_source) { + auto p = std::filesystem::path(source_file); + if (p.has_parent_path()) { + cmd_help.group = string(p.parent_path()); + } + } + // implicit !has_source + else if (name.find("equiv") == 0) + cmd_help.group = "passes/equiv"; + else if (name.find("fsm") == 0) + cmd_help.group = "passes/fsm"; + else if (name.find("memory") == 0) + cmd_help.group = "passes/memory"; + else if (name.find("opt") == 0) + cmd_help.group = "passes/opt"; + else if (name.find("proc") == 0) + cmd_help.group = "passes/proc"; + else if (name.find("test") == 0) + cmd_help.group = "passes/tests"; + } + + if (groups.count(cmd_help.group) == 0) { + groups[cmd_help.group] = vector(); + } + groups[cmd_help.group].push_back(name); + // write to json json.name(name.c_str()); json.begin_object(); json.entry("title", title); @@ -961,12 +999,17 @@ struct HelpPass : public Pass { for (auto content : cmd_help.get_content()) json.value(content->to_json()); json.end_array(); - json.entry("source_file", cmd_help.source_file()); + json.entry("group", cmd_help.group); + json.entry("source_file", pass->location.file_name()); + json.entry("source_line", pass->location.line()); + json.entry("source_func", pass->location.function_name()); json.entry("experimental_flag", experimental_flag); json.end_object(); } json.end_object(); + json.entry("groups", groups); + json.end_object(); return raise_error; } From 6f0ee431521be8844fbbccf587774d44c971fbef Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:32 +1200 Subject: [PATCH 066/181] Docs: Downgrade missing cmdgroup warning Log an info message, and put a warning for the content instead. --- docs/util/newcmdref.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/util/newcmdref.py b/docs/util/newcmdref.py index 6db7493af..cd6f84da5 100644 --- a/docs/util/newcmdref.py +++ b/docs/util/newcmdref.py @@ -223,11 +223,17 @@ class YosysCmdGroupDocumenter(Documenter): ) return + sourcename = self.get_sourcename() + if not self.import_object(): - logger.warning( - f"unable to load {self.name} with {type(self)}", - type = 'cmdref' - ) + log_msg = f"unable to load {self.name} with {type(self)}" + if self.lib_key == 'groups': + logger.info(log_msg, type = 'cmdref') + self.add_line(f'.. warning:: No commands found for group {self.name!r}', sourcename) + self.add_line('', sourcename) + self.add_line(' Documentation may have been built without ``source_location`` support.', sourcename) + else: + logger.warning(log_msg, type = 'cmdref') return # check __module__ of object (for members not given explicitly) @@ -235,7 +241,6 @@ class YosysCmdGroupDocumenter(Documenter): # if not self.check_module(): # return - sourcename = self.get_sourcename() self.add_line('', sourcename) # format the object's signature, if any From 7647d2c74120b3eb0d06d3c79ee1a423e52a010e Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:32 +1200 Subject: [PATCH 067/181] Docs: Fix warnings Changes a bunch of :doc:`/cmd/*` to :cmd:ref:`*` with the intention of changing it later to something that replicates the previous effect of displaying the commands `short_help`. --- docs/source/appendix/auxlibs.rst | 5 +- docs/source/cmd/index_techlibs.rst | 4 ++ .../code_examples/macro_commands/prep.ys | 23 +++++++ docs/source/getting_started/example_synth.rst | 61 +++++++++---------- .../getting_started/scripting_intro.rst | 4 +- .../interactive_investigation.rst | 8 +-- .../more_scripting/load_design.rst | 10 +-- .../using_yosys/more_scripting/selections.rst | 6 +- docs/source/using_yosys/synthesis/fsm.rst | 2 +- docs/source/using_yosys/synthesis/memory.rst | 2 +- docs/source/using_yosys/synthesis/opt.rst | 8 +-- docs/source/using_yosys/synthesis/proc.rst | 2 +- docs/source/using_yosys/synthesis/synth.rst | 33 ++-------- .../using_yosys/synthesis/techmap_synth.rst | 6 +- docs/util/newcmdref.py | 5 +- 15 files changed, 90 insertions(+), 89 deletions(-) create mode 100644 docs/source/code_examples/macro_commands/prep.ys diff --git a/docs/source/appendix/auxlibs.rst b/docs/source/appendix/auxlibs.rst index 8c78ed6b3..192ac0944 100644 --- a/docs/source/appendix/auxlibs.rst +++ b/docs/source/appendix/auxlibs.rst @@ -29,8 +29,7 @@ ezSAT The files in ``libs/ezsat`` provide a library for simplifying generating CNF formulas for SAT solvers. It also contains bindings of MiniSAT. The ezSAT -library is written by C. Wolf. It is used by the `sat` pass (see -:doc:`/cmd/sat`). +library is written by C. Wolf. It is used by the `sat` pass. fst --- @@ -78,4 +77,4 @@ SubCircuit The files in ``libs/subcircuit`` provide a library for solving the subcircuit isomorphism problem. It is written by C. Wolf and based on the Ullmann Subgraph Isomorphism Algorithm :cite:p:`UllmannSubgraphIsomorphism`. It is used by the -extract pass (see :doc:`../cmd/extract`). +`extract` pass. diff --git a/docs/source/cmd/index_techlibs.rst b/docs/source/cmd/index_techlibs.rst index aeeb17c90..c80fccf5f 100644 --- a/docs/source/cmd/index_techlibs.rst +++ b/docs/source/cmd/index_techlibs.rst @@ -1,5 +1,9 @@ techlibs ------------------ +.. TODO:: disambiguate `synth_intel` and `synth_intel_alm` + + (MAX10, Cyclone IV) and (Cyclone V, Arria V, Cyclone 10 GX) respectively + .. autocmdgroup:: techlibs :members: diff --git a/docs/source/code_examples/macro_commands/prep.ys b/docs/source/code_examples/macro_commands/prep.ys new file mode 100644 index 000000000..1bec907f6 --- /dev/null +++ b/docs/source/code_examples/macro_commands/prep.ys @@ -0,0 +1,23 @@ +#start:The following commands are executed by this synthesis command: +#end:$ +begin: + hierarchy -check [-top | -auto-top] + +coarse: + proc [-ifx] + flatten (if -flatten) + future + opt_expr -keepdc + opt_clean + check + opt -noff -keepdc + wreduce -keepdc [-memx] + memory_dff (if -rdff) + memory_memx (if -memx) + opt_clean + memory_collect + opt -noff -keepdc -fast + +check: + stat + check diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst index e215586cc..c12acb3dc 100644 --- a/docs/source/getting_started/example_synth.rst +++ b/docs/source/getting_started/example_synth.rst @@ -70,7 +70,7 @@ At the bottom of the `help` output for `synth_ice40` is the complete list of commands called by this script. Let's start with the section labeled ``begin``: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: begin: :end-before: flatten: @@ -143,7 +143,7 @@ line refers to the line numbers of the start/end of the corresponding ``always @`` block. In the case of an ``initial`` block, we instead see the ``PROC`` referring to line 0. -To handle these, let us now introduce the next command: :doc:`/cmd/proc`. `proc` +To handle these, let us now introduce the next command: :cmd:ref:`proc`. `proc` is a macro command like `synth_ice40`. Rather than modifying the design directly, it instead calls a series of other commands. In the case of `proc`, these sub-commands work to convert the behavioral logic of processes into @@ -188,7 +188,7 @@ opt_expr `. .. note:: - :doc:`/cmd/clean` can also be called with two semicolons after any command, + :cmd:ref:`clean` can also be called with two semicolons after any command, for example we could have called :yoscrypt:`opt_expr;;` instead of :yoscrypt:`opt_expr; clean`. You may notice some scripts will end each line with ``;;``. It is beneficial to run `clean` before inspecting intermediate @@ -215,8 +215,8 @@ Note that if we tried to run this command now then we would get an error. This is because we already removed all of the modules other than ``addr_gen``. We could restart our shell session, but instead let's use two new commands: -- :doc:`/cmd/design`, and -- :doc:`/cmd/read_verilog`. +- :cmd:ref:`design`, and +- :cmd:ref:`read_verilog`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -251,7 +251,7 @@ our design won't run into this issue, we can skip the ``-defer``. We can also run `proc` now to finish off the full :ref:`synth_begin`. Because the design schematic is quite large, we will be showing just the data path for the ``rdata`` output. If you would like to see the entire design for yourself, -you can do so with :doc:`/cmd/show`. Note that the `show` command only works +you can do so with :cmd:ref:`show`. Note that the `show` command only works with a single module, so you may need to call it with :yoscrypt:`show fifo`. :ref:`show_intro` section in :doc:`/getting_started/scripting_intro` has more on how to use `show`. @@ -283,7 +283,7 @@ Flattening At this stage of a synthesis flow there are a few other commands we could run. In `synth_ice40` we get these: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: flatten: :end-before: coarse: @@ -355,7 +355,7 @@ Part 1 In the iCE40 flow, we start with the following commands: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: coarse: :end-before: wreduce @@ -371,7 +371,7 @@ wasting time on something we know is impossible. Next up is :yoscrypt:`opt -nodffe -nosdff` performing a set of simple optimizations on the design. This command also ensures that only a specific subset of FF types are included, in preparation for the next command: -:doc:`/cmd/fsm`. Both `opt` and `fsm` are macro commands which are explored in +:cmd:ref:`fsm`. Both `opt` and `fsm` are macro commands which are explored in more detail in :doc:`/using_yosys/synthesis/opt` and :doc:`/using_yosys/synthesis/fsm` respectively. @@ -403,7 +403,7 @@ Part 2 The next group of commands performs a series of optimizations: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: wreduce :end-before: t:$mul @@ -411,7 +411,7 @@ The next group of commands performs a series of optimizations: :caption: ``coarse`` section (part 2) :name: synth_coarse2 -First up is :doc:`/cmd/wreduce`. If we run this we get the following: +First up is :cmd:ref:`wreduce`. If we run this we get the following: .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -432,7 +432,7 @@ the schematic and see the output of that cell has now changed. ``rdata`` output after `wreduce` -The next two (new) commands are :doc:`/cmd/peepopt` and :doc:`/cmd/share`. +The next two (new) commands are :cmd:ref:`peepopt` and :cmd:ref:`share`. Neither of these affect our design, and they're explored in more detail in :doc:`/using_yosys/synthesis/opt`, so let's skip over them. :yoscrypt:`techmap -map +/cmp2lut.v -D LUT_WIDTH=4` optimizes certain comparison operators by @@ -440,7 +440,7 @@ converting them to LUTs instead. The usage of `techmap` is explored more in :doc:`/using_yosys/synthesis/techmap_synth`. Our next command to run is -:doc:`/cmd/memory_dff`. +:cmd:ref:`memory_dff`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -475,7 +475,7 @@ will only be performed if called with the ``-dsp`` flag: :yoscrypt:`synth_ice40 -dsp`. While our example has nothing that could be mapped to DSPs we can still take a quick look at the commands here and describe what they do. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: t:$mul :end-before: alumacc @@ -514,7 +514,7 @@ Part 4 That brings us to the fourth and final part for the iCE40 synthesis flow: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: alumacc :end-before: map_ram: @@ -543,7 +543,7 @@ Once these cells have been inserted, the call to `opt` can combine cells which are now identical but may have been missed due to e.g. the difference between `$add` and `$sub`. -The other new command in this part is :doc:`/cmd/memory`. `memory` is another +The other new command in this part is :cmd:ref:`memory`. `memory` is another macro command which we examine in more detail in :doc:`/using_yosys/synthesis/memory`. For this document, let us focus just on the step most relevant to our example: `memory_collect`. Up until this point, @@ -594,7 +594,7 @@ Memory blocks Mapping to hard memory blocks uses a combination of `memory_libmap` and `techmap`. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ram: :end-before: map_ffram: @@ -636,7 +636,7 @@ into flip flops (the ``logic fallback``) with `memory_map`. .. |techlibs/ice40/brams_map.v| replace:: :file:`techlibs/ice40/brams_map.v` .. _techlibs/ice40/brams_map.v: https://github.com/YosysHQ/yosys/tree/main/techlibs/ice40/brams_map.v -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ffram: :end-before: map_gates: @@ -671,7 +671,7 @@ an explosion in cells as multi-bit `$mux` and `$adffe` are replaced with single-bit `$_MUX_` and `$_DFFE_PP0P_` cells, while the `$alu` is replaced with primitive `$_OR_` and `$_NOT_` gates and a `$lut` cell. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_gates: :end-before: map_ffs: @@ -700,7 +700,7 @@ mapped to hardware into gate-level primitives. This includes optimizing `$_MUX_` cells where one of the inputs is a constant ``1'0``, replacing it instead with an `$_AND_` cell. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ffs: :end-before: map_luts: @@ -725,7 +725,7 @@ LUTs `abc`. For more on what these do, and what the difference between these two commands are, refer to :doc:`/using_yosys/synthesis/abc`. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_luts: :end-before: map_cells: @@ -742,7 +742,7 @@ commands are, refer to :doc:`/using_yosys/synthesis/abc`. Finally we use `techmap` to map the generic `$lut` cells to iCE40 ``SB_LUT4`` cells. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_cells: :end-before: check: @@ -784,19 +784,18 @@ Final steps The next section of the iCE40 synth flow performs some sanity checking and final tidy up: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: check: - :end-before: blif: :dedent: :name: check :caption: ``check`` section The new commands here are: -- :doc:`/cmd/autoname`, -- :doc:`/cmd/stat`, and -- :doc:`/cmd/blackbox`. +- :cmd:ref:`autoname`, +- :cmd:ref:`stat`, and +- :cmd:ref:`blackbox`. The output from `stat` is useful for checking resource utilization; providing a list of cells used in the design and the number of each, as well as the number @@ -835,9 +834,9 @@ Synthesis output The iCE40 synthesis flow has the following output modes available: -- :doc:`/cmd/write_blif`, -- :doc:`/cmd/write_edif`, and -- :doc:`/cmd/write_json`. +- `write_blif`, +- `write_edif`, and +- `write_json`. As an example, if we called :yoscrypt:`synth_ice40 -top fifo -json fifo.json`, our synthesized ``fifo`` design will be output as :file:`fifo.json`. We can @@ -848,4 +847,4 @@ is beyond the scope of this documentation. .. _nextpnr: https://github.com/YosysHQ/nextpnr -.. seealso:: :doc:`/cmd/synth_ice40` +.. seealso:: :cmd:ref:`synth_ice40` diff --git a/docs/source/getting_started/scripting_intro.rst b/docs/source/getting_started/scripting_intro.rst index 01954c661..bc6782eaa 100644 --- a/docs/source/getting_started/scripting_intro.rst +++ b/docs/source/getting_started/scripting_intro.rst @@ -122,7 +122,7 @@ module. Detailed documentation of the select framework can be found under :doc:`/using_yosys/more_scripting/selections` or in the command reference at -:doc:`/cmd/select`. +:cmd:ref:`select`. .. _show_intro: @@ -219,7 +219,7 @@ those used in options, must be a single expression instead. .. _GraphViz color docs: https://graphviz.org/doc/info/colors For all of the options available to `show`, check the command reference at -:doc:`/cmd/show`. +:cmd:ref:`show`. .. seealso:: :ref:`interactive_show` on the :doc:`/using_yosys/more_scripting/interactive_investigation` page. diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst index e9c9bc9ac..fa031f94e 100644 --- a/docs/source/using_yosys/more_scripting/interactive_investigation.rst +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -323,9 +323,9 @@ tools). design into an equivalent design that is easier to analyse. - Commands such as `eval` and `sat` can be used to investigate the behavior of the circuit. -- :doc:`/cmd/show`. -- :doc:`/cmd/dump`. -- :doc:`/cmd/add` and :doc:`/cmd/delete` can be used to modify and reorganize a +- :cmd:ref:`show`. +- :cmd:ref:`dump`. +- :cmd:ref:`add` and :cmd:ref:`delete` can be used to modify and reorganize a design dynamically. The code used is included in the Yosys code base under @@ -358,7 +358,7 @@ reorganizing a module in Yosys and checking the resulting circuit. .. figure:: /_images/code_examples/scrambler/scrambler_p02.* :class: width-helper invert-helper -Analyzing the resulting circuit with :doc:`/cmd/eval`: +Analyzing the resulting circuit with :cmd:ref:`eval`: .. todo:: replace inline code diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst index bbc55a36b..39e5711e6 100644 --- a/docs/source/using_yosys/more_scripting/load_design.rst +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -1,9 +1,11 @@ Loading a design ~~~~~~~~~~~~~~~~ +.. TODO:: fill out this page better + keyword: Frontends -- :doc:`/cmd/read_verilog` +- :doc:`/cmd/index_frontends` .. todo:: include ``read_verilog < str: - return self.object.source_file + try: + return self.object.source_file + except AttributeError: + return super().get_sourcename() def format_name(self) -> str: return self.object.name From 4722b074856b597277a73f9f4d142fefce04a636 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:54 +1200 Subject: [PATCH 068/181] Docs: docs/source/cmd is source only i.e. we don't need to clean it, and we don't need to include it in the docs artifact --- .github/workflows/prepare-docs.yml | 1 - Makefile | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml index fb1fab426..b47f5f3dd 100644 --- a/.github/workflows/prepare-docs.yml +++ b/.github/workflows/prepare-docs.yml @@ -59,7 +59,6 @@ jobs: with: name: cmd-ref-${{ github.sha }} path: | - docs/source/cmd docs/source/generated docs/source/_images docs/source/code_examples diff --git a/Makefile b/Makefile index 5690410a7..314daa05f 100644 --- a/Makefile +++ b/Makefile @@ -1123,7 +1123,7 @@ clean: rm -f tests/tools/cmp_tbdata rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS)) -$(MAKE) -C docs clean - rm -rf docs/source/cmd docs/util/__pycache__ + rm -rf docs/util/__pycache__ rm -f *.whl rm -f libyosys.so From 534163cf4b38f2a235552ee9db5dff8b7a1da3ae Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:54 +1200 Subject: [PATCH 069/181] source_location: Auto detect library Drop `ENABLE_SOURCE_LOCATION` flag. --- Makefile | 4 ---- kernel/register.h | 12 +++++++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 314daa05f..2673cff2a 100644 --- a/Makefile +++ b/Makefile @@ -532,10 +532,6 @@ LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VE endif endif -ifeq ($(ENABLE_SOURCE_LOCATION),1) -CXXFLAGS += -DYOSYS_ENABLE_SOURCE_LOCATION -endif - ifeq ($(ENABLE_COVER),1) CXXFLAGS += -DYOSYS_ENABLE_COVER endif diff --git a/kernel/register.h b/kernel/register.h index 82ff98781..07d6f09c1 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -23,10 +23,15 @@ #include "kernel/yosys_common.h" #include "kernel/yosys.h" -#ifdef YOSYS_ENABLE_SOURCE_LOCATION -#include -using std::experimental::source_location; +#include +#if __cpp_lib_source_location == 201907L + #include + using std::source_location; #else + #include +# ifdef __cpp_lib_experimental_source_location + using std::experimental::source_location; +# else struct source_location { // dummy placeholder int line() const { return 0; } int column() const { return 0; } @@ -34,6 +39,7 @@ struct source_location { // dummy placeholder const char* function_name() const { return "unknown"; } static const source_location current(...) { return source_location(); } }; +# endif #endif YOSYS_NAMESPACE_BEGIN From 041f390a24bebc014d77157551cf9169146d823d Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:54 +1200 Subject: [PATCH 070/181] register: Remove Use `string::find_last_of()` instead. Not sure how this works on windows, but it was already using '/' so at least it's not any worse. --- kernel/register.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/register.cc b/kernel/register.cc index 93107c21d..2a590a2dd 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -27,7 +27,6 @@ #include #include #include -#include YOSYS_NAMESPACE_BEGIN @@ -967,9 +966,10 @@ struct HelpPass : public Pass { else if (source_file.find("techlibs/") == 0 || (!has_source && name.find("synth_") == 0)) cmd_help.group = "techlibs"; else if (has_source) { - auto p = std::filesystem::path(source_file); - if (p.has_parent_path()) { - cmd_help.group = string(p.parent_path()); + auto last_slash = source_file.find_last_of('/'); + if (last_slash != string::npos) { + auto parent_path = source_file.substr(0, last_slash); + cmd_help.group = parent_path; } } // implicit !has_source From 23a066f71f2c508c043e2fd2457c5e82326e3a38 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:54 +1200 Subject: [PATCH 071/181] source_location: Try use __has_include --- kernel/register.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/kernel/register.h b/kernel/register.h index 07d6f09c1..5cdb62ebe 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -27,11 +27,18 @@ #if __cpp_lib_source_location == 201907L #include using std::source_location; -#else - #include -# ifdef __cpp_lib_experimental_source_location +#elif defined(__has_include) +# if __has_include() + #include using std::experimental::source_location; # else + #define SOURCE_FALLBACK +# endif +#else + #define SOURCE_FALLBACK +#endif + +#ifdef SOURCE_FALLBACK struct source_location { // dummy placeholder int line() const { return 0; } int column() const { return 0; } @@ -39,7 +46,6 @@ struct source_location { // dummy placeholder const char* function_name() const { return "unknown"; } static const source_location current(...) { return source_location(); } }; -# endif #endif YOSYS_NAMESPACE_BEGIN From 605f12c2ae9fb7e8dbc78fbb040adfe7a0c192a6 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:54 +1200 Subject: [PATCH 072/181] Rename help_v2 to formatted_help Also add comments to `help()` and `formatted_help()` to clarify usage. --- kernel/register.cc | 6 +++--- kernel/register.h | 4 +++- passes/cmds/chformal.cc | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/kernel/register.cc b/kernel/register.cc index 2a590a2dd..2e68c3d85 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -118,14 +118,14 @@ void Pass::post_execute(Pass::pre_post_exec_state_t state) void Pass::help() { - if (!help_v2()) { + if (!formatted_help()) { log("\n"); log("No help message for command `%s'.\n", pass_name.c_str()); log("\n"); } } -bool Pass::help_v2() +bool Pass::formatted_help() { return false; } @@ -825,7 +825,7 @@ struct HelpPass : public Pass { auto experimental_flag = pass->experimental_flag; auto cmd_help = PrettyHelp(PrettyHelp::Mode::LISTING); - auto has_pretty_help = pass->help_v2(); + auto has_pretty_help = pass->formatted_help(); if (!has_pretty_help) { enum PassUsageState { diff --git a/kernel/register.h b/kernel/register.h index 5cdb62ebe..537e1670d 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -59,8 +59,10 @@ struct Pass // Prefer overriding 'Pass::on_shutdown()' if possible virtual ~Pass(); + // Makes calls to log() to generate help message virtual void help(); - virtual bool help_v2(); + // Uses PrettyHelp::get_current() to produce a more portable formatted help message + virtual bool formatted_help(); virtual void clear_flags(); virtual void execute(std::vector args, RTLIL::Design *design) = 0; diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index c233390cd..8194b7365 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -73,7 +73,7 @@ static bool is_triggered_check_cell(RTLIL::Cell * cell) struct ChformalPass : public Pass { ChformalPass() : Pass("chformal", "change formal constraints of the design") {} - bool help_v2() override { + bool formatted_help() override { auto *help = PrettyHelp::get_current(); help->set_group("formal"); help->usage("chformal [types] [mode] [options] [selection]"); From 33be9994883b84403c04da041005f70fb600af24 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:55 +1200 Subject: [PATCH 073/181] cmdref: Add cmd titles Display subheadings for each command. Remove now redundant toc entry for `yosys> help ` line. --- docs/util/cmdref.py | 2 +- docs/util/newcmdref.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/util/cmdref.py b/docs/util/cmdref.py index e471ea46d..1ef09dcf7 100644 --- a/docs/util/cmdref.py +++ b/docs/util/cmdref.py @@ -33,7 +33,7 @@ class TocNode(ObjectDescription): signode['ids'].append(idx) def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature) -> tuple[str, ...]: - if 'fullname' not in sig_node: + if 'tocname' not in sig_node: return () modname = sig_node.get('module') diff --git a/docs/util/newcmdref.py b/docs/util/newcmdref.py index a00df3095..e014c03e5 100644 --- a/docs/util/newcmdref.py +++ b/docs/util/newcmdref.py @@ -322,6 +322,10 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): source_name = self.object.source_file source_line = self.object.source_line + title = f'{self.object.name} - {self.object.title}' + self.add_line(title, source_name, source_line) + self.add_line('#' * len(title), source_name, source_line) + # cmd definition self.add_line(f'.. {domain}:{directive}:: {sig}', source_name, source_line) if self.object.title: From c97b0084b78781446d293d240c8c691a3ce32104 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:34:55 +1200 Subject: [PATCH 074/181] Docs: Show warning on experimental cmds --- docs/util/newcmdref.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/util/newcmdref.py b/docs/util/newcmdref.py index e014c03e5..07c93c0d1 100644 --- a/docs/util/newcmdref.py +++ b/docs/util/newcmdref.py @@ -340,6 +340,10 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): source_name = self.object.source_file source_line = self.object.source_line + if self.object.experimental_flag: + self.add_line(f'.. warning:: This command is experimental', source_name, source_line) + self.add_line('\n', source_name) + def render(content_list: YosysCmdContentListing, indent: int=0): content_source = content_list.source_file or source_name indent_str = ' '*indent From 1529d991fd26f3efa326df8a4b02d5865e2dec8b Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:18 +1200 Subject: [PATCH 075/181] log_help: Refactor help content adding Content is now added to the `ContentListing` rather than the `PrettyHelp`. `open_*` methods return the `ContentListing` that was added instead of leaving a hanging continuation. This allows for (e.g.) options to be added directly to optiongroups, instead of requiring that groups be closed before continuation. This also means that all `PrettyHelp`s are a listing, with the actual log being called by the default `Pass::help()`; making the mode field redundant. Added `PrettyHelp::log_help()` which replaces the `PrettyHelp::Mode::LOG` logic. Added `ContentListing::back()` which just returns the last element of the underlying content vector. Some of the content tracking was made redundant and removed, in particular `PrettyHelp::_current_listing` and `ContentListing::parent`. Converted `ContentListing` to a class instead of a struct, adjusting constructors to match. Added `ContentListing` constructor that accepts a `source_location`. Update `HelpPass::dump_cmds_json()` for new log_help. --- kernel/log_help.cc | 189 +++++++++++++--------------------------- kernel/log_help.h | 116 ++++++++++++------------ kernel/register.cc | 36 ++++---- passes/cmds/chformal.cc | 54 ++++++------ 4 files changed, 167 insertions(+), 228 deletions(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 82967b0b6..7e103f83d 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -28,11 +28,56 @@ Json ContentListing::to_json() { if (strcmp(source_file, "unknown") != 0) object["source_file"] = source_file; if (source_line != 0) object["source_line"] = source_line; Json::array content_array; - for (auto child : content) content_array.push_back(child->to_json()); + for (auto child : _content) content_array.push_back(child->to_json()); object["content"] = content_array; return object; } +void ContentListing::usage(const string &usage, + const source_location location) +{ + log_assert(type.compare("root") == 0); + add_content("usage", usage, location); +} + +void ContentListing::option(const string &text, const string &description, + const source_location location) +{ + auto option = open_option(text); + if (description.length()) + option->add_content("text", description, location); +} + +void ContentListing::codeblock(const string &code, const string &language, + const source_location location) +{ + add_content("code", code, location); +} + +void ContentListing::paragraph(const string &text, + const source_location location) +{ + add_content("text", text, location); +} + +ContentListing* ContentListing::open_optiongroup(const string &name, + const source_location location) +{ + log_assert(type.compare("root") == 0); + auto optiongroup = new ContentListing("optiongroup", name, location); + add_content(optiongroup); + return optiongroup; +} + +ContentListing* ContentListing::open_option(const string &text, + const source_location location) +{ + log_assert(type.compare("optiongroup") == 0); + auto option = new ContentListing("option", text, location); + add_content(option); + return option; +} + #define MAX_LINE_LEN 80 void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) { if (pass_str.empty()) @@ -66,15 +111,10 @@ void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newlin PrettyHelp *current_help = nullptr; -PrettyHelp::PrettyHelp(Mode mode) +PrettyHelp::PrettyHelp() { _prior = current_help; - _mode = mode; - _root_listing = ContentListing({ - .type = "root" - }); - _root_listing.type = "root"; - _current_listing = &_root_listing; + _root_listing = ContentListing("root", ""); current_help = this; } @@ -91,127 +131,22 @@ PrettyHelp *PrettyHelp::get_current() return current_help; } -void PrettyHelp::usage(const string &usage, - const source_location location) +void PrettyHelp::log_help() { - switch (_mode) - { - case LOG: - log_pass_str(usage, _current_indent+1, true); - log("\n"); - break; - case LISTING: - add_content("usage", usage, location); - break; - default: - log_abort(); - } -} - -void PrettyHelp::option(const string &text, const string &description, - const source_location location) -{ - open_option(text); - if (description.length()) { - switch (_mode) - { - case LOG: - log_pass_str(description, _current_indent); + for (auto content : _root_listing.get_content()) { + if (content->type.compare("usage") == 0) { + log_pass_str(content->body, 1, true); + } else if (content->type.compare("optiongroup") == 0) { + for (auto option : content->get_content()) { + log_pass_str(option->body, 1); + for (auto text : option->get_content()) { + log_pass_str(text->body, 2); + log("\n"); + } + } + } else { + log_pass_str(content->body, 0, true); log("\n"); - break; - case LISTING: - add_content("text", description, location); - break; - default: - log_abort(); } } - close(1); -} - -void PrettyHelp::codeblock(const string &code, const string &language, - const source_location location) -{ - switch (_mode) - { - case LOG: - log("%s\n", code.c_str()); - break; - case LISTING: - add_content("code", code, location); - break; - default: - log_abort(); - } -} - -void PrettyHelp::paragraph(const string &text, - const source_location location) -{ - switch (_mode) - { - case LOG: - log_pass_str(text, _current_indent); - log("\n"); - break; - case LISTING: - add_content("text", text, location); - break; - default: - log_abort(); - } -} - -void PrettyHelp::open_optiongroup(const string &name, - const source_location location) -{ - switch (_mode) - { - case LOG: - break; - case LISTING: - push_content("optiongroup", name, location); - break; - default: - log_abort(); - } - _current_indent += 1; -} - -void PrettyHelp::open_option(const string &text, - const source_location location) -{ - switch (_mode) - { - case LOG: - log_pass_str(text, _current_indent); - break; - case LISTING: - push_content("option", text, location); - break; - default: - log_abort(); - } - _current_indent += 1; -} - -void PrettyHelp::close(int levels) -{ - - switch (_mode) - { - case LOG: - _current_indent -= levels; - log_assert(_current_indent >= 0); - break; - case LISTING: - for (int i=0; i= 0); - pop_content(); - } - break; - default: - log_abort(); - } } diff --git a/kernel/log_help.h b/kernel/log_help.h index 730d20c27..169b90ca2 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -25,73 +25,41 @@ YOSYS_NAMESPACE_BEGIN -struct ContentListing { - string type = "root"; - string body = ""; - const char* source_file = "unknown"; - int source_line = 0; - vector content = {}; - ContentListing *parent = nullptr; +class ContentListing { + vector _content; +public: + string type; + string body; + const char* source_file; + int source_line; + + ContentListing( + string type = "root", string body = "", + const char* source_file = "unknown", int source_line = 0 + ) : type(type), body(body), source_file(source_file), source_line(source_line) { + _content = {}; + } + + ContentListing(string type, string body, source_location location) : + ContentListing(type, body, location.file_name(), location.line()) { } void add_content(ContentListing *new_content) { - new_content->parent = this; - content.push_back(new_content); + _content.push_back(new_content); } void add_content(string type, string body, source_location location) { - auto new_content = new ContentListing(); - new_content->type = type; - new_content->body = body; - new_content->source_file = location.file_name(); - new_content->source_line = location.line(); + auto new_content = new ContentListing(type, body, location); add_content(new_content); } - Json to_json(); -}; - -class PrettyHelp -{ -public: - string group = "unknown"; - enum Mode { - LOG, - LISTING, - }; - -private: - PrettyHelp *_prior; - Mode _mode; - - int _current_indent = 0; - ContentListing _root_listing; - ContentListing *_current_listing; - - void add_content(string type, string body, source_location location) { - _current_listing->add_content(type, body, location); - } - void push_content(string type, string body, source_location location) { - add_content(type, body, location); - _current_listing = _current_listing->content.back(); - } - void pop_content() { - _current_listing = _current_listing->parent; - log_assert(_current_listing != nullptr); - } -public: - PrettyHelp(Mode mode = LOG); - ~PrettyHelp(); - - static PrettyHelp *get_current(); - - bool has_content() { return _root_listing.content.size();} + bool has_content() { return _content.size() != 0; } const vector get_content() { - const vector content = _root_listing.content; + const vector content = _content; return content; } - - void set_group(const string g) { group = g; } - bool has_group() { return group.compare("unknown") != 0; } + ContentListing* back() { + return _content.back(); + } void usage( const string &usage, @@ -112,15 +80,45 @@ public: const source_location location = source_location::current() ); - void open_optiongroup( + ContentListing* open_optiongroup( const string &name = "", const source_location location = source_location::current() ); - void open_option( + ContentListing* open_option( const string &text, const source_location location = source_location::current() ); - void close(int levels = 1); + + Json to_json(); +}; + +class PrettyHelp +{ +public: + string group = "unknown"; + +private: + PrettyHelp *_prior; + ContentListing _root_listing; + +public: + PrettyHelp(); + ~PrettyHelp(); + + static PrettyHelp *get_current(); + + bool has_content() { return _root_listing.has_content(); } + const vector get_content() { + return _root_listing.get_content(); + } + ContentListing* get_root() { + return &_root_listing; + } + + void set_group(const string g) { group = g; } + bool has_group() { return group.compare("unknown") != 0; } + + void log_help(); }; YOSYS_NAMESPACE_END diff --git a/kernel/register.cc b/kernel/register.cc index 2e68c3d85..cf8b9f638 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -118,7 +118,10 @@ void Pass::post_execute(Pass::pre_post_exec_state_t state) void Pass::help() { - if (!formatted_help()) { + auto prettyHelp = PrettyHelp(); + if (formatted_help()) { + prettyHelp.log_help(); + } else { log("\n"); log("No help message for command `%s'.\n", pass_name.c_str()); log("\n"); @@ -824,7 +827,7 @@ struct HelpPass : public Pass { auto title = pass->short_help; auto experimental_flag = pass->experimental_flag; - auto cmd_help = PrettyHelp(PrettyHelp::Mode::LISTING); + auto cmd_help = PrettyHelp(); auto has_pretty_help = pass->formatted_help(); if (!has_pretty_help) { @@ -837,6 +840,8 @@ struct HelpPass : public Pass { source_location null_source; string current_buffer = ""; + auto root_listing = cmd_help.get_root(); + auto current_listing = root_listing; // dump command help std::ostringstream buf; @@ -859,7 +864,8 @@ struct HelpPass : public Pass { switch (current_state) { case PUState_signature: - cmd_help.usage(current_buffer, null_source); + root_listing->usage(current_buffer, null_source); + current_listing = root_listing; current_state = PUState_none; current_buffer = ""; break; @@ -886,16 +892,16 @@ struct HelpPass : public Pass { if (IsSignature && first_pos <= 4 && (blank_lines >= 2 || current_state == PUState_signature)) { if (current_state == PUState_options || current_state == PUState_optionbody) { - cmd_help.codeblock(current_buffer, "none", null_source); + current_listing->codeblock(current_buffer, "none", null_source); current_buffer = ""; - cmd_help.close(2); } else if (current_state == PUState_signature) { - cmd_help.usage(current_buffer, null_source); + root_listing->usage(current_buffer, null_source); current_buffer = ""; } else if (current_state == PUState_none && !current_buffer.empty()) { - cmd_help.codeblock(current_buffer, "none", null_source); + current_listing->codeblock(current_buffer, "none", null_source); current_buffer = ""; } + current_listing = root_listing; current_state = PUState_signature; def_strip_count = first_pos; catch_verific = false; @@ -903,15 +909,15 @@ struct HelpPass : public Pass { def_strip_count = first_pos; if (current_state == PUState_optionbody) { if (!current_buffer.empty()) { - cmd_help.codeblock(current_buffer, "none", null_source); + current_listing->codeblock(current_buffer, "none", null_source); current_buffer = ""; } if (IsIndent) { current_state = PUState_options; - cmd_help.close(1); + current_listing = root_listing->back(); } else { current_state = PUState_none; - cmd_help.close(2); + current_listing = root_listing; } } else { current_state = PUState_none; @@ -920,16 +926,16 @@ struct HelpPass : public Pass { if (IsDefinition && !catch_verific && current_state != PUState_signature) { if (!current_buffer.empty()) { - cmd_help.codeblock(current_buffer, "none", null_source); + current_listing->codeblock(current_buffer, "none", null_source); current_buffer = ""; } if (current_state == PUState_options || current_state == PUState_optionbody) { - cmd_help.close(1); + current_listing = root_listing->back(); } else { - cmd_help.open_optiongroup("", null_source); + current_listing = root_listing->open_optiongroup("", null_source); } current_state = PUState_options; - cmd_help.open_option(stripped_line, null_source); + current_listing = current_listing->open_option(stripped_line, null_source); def_strip_count = first_pos; } else { if (current_state == PUState_options) { @@ -950,7 +956,7 @@ struct HelpPass : public Pass { } if (!current_buffer.empty()) { - cmd_help.codeblock(current_buffer, "none", null_source); + current_listing->codeblock(current_buffer, "none", null_source); current_buffer = ""; } } diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index 8194b7365..53884b10f 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -76,58 +76,58 @@ struct ChformalPass : public Pass { bool formatted_help() override { auto *help = PrettyHelp::get_current(); help->set_group("formal"); - help->usage("chformal [types] [mode] [options] [selection]"); - help->paragraph( + + auto content_root = help->get_root(); + + content_root->usage("chformal [types] [mode] [options] [selection]"); + content_root->paragraph( "Make changes to the formal constraints of the design. The [types] options " "the type of constraint to operate on. If none of the following options are " "given, the command will operate on all constraint types:" ); - help->open_optiongroup("[types]"); - help->option("-assert", "`$assert` cells, representing ``assert(...)`` constraints"); - help->option("-assume", "`$assume` cells, representing ``assume(...)`` constraints"); - help->option("-live", "`$live` cells, representing ``assert(s_eventually ...)``"); - help->option("-fair", "`$fair` cells, representing ``assume(s_eventually ...)``"); - help->option("-cover", "`$cover` cells, representing ``cover()`` statements"); - help->paragraph( + auto types_group = content_root->open_optiongroup("[types]"); + types_group->option("-assert", "`$assert` cells, representing ``assert(...)`` constraints"); + types_group->option("-assume", "`$assume` cells, representing ``assume(...)`` constraints"); + types_group->option("-live", "`$live` cells, representing ``assert(s_eventually ...)``"); + types_group->option("-fair", "`$fair` cells, representing ``assume(s_eventually ...)``"); + types_group->option("-cover", "`$cover` cells, representing ``cover()`` statements"); + types_group->paragraph( "Additionally chformal will operate on `$check` cells corresponding to the " "selected constraint types." ); - help->close(); - help->paragraph("Exactly one of the following modes must be specified:"); + content_root->paragraph("Exactly one of the following modes must be specified:"); - help->open_optiongroup("[mode]"); - help->option("-remove", "remove the cells and thus constraints from the design"); - help->option("-early", + auto modes_group = content_root->open_optiongroup("[mode]"); + modes_group->option("-remove", "remove the cells and thus constraints from the design"); + modes_group->option("-early", "bypass FFs that only delay the activation of a constraint. When inputs " "of the bypassed FFs do not remain stable between clock edges, this may " "result in unexpected behavior." ); - help->option("-delay ", "delay activation of the constraint by clock cycles"); - help->option("-skip ", "ignore activation of the constraint in the first clock cycles"); - help->open_option("-coverenable"); - help->paragraph( + modes_group->option("-delay ", "delay activation of the constraint by clock cycles"); + modes_group->option("-skip ", "ignore activation of the constraint in the first clock cycles"); + auto cover_option = modes_group->open_option("-coverenable"); + cover_option->paragraph( "add cover statements for the enable signals of the constraints" ); #ifdef YOSYS_ENABLE_VERIFIC - help->paragraph( + cover_option->paragraph( "Note: For the Verific frontend it is currently not guaranteed that a " "reachable SVA statement corresponds to an active enable signal." ); #endif - help->close(); - help->option("-assert2assume"); - help->option("-assert2cover"); - help->option("-assume2assert"); - help->option("-live2fair"); - help->option("-fair2live", "change the roles of cells as indicated. these options can be combined"); - help->option("-lower", + modes_group->option("-assert2assume"); + modes_group->option("-assert2cover"); + modes_group->option("-assume2assert"); + modes_group->option("-live2fair"); + modes_group->option("-fair2live", "change the roles of cells as indicated. these options can be combined"); + modes_group->option("-lower", "convert each $check cell into an $assert, $assume, $live, $fair or " "$cover cell. If the $check cell contains a message, also produce a " "$print cell." ); - help->close(); return true; } void execute(std::vector args, RTLIL::Design *design) override From d62a110dc80a2922dd7717420a23ff86f0fa3cbd Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:19 +1200 Subject: [PATCH 076/181] register.h: Add internal_flag to Pass Update experimental pass warnings to use a shared function. Reduces repetition, and also allows all of the warning flags to be combined (which at present is just experimental and the new internal). Update `test_*` passes to call `internal()` in their constructors. --- backends/functional/test_generic.cc | 4 +++- kernel/register.cc | 35 ++++++++++++++++------------- kernel/register.h | 5 +++++ passes/pmgen/test_pmgen.cc | 4 +++- passes/tests/test_abcloop.cc | 4 +++- passes/tests/test_autotb.cc | 4 +++- passes/tests/test_cell.cc | 4 +++- 7 files changed, 40 insertions(+), 20 deletions(-) diff --git a/backends/functional/test_generic.cc b/backends/functional/test_generic.cc index a9dfd0c70..42d6c2b95 100644 --- a/backends/functional/test_generic.cc +++ b/backends/functional/test_generic.cc @@ -116,7 +116,9 @@ struct MemContentsTest { struct FunctionalTestGeneric : public Pass { - FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {} + FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") { + internal(); + } void help() override { diff --git a/kernel/register.cc b/kernel/register.cc index cf8b9f638..c3414f5db 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -693,6 +693,23 @@ static string get_cell_name(string name) { return is_code_getter(name) ? name.substr(0, name.length()-1) : name; } +static void log_warning_flags(Pass *pass) { + bool has_warnings = false; + const string name = pass->pass_name; + if (pass->experimental_flag) { + if (!has_warnings) log("\n"); + has_warnings = true; + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", name.c_str()); + } + if (pass->internal_flag) { + if (!has_warnings) log("\n"); + has_warnings = true; + log("WARNING: THE '%s' COMMAND IS INTENDED FOR INTERNAL DEVELOPER USE ONLY.\n", name.c_str()); + } + if (has_warnings) + log("\n"); +} + static struct CellHelpMessages { dict cell_help; CellHelpMessages() { @@ -1123,11 +1140,7 @@ struct HelpPass : public Pass { log("="); log("\n"); it.second->help(); - if (it.second->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); - log("\n"); - } + log_warning_flags(it.second); } } else if (args[1] == "-cells") { @@ -1147,22 +1160,14 @@ struct HelpPass : public Pass { std::ostringstream buf; log_streams.push_back(&buf); it.second->help(); - if (it.second->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); - log("\n"); - } + log_warning_flags(it.second); log_streams.pop_back(); write_cmd_rst(it.first, it.second->short_help, buf.str()); } } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); - if (pass_register.at(args[1])->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", args[1].c_str()); - log("\n"); - } + log_warning_flags(pass_register.at(args[1])); } else if (cell_help_messages.contains(args[1])) { auto help_cell = cell_help_messages.get(args[1]); diff --git a/kernel/register.h b/kernel/register.h index 537e1670d..e8c017c1d 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -69,11 +69,16 @@ struct Pass int call_counter; int64_t runtime_ns; bool experimental_flag = false; + bool internal_flag = false; void experimental() { experimental_flag = true; } + void internal() { + internal_flag = true; + } + struct pre_post_exec_state_t { Pass *parent_pass; int64_t begin_ns; diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index fc41848f7..892500850 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -117,7 +117,9 @@ void opt_eqpmux(test_pmgen_pm &pm) } struct TestPmgenPass : public Pass { - TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } + TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc index 9e7adaab1..ed54ce164 100644 --- a/passes/tests/test_abcloop.cc +++ b/passes/tests/test_abcloop.cc @@ -243,7 +243,9 @@ static void test_abcloop() } struct TestAbcloopPass : public Pass { - TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { } + TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index 404d1e48d..306e760ee 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -326,7 +326,9 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int s } struct TestAutotbBackend : public Backend { - TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { } + TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index a34eafc2f..b6385766c 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -705,7 +705,9 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: } struct TestCellPass : public Pass { - TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { } + TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From 14fdc9e76c09c08276544b8b2059cecadd814fc8 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:19 +1200 Subject: [PATCH 077/181] cmdref: Export internal_flag to json Commands flagged as internal will display a warning, just like experimental commands. Drop `passes/tests` group in favour of `internal` group, which is automatically assigned for any command without an assigned group which is flagged as internal. --- docs/source/cmd/index_internal.rst | 5 +++++ docs/source/cmd/index_passes_tests.rst | 5 ----- docs/source/cmd_ref.rst | 1 + docs/util/newcmdref.py | 9 ++++++++- kernel/register.cc | 10 +++++----- 5 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 docs/source/cmd/index_internal.rst delete mode 100644 docs/source/cmd/index_passes_tests.rst diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst new file mode 100644 index 000000000..c1b7f5eb8 --- /dev/null +++ b/docs/source/cmd/index_internal.rst @@ -0,0 +1,5 @@ +internal +------------------ + +.. autocmdgroup:: internal + :members: diff --git a/docs/source/cmd/index_passes_tests.rst b/docs/source/cmd/index_passes_tests.rst deleted file mode 100644 index 72453f55f..000000000 --- a/docs/source/cmd/index_passes_tests.rst +++ /dev/null @@ -1,5 +0,0 @@ -passes/tests ------------------- - -.. autocmdgroup:: passes/tests - :members: diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index bc0707d67..37b6966ef 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -19,4 +19,5 @@ Command line reference /cmd/index_formal /cmd/index_passes* /cmd/index_techlibs + /cmd/index_internal /cmd/index_other diff --git a/docs/util/newcmdref.py b/docs/util/newcmdref.py index 07c93c0d1..61753fa57 100644 --- a/docs/util/newcmdref.py +++ b/docs/util/newcmdref.py @@ -53,6 +53,7 @@ class YosysCmd: source_line: int source_func: str experimental_flag: bool + internal_flag: bool def __init__( self, @@ -62,7 +63,8 @@ class YosysCmd: source_file: str = "", source_line: int = 0, source_func: str = "", - experimental_flag: bool = False + experimental_flag: bool = False, + internal_flag: bool = False, ) -> None: self.name = name self.title = title @@ -72,6 +74,7 @@ class YosysCmd: self.source_line = source_line self.source_func = source_func self.experimental_flag = experimental_flag + self.internal_flag = internal_flag class YosysCmdGroupDocumenter(Documenter): objtype = 'cmdgroup' @@ -344,6 +347,10 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): self.add_line(f'.. warning:: This command is experimental', source_name, source_line) self.add_line('\n', source_name) + if self.object.internal_flag: + self.add_line(f'.. warning:: This command is intended for internal developer use only', source_name, source_line) + self.add_line('\n', source_name) + def render(content_list: YosysCmdContentListing, indent: int=0): content_source = content_list.source_file or source_name indent_str = ' '*indent diff --git a/kernel/register.cc b/kernel/register.cc index c3414f5db..b15b94411 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -842,7 +842,6 @@ struct HelpPass : public Pass { auto name = it.first; auto pass = it.second; auto title = pass->short_help; - auto experimental_flag = pass->experimental_flag; auto cmd_help = PrettyHelp(); auto has_pretty_help = pass->formatted_help(); @@ -982,7 +981,9 @@ struct HelpPass : public Pass { if (!cmd_help.has_group()) { string source_file = pass->location.file_name(); bool has_source = source_file.compare("unknown") != 0; - if (source_file.find("backends/") == 0 || (!has_source && name.find("read_") == 0)) + if (pass->internal_flag) + cmd_help.group = "internal"; + else if (source_file.find("backends/") == 0 || (!has_source && name.find("read_") == 0)) cmd_help.group = "backends"; else if (source_file.find("frontends/") == 0 || (!has_source && name.find("write_") == 0)) cmd_help.group = "frontends"; @@ -1006,8 +1007,6 @@ struct HelpPass : public Pass { cmd_help.group = "passes/opt"; else if (name.find("proc") == 0) cmd_help.group = "passes/proc"; - else if (name.find("test") == 0) - cmd_help.group = "passes/tests"; } if (groups.count(cmd_help.group) == 0) { @@ -1026,7 +1025,8 @@ struct HelpPass : public Pass { json.entry("source_file", pass->location.file_name()); json.entry("source_line", pass->location.line()); json.entry("source_func", pass->location.function_name()); - json.entry("experimental_flag", experimental_flag); + json.entry("experimental_flag", pass->experimental_flag); + json.entry("internal_flag", pass->internal_flag); json.end_object(); } json.end_object(); From 0ec336ba238983c4072ab4a0f4bb5ba695fef2f7 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:19 +1200 Subject: [PATCH 078/181] Docs: Add :cmd:title: directive Calling :cmd:title:`` will generate a cross reference to ``, but unlike :cmd:ref: which displays a literal block and puts the title (short_help) in the hovertext (the title field of an a-ref), :cmd:title: will display " - " as plain text. Thus replacing the previous use case of referring to :doc:`cmd/`. Also refactor util py scripts to have more descriptive names. --- docs/source/conf.py | 6 ++-- docs/source/getting_started/example_synth.rst | 30 +++++++++---------- .../getting_started/scripting_intro.rst | 4 +-- .../interactive_investigation.rst | 10 +++---- .../more_scripting/load_design.rst | 8 ++--- .../using_yosys/more_scripting/selections.rst | 6 ++-- docs/source/using_yosys/synthesis/synth.rst | 2 +- docs/util/{cellref.py => cell_documenter.py} | 0 docs/util/{newcmdref.py => cmd_documenter.py} | 0 docs/util/{cmdref.py => custom_directives.py} | 26 +++++++++++----- 10 files changed, 52 insertions(+), 40 deletions(-) rename docs/util/{cellref.py => cell_documenter.py} (100%) rename docs/util/{newcmdref.py => cmd_documenter.py} (100%) rename docs/util/{cmdref.py => custom_directives.py} (97%) diff --git a/docs/source/conf.py b/docs/source/conf.py index 813befa47..725ac42bb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -110,13 +110,13 @@ latex_elements = { # custom cmd-ref parsing/linking sys.path += [os.path.dirname(__file__) + "/../"] -extensions.append('util.cmdref') +extensions.append('util.custom_directives') # use autodocs extensions.append('sphinx.ext.autodoc') -extensions.append('util.cellref') +extensions.append('util.cell_documenter') cells_json = Path(__file__).parent / 'generated' / 'cells.json' -extensions.append('util.newcmdref') +extensions.append('util.cmd_documenter') cmds_json = Path(__file__).parent / 'generated' / 'cmds.json' from sphinx.application import Sphinx diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst index c12acb3dc..ccf0d252b 100644 --- a/docs/source/getting_started/example_synth.rst +++ b/docs/source/getting_started/example_synth.rst @@ -143,8 +143,8 @@ line refers to the line numbers of the start/end of the corresponding ``always @`` block. In the case of an ``initial`` block, we instead see the ``PROC`` referring to line 0. -To handle these, let us now introduce the next command: :cmd:ref:`proc`. `proc` -is a macro command like `synth_ice40`. Rather than modifying the design +To handle these, let us now introduce the next command: :cmd:title:`proc`. +`proc` is a macro command like `synth_ice40`. Rather than modifying the design directly, it instead calls a series of other commands. In the case of `proc`, these sub-commands work to convert the behavioral logic of processes into multiplexers and registers. Let's see what happens when we run it. For now, we @@ -188,7 +188,7 @@ opt_expr `. .. note:: - :cmd:ref:`clean` can also be called with two semicolons after any command, + :cmd:title:`clean` can also be called with two semicolons after any command, for example we could have called :yoscrypt:`opt_expr;;` instead of :yoscrypt:`opt_expr; clean`. You may notice some scripts will end each line with ``;;``. It is beneficial to run `clean` before inspecting intermediate @@ -215,8 +215,8 @@ Note that if we tried to run this command now then we would get an error. This is because we already removed all of the modules other than ``addr_gen``. We could restart our shell session, but instead let's use two new commands: -- :cmd:ref:`design`, and -- :cmd:ref:`read_verilog`. +- :cmd:title:`design`, and +- :cmd:title:`read_verilog`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -251,7 +251,7 @@ our design won't run into this issue, we can skip the ``-defer``. We can also run `proc` now to finish off the full :ref:`synth_begin`. Because the design schematic is quite large, we will be showing just the data path for the ``rdata`` output. If you would like to see the entire design for yourself, -you can do so with :cmd:ref:`show`. Note that the `show` command only works +you can do so with :cmd:title:`show`. Note that the `show` command only works with a single module, so you may need to call it with :yoscrypt:`show fifo`. :ref:`show_intro` section in :doc:`/getting_started/scripting_intro` has more on how to use `show`. @@ -371,7 +371,7 @@ wasting time on something we know is impossible. Next up is :yoscrypt:`opt -nodffe -nosdff` performing a set of simple optimizations on the design. This command also ensures that only a specific subset of FF types are included, in preparation for the next command: -:cmd:ref:`fsm`. Both `opt` and `fsm` are macro commands which are explored in +:cmd:title:`fsm`. Both `opt` and `fsm` are macro commands which are explored in more detail in :doc:`/using_yosys/synthesis/opt` and :doc:`/using_yosys/synthesis/fsm` respectively. @@ -411,7 +411,7 @@ The next group of commands performs a series of optimizations: :caption: ``coarse`` section (part 2) :name: synth_coarse2 -First up is :cmd:ref:`wreduce`. If we run this we get the following: +First up is :cmd:title:`wreduce`. If we run this we get the following: .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -432,7 +432,7 @@ the schematic and see the output of that cell has now changed. ``rdata`` output after `wreduce` -The next two (new) commands are :cmd:ref:`peepopt` and :cmd:ref:`share`. +The next two (new) commands are :cmd:title:`peepopt` and :cmd:title:`share`. Neither of these affect our design, and they're explored in more detail in :doc:`/using_yosys/synthesis/opt`, so let's skip over them. :yoscrypt:`techmap -map +/cmp2lut.v -D LUT_WIDTH=4` optimizes certain comparison operators by @@ -440,7 +440,7 @@ converting them to LUTs instead. The usage of `techmap` is explored more in :doc:`/using_yosys/synthesis/techmap_synth`. Our next command to run is -:cmd:ref:`memory_dff`. +:cmd:title:`memory_dff`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -543,7 +543,7 @@ Once these cells have been inserted, the call to `opt` can combine cells which are now identical but may have been missed due to e.g. the difference between `$add` and `$sub`. -The other new command in this part is :cmd:ref:`memory`. `memory` is another +The other new command in this part is :cmd:title:`memory`. `memory` is another macro command which we examine in more detail in :doc:`/using_yosys/synthesis/memory`. For this document, let us focus just on the step most relevant to our example: `memory_collect`. Up until this point, @@ -793,9 +793,9 @@ tidy up: The new commands here are: -- :cmd:ref:`autoname`, -- :cmd:ref:`stat`, and -- :cmd:ref:`blackbox`. +- :cmd:title:`autoname`, +- :cmd:title:`stat`, and +- :cmd:title:`blackbox`. The output from `stat` is useful for checking resource utilization; providing a list of cells used in the design and the number of each, as well as the number @@ -847,4 +847,4 @@ is beyond the scope of this documentation. .. _nextpnr: https://github.com/YosysHQ/nextpnr -.. seealso:: :cmd:ref:`synth_ice40` +.. seealso:: :cmd:title:`synth_ice40` diff --git a/docs/source/getting_started/scripting_intro.rst b/docs/source/getting_started/scripting_intro.rst index bc6782eaa..b9e2d395a 100644 --- a/docs/source/getting_started/scripting_intro.rst +++ b/docs/source/getting_started/scripting_intro.rst @@ -122,7 +122,7 @@ module. Detailed documentation of the select framework can be found under :doc:`/using_yosys/more_scripting/selections` or in the command reference at -:cmd:ref:`select`. +:cmd:title:`select`. .. _show_intro: @@ -219,7 +219,7 @@ those used in options, must be a single expression instead. .. _GraphViz color docs: https://graphviz.org/doc/info/colors For all of the options available to `show`, check the command reference at -:cmd:ref:`show`. +:cmd:title:`show`. .. seealso:: :ref:`interactive_show` on the :doc:`/using_yosys/more_scripting/interactive_investigation` page. diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst index fa031f94e..5da8c04d4 100644 --- a/docs/source/using_yosys/more_scripting/interactive_investigation.rst +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -323,10 +323,10 @@ tools). design into an equivalent design that is easier to analyse. - Commands such as `eval` and `sat` can be used to investigate the behavior of the circuit. -- :cmd:ref:`show`. -- :cmd:ref:`dump`. -- :cmd:ref:`add` and :cmd:ref:`delete` can be used to modify and reorganize a - design dynamically. +- :cmd:title:`show`. +- :cmd:title:`dump`. +- :cmd:title:`add` and :cmd:title:`delete` can be used to modify and reorganize + a design dynamically. The code used is included in the Yosys code base under |code_examples/scrambler|_. @@ -358,7 +358,7 @@ reorganizing a module in Yosys and checking the resulting circuit. .. figure:: /_images/code_examples/scrambler/scrambler_p02.* :class: width-helper invert-helper -Analyzing the resulting circuit with :cmd:ref:`eval`: +Analyzing the resulting circuit with :cmd:title:`eval`: .. todo:: replace inline code diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst index 39e5711e6..d1e3e1cc0 100644 --- a/docs/source/using_yosys/more_scripting/load_design.rst +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -31,10 +31,10 @@ keyword: Frontends .. note:: - The Verific frontend for Yosys, which provides the :cmd:ref:`verific` - command, requires Yosys to be built with Verific. For full functionality, - custom modifications to the Verific source code from YosysHQ are required, - but limited useability can be achieved with some stock Verific builds. Check + The Verific frontend for Yosys, which provides the `verific` command, + requires Yosys to be built with Verific. For full functionality, custom + modifications to the Verific source code from YosysHQ are required, but + limited useability can be achieved with some stock Verific builds. Check :doc:`/yosys_internals/extending_yosys/build_verific` for more. Others: diff --git a/docs/source/using_yosys/more_scripting/selections.rst b/docs/source/using_yosys/more_scripting/selections.rst index 0b1fdbcb3..1f3912956 100644 --- a/docs/source/using_yosys/more_scripting/selections.rst +++ b/docs/source/using_yosys/more_scripting/selections.rst @@ -93,7 +93,7 @@ Special patterns can be used to select by object property or type. For example: A:blabla` - select all `$add` cells from the module foo: :yoscrypt:`select foo/t:$add` -A complete list of pattern expressions can be found in :cmd:ref:`select`. +A complete list of pattern expressions can be found in :cmd:title:`select`. Operations on selections ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -141,7 +141,7 @@ Some of the special ``%``-codes: - ``%i``: intersection of top two elements on stack -- pop 2, push 1 - ``%n``: inverse of top element on stack -- pop 1, push 1 -See :cmd:ref:`select` for the full list. +See :cmd:title:`select` for the full list. Expanding selections ^^^^^^^^^^^^^^^^^^^^ @@ -354,7 +354,7 @@ boolean operations such as intersection (``%i``) and difference (``%d``) are powerful tools for extracting the relevant portions of the circuit under investigation. -Again, see :cmd:ref:`select` for full documentation of these expressions. +Again, see :cmd:title:`select` for full documentation of these expressions. Incremental selection ^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/using_yosys/synthesis/synth.rst b/docs/source/using_yosys/synthesis/synth.rst index 7a248ae29..db81d0790 100644 --- a/docs/source/using_yosys/synthesis/synth.rst +++ b/docs/source/using_yosys/synthesis/synth.rst @@ -15,7 +15,7 @@ General synthesis ~~~~~~~~~~~~~~~~~ In addition to the above hardware-specific synth commands, there is also -:cmd:ref:`prep`. This command is limited to coarse-grain synthesis, without +:cmd:title:`prep`. This command is limited to coarse-grain synthesis, without getting into any architecture-specific mappings or optimizations. Among other things, this is useful for design verification. diff --git a/docs/util/cellref.py b/docs/util/cell_documenter.py similarity index 100% rename from docs/util/cellref.py rename to docs/util/cell_documenter.py diff --git a/docs/util/newcmdref.py b/docs/util/cmd_documenter.py similarity index 100% rename from docs/util/newcmdref.py rename to docs/util/cmd_documenter.py diff --git a/docs/util/cmdref.py b/docs/util/custom_directives.py similarity index 97% rename from docs/util/cmdref.py rename to docs/util/custom_directives.py index 1ef09dcf7..a263e06ab 100644 --- a/docs/util/cmdref.py +++ b/docs/util/custom_directives.py @@ -7,9 +7,8 @@ from typing import cast import warnings from docutils import nodes -from docutils.nodes import Node, Element +from docutils.nodes import Node, Element, Text from docutils.parsers.rst import directives -from docutils.parsers.rst.roles import GenericRole from docutils.parsers.rst.states import Inliner from sphinx.application import Sphinx from sphinx.domains import Domain, Index @@ -598,12 +597,17 @@ class PropIndex(TagIndex): return (ret, True) +class TitleRefRole(XRefRole): + """XRefRole used which has the cmd title as the displayed text.""" + pass + class CommandDomain(Domain): name = 'cmd' label = 'Yosys commands' roles = { - 'ref': XRefRole() + 'ref': XRefRole(), + 'title': TitleRefRole(), } directives = { @@ -635,7 +639,7 @@ class CommandDomain(Domain): def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): - + match = [(docname, anchor, name) for name, sig, typ, docname, anchor, prio in self.get_objects() if sig == target] @@ -645,9 +649,17 @@ class CommandDomain(Domain): targ = match[0][1] qual_name = match[0][2] title = self.data['obj2title'].get(qual_name, targ) - - return make_refnode(builder,fromdocname,todocname, - targ, contnode, title) + + if typ == 'title': + # caller wants the title in the content of the node + cmd = contnode.astext() + contnode = Text(f'{cmd} - {title}') + return make_refnode(builder, fromdocname, todocname, + targ, contnode) + else: + # cmd title as hover text + return make_refnode(builder, fromdocname, todocname, + targ, contnode, title) else: print(f"Missing ref for {target} in {fromdocname} ") return None From 37782cb92bb6181d95d60b4875630409b59abe9e Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:56 +1200 Subject: [PATCH 079/181] Docs: Grouping changes Keep techlibs folder hierarchy. techlibs/* and passes/* groups are now nested under index_techlibs and index_passes respectively, with most (all?) of the passes/* pages getting proper headings, as well as backends/frontends/kernel. `index_passes_techmap` also references `index_techlibs`. Split command reference toc in twain, one with maxdepth=2 and one with maxdepth=3, since passes and techlibs now have an extra level of nesting. Move the `cmd_ref` link to the command reference, instead of top of the page. Remove `index_internal` and `index_other` from the toc, and mark the pages as orphan. Internal commands get a note callout after the command reference toc (although this doesn't work for the pdf build), while other commands are linked in the warning for missing `source_location` (since that *should* be the only time when there are any commands in the "unknown" group). Update autodoc extension versions, and mark the directives extension as not `parallel_read_safe` (it might be, but I'm not sure about how the xref lookups work if it is parallel so better to be safe). --- docs/source/cmd/index_backends.rst | 4 +-- docs/source/cmd/index_frontends.rst | 4 +-- docs/source/cmd/index_internal.rst | 2 ++ docs/source/cmd/index_kernel.rst | 4 +-- docs/source/cmd/index_other.rst | 2 ++ docs/source/cmd/index_passes.rst | 14 ++++++++++ docs/source/cmd/index_passes_cmds.rst | 4 +-- docs/source/cmd/index_passes_equiv.rst | 4 +-- docs/source/cmd/index_passes_fsm.rst | 4 +-- docs/source/cmd/index_passes_hierarchy.rst | 2 +- docs/source/cmd/index_passes_memory.rst | 4 +-- docs/source/cmd/index_passes_opt.rst | 4 +-- docs/source/cmd/index_passes_proc.rst | 4 +-- docs/source/cmd/index_passes_sat.rst | 2 +- docs/source/cmd/index_passes_techmap.rst | 4 ++- docs/source/cmd/index_techlibs.rst | 14 ++++++---- docs/source/cmd/index_techlibs_achronix.rst | 5 ++++ docs/source/cmd/index_techlibs_anlogic.rst | 5 ++++ docs/source/cmd/index_techlibs_common.rst | 5 ++++ .../source/cmd/index_techlibs_coolrunner2.rst | 5 ++++ docs/source/cmd/index_techlibs_easic.rst | 5 ++++ docs/source/cmd/index_techlibs_ecp5.rst | 5 ++++ docs/source/cmd/index_techlibs_fabulous.rst | 5 ++++ docs/source/cmd/index_techlibs_gatemate.rst | 5 ++++ docs/source/cmd/index_techlibs_gowin.rst | 5 ++++ docs/source/cmd/index_techlibs_greenpak4.rst | 5 ++++ docs/source/cmd/index_techlibs_ice40.rst | 5 ++++ docs/source/cmd/index_techlibs_intel.rst | 5 ++++ docs/source/cmd/index_techlibs_intel_alm.rst | 5 ++++ docs/source/cmd/index_techlibs_lattice.rst | 5 ++++ docs/source/cmd/index_techlibs_microchip.rst | 5 ++++ docs/source/cmd/index_techlibs_nanoxplore.rst | 5 ++++ docs/source/cmd/index_techlibs_nexus.rst | 5 ++++ docs/source/cmd/index_techlibs_quicklogic.rst | 5 ++++ docs/source/cmd/index_techlibs_sf2.rst | 5 ++++ docs/source/cmd/index_techlibs_xilinx.rst | 5 ++++ docs/source/cmd_ref.rst | 28 +++++++++++++------ docs/source/index.rst | 2 +- docs/util/cmd_documenter.py | 11 ++++++-- docs/util/custom_directives.py | 5 +++- kernel/register.cc | 2 -- 41 files changed, 183 insertions(+), 41 deletions(-) create mode 100644 docs/source/cmd/index_passes.rst create mode 100644 docs/source/cmd/index_techlibs_achronix.rst create mode 100644 docs/source/cmd/index_techlibs_anlogic.rst create mode 100644 docs/source/cmd/index_techlibs_common.rst create mode 100644 docs/source/cmd/index_techlibs_coolrunner2.rst create mode 100644 docs/source/cmd/index_techlibs_easic.rst create mode 100644 docs/source/cmd/index_techlibs_ecp5.rst create mode 100644 docs/source/cmd/index_techlibs_fabulous.rst create mode 100644 docs/source/cmd/index_techlibs_gatemate.rst create mode 100644 docs/source/cmd/index_techlibs_gowin.rst create mode 100644 docs/source/cmd/index_techlibs_greenpak4.rst create mode 100644 docs/source/cmd/index_techlibs_ice40.rst create mode 100644 docs/source/cmd/index_techlibs_intel.rst create mode 100644 docs/source/cmd/index_techlibs_intel_alm.rst create mode 100644 docs/source/cmd/index_techlibs_lattice.rst create mode 100644 docs/source/cmd/index_techlibs_microchip.rst create mode 100644 docs/source/cmd/index_techlibs_nanoxplore.rst create mode 100644 docs/source/cmd/index_techlibs_nexus.rst create mode 100644 docs/source/cmd/index_techlibs_quicklogic.rst create mode 100644 docs/source/cmd/index_techlibs_sf2.rst create mode 100644 docs/source/cmd/index_techlibs_xilinx.rst diff --git a/docs/source/cmd/index_backends.rst b/docs/source/cmd/index_backends.rst index fada942d8..373c26def 100644 --- a/docs/source/cmd/index_backends.rst +++ b/docs/source/cmd/index_backends.rst @@ -1,5 +1,5 @@ -backends ------------------- +Writing output files +-------------------- .. autocmdgroup:: backends :members: diff --git a/docs/source/cmd/index_frontends.rst b/docs/source/cmd/index_frontends.rst index cf046e2db..b64fdc9b9 100644 --- a/docs/source/cmd/index_frontends.rst +++ b/docs/source/cmd/index_frontends.rst @@ -1,5 +1,5 @@ -frontends ------------------- +Reading input files +------------------- .. autocmdgroup:: frontends :members: diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst index c1b7f5eb8..127010980 100644 --- a/docs/source/cmd/index_internal.rst +++ b/docs/source/cmd/index_internal.rst @@ -1,3 +1,5 @@ +:orphan: + internal ------------------ diff --git a/docs/source/cmd/index_kernel.rst b/docs/source/cmd/index_kernel.rst index c3f323e16..c6891b5e5 100644 --- a/docs/source/cmd/index_kernel.rst +++ b/docs/source/cmd/index_kernel.rst @@ -1,5 +1,5 @@ -kernel ------------------- +Yosys kernel commands +--------------------- .. autocmdgroup:: kernel :members: diff --git a/docs/source/cmd/index_other.rst b/docs/source/cmd/index_other.rst index abd41bcdc..540cf9e49 100644 --- a/docs/source/cmd/index_other.rst +++ b/docs/source/cmd/index_other.rst @@ -1,3 +1,5 @@ +:orphan: + Other commands ============== diff --git a/docs/source/cmd/index_passes.rst b/docs/source/cmd/index_passes.rst new file mode 100644 index 000000000..b652be004 --- /dev/null +++ b/docs/source/cmd/index_passes.rst @@ -0,0 +1,14 @@ +Passes +------ + +.. toctree:: + :maxdepth: 2 + :glob: + + /cmd/index_passes_hierarchy + /cmd/index_passes_proc + /cmd/index_passes_fsm + /cmd/index_passes_memory + /cmd/index_passes_opt + /cmd/index_passes_techmap + /cmd/index_passes_* diff --git a/docs/source/cmd/index_passes_cmds.rst b/docs/source/cmd/index_passes_cmds.rst index 6d2c5026d..c7d9fa0a3 100644 --- a/docs/source/cmd/index_passes_cmds.rst +++ b/docs/source/cmd/index_passes_cmds.rst @@ -1,5 +1,5 @@ -passes/cmds ------------------- +General passes +-------------- .. autocmdgroup:: passes/cmds :members: diff --git a/docs/source/cmd/index_passes_equiv.rst b/docs/source/cmd/index_passes_equiv.rst index 5b0bc2741..6ed2c3c18 100644 --- a/docs/source/cmd/index_passes_equiv.rst +++ b/docs/source/cmd/index_passes_equiv.rst @@ -1,5 +1,5 @@ -passes/equiv ------------------- +Equivalence checking +-------------------- .. autocmdgroup:: passes/equiv :members: diff --git a/docs/source/cmd/index_passes_fsm.rst b/docs/source/cmd/index_passes_fsm.rst index ddb2a9168..43af5dce6 100644 --- a/docs/source/cmd/index_passes_fsm.rst +++ b/docs/source/cmd/index_passes_fsm.rst @@ -1,5 +1,5 @@ -passes/fsm ------------------- +FSM handling +------------ .. autocmdgroup:: passes/fsm :members: diff --git a/docs/source/cmd/index_passes_hierarchy.rst b/docs/source/cmd/index_passes_hierarchy.rst index 1d9f237c7..2032be636 100644 --- a/docs/source/cmd/index_passes_hierarchy.rst +++ b/docs/source/cmd/index_passes_hierarchy.rst @@ -1,4 +1,4 @@ -passes/hierarchy +hierarchy ------------------ .. autocmdgroup:: passes/hierarchy diff --git a/docs/source/cmd/index_passes_memory.rst b/docs/source/cmd/index_passes_memory.rst index 74c044135..b4edb88e7 100644 --- a/docs/source/cmd/index_passes_memory.rst +++ b/docs/source/cmd/index_passes_memory.rst @@ -1,5 +1,5 @@ -passes/memory ------------------- +Memory handling +--------------- .. autocmdgroup:: passes/memory :members: diff --git a/docs/source/cmd/index_passes_opt.rst b/docs/source/cmd/index_passes_opt.rst index 66d78b74a..ddeb5ce10 100644 --- a/docs/source/cmd/index_passes_opt.rst +++ b/docs/source/cmd/index_passes_opt.rst @@ -1,5 +1,5 @@ -passes/opt ------------------- +Optimization passes +------------------- .. autocmdgroup:: passes/opt :members: diff --git a/docs/source/cmd/index_passes_proc.rst b/docs/source/cmd/index_passes_proc.rst index 05081f29a..1ad8d85b4 100644 --- a/docs/source/cmd/index_passes_proc.rst +++ b/docs/source/cmd/index_passes_proc.rst @@ -1,5 +1,5 @@ -passes/proc ------------------- +Converting process blocks +------------------------- .. autocmdgroup:: passes/proc :members: diff --git a/docs/source/cmd/index_passes_sat.rst b/docs/source/cmd/index_passes_sat.rst index af041b040..1f2c6904d 100644 --- a/docs/source/cmd/index_passes_sat.rst +++ b/docs/source/cmd/index_passes_sat.rst @@ -1,4 +1,4 @@ -passes/sat +sat ------------------ .. autocmdgroup:: passes/sat diff --git a/docs/source/cmd/index_passes_techmap.rst b/docs/source/cmd/index_passes_techmap.rst index fa9b54575..1682cd181 100644 --- a/docs/source/cmd/index_passes_techmap.rst +++ b/docs/source/cmd/index_passes_techmap.rst @@ -1,5 +1,7 @@ -passes/techmap +Technology mapping ------------------ +.. seealso:: :doc:`/cmd/index_techlibs` + .. autocmdgroup:: passes/techmap :members: diff --git a/docs/source/cmd/index_techlibs.rst b/docs/source/cmd/index_techlibs.rst index c80fccf5f..043620a3b 100644 --- a/docs/source/cmd/index_techlibs.rst +++ b/docs/source/cmd/index_techlibs.rst @@ -1,9 +1,11 @@ -techlibs ------------------- +Technology libraries +==================== -.. TODO:: disambiguate `synth_intel` and `synth_intel_alm` +Listed in alphabetical order. - (MAX10, Cyclone IV) and (Cyclone V, Arria V, Cyclone 10 GX) respectively +.. toctree:: + :maxdepth: 2 + :glob: -.. autocmdgroup:: techlibs - :members: + /cmd/index_techlibs_common + /cmd/index_techlibs_* diff --git a/docs/source/cmd/index_techlibs_achronix.rst b/docs/source/cmd/index_techlibs_achronix.rst new file mode 100644 index 000000000..babf4e979 --- /dev/null +++ b/docs/source/cmd/index_techlibs_achronix.rst @@ -0,0 +1,5 @@ +achronix +------------------ + +.. autocmdgroup:: techlibs/achronix + :members: diff --git a/docs/source/cmd/index_techlibs_anlogic.rst b/docs/source/cmd/index_techlibs_anlogic.rst new file mode 100644 index 000000000..13ef12fc7 --- /dev/null +++ b/docs/source/cmd/index_techlibs_anlogic.rst @@ -0,0 +1,5 @@ +anlogic +------------------ + +.. autocmdgroup:: techlibs/anlogic + :members: diff --git a/docs/source/cmd/index_techlibs_common.rst b/docs/source/cmd/index_techlibs_common.rst new file mode 100644 index 000000000..532f4291e --- /dev/null +++ b/docs/source/cmd/index_techlibs_common.rst @@ -0,0 +1,5 @@ +Generic +------------------ + +.. autocmdgroup:: techlibs/common + :members: diff --git a/docs/source/cmd/index_techlibs_coolrunner2.rst b/docs/source/cmd/index_techlibs_coolrunner2.rst new file mode 100644 index 000000000..23e87d03e --- /dev/null +++ b/docs/source/cmd/index_techlibs_coolrunner2.rst @@ -0,0 +1,5 @@ +coolrunner2 +------------------ + +.. autocmdgroup:: techlibs/coolrunner2 + :members: diff --git a/docs/source/cmd/index_techlibs_easic.rst b/docs/source/cmd/index_techlibs_easic.rst new file mode 100644 index 000000000..0cd375f11 --- /dev/null +++ b/docs/source/cmd/index_techlibs_easic.rst @@ -0,0 +1,5 @@ +easic +------------------ + +.. autocmdgroup:: techlibs/easic + :members: diff --git a/docs/source/cmd/index_techlibs_ecp5.rst b/docs/source/cmd/index_techlibs_ecp5.rst new file mode 100644 index 000000000..42a12e8e9 --- /dev/null +++ b/docs/source/cmd/index_techlibs_ecp5.rst @@ -0,0 +1,5 @@ +ecp5 +------------------ + +.. autocmdgroup:: techlibs/ecp5 + :members: diff --git a/docs/source/cmd/index_techlibs_fabulous.rst b/docs/source/cmd/index_techlibs_fabulous.rst new file mode 100644 index 000000000..ce075c025 --- /dev/null +++ b/docs/source/cmd/index_techlibs_fabulous.rst @@ -0,0 +1,5 @@ +fabulous +------------------ + +.. autocmdgroup:: techlibs/fabulous + :members: diff --git a/docs/source/cmd/index_techlibs_gatemate.rst b/docs/source/cmd/index_techlibs_gatemate.rst new file mode 100644 index 000000000..5a362b0fe --- /dev/null +++ b/docs/source/cmd/index_techlibs_gatemate.rst @@ -0,0 +1,5 @@ +gatemate +------------------ + +.. autocmdgroup:: techlibs/gatemate + :members: diff --git a/docs/source/cmd/index_techlibs_gowin.rst b/docs/source/cmd/index_techlibs_gowin.rst new file mode 100644 index 000000000..379cb773e --- /dev/null +++ b/docs/source/cmd/index_techlibs_gowin.rst @@ -0,0 +1,5 @@ +gowin +------------------ + +.. autocmdgroup:: techlibs/gowin + :members: diff --git a/docs/source/cmd/index_techlibs_greenpak4.rst b/docs/source/cmd/index_techlibs_greenpak4.rst new file mode 100644 index 000000000..1f733488d --- /dev/null +++ b/docs/source/cmd/index_techlibs_greenpak4.rst @@ -0,0 +1,5 @@ +greenpak4 +------------------ + +.. autocmdgroup:: techlibs/greenpak4 + :members: diff --git a/docs/source/cmd/index_techlibs_ice40.rst b/docs/source/cmd/index_techlibs_ice40.rst new file mode 100644 index 000000000..3d04fee05 --- /dev/null +++ b/docs/source/cmd/index_techlibs_ice40.rst @@ -0,0 +1,5 @@ +ice40 +------------------ + +.. autocmdgroup:: techlibs/ice40 + :members: diff --git a/docs/source/cmd/index_techlibs_intel.rst b/docs/source/cmd/index_techlibs_intel.rst new file mode 100644 index 000000000..6b3a26222 --- /dev/null +++ b/docs/source/cmd/index_techlibs_intel.rst @@ -0,0 +1,5 @@ +Intel (MAX10, Cyclone IV) +------------------------- + +.. autocmdgroup:: techlibs/intel + :members: diff --git a/docs/source/cmd/index_techlibs_intel_alm.rst b/docs/source/cmd/index_techlibs_intel_alm.rst new file mode 100644 index 000000000..afadfc13e --- /dev/null +++ b/docs/source/cmd/index_techlibs_intel_alm.rst @@ -0,0 +1,5 @@ +Intel ALM (Cyclone V, Arria V, Cyclone 10 GX) +--------------------------------------------- + +.. autocmdgroup:: techlibs/intel_alm + :members: diff --git a/docs/source/cmd/index_techlibs_lattice.rst b/docs/source/cmd/index_techlibs_lattice.rst new file mode 100644 index 000000000..55bed7afc --- /dev/null +++ b/docs/source/cmd/index_techlibs_lattice.rst @@ -0,0 +1,5 @@ +lattice +------------------ + +.. autocmdgroup:: techlibs/lattice + :members: diff --git a/docs/source/cmd/index_techlibs_microchip.rst b/docs/source/cmd/index_techlibs_microchip.rst new file mode 100644 index 000000000..79340febc --- /dev/null +++ b/docs/source/cmd/index_techlibs_microchip.rst @@ -0,0 +1,5 @@ +microchip +------------------ + +.. autocmdgroup:: techlibs/microchip + :members: diff --git a/docs/source/cmd/index_techlibs_nanoxplore.rst b/docs/source/cmd/index_techlibs_nanoxplore.rst new file mode 100644 index 000000000..c941c32ba --- /dev/null +++ b/docs/source/cmd/index_techlibs_nanoxplore.rst @@ -0,0 +1,5 @@ +nanoxplore +------------------ + +.. autocmdgroup:: techlibs/nanoxplore + :members: diff --git a/docs/source/cmd/index_techlibs_nexus.rst b/docs/source/cmd/index_techlibs_nexus.rst new file mode 100644 index 000000000..a0289a571 --- /dev/null +++ b/docs/source/cmd/index_techlibs_nexus.rst @@ -0,0 +1,5 @@ +nexus +------------------ + +.. autocmdgroup:: techlibs/nexus + :members: diff --git a/docs/source/cmd/index_techlibs_quicklogic.rst b/docs/source/cmd/index_techlibs_quicklogic.rst new file mode 100644 index 000000000..d11d4faca --- /dev/null +++ b/docs/source/cmd/index_techlibs_quicklogic.rst @@ -0,0 +1,5 @@ +quicklogic +------------------ + +.. autocmdgroup:: techlibs/quicklogic + :members: diff --git a/docs/source/cmd/index_techlibs_sf2.rst b/docs/source/cmd/index_techlibs_sf2.rst new file mode 100644 index 000000000..b97f96c9d --- /dev/null +++ b/docs/source/cmd/index_techlibs_sf2.rst @@ -0,0 +1,5 @@ +sf2 +------------------ + +.. autocmdgroup:: techlibs/sf2 + :members: diff --git a/docs/source/cmd/index_techlibs_xilinx.rst b/docs/source/cmd/index_techlibs_xilinx.rst new file mode 100644 index 000000000..5ce3d115b --- /dev/null +++ b/docs/source/cmd/index_techlibs_xilinx.rst @@ -0,0 +1,5 @@ +xilinx +------------------ + +.. autocmdgroup:: techlibs/xilinx + :members: diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index 37b6966ef..73a383ad5 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -1,5 +1,3 @@ -.. _cmd_ref: - ================================================================================ Command line reference ================================================================================ @@ -7,17 +5,31 @@ Command line reference .. literalinclude:: /generated/yosys :start-at: Usage +.. _cmd_ref: + +Command reference +----------------- + +.. todo:: Can we warn on command groups that aren't included anywhere? + .. toctree:: - :caption: Command reference :maxdepth: 2 - :glob: /appendix/env_vars - /cmd/index_backends /cmd/index_frontends + /cmd/index_backends /cmd/index_kernel /cmd/index_formal - /cmd/index_passes* + +.. toctree:: + :maxdepth: 3 + + /cmd/index_passes /cmd/index_techlibs - /cmd/index_internal - /cmd/index_other + +.. TODO:: Fix index_internal not being included in pdf + +.. note:: + + Commands intended for internal developer use can also be found under + :doc:`/cmd/index_internal` diff --git a/docs/source/index.rst b/docs/source/index.rst index 61dc114ef..403100093 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -5,7 +5,7 @@ Yosys Open SYnthesis Suite Yosys is an open source framework for RTL synthesis. To learn more about Yosys, see :doc:`/introduction`. For a quick guide on how to get started using Yosys, check out :doc:`/getting_started/index`. For the complete list of commands -available, go to :ref:`commandindex`. +available, go to :ref:`cmd_ref`. .. todo:: look into command ref improvements diff --git a/docs/util/cmd_documenter.py b/docs/util/cmd_documenter.py index 61753fa57..52f3d2cf4 100644 --- a/docs/util/cmd_documenter.py +++ b/docs/util/cmd_documenter.py @@ -228,13 +228,20 @@ class YosysCmdGroupDocumenter(Documenter): sourcename = self.get_sourcename() - if not self.import_object(): + imported_object = self.import_object(); + if self.lib_key == 'groups' and self.name == 'unknown': + if imported_object: + logger.warning(f"Found commands assigned to group {self.name}: {[x[0] for x in self.object]}", type='cmdref') + else: + return + elif not imported_object: log_msg = f"unable to load {self.name} with {type(self)}" if self.lib_key == 'groups': logger.info(log_msg, type = 'cmdref') self.add_line(f'.. warning:: No commands found for group {self.name!r}', sourcename) self.add_line('', sourcename) self.add_line(' Documentation may have been built without ``source_location`` support.', sourcename) + self.add_line(' Try check :doc:`/cmd/index_other`.', sourcename) else: logger.warning(log_msg, type = 'cmdref') return @@ -401,6 +408,6 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_autodocumenter(YosysCmdGroupDocumenter) app.add_autodocumenter(YosysCmdDocumenter) return { - 'version': '1', + 'version': '2', 'parallel_read_safe': True, } diff --git a/docs/util/custom_directives.py b/docs/util/custom_directives.py index a263e06ab..041472386 100644 --- a/docs/util/custom_directives.py +++ b/docs/util/custom_directives.py @@ -735,4 +735,7 @@ def setup(app: Sphinx): app.add_role('autoref', autoref) - return {'version': '0.2'} + return { + 'version': '0.3', + 'parallel_read_safe': False, + } diff --git a/kernel/register.cc b/kernel/register.cc index b15b94411..a688146ed 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -987,8 +987,6 @@ struct HelpPass : public Pass { cmd_help.group = "backends"; else if (source_file.find("frontends/") == 0 || (!has_source && name.find("write_") == 0)) cmd_help.group = "frontends"; - else if (source_file.find("techlibs/") == 0 || (!has_source && name.find("synth_") == 0)) - cmd_help.group = "techlibs"; else if (has_source) { auto last_slash = source_file.find_last_of('/'); if (last_slash != string::npos) { From 92ab1251134f4eba7df45843c20e8529a3e22653 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:56 +1200 Subject: [PATCH 080/181] cmdref: Assigning cmds to formal group Give formal index a proper title. Use `Pass::formatted_help()` to assign the group, but still return `false` because the help text still comes from `Pass::help()`. Tidy up some of the affected files' includes to make use of the shared `yosys.h` includes. --- docs/source/cmd/index_formal.rst | 4 ++-- passes/cmds/dft_tag.cc | 6 ++++++ passes/cmds/future.cc | 6 ++++++ passes/cmds/glift.cc | 11 ++++++++--- passes/cmds/xprop.cc | 6 ++++++ passes/sat/assertpmux.cc | 6 ++++++ passes/sat/async2sync.cc | 6 ++++++ passes/sat/clk2fflogic.cc | 6 ++++++ passes/sat/cutpoint.cc | 6 ++++++ passes/sat/fmcombine.cc | 6 ++++++ passes/sat/fminit.cc | 6 ++++++ passes/sat/formalff.cc | 6 ++++++ passes/sat/freduce.cc | 14 +++++++------- passes/sat/miter.cc | 10 +++++++--- passes/sat/mutate.cc | 6 ++++++ passes/sat/qbfsat.cc | 6 ++++++ passes/sat/sat.cc | 14 +++++++------- passes/sat/supercover.cc | 6 ++++++ passes/sat/synthprop.cc | 8 ++++++++ 19 files changed, 117 insertions(+), 22 deletions(-) diff --git a/docs/source/cmd/index_formal.rst b/docs/source/cmd/index_formal.rst index 53701a437..b8b134c17 100644 --- a/docs/source/cmd/index_formal.rst +++ b/docs/source/cmd/index_formal.rst @@ -1,5 +1,5 @@ -formal ------------------- +Formal verification +------------------- .. autocmdgroup:: formal :members: diff --git a/passes/cmds/dft_tag.cc b/passes/cmds/dft_tag.cc index b6afe6f89..347c8efa4 100644 --- a/passes/cmds/dft_tag.cc +++ b/passes/cmds/dft_tag.cc @@ -22,6 +22,7 @@ #include "kernel/modtools.h" #include "kernel/sigtools.h" #include "kernel/yosys.h" +#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -952,6 +953,11 @@ struct DftTagWorker { struct DftTagPass : public Pass { DftTagPass() : Pass("dft_tag", "create tagging logic for data flow tracking") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/future.cc b/passes/cmds/future.cc index b03613c9b..5dcf46bcf 100644 --- a/passes/cmds/future.cc +++ b/passes/cmds/future.cc @@ -24,6 +24,7 @@ #include "kernel/sigtools.h" #include "kernel/utils.h" #include "kernel/yosys.h" +#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -110,6 +111,11 @@ struct FutureWorker { struct FuturePass : public Pass { FuturePass() : Pass("future", "resolve future sampled value functions") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/glift.cc b/passes/cmds/glift.cc index 6a7d070d7..0c321eba6 100644 --- a/passes/cmds/glift.cc +++ b/passes/cmds/glift.cc @@ -17,10 +17,9 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" #include "kernel/utils.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -425,6 +424,12 @@ public: struct GliftPass : public Pass { GliftPass() : Pass("glift", "create GLIFT models and optimization problems") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/xprop.cc b/passes/cmds/xprop.cc index dc5befc27..d2d0c4d8e 100644 --- a/passes/cmds/xprop.cc +++ b/passes/cmds/xprop.cc @@ -24,6 +24,7 @@ #include "kernel/sigtools.h" #include "kernel/utils.h" #include "kernel/yosys.h" +#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -1100,6 +1101,11 @@ struct XpropWorker struct XpropPass : public Pass { XpropPass() : Pass("xprop", "formal x propagation") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc index 991977ab9..7b3357f82 100644 --- a/passes/sat/assertpmux.cc +++ b/passes/sat/assertpmux.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/utils.h" @@ -182,6 +183,11 @@ struct AssertpmuxWorker struct AssertpmuxPass : public Pass { AssertpmuxPass() : Pass("assertpmux", "adds asserts for parallel muxes") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index 93c7e96c8..e86a78d81 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -27,6 +28,11 @@ PRIVATE_NAMESPACE_BEGIN struct Async2syncPass : public Pass { Async2syncPass() : Pass("async2sync", "convert async FF inputs to sync circuits") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index 348bab727..6b94cbe19 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -33,6 +34,11 @@ struct SampledSig { struct Clk2fflogicPass : public Pass { Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index dcd399a57..485e44fd6 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -25,6 +26,11 @@ PRIVATE_NAMESPACE_BEGIN struct CutpointPass : public Pass { CutpointPass() : Pass("cutpoint", "adds formal cut points to the design") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc index 220cf5c52..575b2f40d 100644 --- a/passes/sat/fmcombine.cc +++ b/passes/sat/fmcombine.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" @@ -237,6 +238,11 @@ struct FmcombineWorker struct FmcombinePass : public Pass { FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/fminit.cc b/passes/sat/fminit.cc index 5f4ec0068..547082164 100644 --- a/passes/sat/fminit.cc +++ b/passes/sat/fminit.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -25,6 +26,11 @@ PRIVATE_NAMESPACE_BEGIN struct FminitPass : public Pass { FminitPass() : Pass("fminit", "set init values/sequences for formal") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/formalff.cc b/passes/sat/formalff.cc index 1d87fcc3b..286bf2976 100644 --- a/passes/sat/formalff.cc +++ b/passes/sat/formalff.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -486,6 +487,11 @@ void HierarchyWorker::propagate() struct FormalFfPass : public Pass { FormalFfPass() : Pass("formalff", "prepare FFs for formal") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index 52e80f667..6063c5ab9 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -17,17 +17,12 @@ * */ -#include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/consteval.h" #include "kernel/sigtools.h" -#include "kernel/log.h" #include "kernel/satgen.h" -#include -#include -#include -#include -#include +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -761,6 +756,11 @@ struct FreduceWorker struct FreducePass : public Pass { FreducePass() : Pass("freduce", "perform functional reduction") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 8f27c4c6f..9bcf25547 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -17,9 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -395,6 +394,11 @@ void create_miter_assert(struct Pass *that, std::vector args, RTLIL struct MiterPass : public Pass { MiterPass() : Pass("miter", "automatically create a miter circuit") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc index 3075ef3f0..43373ce0e 100644 --- a/passes/sat/mutate.cc +++ b/passes/sat/mutate.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -728,6 +729,11 @@ void mutate_cnot(Design *design, const mutate_opts_t &opts, bool one) struct MutatePass : public Pass { MutatePass() : Pass("mutate", "generate or apply design mutations") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc index db6836ea1..7a7a31806 100644 --- a/passes/sat/qbfsat.cc +++ b/passes/sat/qbfsat.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/consteval.h" #include "qbfsat.h" @@ -504,6 +505,11 @@ QbfSolveOptions parse_args(const std::vector &args) { struct QbfSatPass : public Pass { QbfSatPass() : Pass("qbfsat", "solve a 2QBF-SAT problem in the circuit") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 0c2143fc9..2f20880cb 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -21,17 +21,12 @@ // Niklas Een and Niklas Sörensson (2003) // http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.8161 -#include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/consteval.h" #include "kernel/sigtools.h" -#include "kernel/log.h" #include "kernel/satgen.h" -#include -#include -#include -#include -#include +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -902,6 +897,11 @@ void print_qed() struct SatPass : public Pass { SatPass() : Pass("sat", "solve a SAT problem in the circuit") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/supercover.cc b/passes/sat/supercover.cc index 38dbd3cf9..f1b3ad09c 100644 --- a/passes/sat/supercover.cc +++ b/passes/sat/supercover.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -25,6 +26,11 @@ PRIVATE_NAMESPACE_BEGIN struct SupercoverPass : public Pass { SupercoverPass() : Pass("supercover", "add hi/lo cover cells for each wire bit") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/synthprop.cc b/passes/sat/synthprop.cc index 5553abec2..af69b1172 100644 --- a/passes/sat/synthprop.cc +++ b/passes/sat/synthprop.cc @@ -18,7 +18,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ + #include "kernel/yosys.h" +#include "kernel/log_help.h" YOSYS_NAMESPACE_BEGIN @@ -179,6 +181,12 @@ void SynthPropWorker::run() struct SyntProperties : public Pass { SyntProperties() : Pass("synthprop", "synthesize SVA properties") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } + virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From 2e5b029ba5184872b50e8eb46f90e013aa407ffe Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:56 +1200 Subject: [PATCH 081/181] Docs: Option lists have yoscrypt highlights --- docs/source/_static/custom.css | 5 +++++ docs/util/custom_directives.py | 14 +++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css index b08194c05..60faf6812 100644 --- a/docs/source/_static/custom.css +++ b/docs/source/_static/custom.css @@ -18,3 +18,8 @@ .literal-block-wrapper .code-block-caption .caption-number { padding-right: 0.5em } + +/* Don't double shrink text in a literal in an optionlist */ +kbd .option>.literal { + font-size: revert; +} diff --git a/docs/util/custom_directives.py b/docs/util/custom_directives.py index 041472386..58e5b37fd 100644 --- a/docs/util/custom_directives.py +++ b/docs/util/custom_directives.py @@ -14,7 +14,7 @@ from sphinx.application import Sphinx from sphinx.domains import Domain, Index from sphinx.domains.std import StandardDomain from sphinx.environment import BuildEnvironment -from sphinx.roles import XRefRole +from sphinx.roles import XRefRole, SphinxRole from sphinx.directives import ObjectDescription from sphinx.directives.code import container_wrapper from sphinx.util.nodes import make_refnode @@ -155,7 +155,7 @@ class CommandOptionGroupNode(CommandUsageNode): ] def transform_content(self, contentnode: addnodes.desc_content) -> None: - """hack `:option -thing: desc` into a proper option list""" + """hack `:option -thing: desc` into a proper option list with yoscrypt highlighting""" newchildren = [] for node in contentnode: newnode = node @@ -172,7 +172,10 @@ class CommandOptionGroupNode(CommandUsageNode): option_group += option name, text = child.rawsource.split(' ', 1) is_option = name == 'option' - option += nodes.option_string(text=text) + literal = nodes.literal(text=text) + literal['classes'] += ['code', 'highlight', 'yoscrypt'] + literal['language'] = 'yoscrypt' + option += literal if not is_option: warnings.warn(f'unexpected option \'{name}\' in {field.source}') elif isinstance(child, nodes.field_body): description = nodes.description() @@ -601,6 +604,10 @@ class TitleRefRole(XRefRole): """XRefRole used which has the cmd title as the displayed text.""" pass +class OptionRole(SphinxRole): + def run(self) -> tuple[list[Node], list]: + return self.inliner.interpreted(self.rawtext, self.text, 'yoscrypt', self.lineno) + class CommandDomain(Domain): name = 'cmd' label = 'Yosys commands' @@ -608,6 +615,7 @@ class CommandDomain(Domain): roles = { 'ref': XRefRole(), 'title': TitleRefRole(), + 'option': OptionRole(), } directives = { From 726a30fe3730be0fc125ca8bcbec3446730c0c20 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:57 +1200 Subject: [PATCH 082/181] cmd_documenter: Fix option_spec Need to copy parent class `option_spec` to retain default options (e.g. `:noindex:`). --- docs/util/cmd_documenter.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/util/cmd_documenter.py b/docs/util/cmd_documenter.py index 52f3d2cf4..2b93772e8 100644 --- a/docs/util/cmd_documenter.py +++ b/docs/util/cmd_documenter.py @@ -82,12 +82,13 @@ class YosysCmdGroupDocumenter(Documenter): object: tuple[str, list[str]] lib_key = 'groups' - option_spec = { + option_spec = Documenter.option_spec.copy() + option_spec.update({ 'caption': autodoc.annotation_option, 'members': autodoc.members_option, 'source': autodoc.bool_option, 'linenos': autodoc.bool_option, - } + }) __cmd_lib: dict[str, list[str] | dict[str]] | None = None @property From 06689f998b191ca7e99d0dce89a9476868daa4cf Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:57 +1200 Subject: [PATCH 083/181] autocmd_rst: autodoc for generated RST Adds `autocmd_rst` directive, which effectively calls `autocmd` for the same command, but wraps it in a code-block in order to render the raw RST generated. --- docs/util/cmd_documenter.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/util/cmd_documenter.py b/docs/util/cmd_documenter.py index 2b93772e8..bf71a00ac 100644 --- a/docs/util/cmd_documenter.py +++ b/docs/util/cmd_documenter.py @@ -403,11 +403,35 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): return False, [] +class YosysCmdRstDocumenter(YosysCmdDocumenter): + objtype = 'cmd_rst' + priority = 0 + + @classmethod + def can_document_member(cls, *args) -> bool: + return False + + def add_directive_header(self, sig): + source_name = self.object.source_file + cmd = self.object.name + self.add_line(f'.. code-block:: rst', source_name) + self.add_line(f' :caption: Generated rst for ``.. autocmd:: {cmd}``', source_name) + + def add_content(self, more_content): + source_name = self.object.source_file + cmd = self.object.name + self.domain = 'cmd' + super().add_directive_header(cmd) + self.add_line('', source_name) + self.indent += self.content_indent + super().add_content(more_content) + def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value('cmds_json', False, 'html', [Path, PosixPath, WindowsPath]) app.setup_extension('sphinx.ext.autodoc') app.add_autodocumenter(YosysCmdGroupDocumenter) app.add_autodocumenter(YosysCmdDocumenter) + app.add_autodocumenter(YosysCmdRstDocumenter) return { 'version': '2', 'parallel_read_safe': True, From d88688781d5c216ec5371d1a72e1e9f34bb1ecc1 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:57 +1200 Subject: [PATCH 084/181] Docs: Skeleton documentation for writing command help Use `chformal` as example, comparing the `autocmd` output with `ChformalPass::formal_help()`, the json dump from the `ContentListing`, the command line output, and the RST generated (using `autocmd_rst`). Includes bullet points on each step for more information. Should eventually end up in `yosys_internals/extending_yosys/contributing.rst`, but it currently lives in `cmd/index_internal.rst` to avoid merge conflicts since cell help documentation is still WIP. Also exports chformal source and help output to `docs/source/generated` during `make docs/prep`. --- Makefile | 11 ++- docs/source/cmd/index_internal.rst | 147 +++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2673cff2a..3b6a3bfaf 100644 --- a/Makefile +++ b/Makefile @@ -1060,6 +1060,15 @@ docs/source/generated/functional/rosette.diff: backends/functional/smtlib.cc bac PHONY: docs/gen/functional_ir docs/gen/functional_ir: docs/source/generated/functional/smtlib.cc docs/source/generated/functional/rosette.diff +docs/source/generated/%.log: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) + $(Q) ./$(PROGRAM_PREFIX)yosys -qQT -h '$*' -l $@ + +docs/source/generated/chformal.cc: passes/cmds/chformal.cc docs/source/generated + cp $^ $@ + +PHONY: docs/gen/chformal +docs/gen/chformal: docs/source/generated/chformal.log docs/source/generated/chformal.cc + PHONY: docs/gen docs/usage docs/reqs docs/gen: $(TARGETS) $(Q) $(MAKE) -C docs gen @@ -1095,7 +1104,7 @@ docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep -docs/prep: docs/source/generated/cells.json docs/source/generated/cmds.json docs/gen docs/usage docs/gen/functional_ir +docs/prep: docs/source/generated/cells.json docs/source/generated/cmds.json docs/gen docs/usage docs/gen/functional_ir docs/gen/chformal DOC_TARGET ?= html docs: docs/prep diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst index 127010980..f992d27c4 100644 --- a/docs/source/cmd/index_internal.rst +++ b/docs/source/cmd/index_internal.rst @@ -5,3 +5,150 @@ internal .. autocmdgroup:: internal :members: + +Writing command help +-------------------- + +- use `chformal` as an example +- generated help content below + +.. _chformal autocmd: + +.. autocmd:: chformal + :noindex: + +The ``formatted_help()`` method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``PrettyHelp::get_current()`` +- ``PrettyHelp::set_group()`` + + + used with ``.. autocmdgroup:: `` + + can assign group and return false + + if no group is set, will try to use ``source_location`` and assign group + from path to source file + +- return value + + + true means help content added to current ``PrettyHelp`` + + false to use ``Pass::help()`` + +- adding content + + + help content is a list of ``ContentListing`` nodes, each one having a type, + body, and its own list of children ``ContentListing``\ s + + ``PrettyHelp::get_root()`` returns the root ``ContentListing`` (``type="root"``) + + ``ContentListing::{usage, option, codeblock, paragraph}`` each add a + ``ContentListing`` to the current node, with type the same as the method + + * the first argument is the body of the new node + * ``usage`` shows how to call the command (i.e. its "signature") + * ``paragraph`` content is formatted as a paragraph of text with line breaks + added automatically + * ``codeblock`` content is displayed verbatim, use line breaks as desired; + takes an optional ``language`` argument for assigning the language in RST + output for code syntax highlighting (currently not implemented) + * ``option`` lists a single option for the command, usually starting with a + dash (``-``); takes an optional second argument which adds a paragraph + node as a means of description + + + ``ContentListing::open_optiongroup`` + + * each option must be in an optiongroup + * optional name argument, which will be rendered in (RST) output + + + ``ContentListing::open_option`` creates and returns a new option node, can + be used to e.g. add multiple paragraphs to an option's description + + paragraphs are treated as raw RST, allowing for inline formatting and + references as if it were written in the RST file itself + +.. todo:: Support ``ContentListing::codeblock`` language argument + +.. todo:: Support anonymous optiongroup + + If an option is added to the root node it should add the option to the last + child of the root, making a new child if the last child is not an optiongroup + +.. literalinclude:: /generated/chformal.cc + :language: c++ + :start-at: bool formatted_help() + :end-before: void execute + :caption: ``ChformalPass::formatted_help()`` from :file:`passes/cmds/chformal.cc` + +Dumping command help to json +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `help -dump-cells-json cmds.json` + + + generates a ``ContentListing`` for each command registered in Yosys + + tries to parse unformatted ``Pass::help()`` output if + ``Pass::formatted_help()`` is unimplemented or returns false + + * if a line starts with four spaces followed by the name of the command then + a space, it is parsed as a signature (usage node) + * if a line is indented and starts with a dash (``-``), it is parsed as an + option + * anything else is parsed as a codeblock and added to either the root node + or the current option/optiongroup depending on the indentation + + + dictionary of command name to ``ContentListing`` + + * uses ``ContentListing::to_json()`` recursively for each node in root + * root node used for source location of class definition + * includes flags set during pass constructor (e.g. ``experimental_flag`` set + by ``Pass::experimental()``) + * also title (``short_help`` argument in ``Pass::Pass``), group, and class + name + + + dictionary of group name to list of commands in that group + +- used by sphinx autodoc to generate help content + +.. literalinclude:: /generated/cmds.json + :language: json + :start-at: "chformal": { + :end-before: "chparam": { + :caption: `chformal` in generated :file:`cmds.json` + +Command line rendering +~~~~~~~~~~~~~~~~~~~~~~ + +- if ``Pass::formatted_help()`` returns true, will call + ``PrettyHelp::log_help()`` + + + traverse over the children of the root node and render as plain text + + effectively the reverse of converting unformatted ``Pass::help()`` text + + lines are broken at 80 characters while maintaining indentation (controlled + by ``MAX_LINE_LEN`` in :file:`kernel/log_help.cc`) + + each line is broken into words separated by spaces, if a given word starts + and ends with backticks they will be stripped + +- if it returns false it will call ``Pass::help()`` which should call ``log()`` + directly to print and format help text + + + if ``Pass::help()`` is not overridden then a default message about missing + help will be displayed + +.. literalinclude:: /generated/chformal.log + :lines: 2- + +RST generated from autocmd +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- below is the raw RST output from ``autocmd`` (``YosysCmdDocumenter`` class in + :file:`docs/util/cmd_documenter.py`) for `chformal` command +- heading will be rendered as a subheading of the most recent heading (see + `chformal autocmd`_ above rendered under `Writing command help`_) +- ``.. cmd:def:: `` line is indexed for cross references with ``:cmd:ref:`` + directive (`chformal autocmd`_ above uses ``:noindex:`` option so that + `chformal` still links to the correct location) + + + ``:title:`` option controls text that appears when hovering over the + `chformal` link + +- commands with warning flags (experimental or internal) add a ``.. warning`` + block before any of the help content +- if a command has no ``source_location`` the ``.. note`` at the bottom will + instead link to :doc:`/cmd/index_other` + +.. autocmd_rst:: chformal From 862fe4cb5690e75c377810165015dfaf967e0cec Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:57 +1200 Subject: [PATCH 085/181] Docs: Title for index_internal --- docs/source/cmd/index_internal.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst index f992d27c4..55c99f05e 100644 --- a/docs/source/cmd/index_internal.rst +++ b/docs/source/cmd/index_internal.rst @@ -1,7 +1,7 @@ :orphan: -internal ------------------- +Internal commands for developers +-------------------------------- .. autocmdgroup:: internal :members: From f2ef17b5816b7df9930d2975c2d394b06edf4edf Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:57 +1200 Subject: [PATCH 086/181] cmdref: Assign rmports to greenpak group Also tidy `#include`s. --- passes/opt/rmports.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc index 9fa9f5c2d..0ac391790 100644 --- a/passes/opt/rmports.cc +++ b/passes/opt/rmports.cc @@ -17,17 +17,20 @@ * */ -#include "kernel/register.h" #include "kernel/sigtools.h" -#include "kernel/log.h" -#include -#include +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct RmportsPassPass : public Pass { RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("techlibs/greenpak4"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From 5d010789e23f500680be739d1f5530901064fa29 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:57 +1200 Subject: [PATCH 087/181] cmdref: Split passes/status from passes/cmds Rename passes/cmds from "General passes" to "Design modification". More `yosys.h` includes. cmdref: Split passes/status from passes/cmds Rename passes/cmds from "General passes" to "Design modification". More `yosys.h` includes. --- docs/source/cmd/index_passes_cmds.rst | 4 ++-- docs/source/cmd/index_passes_status.rst | 5 +++++ passes/cmds/check.cc | 6 ++++++ passes/cmds/cover.cc | 14 +++++++++----- passes/cmds/edgetypes.cc | 6 ++++++ passes/cmds/example_dt.cc | 8 +++++--- passes/cmds/exec.cc | 9 +++++++-- passes/cmds/internal_stats.cc | 7 ++++--- passes/cmds/logcmd.cc | 10 +++++++--- passes/cmds/logger.cc | 9 +++++++-- passes/cmds/ltp.cc | 6 ++++++ passes/cmds/plugin.cc | 6 ++++++ passes/cmds/portarcs.cc | 6 ++++++ passes/cmds/portlist.cc | 6 ++++++ passes/cmds/printattrs.cc | 6 ++++++ passes/cmds/scc.cc | 11 +++++++---- passes/cmds/scratchpad.cc | 10 +++++++--- passes/cmds/select.cc | 18 ++++++++++++++++-- passes/cmds/setenv.cc | 11 +++++++---- passes/cmds/show.cc | 10 +++++++--- passes/cmds/sta.cc | 6 ++++++ passes/cmds/stat.cc | 6 ++++++ passes/cmds/tee.cc | 10 +++++++--- passes/cmds/torder.cc | 6 ++++++ passes/cmds/trace.cc | 11 +++++++++++ passes/cmds/viz.cc | 6 ++++++ passes/cmds/write_file.cc | 6 ++++++ 27 files changed, 180 insertions(+), 39 deletions(-) create mode 100644 docs/source/cmd/index_passes_status.rst diff --git a/docs/source/cmd/index_passes_cmds.rst b/docs/source/cmd/index_passes_cmds.rst index c7d9fa0a3..d7b448f07 100644 --- a/docs/source/cmd/index_passes_cmds.rst +++ b/docs/source/cmd/index_passes_cmds.rst @@ -1,5 +1,5 @@ -General passes --------------- +Design modification +------------------- .. autocmdgroup:: passes/cmds :members: diff --git a/docs/source/cmd/index_passes_status.rst b/docs/source/cmd/index_passes_status.rst new file mode 100644 index 000000000..a157ed840 --- /dev/null +++ b/docs/source/cmd/index_passes_status.rst @@ -0,0 +1,5 @@ +Design status +------------- + +.. autocmdgroup:: passes/status + :members: diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 83fe781a0..8bbcb8da0 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -22,12 +22,18 @@ #include "kernel/celledges.h" #include "kernel/celltypes.h" #include "kernel/utils.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct CheckPass : public Pass { CheckPass() : Pass("check", "check for obvious problems in the design") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 1db3e2ca0..47354f1d5 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include #ifndef _WIN32 @@ -26,15 +27,18 @@ # include #endif -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" - USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct CoverPass : public Pass { - CoverPass() : Pass("cover", "print code coverage counters") { } + CoverPass() : Pass("cover", "print code coverage counters") { + internal(); + } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc index 5b53f50cc..933bd457f 100644 --- a/passes/cmds/edgetypes.cc +++ b/passes/cmds/edgetypes.cc @@ -19,12 +19,18 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct EdgetypePass : public Pass { EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/example_dt.cc b/passes/cmds/example_dt.cc index 2870e062b..b10f50502 100644 --- a/passes/cmds/example_dt.cc +++ b/passes/cmds/example_dt.cc @@ -21,15 +21,17 @@ struct ExampleWorker struct ExampleDtPass : public Pass { - ExampleDtPass() : Pass("example_dt", "drivertools example") {} + ExampleDtPass() : Pass("example_dt", "drivertools example") { + internal(); + } - void help() override + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log("TODO: add help message\n"); log("\n"); - } + } void execute(std::vector args, RTLIL::Design *design) override diff --git a/passes/cmds/exec.cc b/passes/cmds/exec.cc index e9cc34b30..486fa1c2b 100644 --- a/passes/cmds/exec.cc +++ b/passes/cmds/exec.cc @@ -17,8 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" #include #if defined(_WIN32) @@ -38,6 +38,11 @@ PRIVATE_NAMESPACE_BEGIN struct ExecPass : public Pass { ExecPass() : Pass("exec", "execute commands in the operating system shell") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/internal_stats.cc b/passes/cmds/internal_stats.cc index 28822f237..00456b8f9 100644 --- a/passes/cmds/internal_stats.cc +++ b/passes/cmds/internal_stats.cc @@ -18,8 +18,6 @@ */ #include -#include -#include #include "kernel/yosys.h" #include "kernel/celltypes.h" @@ -71,7 +69,10 @@ std::optional current_mem_bytes() { } struct InternalStatsPass : public Pass { - InternalStatsPass() : Pass("internal_stats", "print internal statistics") { } + InternalStatsPass() : Pass("internal_stats", "print internal statistics") { + experimental(); + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/logcmd.cc b/passes/cmds/logcmd.cc index 3b82ac48c..0238627d1 100644 --- a/passes/cmds/logcmd.cc +++ b/passes/cmds/logcmd.cc @@ -18,15 +18,19 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct LogPass : public Pass { LogPass() : Pass("log", "print text and log files") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/logger.cc b/passes/cmds/logger.cc index 241a8799f..276810201 100644 --- a/passes/cmds/logger.cc +++ b/passes/cmds/logger.cc @@ -17,14 +17,19 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct LoggerPass : public Pass { LoggerPass() : Pass("logger", "set logger properties") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/ltp.cc b/passes/cmds/ltp.cc index 22bdaab44..b3134b110 100644 --- a/passes/cmds/ltp.cc +++ b/passes/cmds/ltp.cc @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -141,6 +142,11 @@ struct LtpWorker struct LtpPass : public Pass { LtpPass() : Pass("ltp", "print longest topological path") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index 4ad7c165b..a653844b7 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #ifdef YOSYS_ENABLE_PLUGINS # include @@ -122,6 +123,11 @@ void load_plugin(std::string, std::vector) struct PluginPass : public Pass { PluginPass() : Pass("plugin", "load and list loaded plugins") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/portarcs.cc b/passes/cmds/portarcs.cc index a6ed75de3..97682efbb 100644 --- a/passes/cmds/portarcs.cc +++ b/passes/cmds/portarcs.cc @@ -22,6 +22,7 @@ #include "kernel/rtlil.h" #include "kernel/utils.h" #include "kernel/celltypes.h" +#include "kernel/log_help.h" PRIVATE_NAMESPACE_BEGIN USING_YOSYS_NAMESPACE @@ -38,6 +39,11 @@ static RTLIL::SigBit canonical_bit(RTLIL::SigBit bit) struct PortarcsPass : Pass { PortarcsPass() : Pass("portarcs", "derive port arcs for propagation delay") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { diff --git a/passes/cmds/portlist.cc b/passes/cmds/portlist.cc index 03048422d..f78d9d3b6 100644 --- a/passes/cmds/portlist.cc +++ b/passes/cmds/portlist.cc @@ -19,12 +19,18 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct PortlistPass : public Pass { PortlistPass() : Pass("portlist", "list (top-level) ports") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/printattrs.cc b/passes/cmds/printattrs.cc index 2a5034c13..c8b0a1d1f 100644 --- a/passes/cmds/printattrs.cc +++ b/passes/cmds/printattrs.cc @@ -18,12 +18,18 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct PrintAttrsPass : public Pass { PrintAttrsPass() : Pass("printattrs", "print attributes of selected objects") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 0f988e57a..e55e63828 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -21,12 +21,10 @@ // Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010 // http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm -#include "kernel/register.h" +#include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/log.h" -#include -#include +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -252,6 +250,11 @@ struct SccWorker struct SccPass : public Pass { SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/scratchpad.cc b/passes/cmds/scratchpad.cc index aecc4c17d..4a63f2f60 100644 --- a/passes/cmds/scratchpad.cc +++ b/passes/cmds/scratchpad.cc @@ -18,15 +18,19 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct ScratchpadPass : public Pass { ScratchpadPass() : Pass("scratchpad", "get/set values in the scratchpad") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 1d75091af..901f923f8 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -20,8 +20,7 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include -#include +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -1085,6 +1084,11 @@ PRIVATE_NAMESPACE_BEGIN struct SelectPass : public Pass { SelectPass() : Pass("select", "modify and view the list of selected objects") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -1664,6 +1668,11 @@ struct SelectPass : public Pass { struct CdPass : public Pass { CdPass() : Pass("cd", "a shortcut for 'select -module '") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -1776,6 +1785,11 @@ static void log_matches(const char *title, Module *module, const T &list) struct LsPass : public Pass { LsPass() : Pass("ls", "list modules or objects in modules") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/setenv.cc b/passes/cmds/setenv.cc index 27f2eea28..850d7c961 100644 --- a/passes/cmds/setenv.cc +++ b/passes/cmds/setenv.cc @@ -17,15 +17,18 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" -#include +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SetenvPass : public Pass { SetenvPass() : Pass("setenv", "set an environment variable") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 8a1bd58c4..4eb6569e6 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -17,10 +17,9 @@ * */ -#include "kernel/register.h" +#include "kernel/yosys.h" #include "kernel/celltypes.h" -#include "kernel/log.h" -#include +#include "kernel/log_help.h" #ifndef _WIN32 # include @@ -658,6 +657,11 @@ struct ShowWorker struct ShowPass : public Pass { ShowPass() : Pass("show", "generate schematics using graphviz") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/sta.cc b/passes/cmds/sta.cc index 4ad0e96be..5dfac1575 100644 --- a/passes/cmds/sta.cc +++ b/passes/cmds/sta.cc @@ -21,6 +21,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/timinginfo.h" +#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -275,6 +276,11 @@ struct StaWorker struct StaPass : public Pass { StaPass() : Pass("sta", "perform static timing analysis") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 6b93621f1..af7023bdd 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -25,6 +25,7 @@ #include "kernel/cost.h" #include "kernel/gzip.h" #include "libs/json11/json11.hpp" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -367,6 +368,11 @@ void read_liberty_cellarea(dict &cell_area, string libert struct StatPass : public Pass { StatPass() : Pass("stat", "print some statistics") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index 853f1bad3..fbd42e311 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -18,15 +18,19 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct TeePass : public Pass { TeePass() : Pass("tee", "redirect command output to file") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc index 1620c0bca..537b6793d 100644 --- a/passes/cmds/torder.cc +++ b/passes/cmds/torder.cc @@ -21,12 +21,18 @@ #include "kernel/celltypes.h" #include "kernel/sigtools.h" #include "kernel/utils.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct TorderPass : public Pass { TorderPass() : Pass("torder", "print cells in topological order") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc index 400542776..39ed8e60e 100644 --- a/passes/cmds/trace.cc +++ b/passes/cmds/trace.cc @@ -19,6 +19,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -60,6 +61,11 @@ struct TraceMonitor : public RTLIL::Monitor struct TracePass : public Pass { TracePass() : Pass("trace", "redirect command output to file") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -96,6 +102,11 @@ struct TracePass : public Pass { struct DebugPass : public Pass { DebugPass() : Pass("debug", "run command with debug log messages enabled") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/viz.cc b/passes/cmds/viz.cc index 131e799ab..4c73b4d71 100644 --- a/passes/cmds/viz.cc +++ b/passes/cmds/viz.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" #ifndef _WIN32 # include @@ -817,6 +818,11 @@ struct VizWorker struct VizPass : public Pass { VizPass() : Pass("viz", "visualize data flow graph") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index ea9b3f556..a22fdaf2a 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -19,12 +19,18 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct WriteFileFrontend : public Frontend { WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From d344125a03b6dd7b3bb39a5e5e4fddd21766f21d Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:58 +1200 Subject: [PATCH 088/181] cmdref: Drop pmgen index --- docs/source/cmd/index_passes_pmgen.rst | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 docs/source/cmd/index_passes_pmgen.rst diff --git a/docs/source/cmd/index_passes_pmgen.rst b/docs/source/cmd/index_passes_pmgen.rst deleted file mode 100644 index aac8c1b47..000000000 --- a/docs/source/cmd/index_passes_pmgen.rst +++ /dev/null @@ -1,5 +0,0 @@ -passes/pmgen ------------------- - -.. autocmdgroup:: passes/pmgen - :members: From 5f771a965be3bd407d1cdfefcafe2871116a179b Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:58 +1200 Subject: [PATCH 089/181] cmdref: codeblock language now works Add `options` map for setting `ContentListing` options with key:val pairs; currently only used with `ContentListing::codeblock()` language arg. Fix generated codeblock rst to print each line of code with indentation, and change it to use explicit an `code-block` so we can set a language on it. --- docs/source/cmd/index_internal.rst | 5 ++--- docs/util/cmd_documenter.py | 9 +++++++-- kernel/log_help.cc | 2 ++ kernel/log_help.h | 6 ++++++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst index 55c99f05e..597d35639 100644 --- a/docs/source/cmd/index_internal.rst +++ b/docs/source/cmd/index_internal.rst @@ -47,7 +47,8 @@ The ``formatted_help()`` method added automatically * ``codeblock`` content is displayed verbatim, use line breaks as desired; takes an optional ``language`` argument for assigning the language in RST - output for code syntax highlighting (currently not implemented) + output for code syntax highlighting (use ``yoscrypt`` for yosys script + syntax highlighting) * ``option`` lists a single option for the command, usually starting with a dash (``-``); takes an optional second argument which adds a paragraph node as a means of description @@ -62,8 +63,6 @@ The ``formatted_help()`` method + paragraphs are treated as raw RST, allowing for inline formatting and references as if it were written in the RST file itself -.. todo:: Support ``ContentListing::codeblock`` language argument - .. todo:: Support anonymous optiongroup If an option is added to the root node it should add the option to the last diff --git a/docs/util/cmd_documenter.py b/docs/util/cmd_documenter.py index bf71a00ac..39d86cc02 100644 --- a/docs/util/cmd_documenter.py +++ b/docs/util/cmd_documenter.py @@ -28,6 +28,7 @@ class YosysCmdContentListing: body: str source_file: str source_line: int + options: dict[str, str] content: list[YosysCmdContentListing] def __init__( @@ -36,12 +37,14 @@ class YosysCmdContentListing: body: str = "", source_file: str = "unknown", source_line: int = 0, + options: dict[str, str] = {}, content: list[dict[str]] = [], ): self.type = type self.body = body self.source_file = source_file self.source_line = source_line + self.options = options self.content = [YosysCmdContentListing(**c) for c in content] class YosysCmd: @@ -375,9 +378,11 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): self.add_line(f'{indent_str}{content_list.body}', content_source) self.add_line('', source_name) elif content_list.type == 'code': - self.add_line(f'{indent_str}::', source_name) + language_str = content_list.options.get('language', '') + self.add_line(f'{indent_str}.. code-block:: {language_str}', source_name) self.add_line('', source_name) - self.add_line(f'{indent_str} {content_list.body}', content_source) + for body_line in content_list.body.splitlines(): + self.add_line(f'{indent_str} {body_line}', content_source) self.add_line('', source_name) else: logger.warning(f"unknown content type '{content_list.type}'") diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 7e103f83d..9c1687910 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -27,6 +27,7 @@ Json ContentListing::to_json() { if (body.length()) object["body"] = body; if (strcmp(source_file, "unknown") != 0) object["source_file"] = source_file; if (source_line != 0) object["source_line"] = source_line; + object["options"] = Json(options); Json::array content_array; for (auto child : _content) content_array.push_back(child->to_json()); object["content"] = content_array; @@ -52,6 +53,7 @@ void ContentListing::codeblock(const string &code, const string &language, const source_location location) { add_content("code", code, location); + back()->set_option("language", language); } void ContentListing::paragraph(const string &text, diff --git a/kernel/log_help.h b/kernel/log_help.h index 169b90ca2..60f378e00 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -32,12 +32,14 @@ public: string body; const char* source_file; int source_line; + std::map options; ContentListing( string type = "root", string body = "", const char* source_file = "unknown", int source_line = 0 ) : type(type), body(body), source_file(source_file), source_line(source_line) { _content = {}; + options = {}; } ContentListing(string type, string body, source_location location) : @@ -61,6 +63,10 @@ public: return _content.back(); } + void set_option(string key, string val = "") { + options[key] = val; + } + void usage( const string &usage, const source_location location = source_location::current() From 7ebccd2dea54867ed2e4c89739a1856af6b85fe3 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:35:58 +1200 Subject: [PATCH 090/181] cmdref: Format help_script() output as yoscrypt Or at least all of the synth commands, because they have the same preceding text. Except `synth_fabulous`, because that has unconditional `read_verilog`s at the start. Which seems like a bad idea and and not compatible with `-run`? --- kernel/register.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/register.cc b/kernel/register.cc index a688146ed..f0db7f7af 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -972,7 +972,11 @@ struct HelpPass : public Pass { } if (!current_buffer.empty()) { - current_listing->codeblock(current_buffer, "none", null_source); + if (current_buffer.size() > 64 && current_buffer.substr(0, 64).compare("The following commands are executed by this synthesis command:\n\n") == 0) { + current_listing->paragraph(current_buffer.substr(0, 62), null_source); + current_listing->codeblock(current_buffer.substr(64), "yoscrypt", null_source); + } else + current_listing->codeblock(current_buffer, "none", null_source); current_buffer = ""; } } From da9bf5d6104fe5e888eccf77ddfa66d18ae7d947 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:36:18 +1200 Subject: [PATCH 091/181] cmdref: Drop optiongroups Linking to optiongroups doesn't add *that* much, and is kind of a pain; meanwhile having the optiongroups adds an extra level of indentation. Instead of options needing to be in an option group, they instead go in either the root node or nested in a usage node. Putting them in a usage node allows for more-or-less the previous behaviour but without making it the default. --- docs/source/cmd/index_internal.rst | 14 +-- docs/util/cmd_documenter.py | 4 +- docs/util/custom_directives.py | 171 +++++++++++++---------------- kernel/log_help.cc | 29 +++-- kernel/log_help.h | 6 +- kernel/register.cc | 7 +- passes/cmds/chformal.cc | 36 +++--- 7 files changed, 118 insertions(+), 149 deletions(-) diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst index 597d35639..a077dc2a4 100644 --- a/docs/source/cmd/index_internal.rst +++ b/docs/source/cmd/index_internal.rst @@ -53,21 +53,13 @@ The ``formatted_help()`` method dash (``-``); takes an optional second argument which adds a paragraph node as a means of description - + ``ContentListing::open_optiongroup`` - - * each option must be in an optiongroup - * optional name argument, which will be rendered in (RST) output - + + ``ContentListing::open_usage`` creates and returns a new usage node, can be + used to e.g. add text/options specific to a given usage of the command + ``ContentListing::open_option`` creates and returns a new option node, can be used to e.g. add multiple paragraphs to an option's description + paragraphs are treated as raw RST, allowing for inline formatting and references as if it were written in the RST file itself -.. todo:: Support anonymous optiongroup - - If an option is added to the root node it should add the option to the last - child of the root, making a new child if the last child is not an optiongroup - .. literalinclude:: /generated/chformal.cc :language: c++ :start-at: bool formatted_help() @@ -88,7 +80,7 @@ Dumping command help to json * if a line is indented and starts with a dash (``-``), it is parsed as an option * anything else is parsed as a codeblock and added to either the root node - or the current option/optiongroup depending on the indentation + or the current option depending on the indentation + dictionary of command name to ``ContentListing`` diff --git a/docs/util/cmd_documenter.py b/docs/util/cmd_documenter.py index 39d86cc02..9347d8ffd 100644 --- a/docs/util/cmd_documenter.py +++ b/docs/util/cmd_documenter.py @@ -365,14 +365,14 @@ class YosysCmdDocumenter(YosysCmdGroupDocumenter): def render(content_list: YosysCmdContentListing, indent: int=0): content_source = content_list.source_file or source_name indent_str = ' '*indent - if content_list.type in ['usage', 'optiongroup']: + if content_list.type == 'usage': if content_list.body: self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::{content_list.body}', content_source) else: self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::', content_source) self.add_line(f'{indent_str} :noindex:', source_name) self.add_line('', source_name) - elif content_list.type in ['option']: + elif content_list.type == 'option': self.add_line(f'{indent_str}:{content_list.type} {content_list.body}:', content_source) elif content_list.type == 'text': self.add_line(f'{indent_str}{content_list.body}', content_source) diff --git a/docs/util/custom_directives.py b/docs/util/custom_directives.py index 58e5b37fd..e8c400047 100644 --- a/docs/util/custom_directives.py +++ b/docs/util/custom_directives.py @@ -58,101 +58,12 @@ class TocNode(ObjectDescription): return '.'.join(parents + [name]) return '' -class CommandNode(TocNode): - """A custom node that describes a command.""" - - name = 'cmd' - required_arguments = 1 - - option_spec = TocNode.option_spec.copy() - option_spec.update({ - 'title': directives.unchanged, - 'tags': directives.unchanged - }) +class NodeWithOptions(TocNode): + """A custom node with options.""" doc_field_types = [ GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')), ] - - def handle_signature(self, sig, signode: addnodes.desc_signature): - signode['fullname'] = sig - signode += addnodes.desc_addname(text="yosys> help ") - signode += addnodes.desc_name(text=sig) - return signode['fullname'] - - def add_target_and_index(self, name_cls, sig, signode): - idx = type(self).name + '-' + sig - signode['ids'].append(idx) - if 'noindex' not in self.options: - name = "{}.{}.{}".format(self.name, type(self).__name__, sig) - tagmap = self.env.domaindata[type(self).name]['obj2tag'] - tagmap[name] = list(self.options.get('tags', '').split(' ')) - title = self.options.get('title', sig) - titlemap = self.env.domaindata[type(self).name]['obj2title'] - titlemap[name] = title - objs = self.env.domaindata[type(self).name]['objects'] - # (name, sig, typ, docname, anchor, prio) - objs.append((name, - sig, - type(self).name, - self.env.docname, - idx, - 0)) - -class CommandUsageNode(TocNode): - """A custom node that describes command usages""" - - name = 'cmdusage' - - option_spec = TocNode.option_spec - option_spec.update({ - 'usage': directives.unchanged, - }) - - doc_field_types = [ - GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')), - ] - - def handle_signature(self, sig: str, signode: addnodes.desc_signature): - parts = sig.split('::') - if len(parts) > 2: parts.pop(0) - use = parts[-1] - signode['fullname'] = '::'.join(parts) - usage = self.options.get('usage', use) - if usage: - signode['tocname'] = usage - signode += addnodes.desc_name(text=usage) - return signode['fullname'] - - def add_target_and_index( - self, - name: str, - sig: str, - signode: addnodes.desc_signature - ) -> None: - idx = ".".join(name.split("::")) - signode['ids'].append(idx) - if 'noindex' not in self.options: - tocname: str = signode.get('tocname', name) - objs = self.env.domaindata[self.domain]['objects'] - # (name, sig, typ, docname, anchor, prio) - objs.append((name, - tocname, - type(self).name, - self.env.docname, - idx, - 1)) - -class CommandOptionGroupNode(CommandUsageNode): - """A custom node that describes a group of related options""" - - name = 'cmdoptiongroup' - - option_spec = CommandUsageNode.option_spec - - doc_field_types = [ - Field('opt', ('option',), label='', rolename='option') - ] def transform_content(self, contentnode: addnodes.desc_content) -> None: """hack `:option -thing: desc` into a proper option list with yoscrypt highlighting""" @@ -186,6 +97,83 @@ class CommandOptionGroupNode(CommandUsageNode): newchildren.append(newnode) contentnode.children = newchildren +class CommandNode(NodeWithOptions): + """A custom node that describes a command.""" + + name = 'cmd' + required_arguments = 1 + + option_spec = NodeWithOptions.option_spec.copy() + option_spec.update({ + 'title': directives.unchanged, + 'tags': directives.unchanged + }) + + def handle_signature(self, sig, signode: addnodes.desc_signature): + signode['fullname'] = sig + signode += addnodes.desc_addname(text="yosys> help ") + signode += addnodes.desc_name(text=sig) + return signode['fullname'] + + def add_target_and_index(self, name_cls, sig, signode): + idx = type(self).name + '-' + sig + signode['ids'].append(idx) + if 'noindex' not in self.options: + name = "{}.{}.{}".format(self.name, type(self).__name__, sig) + tagmap = self.env.domaindata[type(self).name]['obj2tag'] + tagmap[name] = list(self.options.get('tags', '').split(' ')) + title = self.options.get('title', sig) + titlemap = self.env.domaindata[type(self).name]['obj2title'] + titlemap[name] = title + objs = self.env.domaindata[type(self).name]['objects'] + # (name, sig, typ, docname, anchor, prio) + objs.append((name, + sig, + type(self).name, + self.env.docname, + idx, + 0)) + +class CommandUsageNode(NodeWithOptions): + """A custom node that describes command usages""" + + name = 'cmdusage' + + option_spec = NodeWithOptions.option_spec + option_spec.update({ + 'usage': directives.unchanged, + }) + + def handle_signature(self, sig: str, signode: addnodes.desc_signature): + parts = sig.split('::') + if len(parts) > 2: parts.pop(0) + use = parts[-1] + signode['fullname'] = '::'.join(parts) + usage = self.options.get('usage', use) + if usage: + signode['tocname'] = usage + signode += addnodes.desc_name(text=usage) + return signode['fullname'] + + def add_target_and_index( + self, + name: str, + sig: str, + signode: addnodes.desc_signature + ) -> None: + idx = ".".join(name.split("::")) + signode['ids'].append(idx) + if 'noindex' not in self.options: + tocname: str = signode.get('tocname', name) + objs = self.env.domaindata[self.domain]['objects'] + # (name, sig, typ, docname, anchor, prio) + objs.append((name, + tocname, + type(self).name, + self.env.docname, + idx, + 1)) + class PropNode(TocNode): name = 'prop' fieldname = 'props' @@ -621,7 +609,6 @@ class CommandDomain(Domain): directives = { 'def': CommandNode, 'usage': CommandUsageNode, - 'optiongroup': CommandOptionGroupNode, } indices = { diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 9c1687910..45228b024 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -34,11 +34,11 @@ Json ContentListing::to_json() { return object; } -void ContentListing::usage(const string &usage, +void ContentListing::usage(const string &text, const source_location location) { log_assert(type.compare("root") == 0); - add_content("usage", usage, location); + add_content("usage", text, location); } void ContentListing::option(const string &text, const string &description, @@ -62,19 +62,17 @@ void ContentListing::paragraph(const string &text, add_content("text", text, location); } -ContentListing* ContentListing::open_optiongroup(const string &name, +ContentListing* ContentListing::open_usage(const string &text, const source_location location) { - log_assert(type.compare("root") == 0); - auto optiongroup = new ContentListing("optiongroup", name, location); - add_content(optiongroup); - return optiongroup; + usage(text, location); + return back(); } ContentListing* ContentListing::open_option(const string &text, const source_location location) { - log_assert(type.compare("optiongroup") == 0); + log_assert(type.compare("root") == 0 || type.compare("usage") == 0); auto option = new ContentListing("option", text, location); add_content(option); return option; @@ -138,16 +136,15 @@ void PrettyHelp::log_help() for (auto content : _root_listing.get_content()) { if (content->type.compare("usage") == 0) { log_pass_str(content->body, 1, true); - } else if (content->type.compare("optiongroup") == 0) { - for (auto option : content->get_content()) { - log_pass_str(option->body, 1); - for (auto text : option->get_content()) { - log_pass_str(text->body, 2); - log("\n"); - } + log("\n"); + } else if (content->type.compare("option") == 0) { + log_pass_str(content->body, 1); + for (auto text : content->get_content()) { + log_pass_str(text->body, 2); + log("\n"); } } else { - log_pass_str(content->body, 0, true); + log_pass_str(content->body, 0); log("\n"); } } diff --git a/kernel/log_help.h b/kernel/log_help.h index 60f378e00..0a40fc531 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -68,7 +68,7 @@ public: } void usage( - const string &usage, + const string &text, const source_location location = source_location::current() ); void option( @@ -86,8 +86,8 @@ public: const source_location location = source_location::current() ); - ContentListing* open_optiongroup( - const string &name = "", + ContentListing* open_usage( + const string &text, const source_location location = source_location::current() ); ContentListing* open_option( diff --git a/kernel/register.cc b/kernel/register.cc index f0db7f7af..54faa9a81 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -945,13 +945,8 @@ struct HelpPass : public Pass { current_listing->codeblock(current_buffer, "none", null_source); current_buffer = ""; } - if (current_state == PUState_options || current_state == PUState_optionbody) { - current_listing = root_listing->back(); - } else { - current_listing = root_listing->open_optiongroup("", null_source); - } current_state = PUState_options; - current_listing = current_listing->open_option(stripped_line, null_source); + current_listing = root_listing->open_option(stripped_line, null_source); def_strip_count = first_pos; } else { if (current_state == PUState_options) { diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index 53884b10f..ccda023c0 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -86,29 +86,27 @@ struct ChformalPass : public Pass { "given, the command will operate on all constraint types:" ); - auto types_group = content_root->open_optiongroup("[types]"); - types_group->option("-assert", "`$assert` cells, representing ``assert(...)`` constraints"); - types_group->option("-assume", "`$assume` cells, representing ``assume(...)`` constraints"); - types_group->option("-live", "`$live` cells, representing ``assert(s_eventually ...)``"); - types_group->option("-fair", "`$fair` cells, representing ``assume(s_eventually ...)``"); - types_group->option("-cover", "`$cover` cells, representing ``cover()`` statements"); - types_group->paragraph( + content_root->option("-assert", "`$assert` cells, representing ``assert(...)`` constraints"); + content_root->option("-assume", "`$assume` cells, representing ``assume(...)`` constraints"); + content_root->option("-live", "`$live` cells, representing ``assert(s_eventually ...)``"); + content_root->option("-fair", "`$fair` cells, representing ``assume(s_eventually ...)``"); + content_root->option("-cover", "`$cover` cells, representing ``cover()`` statements"); + content_root->paragraph( "Additionally chformal will operate on `$check` cells corresponding to the " "selected constraint types." ); content_root->paragraph("Exactly one of the following modes must be specified:"); - auto modes_group = content_root->open_optiongroup("[mode]"); - modes_group->option("-remove", "remove the cells and thus constraints from the design"); - modes_group->option("-early", + content_root->option("-remove", "remove the cells and thus constraints from the design"); + content_root->option("-early", "bypass FFs that only delay the activation of a constraint. When inputs " "of the bypassed FFs do not remain stable between clock edges, this may " "result in unexpected behavior." ); - modes_group->option("-delay ", "delay activation of the constraint by clock cycles"); - modes_group->option("-skip ", "ignore activation of the constraint in the first clock cycles"); - auto cover_option = modes_group->open_option("-coverenable"); + content_root->option("-delay ", "delay activation of the constraint by clock cycles"); + content_root->option("-skip ", "ignore activation of the constraint in the first clock cycles"); + auto cover_option = content_root->open_option("-coverenable"); cover_option->paragraph( "add cover statements for the enable signals of the constraints" ); @@ -118,12 +116,12 @@ struct ChformalPass : public Pass { "reachable SVA statement corresponds to an active enable signal." ); #endif - modes_group->option("-assert2assume"); - modes_group->option("-assert2cover"); - modes_group->option("-assume2assert"); - modes_group->option("-live2fair"); - modes_group->option("-fair2live", "change the roles of cells as indicated. these options can be combined"); - modes_group->option("-lower", + content_root->option("-assert2assume"); + content_root->option("-assert2cover"); + content_root->option("-assume2assert"); + content_root->option("-live2fair"); + content_root->option("-fair2live", "change the roles of cells as indicated. these options can be combined"); + content_root->option("-lower", "convert each $check cell into an $assert, $assume, $live, $fair or " "$cover cell. If the $check cell contains a message, also produce a " "$print cell." From 87ec4869628d39ee2ba56c5ddb6e0b8349556aaf Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:36:18 +1200 Subject: [PATCH 092/181] docs/internal: Add note on synth_* script highlighting --- docs/source/cmd/index_internal.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst index a077dc2a4..dc9980171 100644 --- a/docs/source/cmd/index_internal.rst +++ b/docs/source/cmd/index_internal.rst @@ -101,6 +101,15 @@ Dumping command help to json :end-before: "chparam": { :caption: `chformal` in generated :file:`cmds.json` +.. note:: Synthesis command scripts are special cased + + If the final block of help output starts with the string `"The following + commands are executed by this synthesis command:\n"`, then the rest of the + code block is formatted as ``yoscrypt`` (e.g. `synth_ice40`). The caveat + here is that if the ``script()`` calls ``run()`` on any commands *prior* to + the first ``check_label`` then the auto detection will break and revert to + unformatted code (e.g. `synth_fabulous`). + Command line rendering ~~~~~~~~~~~~~~~~~~~~~~ From af3c28f274e0f95dd3ffcc797dc5d31d4cb8f14c Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:36:18 +1200 Subject: [PATCH 093/181] synthprop: Use override keyword `formatted_help()` introduced the override keyword, which means that the other two methods that were marked as virtual instead raised a warning about inconsistent use of override. This fixes that by bringing the synthprop (more) in line with the rest of the code-base. --- passes/sat/synthprop.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passes/sat/synthprop.cc b/passes/sat/synthprop.cc index af69b1172..4703e4ad7 100644 --- a/passes/sat/synthprop.cc +++ b/passes/sat/synthprop.cc @@ -187,7 +187,7 @@ struct SyntProperties : public Pass { return false; } - virtual void help() + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -216,7 +216,7 @@ struct SyntProperties : public Pass { log("\n"); } - virtual void execute(std::vector args, RTLIL::Design* design) + void execute(std::vector args, RTLIL::Design* design) override { log_header(design, "Executing SYNTHPROP pass.\n"); SynthPropWorker worker(design); From d0ce6a3bd6db25bb3e32aa16fe2fd94c67815fe4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:36:18 +1200 Subject: [PATCH 094/181] Docs: Include internal cmds in toctree Also fixing some missing refs (using single backticks instead of double). --- docs/source/cmd/index_internal.rst | 2 -- docs/source/cmd_ref.rst | 8 +++----- docs/source/yosys_internals/hashing.rst | 2 +- docs/source/yosys_internals/verilog.rst | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst index dc9980171..bfb369dde 100644 --- a/docs/source/cmd/index_internal.rst +++ b/docs/source/cmd/index_internal.rst @@ -1,5 +1,3 @@ -:orphan: - Internal commands for developers -------------------------------- diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index 73a383ad5..20915d6dc 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -27,9 +27,7 @@ Command reference /cmd/index_passes /cmd/index_techlibs -.. TODO:: Fix index_internal not being included in pdf +.. toctree:: + :maxdepth: 2 -.. note:: - - Commands intended for internal developer use can also be found under - :doc:`/cmd/index_internal` + /cmd/index_internal diff --git a/docs/source/yosys_internals/hashing.rst b/docs/source/yosys_internals/hashing.rst index c6e22c0cf..b9608d99e 100644 --- a/docs/source/yosys_internals/hashing.rst +++ b/docs/source/yosys_internals/hashing.rst @@ -138,7 +138,7 @@ Previously, the interface to implement hashing on custom types was just independently and then ad-hoc combined with the hash function with some xorshift operations thrown in to mix bits together somewhat. A plugin can stay compatible with both versions prior and after the break by implementing both interfaces -based on the existance and value of `YS_HASHING_VERSION`. +based on the existance and value of ``YS_HASHING_VERSION``. .. code-block:: cpp :caption: Example hash compatibility wrapper diff --git a/docs/source/yosys_internals/verilog.rst b/docs/source/yosys_internals/verilog.rst index d67553aa9..fe31aecea 100644 --- a/docs/source/yosys_internals/verilog.rst +++ b/docs/source/yosys_internals/verilog.rst @@ -96,7 +96,7 @@ Verilog Attributes and non-standard features - The ``keep_hierarchy`` attribute on cells and modules keeps the `flatten` command from flattening the indicated cells and modules. -- The `gate_cost_equivalent` attribute on a module can be used to specify +- The ``gate_cost_equivalent`` attribute on a module can be used to specify the estimated cost of the module as a number of basic gate instances. See the help message of command `keep_hierarchy` which interprets this attribute. From 4ba403829b8a460eca5c8032d7e7d2f1eec78ba1 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:36:19 +1200 Subject: [PATCH 095/181] cmdref: Groups and group names --- docs/source/cmd/index_passes_hierarchy.rst | 4 ++-- docs/source/cmd/index_passes_sat.rst | 4 ++-- passes/sat/expose.cc | 10 +++++++--- passes/sat/recover_names.cc | 6 ++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/source/cmd/index_passes_hierarchy.rst b/docs/source/cmd/index_passes_hierarchy.rst index 2032be636..27a0faeb7 100644 --- a/docs/source/cmd/index_passes_hierarchy.rst +++ b/docs/source/cmd/index_passes_hierarchy.rst @@ -1,5 +1,5 @@ -hierarchy ------------------- +Working with hierarchy +---------------------- .. autocmdgroup:: passes/hierarchy :members: diff --git a/docs/source/cmd/index_passes_sat.rst b/docs/source/cmd/index_passes_sat.rst index 1f2c6904d..a2571fedb 100644 --- a/docs/source/cmd/index_passes_sat.rst +++ b/docs/source/cmd/index_passes_sat.rst @@ -1,5 +1,5 @@ -sat ------------------- +Simulating circuits +------------------- .. autocmdgroup:: passes/sat :members: diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 9afe00d5c..1e975db0f 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -17,11 +17,10 @@ * */ -#include "kernel/register.h" +#include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -217,6 +216,11 @@ RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width struct ExposePass : public Pass { ExposePass() : Pass("expose", "convert internal signals to module ports") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/cmds"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/recover_names.cc b/passes/sat/recover_names.cc index cddd8771c..7ed8b1304 100644 --- a/passes/sat/recover_names.cc +++ b/passes/sat/recover_names.cc @@ -23,6 +23,7 @@ #include "kernel/celltypes.h" #include "kernel/utils.h" #include "kernel/satgen.h" +#include "kernel/log_help.h" #include #include @@ -690,6 +691,11 @@ struct RecoverNamesWorker { struct RecoverNamesPass : public Pass { RecoverNamesPass() : Pass("recover_names", "Execute a lossy mapping command and recover original netnames") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/opt"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From 3804af35fcd33f16c613379049564c9f6a5dc8f7 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:36:19 +1200 Subject: [PATCH 096/181] Docs: Naming techlibs --- docs/source/cmd/index_techlibs_achronix.rst | 2 +- docs/source/cmd/index_techlibs_anlogic.rst | 2 +- docs/source/cmd/index_techlibs_coolrunner2.rst | 2 +- docs/source/cmd/index_techlibs_easic.rst | 2 +- docs/source/cmd/index_techlibs_ecp5.rst | 2 +- docs/source/cmd/index_techlibs_fabulous.rst | 2 +- docs/source/cmd/index_techlibs_gatemate.rst | 2 +- docs/source/cmd/index_techlibs_gowin.rst | 2 +- docs/source/cmd/index_techlibs_greenpak4.rst | 2 +- docs/source/cmd/index_techlibs_ice40.rst | 2 +- docs/source/cmd/index_techlibs_lattice.rst | 2 +- ...x_techlibs_nexus.rst => index_techlibs_lattice_nexus.rst} | 2 +- docs/source/cmd/index_techlibs_microchip.rst | 2 +- docs/source/cmd/index_techlibs_microchip_sf2.rst | 5 +++++ docs/source/cmd/index_techlibs_nanoxplore.rst | 2 +- docs/source/cmd/index_techlibs_quicklogic.rst | 2 +- docs/source/cmd/index_techlibs_sf2.rst | 5 ----- docs/source/cmd/index_techlibs_xilinx.rst | 2 +- 18 files changed, 21 insertions(+), 21 deletions(-) rename docs/source/cmd/{index_techlibs_nexus.rst => index_techlibs_lattice_nexus.rst} (82%) create mode 100644 docs/source/cmd/index_techlibs_microchip_sf2.rst delete mode 100644 docs/source/cmd/index_techlibs_sf2.rst diff --git a/docs/source/cmd/index_techlibs_achronix.rst b/docs/source/cmd/index_techlibs_achronix.rst index babf4e979..d4d96d24a 100644 --- a/docs/source/cmd/index_techlibs_achronix.rst +++ b/docs/source/cmd/index_techlibs_achronix.rst @@ -1,4 +1,4 @@ -achronix +Achronix ------------------ .. autocmdgroup:: techlibs/achronix diff --git a/docs/source/cmd/index_techlibs_anlogic.rst b/docs/source/cmd/index_techlibs_anlogic.rst index 13ef12fc7..8a2e6b577 100644 --- a/docs/source/cmd/index_techlibs_anlogic.rst +++ b/docs/source/cmd/index_techlibs_anlogic.rst @@ -1,4 +1,4 @@ -anlogic +Anlogic ------------------ .. autocmdgroup:: techlibs/anlogic diff --git a/docs/source/cmd/index_techlibs_coolrunner2.rst b/docs/source/cmd/index_techlibs_coolrunner2.rst index 23e87d03e..23d91a500 100644 --- a/docs/source/cmd/index_techlibs_coolrunner2.rst +++ b/docs/source/cmd/index_techlibs_coolrunner2.rst @@ -1,4 +1,4 @@ -coolrunner2 +CoolRunner-II ------------------ .. autocmdgroup:: techlibs/coolrunner2 diff --git a/docs/source/cmd/index_techlibs_easic.rst b/docs/source/cmd/index_techlibs_easic.rst index 0cd375f11..c6398ddf3 100644 --- a/docs/source/cmd/index_techlibs_easic.rst +++ b/docs/source/cmd/index_techlibs_easic.rst @@ -1,4 +1,4 @@ -easic +eASIC ------------------ .. autocmdgroup:: techlibs/easic diff --git a/docs/source/cmd/index_techlibs_ecp5.rst b/docs/source/cmd/index_techlibs_ecp5.rst index 42a12e8e9..29fd309cf 100644 --- a/docs/source/cmd/index_techlibs_ecp5.rst +++ b/docs/source/cmd/index_techlibs_ecp5.rst @@ -1,4 +1,4 @@ -ecp5 +ECP5 ------------------ .. autocmdgroup:: techlibs/ecp5 diff --git a/docs/source/cmd/index_techlibs_fabulous.rst b/docs/source/cmd/index_techlibs_fabulous.rst index ce075c025..96f04f40a 100644 --- a/docs/source/cmd/index_techlibs_fabulous.rst +++ b/docs/source/cmd/index_techlibs_fabulous.rst @@ -1,4 +1,4 @@ -fabulous +FABulous ------------------ .. autocmdgroup:: techlibs/fabulous diff --git a/docs/source/cmd/index_techlibs_gatemate.rst b/docs/source/cmd/index_techlibs_gatemate.rst index 5a362b0fe..951d0000b 100644 --- a/docs/source/cmd/index_techlibs_gatemate.rst +++ b/docs/source/cmd/index_techlibs_gatemate.rst @@ -1,4 +1,4 @@ -gatemate +Gatemate ------------------ .. autocmdgroup:: techlibs/gatemate diff --git a/docs/source/cmd/index_techlibs_gowin.rst b/docs/source/cmd/index_techlibs_gowin.rst index 379cb773e..cdcb1c2ee 100644 --- a/docs/source/cmd/index_techlibs_gowin.rst +++ b/docs/source/cmd/index_techlibs_gowin.rst @@ -1,4 +1,4 @@ -gowin +Gowin ------------------ .. autocmdgroup:: techlibs/gowin diff --git a/docs/source/cmd/index_techlibs_greenpak4.rst b/docs/source/cmd/index_techlibs_greenpak4.rst index 1f733488d..add1ab102 100644 --- a/docs/source/cmd/index_techlibs_greenpak4.rst +++ b/docs/source/cmd/index_techlibs_greenpak4.rst @@ -1,4 +1,4 @@ -greenpak4 +GreenPAK4 ------------------ .. autocmdgroup:: techlibs/greenpak4 diff --git a/docs/source/cmd/index_techlibs_ice40.rst b/docs/source/cmd/index_techlibs_ice40.rst index 3d04fee05..1c4b2d07a 100644 --- a/docs/source/cmd/index_techlibs_ice40.rst +++ b/docs/source/cmd/index_techlibs_ice40.rst @@ -1,4 +1,4 @@ -ice40 +iCE40 ------------------ .. autocmdgroup:: techlibs/ice40 diff --git a/docs/source/cmd/index_techlibs_lattice.rst b/docs/source/cmd/index_techlibs_lattice.rst index 55bed7afc..985bf0bbd 100644 --- a/docs/source/cmd/index_techlibs_lattice.rst +++ b/docs/source/cmd/index_techlibs_lattice.rst @@ -1,4 +1,4 @@ -lattice +Lattice ------------------ .. autocmdgroup:: techlibs/lattice diff --git a/docs/source/cmd/index_techlibs_nexus.rst b/docs/source/cmd/index_techlibs_lattice_nexus.rst similarity index 82% rename from docs/source/cmd/index_techlibs_nexus.rst rename to docs/source/cmd/index_techlibs_lattice_nexus.rst index a0289a571..d5ac4184c 100644 --- a/docs/source/cmd/index_techlibs_nexus.rst +++ b/docs/source/cmd/index_techlibs_lattice_nexus.rst @@ -1,4 +1,4 @@ -nexus +Lattice Nexus ------------------ .. autocmdgroup:: techlibs/nexus diff --git a/docs/source/cmd/index_techlibs_microchip.rst b/docs/source/cmd/index_techlibs_microchip.rst index 79340febc..06613a59c 100644 --- a/docs/source/cmd/index_techlibs_microchip.rst +++ b/docs/source/cmd/index_techlibs_microchip.rst @@ -1,4 +1,4 @@ -microchip +Microchip ------------------ .. autocmdgroup:: techlibs/microchip diff --git a/docs/source/cmd/index_techlibs_microchip_sf2.rst b/docs/source/cmd/index_techlibs_microchip_sf2.rst new file mode 100644 index 000000000..4ebe47f33 --- /dev/null +++ b/docs/source/cmd/index_techlibs_microchip_sf2.rst @@ -0,0 +1,5 @@ +Microchip - SmartFusion2/IGLOO2 +----------------------------------- + +.. autocmdgroup:: techlibs/sf2 + :members: diff --git a/docs/source/cmd/index_techlibs_nanoxplore.rst b/docs/source/cmd/index_techlibs_nanoxplore.rst index c941c32ba..9eff4681c 100644 --- a/docs/source/cmd/index_techlibs_nanoxplore.rst +++ b/docs/source/cmd/index_techlibs_nanoxplore.rst @@ -1,4 +1,4 @@ -nanoxplore +NanoXplore ------------------ .. autocmdgroup:: techlibs/nanoxplore diff --git a/docs/source/cmd/index_techlibs_quicklogic.rst b/docs/source/cmd/index_techlibs_quicklogic.rst index d11d4faca..54d199eb0 100644 --- a/docs/source/cmd/index_techlibs_quicklogic.rst +++ b/docs/source/cmd/index_techlibs_quicklogic.rst @@ -1,4 +1,4 @@ -quicklogic +QuickLogic ------------------ .. autocmdgroup:: techlibs/quicklogic diff --git a/docs/source/cmd/index_techlibs_sf2.rst b/docs/source/cmd/index_techlibs_sf2.rst deleted file mode 100644 index b97f96c9d..000000000 --- a/docs/source/cmd/index_techlibs_sf2.rst +++ /dev/null @@ -1,5 +0,0 @@ -sf2 ------------------- - -.. autocmdgroup:: techlibs/sf2 - :members: diff --git a/docs/source/cmd/index_techlibs_xilinx.rst b/docs/source/cmd/index_techlibs_xilinx.rst index 5ce3d115b..df5112b7e 100644 --- a/docs/source/cmd/index_techlibs_xilinx.rst +++ b/docs/source/cmd/index_techlibs_xilinx.rst @@ -1,4 +1,4 @@ -xilinx +Xilinx ------------------ .. autocmdgroup:: techlibs/xilinx From 3eb7b35c29741c3c92d2732fd1e40f7ca77717dd Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:36:19 +1200 Subject: [PATCH 097/181] flatten: Move to hierarchy folder --- passes/hierarchy/Makefile.inc | 1 + passes/{techmap => hierarchy}/flatten.cc | 0 passes/techmap/Makefile.inc | 1 - 3 files changed, 1 insertion(+), 1 deletion(-) rename passes/{techmap => hierarchy}/flatten.cc (100%) diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index 3cd1b6180..ff1a2fcc5 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -1,4 +1,5 @@ +OBJS += passes/hierarchy/flatten.o OBJS += passes/hierarchy/hierarchy.o OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o diff --git a/passes/techmap/flatten.cc b/passes/hierarchy/flatten.cc similarity index 100% rename from passes/techmap/flatten.cc rename to passes/hierarchy/flatten.cc diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index c9fe98a74..91b3b563a 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -1,5 +1,4 @@ -OBJS += passes/techmap/flatten.o OBJS += passes/techmap/techmap.o OBJS += passes/techmap/simplemap.o OBJS += passes/techmap/dfflibmap.o From c92f200491adcea39c375f092e7c25ed846efad1 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:37:30 +1200 Subject: [PATCH 098/181] Drop .rst dump --- Makefile | 14 ------ kernel/register.cc | 103 --------------------------------------------- 2 files changed, 117 deletions(-) diff --git a/Makefile b/Makefile index 3b6a3bfaf..0f0a53a07 100644 --- a/Makefile +++ b/Makefile @@ -115,12 +115,6 @@ BISON ?= bison STRIP ?= strip AWK ?= awk -ifneq ($(shell :; command -v rsync),) -RSYNC_CP ?= rsync -rc -else -RSYNC_CP ?= cp -ru -endif - ifeq ($(OS), Darwin) PLUGIN_LINKFLAGS += -undefined dynamic_lookup LINKFLAGS += -rdynamic @@ -1034,14 +1028,6 @@ ifeq ($(ENABLE_PYOSYS),1) endif endif -# also others, but so long as it doesn't fail this is enough to know we tried -docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) - $(Q) mkdir -p docs/source/cmd - $(Q) mkdir -p temp/docs/source/cmd - $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' - $(Q) $(RSYNC_CP) temp/docs/source/cmd docs/source - $(Q) rm -rf temp - docs/source/generated/cmds.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cmds-json $@' diff --git a/kernel/register.cc b/kernel/register.cc index 54faa9a81..ea2a2624f 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -735,98 +735,6 @@ struct HelpPass : public Pass { log(" help + .... print verilog code for given cell type\n"); log("\n"); } - void write_cmd_rst(std::string cmd, std::string title, std::string text) - { - FILE *f = fopen(stringf("docs/source/cmd/%s.rst", cmd.c_str()).c_str(), "wt"); - // make header - size_t char_len = cmd.length() + 3 + title.length(); - std::string title_line = "\n"; - title_line.insert(0, char_len, '='); - fprintf(f, "%s", title_line.c_str()); - fprintf(f, "%s - %s\n", cmd.c_str(), title.c_str()); - fprintf(f, "%s\n", title_line.c_str()); - - // render html - fprintf(f, ".. cmd:def:: %s\n", cmd.c_str()); - fprintf(f, " :title: %s\n\n", title.c_str()); - fprintf(f, " .. only:: html\n\n"); - std::stringstream ss; - std::string textcp = text; - ss << text; - bool IsUsage = true; - int blank_count = 0; - size_t def_strip_count = 0; - bool WasDefinition = false; - for (std::string line; std::getline(ss, line, '\n');) { - // find position of first non space character - std::size_t first_pos = line.find_first_not_of(" \t"); - std::size_t last_pos = line.find_last_not_of(" \t"); - if (first_pos == std::string::npos) { - // skip formatting empty lines - if (!WasDefinition) - fputc('\n', f); - blank_count += 1; - continue; - } - - // strip leading and trailing whitespace - std::string stripped_line = line.substr(first_pos, last_pos - first_pos +1); - bool IsDefinition = stripped_line[0] == '-'; - IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; - bool IsDedent = def_strip_count && first_pos <= def_strip_count; - bool IsIndent = first_pos == 2 || first_pos == 4; - if (cmd.compare(0, 7, "verific") == 0) - // verific.cc has strange and different formatting from the rest - IsIndent = false; - - // another usage block - bool NewUsage = stripped_line.find(cmd) == 0; - - if (IsUsage) { - if (stripped_line.compare(0, 4, "See ") == 0) { - // description refers to another function - fprintf(f, "\n %s\n", stripped_line.c_str()); - } else { - // usage should be the first line of help output - fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); - WasDefinition = true; - } - IsUsage = false; - } else if (IsIndent && NewUsage && (blank_count >= 2 || WasDefinition)) { - // another usage block - fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); - WasDefinition = true; - def_strip_count = 0; - } else if (IsIndent && IsDefinition && (blank_count || WasDefinition)) { - // format definition term - fprintf(f, "\n\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); - WasDefinition = true; - def_strip_count = first_pos; - } else { - if (IsDedent) { - fprintf(f, "\n\n ::\n"); - def_strip_count = first_pos; - } else if (WasDefinition) { - fprintf(f, "::\n"); - WasDefinition = false; - } - fprintf(f, "\n %s", line.substr(def_strip_count, std::string::npos).c_str()); - } - - blank_count = 0; - } - fputc('\n', f); - - // render latex - fprintf(f, ".. only:: latex\n\n"); - fprintf(f, " ::\n\n"); - std::stringstream ss2; - ss2 << textcp; - for (std::string line; std::getline(ss2, line, '\n');) { - fprintf(f, " %s\n", line.c_str()); - } - fclose(f); - } bool dump_cmds_json(PrettyJson &json) { // init json json.begin_object(); @@ -1151,17 +1059,6 @@ struct HelpPass : public Pass { log("\n"); return; } - // this option is undocumented as it is for internal use only - else if (args[1] == "-write-rst-command-reference-manual") { - for (auto &it : pass_register) { - std::ostringstream buf; - log_streams.push_back(&buf); - it.second->help(); - log_warning_flags(it.second); - log_streams.pop_back(); - write_cmd_rst(it.first, it.second->short_help, buf.str()); - } - } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); log_warning_flags(pass_register.at(args[1])); From 14a5cd6c4c6085d833f9231ad19ee111cab7049a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:37:30 +1200 Subject: [PATCH 099/181] Makefile: Fix chformal.cc copy --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0f0a53a07..60643e912 100644 --- a/Makefile +++ b/Makefile @@ -1050,7 +1050,7 @@ docs/source/generated/%.log: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -qQT -h '$*' -l $@ docs/source/generated/chformal.cc: passes/cmds/chformal.cc docs/source/generated - cp $^ $@ + $(Q) cp $< $@ PHONY: docs/gen/chformal docs/gen/chformal: docs/source/generated/chformal.log docs/source/generated/chformal.cc From c770c1e39d43abca1856949de51360053236ea7c Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:47:50 +1200 Subject: [PATCH 100/181] Docs: Improve cmd index Lists all commands with their short help. Also link to it. --- docs/source/cmd_ref.rst | 2 ++ docs/util/custom_directives.py | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index 20915d6dc..668516a0b 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -12,6 +12,8 @@ Command reference .. todo:: Can we warn on command groups that aren't included anywhere? +:ref:`List of all commands` + .. toctree:: :maxdepth: 2 diff --git a/docs/util/custom_directives.py b/docs/util/custom_directives.py index e8c400047..b90584aa7 100644 --- a/docs/util/custom_directives.py +++ b/docs/util/custom_directives.py @@ -474,7 +474,7 @@ class TagIndex(Index): lis.append(( dispname, 0, docname, anchor, - docname, '', typ + '', '', '' )) ret = [(k, v) for k, v in sorted(content.items())] @@ -513,18 +513,19 @@ class CommandIndex(Index): Qualifier and description are not rendered e.g. in LaTeX output. """ - content = {} + content: dict[str, list[tuple]] = {} items = ((name, dispname, typ, docname, anchor) for name, dispname, typ, docname, anchor, prio in self.domain.get_objects() if typ == self.name) items = sorted(items, key=lambda item: item[0]) for name, dispname, typ, docname, anchor in items: + title = self.domain.data['obj2title'].get(name) lis = content.setdefault(self.shortname, []) lis.append(( dispname, 0, docname, anchor, - '', '', typ + '', '', title )) ret = [(k, v) for k, v in sorted(content.items())] From f25f8fe7c445e19a11835ccea87a16947b201c3e Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Mon, 21 Jul 2025 05:32:31 +0000 Subject: [PATCH 101/181] In the Verilog backend, only sort modules that we're going to emit. If you have a large design with a lot of modules and you use the Verilog backend to emit modules one at a time to separate files, performance is very low. The problem is that the Verilog backend calls `design->sort()` every time, which sorts the contents of all modules, and this is slow even when everything is already sorted. We can easily fix this by only sorting the contents of modules that we're actually going to emit. --- backends/verilog/verilog_backend.cc | 3 ++- kernel/rtlil.cc | 6 ++++++ kernel/rtlil.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 1cef7be60..144dad90c 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -2612,7 +2612,7 @@ struct VerilogBackend : public Backend { Pass::call(design, "clean_zerowidth"); log_pop(); - design->sort(); + design->sort_modules(); *f << stringf("/* Generated by %s */\n", yosys_maybe_version()); @@ -2625,6 +2625,7 @@ struct VerilogBackend : public Backend { continue; } log("Dumping module `%s'.\n", module->name.c_str()); + module->sort(); dump_module(*f, "", module); } diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 8a0080dbf..db954a7c3 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1139,6 +1139,12 @@ void RTLIL::Design::sort() it.second->sort(); } +void RTLIL::Design::sort_modules() +{ + scratchpad.sort(); + modules_.sort(sort_by_id_str()); +} + void RTLIL::Design::check() { #ifndef NDEBUG diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 504fa0062..cf3f24c81 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1368,6 +1368,7 @@ struct RTLIL::Design std::string scratchpad_get_string(const std::string &varname, const std::string &default_value = std::string()) const; void sort(); + void sort_modules(); void check(); void optimize(); From ca8af1f8c89910b9b3c9112d8e5efd6e031f53ec Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Mon, 21 Jul 2025 14:15:26 +0300 Subject: [PATCH 102/181] opt_dff: implement simplify_patterns --- passes/opt/opt_dff.cc | 60 ++++++++++++++++++++++++++++++-- tests/arch/quicklogic/pp3/fsm.ys | 6 ++-- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 4ed4b0cb6..b24616928 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -170,9 +170,65 @@ struct OptDffWorker return ret; } - void simplify_patterns(patterns_t&) + void simplify_patterns(patterns_t& patterns) { - // TBD + // remove complimentary patterns + + auto new_patterns = patterns; + bool optimized; + do { + optimized = false; + for (auto i = patterns.begin(); i != patterns.end(); i++) { + for (auto j = std::next(i, 1); j != patterns.end(); j++) { + const auto& smaller = (GetSize(*j) <= GetSize(*i)) ? *j : *i; + const auto& larger = (GetSize(*i) < GetSize(*j)) ? *j : *i; + auto left = std::move(smaller); + auto right = std::move(larger); + + std::optional complimentary_var; + + for (const auto &pt : left) + if (right.count(pt.first) == 0) + goto next; + else if (right[pt.first] == pt.second) + continue; + else + if (complimentary_var) + goto next; + else + complimentary_var = pt.first; + if (complimentary_var) { + new_patterns.erase(right); + right.erase(complimentary_var.value()); + new_patterns.insert(right); + optimized = true; + } + next: + continue; + } + } + patterns = new_patterns; + } while(optimized); + + // remove redundant patterns + + for (auto i = patterns.begin(); i != patterns.end(); ++i) { + for (auto j = std::next(i, 1); j != patterns.end(); ++j) { + const auto& smaller = (GetSize(*j) <= GetSize(*i)) ? *j : *i; + const auto& larger = (GetSize(*i) < GetSize(*j)) ? *j : *i; + auto left = std::move(smaller); + auto right = std::move(larger); + + bool redundant = true; + + for (const auto& pt : left) + if (right.count(pt.first) == 0 || right[pt.first] != pt.second) + redundant = false; + if (redundant) + new_patterns.erase(right); + } + } + patterns = std::move(new_patterns) } ctrl_t make_patterns_logic(const patterns_t &patterns, const ctrls_t &ctrls, bool make_gates) diff --git a/tests/arch/quicklogic/pp3/fsm.ys b/tests/arch/quicklogic/pp3/fsm.ys index 418db8025..9679628e9 100644 --- a/tests/arch/quicklogic/pp3/fsm.ys +++ b/tests/arch/quicklogic/pp3/fsm.ys @@ -11,8 +11,8 @@ sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd fsm # Constrain all select calls below inside the top module -select -assert-count 1 t:LUT2 -select -assert-count 9 t:LUT3 +select -assert-count 2 t:LUT2 +select -assert-count 4 t:LUT3 select -assert-count 4 t:dffepc select -assert-count 1 t:logic_0 select -assert-count 1 t:logic_1 @@ -20,4 +20,4 @@ select -assert-count 3 t:inpad select -assert-count 2 t:outpad select -assert-count 1 t:ckpad -select -assert-none t:LUT2 t:LUT3 t:dffepc t:logic_0 t:logic_1 t:inpad t:outpad t:ckpad %% t:* %D +select -assert-none t:LUT2 t:LUT3 t:LUT4 t:dffepc t:logic_0 t:logic_1 t:inpad t:outpad t:ckpad %% t:* %D From d9fc6dda9ec9354568f372f870af73ccffc6b652 Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Mon, 21 Jul 2025 14:42:52 +0300 Subject: [PATCH 103/181] typo --- passes/opt/opt_dff.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index b24616928..def177133 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -228,7 +228,7 @@ struct OptDffWorker new_patterns.erase(right); } } - patterns = std::move(new_patterns) + patterns = std::move(new_patterns); } ctrl_t make_patterns_logic(const patterns_t &patterns, const ctrls_t &ctrls, bool make_gates) From 2223d7848be5a249b885c355904aa1142081ea23 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 00:26:35 +0000 Subject: [PATCH 104/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 60643e912..1443f3c4a 100644 --- a/Makefile +++ b/Makefile @@ -154,7 +154,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+46 +YOSYS_VER := 0.55+109 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 875e06833494c9386e701b8b1ee1b48c38890fe0 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Tue, 22 Jul 2025 10:15:37 +0200 Subject: [PATCH 105/181] sort and comment .gitignore --- .gitignore | 69 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 7b4a1fb1e..3b77edf1f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,19 @@ +## user config +/Makefile.conf + +## build artifacts +# compiler intermediate files *.o *.d *.dwo -.*.swp *.gch *.gcda *.gcno -*~ -__pycache__ -/.cache -/.cproject -/.project -/.settings -/qtcreator.files -/qtcreator.includes -/qtcreator.config -/qtcreator.creator -/qtcreator.creator.user -/compile_commands.json -/coverage.info -/coverage_html -/Makefile.conf -/viz.js +*.so.dSYM/ + +# compiler output files +/kernel/version_*.cc +/share /yosys /yosys.exe /yosys.js @@ -36,22 +29,50 @@ __pycache__ /yosys-witness-script.py /yosys-filterlib /yosys-filterlib.exe -/kernel/*.pyh -/kernel/python_wrappers.cc -/kernel/version_*.cc -/share /yosys-win32-mxebin-* /yosys-win32-vcxsrc-* /yosysjs-* /libyosys.so + +# build directories /tests/unit/bintest/ /tests/unit/objtest/ /tests/ystests +/build /result /dist -/*.egg-info -/build -/venv + +# pyosys +/kernel/*.pyh +/kernel/python_wrappers.cc /boost /ffi +/venv /*.whl +/*.egg-info + +# yosysjs dependency +/viz.js + +# other +/coverage.info +/coverage_html + + +# these really belong in global gitignore since they're not specific to this project but rather to user tool choice +# but too many people don't have a global gitignore configured: +# https://docs.github.com/en/get-started/git-basics/ignoring-files#configuring-ignored-files-for-all-repositories-on-your-computer +__pycache__ +*~ +.*.swp +/.cache +/.vscode +/.cproject +/.project +/.settings +/qtcreator.files +/qtcreator.includes +/qtcreator.config +/qtcreator.creator +/qtcreator.creator.user +/compile_commands.json From 8b75c061418f4b2efed215e49418e933796b8d02 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 22 Jul 2025 10:38:19 +0000 Subject: [PATCH 106/181] Add a general tests/.gitignore and remove redundant entries in subdirectory .gitignore files. --- techlibs/xilinx/tests/.gitignore | 1 - tests/{opt => }/.gitignore | 2 ++ tests/aiger/.gitignore | 1 - tests/arch/anlogic/.gitignore | 2 -- tests/arch/ecp5/.gitignore | 2 -- tests/arch/efinix/.gitignore | 3 --- tests/arch/fabulous/.gitignore | 2 -- tests/arch/gatemate/.gitignore | 2 -- tests/arch/gowin/.gitignore | 3 --- tests/arch/ice40/.gitignore | 2 -- tests/arch/intel_alm/.gitignore | 2 -- tests/arch/machxo2/.gitignore | 2 -- tests/arch/microchip/.gitignore | 2 -- tests/arch/nanoxplore/.gitignore | 2 -- tests/arch/nexus/.gitignore | 2 -- tests/arch/quicklogic/.gitignore | 2 -- tests/arch/xilinx/.gitignore | 2 -- tests/asicworld/.gitignore | 2 -- tests/bind/.gitignore | 2 -- tests/blif/.gitignore | 1 - tests/fmt/.gitignore | 1 - tests/hana/.gitignore | 2 -- tests/liberty/.gitignore | 1 - tests/lut/.gitignore | 1 - tests/memlib/.gitignore | 3 --- tests/memories/.gitignore | 2 -- tests/peepopt/.gitignore | 1 - tests/proc/.gitignore | 1 - tests/rpc/.gitignore | 1 - tests/sat/.gitignore | 2 -- tests/select/.gitignore | 1 - tests/sim/.gitignore | 3 --- tests/simple/.gitignore | 3 --- tests/simple_abc9/.gitignore | 2 -- tests/svtypes/.gitignore | 3 --- tests/techmap/.gitignore | 3 --- tests/various/.gitignore | 3 --- tests/verific/.gitignore | 3 --- tests/verilog/.gitignore | 4 ---- 39 files changed, 2 insertions(+), 77 deletions(-) rename tests/{opt => }/.gitignore (60%) delete mode 100644 tests/arch/ecp5/.gitignore delete mode 100644 tests/arch/efinix/.gitignore delete mode 100644 tests/arch/gowin/.gitignore delete mode 100644 tests/arch/intel_alm/.gitignore delete mode 100644 tests/arch/machxo2/.gitignore delete mode 100644 tests/arch/nanoxplore/.gitignore delete mode 100644 tests/arch/nexus/.gitignore delete mode 100644 tests/asicworld/.gitignore delete mode 100644 tests/bind/.gitignore delete mode 100644 tests/blif/.gitignore delete mode 100644 tests/hana/.gitignore delete mode 100644 tests/lut/.gitignore delete mode 100644 tests/peepopt/.gitignore delete mode 100644 tests/proc/.gitignore delete mode 100644 tests/rpc/.gitignore delete mode 100644 tests/select/.gitignore delete mode 100644 tests/simple/.gitignore delete mode 100644 tests/svtypes/.gitignore delete mode 100644 tests/techmap/.gitignore delete mode 100644 tests/verific/.gitignore diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore index 0d9c28fde..7cb771190 100644 --- a/techlibs/xilinx/tests/.gitignore +++ b/techlibs/xilinx/tests/.gitignore @@ -1,7 +1,6 @@ bram1_cmp bram1.mk bram1_[0-9]*/ -bram2.log bram2_syn.v bram2_tb dsp_work*/ diff --git a/tests/opt/.gitignore b/tests/.gitignore similarity index 60% rename from tests/opt/.gitignore rename to tests/.gitignore index 8355de9dc..d8e01e026 100644 --- a/tests/opt/.gitignore +++ b/tests/.gitignore @@ -1,2 +1,4 @@ *.log +*.out +*.err run-test.mk diff --git a/tests/aiger/.gitignore b/tests/aiger/.gitignore index 54b4a279b..4bb3e67f6 100644 --- a/tests/aiger/.gitignore +++ b/tests/aiger/.gitignore @@ -1,3 +1,2 @@ /*_ref.v -/*.log /neg.out/ diff --git a/tests/arch/anlogic/.gitignore b/tests/arch/anlogic/.gitignore index 9a71dca69..d9f7e276c 100644 --- a/tests/arch/anlogic/.gitignore +++ b/tests/arch/anlogic/.gitignore @@ -1,4 +1,2 @@ -*.log -/run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/ecp5/.gitignore b/tests/arch/ecp5/.gitignore deleted file mode 100644 index 1d329c933..000000000 --- a/tests/arch/ecp5/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -/run-test.mk diff --git a/tests/arch/efinix/.gitignore b/tests/arch/efinix/.gitignore deleted file mode 100644 index b48f808a1..000000000 --- a/tests/arch/efinix/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/*.log -/*.out -/run-test.mk diff --git a/tests/arch/fabulous/.gitignore b/tests/arch/fabulous/.gitignore index 9a71dca69..d9f7e276c 100644 --- a/tests/arch/fabulous/.gitignore +++ b/tests/arch/fabulous/.gitignore @@ -1,4 +1,2 @@ -*.log -/run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/gatemate/.gitignore b/tests/arch/gatemate/.gitignore index 9a71dca69..d9f7e276c 100644 --- a/tests/arch/gatemate/.gitignore +++ b/tests/arch/gatemate/.gitignore @@ -1,4 +1,2 @@ -*.log -/run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/gowin/.gitignore b/tests/arch/gowin/.gitignore deleted file mode 100644 index b48f808a1..000000000 --- a/tests/arch/gowin/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/*.log -/*.out -/run-test.mk diff --git a/tests/arch/ice40/.gitignore b/tests/arch/ice40/.gitignore index 54f908bdb..a86b36792 100644 --- a/tests/arch/ice40/.gitignore +++ b/tests/arch/ice40/.gitignore @@ -1,5 +1,3 @@ -*.log *.json -/run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/intel_alm/.gitignore b/tests/arch/intel_alm/.gitignore deleted file mode 100644 index ba42e1ee6..000000000 --- a/tests/arch/intel_alm/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/*.log -/run-test.mk diff --git a/tests/arch/machxo2/.gitignore b/tests/arch/machxo2/.gitignore deleted file mode 100644 index 1d329c933..000000000 --- a/tests/arch/machxo2/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -/run-test.mk diff --git a/tests/arch/microchip/.gitignore b/tests/arch/microchip/.gitignore index 9c0a77944..aae26dc02 100644 --- a/tests/arch/microchip/.gitignore +++ b/tests/arch/microchip/.gitignore @@ -1,4 +1,2 @@ -*.log -/run-test.mk *.vm diff --git a/tests/arch/nanoxplore/.gitignore b/tests/arch/nanoxplore/.gitignore deleted file mode 100644 index 1d329c933..000000000 --- a/tests/arch/nanoxplore/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -/run-test.mk diff --git a/tests/arch/nexus/.gitignore b/tests/arch/nexus/.gitignore deleted file mode 100644 index ba42e1ee6..000000000 --- a/tests/arch/nexus/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/*.log -/run-test.mk diff --git a/tests/arch/quicklogic/.gitignore b/tests/arch/quicklogic/.gitignore index ae20ed342..d9f7e276c 100644 --- a/tests/arch/quicklogic/.gitignore +++ b/tests/arch/quicklogic/.gitignore @@ -1,4 +1,2 @@ -*.log -run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/xilinx/.gitignore b/tests/arch/xilinx/.gitignore index c99b79371..15a12d049 100644 --- a/tests/arch/xilinx/.gitignore +++ b/tests/arch/xilinx/.gitignore @@ -1,5 +1,3 @@ -/*.log -/*.out /run-test.mk /*_uut.v /test_macc diff --git a/tests/asicworld/.gitignore b/tests/asicworld/.gitignore deleted file mode 100644 index 073f46157..000000000 --- a/tests/asicworld/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -*.out diff --git a/tests/bind/.gitignore b/tests/bind/.gitignore deleted file mode 100644 index 8355de9dc..000000000 --- a/tests/bind/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -run-test.mk diff --git a/tests/blif/.gitignore b/tests/blif/.gitignore deleted file mode 100644 index 397b4a762..000000000 --- a/tests/blif/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.log diff --git a/tests/fmt/.gitignore b/tests/fmt/.gitignore index a36a15ec4..e6ae0d879 100644 --- a/tests/fmt/.gitignore +++ b/tests/fmt/.gitignore @@ -1,3 +1,2 @@ -*.log iverilog-* yosys-* diff --git a/tests/hana/.gitignore b/tests/hana/.gitignore deleted file mode 100644 index 073f46157..000000000 --- a/tests/hana/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -*.out diff --git a/tests/liberty/.gitignore b/tests/liberty/.gitignore index 2ee56e9d1..b312f8c50 100644 --- a/tests/liberty/.gitignore +++ b/tests/liberty/.gitignore @@ -1,3 +1,2 @@ -*.log /*.filtered *.verilogsim diff --git a/tests/lut/.gitignore b/tests/lut/.gitignore deleted file mode 100644 index 397b4a762..000000000 --- a/tests/lut/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.log diff --git a/tests/memlib/.gitignore b/tests/memlib/.gitignore index 03dbe82ae..bfa1a801e 100644 --- a/tests/memlib/.gitignore +++ b/tests/memlib/.gitignore @@ -1,5 +1,2 @@ -t_*.log -t_*.out t_*.v t_*.ys -run-test.mk diff --git a/tests/memories/.gitignore b/tests/memories/.gitignore index 90a0983a6..14e98cead 100644 --- a/tests/memories/.gitignore +++ b/tests/memories/.gitignore @@ -1,3 +1 @@ -*.log -*.out *.dmp diff --git a/tests/peepopt/.gitignore b/tests/peepopt/.gitignore deleted file mode 100644 index 50e13221d..000000000 --- a/tests/peepopt/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.log diff --git a/tests/proc/.gitignore b/tests/proc/.gitignore deleted file mode 100644 index 397b4a762..000000000 --- a/tests/proc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.log diff --git a/tests/rpc/.gitignore b/tests/rpc/.gitignore deleted file mode 100644 index 397b4a762..000000000 --- a/tests/rpc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.log diff --git a/tests/sat/.gitignore b/tests/sat/.gitignore index 664425d73..3fa128fcc 100644 --- a/tests/sat/.gitignore +++ b/tests/sat/.gitignore @@ -1,4 +1,2 @@ -*.log -run-test.mk *.vcd *.fst diff --git a/tests/select/.gitignore b/tests/select/.gitignore deleted file mode 100644 index 50e13221d..000000000 --- a/tests/select/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.log diff --git a/tests/sim/.gitignore b/tests/sim/.gitignore index 2c96b65f8..769b314cd 100644 --- a/tests/sim/.gitignore +++ b/tests/sim/.gitignore @@ -1,6 +1,3 @@ -*.log -/run-test.mk +*_synth.v +*_testbench -*.out *.fst diff --git a/tests/simple/.gitignore b/tests/simple/.gitignore deleted file mode 100644 index 5daaadbd7..000000000 --- a/tests/simple/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.log -*.out -*.err diff --git a/tests/simple_abc9/.gitignore b/tests/simple_abc9/.gitignore index fda60e577..e31b7dc7f 100644 --- a/tests/simple_abc9/.gitignore +++ b/tests/simple_abc9/.gitignore @@ -1,5 +1,3 @@ *.v *.sv -*.log -*.out *.bak diff --git a/tests/svtypes/.gitignore b/tests/svtypes/.gitignore deleted file mode 100644 index b48f808a1..000000000 --- a/tests/svtypes/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/*.log -/*.out -/run-test.mk diff --git a/tests/techmap/.gitignore b/tests/techmap/.gitignore deleted file mode 100644 index 56c9ba8f9..000000000 --- a/tests/techmap/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.log -*.out -/*.mk diff --git a/tests/various/.gitignore b/tests/various/.gitignore index 3dbd50843..879645f66 100644 --- a/tests/various/.gitignore +++ b/tests/various/.gitignore @@ -1,9 +1,6 @@ -/*.log -/*.out /*.sel /write_gzip.v /write_gzip.v.gz -/run-test.mk /plugin.so /plugin.so.dSYM /temp diff --git a/tests/verific/.gitignore b/tests/verific/.gitignore deleted file mode 100644 index b48f808a1..000000000 --- a/tests/verific/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/*.log -/*.out -/run-test.mk diff --git a/tests/verilog/.gitignore b/tests/verilog/.gitignore index cfd72076e..3702f10cf 100644 --- a/tests/verilog/.gitignore +++ b/tests/verilog/.gitignore @@ -1,7 +1,3 @@ -/*.log -/*.out -/*.err -/run-test.mk /const_arst.v /const_sr.v /doubleslash.v From 81f87ce6ede480de2c976938921eef8b9e79f9db Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Wed, 23 Jul 2025 14:41:49 +0000 Subject: [PATCH 107/181] Revert "Add groups to command reference" --- .github/workflows/prepare-docs.yml | 1 + Makefile | 36 +- backends/functional/test_generic.cc | 4 +- docs/.gitignore | 1 + docs/Makefile | 2 +- docs/source/_static/custom.css | 5 - docs/source/appendix/auxlibs.rst | 5 +- docs/source/cmd/index_backends.rst | 5 - docs/source/cmd/index_formal.rst | 5 - docs/source/cmd/index_frontends.rst | 5 - docs/source/cmd/index_internal.rst | 152 ------ docs/source/cmd/index_kernel.rst | 5 - docs/source/cmd/index_other.rst | 9 - docs/source/cmd/index_passes.rst | 14 - docs/source/cmd/index_passes_cmds.rst | 5 - docs/source/cmd/index_passes_equiv.rst | 5 - docs/source/cmd/index_passes_fsm.rst | 5 - docs/source/cmd/index_passes_hierarchy.rst | 5 - docs/source/cmd/index_passes_memory.rst | 5 - docs/source/cmd/index_passes_opt.rst | 5 - docs/source/cmd/index_passes_proc.rst | 5 - docs/source/cmd/index_passes_sat.rst | 5 - docs/source/cmd/index_passes_status.rst | 5 - docs/source/cmd/index_passes_techmap.rst | 7 - docs/source/cmd/index_techlibs.rst | 11 - docs/source/cmd/index_techlibs_achronix.rst | 5 - docs/source/cmd/index_techlibs_anlogic.rst | 5 - docs/source/cmd/index_techlibs_common.rst | 5 - .../source/cmd/index_techlibs_coolrunner2.rst | 5 - docs/source/cmd/index_techlibs_easic.rst | 5 - docs/source/cmd/index_techlibs_ecp5.rst | 5 - docs/source/cmd/index_techlibs_fabulous.rst | 5 - docs/source/cmd/index_techlibs_gatemate.rst | 5 - docs/source/cmd/index_techlibs_gowin.rst | 5 - docs/source/cmd/index_techlibs_greenpak4.rst | 5 - docs/source/cmd/index_techlibs_ice40.rst | 5 - docs/source/cmd/index_techlibs_intel.rst | 5 - docs/source/cmd/index_techlibs_intel_alm.rst | 5 - docs/source/cmd/index_techlibs_lattice.rst | 5 - .../cmd/index_techlibs_lattice_nexus.rst | 5 - docs/source/cmd/index_techlibs_microchip.rst | 5 - .../cmd/index_techlibs_microchip_sf2.rst | 5 - docs/source/cmd/index_techlibs_nanoxplore.rst | 5 - docs/source/cmd/index_techlibs_quicklogic.rst | 5 - docs/source/cmd/index_techlibs_xilinx.rst | 5 - docs/source/cmd_ref.rst | 33 +- .../code_examples/macro_commands/prep.ys | 23 - docs/source/conf.py | 12 +- docs/source/getting_started/example_synth.rst | 63 +-- .../getting_started/scripting_intro.rst | 4 +- docs/source/index.rst | 2 +- .../interactive_investigation.rst | 10 +- .../more_scripting/load_design.rst | 18 +- .../using_yosys/more_scripting/selections.rst | 6 +- docs/source/using_yosys/synthesis/fsm.rst | 2 +- docs/source/using_yosys/synthesis/memory.rst | 2 +- docs/source/using_yosys/synthesis/opt.rst | 8 +- docs/source/using_yosys/synthesis/proc.rst | 2 +- docs/source/using_yosys/synthesis/synth.rst | 33 +- .../using_yosys/synthesis/techmap_synth.rst | 6 +- docs/source/yosys_internals/hashing.rst | 2 +- docs/source/yosys_internals/verilog.rst | 2 +- docs/util/{cell_documenter.py => cellref.py} | 0 docs/util/cmd_documenter.py | 443 ------------------ docs/util/{custom_directives.py => cmdref.py} | 152 +----- kernel/json.h | 4 +- kernel/log_help.cc | 151 ------ kernel/log_help.h | 132 ------ kernel/register.cc | 438 ++++++++--------- kernel/register.h | 46 +- passes/cmds/check.cc | 6 - passes/cmds/chformal.cc | 109 +++-- passes/cmds/cover.cc | 14 +- passes/cmds/dft_tag.cc | 6 - passes/cmds/edgetypes.cc | 6 - passes/cmds/example_dt.cc | 8 +- passes/cmds/exec.cc | 9 +- passes/cmds/future.cc | 6 - passes/cmds/glift.cc | 11 +- passes/cmds/internal_stats.cc | 7 +- passes/cmds/logcmd.cc | 10 +- passes/cmds/logger.cc | 9 +- passes/cmds/ltp.cc | 6 - passes/cmds/plugin.cc | 6 - passes/cmds/portarcs.cc | 6 - passes/cmds/portlist.cc | 6 - passes/cmds/printattrs.cc | 6 - passes/cmds/scc.cc | 11 +- passes/cmds/scratchpad.cc | 10 +- passes/cmds/select.cc | 18 +- passes/cmds/setenv.cc | 11 +- passes/cmds/show.cc | 10 +- passes/cmds/sta.cc | 6 - passes/cmds/stat.cc | 6 - passes/cmds/tee.cc | 10 +- passes/cmds/torder.cc | 6 - passes/cmds/trace.cc | 11 - passes/cmds/viz.cc | 6 - passes/cmds/write_file.cc | 6 - passes/cmds/xprop.cc | 6 - passes/hierarchy/Makefile.inc | 1 - passes/opt/rmports.cc | 11 +- passes/pmgen/test_pmgen.cc | 4 +- passes/sat/assertpmux.cc | 6 - passes/sat/async2sync.cc | 6 - passes/sat/clk2fflogic.cc | 6 - passes/sat/cutpoint.cc | 6 - passes/sat/expose.cc | 10 +- passes/sat/fmcombine.cc | 6 - passes/sat/fminit.cc | 6 - passes/sat/formalff.cc | 6 - passes/sat/freduce.cc | 14 +- passes/sat/miter.cc | 10 +- passes/sat/mutate.cc | 6 - passes/sat/qbfsat.cc | 6 - passes/sat/recover_names.cc | 6 - passes/sat/sat.cc | 14 +- passes/sat/supercover.cc | 6 - passes/sat/synthprop.cc | 12 +- passes/techmap/Makefile.inc | 1 + passes/{hierarchy => techmap}/flatten.cc | 0 passes/tests/test_abcloop.cc | 4 +- passes/tests/test_autotb.cc | 4 +- passes/tests/test_cell.cc | 4 +- 124 files changed, 474 insertions(+), 2035 deletions(-) delete mode 100644 docs/source/cmd/index_backends.rst delete mode 100644 docs/source/cmd/index_formal.rst delete mode 100644 docs/source/cmd/index_frontends.rst delete mode 100644 docs/source/cmd/index_internal.rst delete mode 100644 docs/source/cmd/index_kernel.rst delete mode 100644 docs/source/cmd/index_other.rst delete mode 100644 docs/source/cmd/index_passes.rst delete mode 100644 docs/source/cmd/index_passes_cmds.rst delete mode 100644 docs/source/cmd/index_passes_equiv.rst delete mode 100644 docs/source/cmd/index_passes_fsm.rst delete mode 100644 docs/source/cmd/index_passes_hierarchy.rst delete mode 100644 docs/source/cmd/index_passes_memory.rst delete mode 100644 docs/source/cmd/index_passes_opt.rst delete mode 100644 docs/source/cmd/index_passes_proc.rst delete mode 100644 docs/source/cmd/index_passes_sat.rst delete mode 100644 docs/source/cmd/index_passes_status.rst delete mode 100644 docs/source/cmd/index_passes_techmap.rst delete mode 100644 docs/source/cmd/index_techlibs.rst delete mode 100644 docs/source/cmd/index_techlibs_achronix.rst delete mode 100644 docs/source/cmd/index_techlibs_anlogic.rst delete mode 100644 docs/source/cmd/index_techlibs_common.rst delete mode 100644 docs/source/cmd/index_techlibs_coolrunner2.rst delete mode 100644 docs/source/cmd/index_techlibs_easic.rst delete mode 100644 docs/source/cmd/index_techlibs_ecp5.rst delete mode 100644 docs/source/cmd/index_techlibs_fabulous.rst delete mode 100644 docs/source/cmd/index_techlibs_gatemate.rst delete mode 100644 docs/source/cmd/index_techlibs_gowin.rst delete mode 100644 docs/source/cmd/index_techlibs_greenpak4.rst delete mode 100644 docs/source/cmd/index_techlibs_ice40.rst delete mode 100644 docs/source/cmd/index_techlibs_intel.rst delete mode 100644 docs/source/cmd/index_techlibs_intel_alm.rst delete mode 100644 docs/source/cmd/index_techlibs_lattice.rst delete mode 100644 docs/source/cmd/index_techlibs_lattice_nexus.rst delete mode 100644 docs/source/cmd/index_techlibs_microchip.rst delete mode 100644 docs/source/cmd/index_techlibs_microchip_sf2.rst delete mode 100644 docs/source/cmd/index_techlibs_nanoxplore.rst delete mode 100644 docs/source/cmd/index_techlibs_quicklogic.rst delete mode 100644 docs/source/cmd/index_techlibs_xilinx.rst delete mode 100644 docs/source/code_examples/macro_commands/prep.ys rename docs/util/{cell_documenter.py => cellref.py} (100%) delete mode 100644 docs/util/cmd_documenter.py rename docs/util/{custom_directives.py => cmdref.py} (81%) delete mode 100644 kernel/log_help.cc delete mode 100644 kernel/log_help.h rename passes/{hierarchy => techmap}/flatten.cc (100%) diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml index b47f5f3dd..fb1fab426 100644 --- a/.github/workflows/prepare-docs.yml +++ b/.github/workflows/prepare-docs.yml @@ -59,6 +59,7 @@ jobs: with: name: cmd-ref-${{ github.sha }} path: | + docs/source/cmd docs/source/generated docs/source/_images docs/source/code_examples diff --git a/Makefile b/Makefile index 1443f3c4a..d7b38c80f 100644 --- a/Makefile +++ b/Makefile @@ -115,6 +115,12 @@ BISON ?= bison STRIP ?= strip AWK ?= awk +ifneq ($(shell :; command -v rsync),) +RSYNC_CP ?= rsync -rc +else +RSYNC_CP ?= cp -ru +endif + ifeq ($(OS), Darwin) PLUGIN_LINKFLAGS += -undefined dynamic_lookup LINKFLAGS += -rdynamic @@ -526,6 +532,7 @@ LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VE endif endif + ifeq ($(ENABLE_COVER),1) CXXFLAGS += -DYOSYS_ENABLE_COVER endif @@ -627,7 +634,6 @@ $(eval $(call add_include_file,frontends/blif/blifparse.h)) $(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 kernel/io.o kernel/gzip.o -OBJS += kernel/log_help.o OBJS += kernel/binding.o kernel/tclapi.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 kernel/sexpr.o OBJS += kernel/drivertools.o kernel/functional.o @@ -1028,8 +1034,19 @@ ifeq ($(ENABLE_PYOSYS),1) endif endif -docs/source/generated/cmds.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) - $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cmds-json $@' +# also others, but so long as it doesn't fail this is enough to know we tried +docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) + $(Q) mkdir -p docs/source/cmd + $(Q) mkdir -p temp/docs/source/cmd + $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' + $(Q) $(RSYNC_CP) temp/docs/source/cmd docs/source + $(Q) rm -rf temp +docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) + $(Q) mkdir -p docs/source/cell + $(Q) mkdir -p temp/docs/source/cell + $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-cells-manual' + $(Q) $(RSYNC_CP) temp/docs/source/cell docs/source + $(Q) rm -rf temp docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' @@ -1046,15 +1063,6 @@ docs/source/generated/functional/rosette.diff: backends/functional/smtlib.cc bac PHONY: docs/gen/functional_ir docs/gen/functional_ir: docs/source/generated/functional/smtlib.cc docs/source/generated/functional/rosette.diff -docs/source/generated/%.log: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) - $(Q) ./$(PROGRAM_PREFIX)yosys -qQT -h '$*' -l $@ - -docs/source/generated/chformal.cc: passes/cmds/chformal.cc docs/source/generated - $(Q) cp $< $@ - -PHONY: docs/gen/chformal -docs/gen/chformal: docs/source/generated/chformal.log docs/source/generated/chformal.cc - PHONY: docs/gen docs/usage docs/reqs docs/gen: $(TARGETS) $(Q) $(MAKE) -C docs gen @@ -1090,7 +1098,7 @@ docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep -docs/prep: docs/source/generated/cells.json docs/source/generated/cmds.json docs/gen docs/usage docs/gen/functional_ir docs/gen/chformal +docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage docs/gen/functional_ir DOC_TARGET ?= html docs: docs/prep @@ -1114,7 +1122,7 @@ clean: rm -f tests/tools/cmp_tbdata rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS)) -$(MAKE) -C docs clean - rm -rf docs/util/__pycache__ + rm -rf docs/source/cmd docs/util/__pycache__ rm -f *.whl rm -f libyosys.so diff --git a/backends/functional/test_generic.cc b/backends/functional/test_generic.cc index 42d6c2b95..a9dfd0c70 100644 --- a/backends/functional/test_generic.cc +++ b/backends/functional/test_generic.cc @@ -116,9 +116,7 @@ struct MemContentsTest { struct FunctionalTestGeneric : public Pass { - FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") { - internal(); - } + FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {} void help() override { diff --git a/docs/.gitignore b/docs/.gitignore index 09bb59048..65bbcdeae 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,4 +1,5 @@ /build/ +/source/cmd /source/generated /source/_images/**/*.log /source/_images/**/*.aux diff --git a/docs/Makefile b/docs/Makefile index fb3e03b79..a8874bb83 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -47,7 +47,7 @@ help: .PHONY: clean clean: clean-examples rm -rf $(BUILDDIR)/* - rm -rf util/__pycache__ + rm -rf source/cmd util/__pycache__ rm -rf source/generated $(MAKE) -C source/_images clean diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css index 60faf6812..b08194c05 100644 --- a/docs/source/_static/custom.css +++ b/docs/source/_static/custom.css @@ -18,8 +18,3 @@ .literal-block-wrapper .code-block-caption .caption-number { padding-right: 0.5em } - -/* Don't double shrink text in a literal in an optionlist */ -kbd .option>.literal { - font-size: revert; -} diff --git a/docs/source/appendix/auxlibs.rst b/docs/source/appendix/auxlibs.rst index 192ac0944..8c78ed6b3 100644 --- a/docs/source/appendix/auxlibs.rst +++ b/docs/source/appendix/auxlibs.rst @@ -29,7 +29,8 @@ ezSAT The files in ``libs/ezsat`` provide a library for simplifying generating CNF formulas for SAT solvers. It also contains bindings of MiniSAT. The ezSAT -library is written by C. Wolf. It is used by the `sat` pass. +library is written by C. Wolf. It is used by the `sat` pass (see +:doc:`/cmd/sat`). fst --- @@ -77,4 +78,4 @@ SubCircuit The files in ``libs/subcircuit`` provide a library for solving the subcircuit isomorphism problem. It is written by C. Wolf and based on the Ullmann Subgraph Isomorphism Algorithm :cite:p:`UllmannSubgraphIsomorphism`. It is used by the -`extract` pass. +extract pass (see :doc:`../cmd/extract`). diff --git a/docs/source/cmd/index_backends.rst b/docs/source/cmd/index_backends.rst deleted file mode 100644 index 373c26def..000000000 --- a/docs/source/cmd/index_backends.rst +++ /dev/null @@ -1,5 +0,0 @@ -Writing output files --------------------- - -.. autocmdgroup:: backends - :members: diff --git a/docs/source/cmd/index_formal.rst b/docs/source/cmd/index_formal.rst deleted file mode 100644 index b8b134c17..000000000 --- a/docs/source/cmd/index_formal.rst +++ /dev/null @@ -1,5 +0,0 @@ -Formal verification -------------------- - -.. autocmdgroup:: formal - :members: diff --git a/docs/source/cmd/index_frontends.rst b/docs/source/cmd/index_frontends.rst deleted file mode 100644 index b64fdc9b9..000000000 --- a/docs/source/cmd/index_frontends.rst +++ /dev/null @@ -1,5 +0,0 @@ -Reading input files -------------------- - -.. autocmdgroup:: frontends - :members: diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst deleted file mode 100644 index bfb369dde..000000000 --- a/docs/source/cmd/index_internal.rst +++ /dev/null @@ -1,152 +0,0 @@ -Internal commands for developers --------------------------------- - -.. autocmdgroup:: internal - :members: - -Writing command help --------------------- - -- use `chformal` as an example -- generated help content below - -.. _chformal autocmd: - -.. autocmd:: chformal - :noindex: - -The ``formatted_help()`` method -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- ``PrettyHelp::get_current()`` -- ``PrettyHelp::set_group()`` - - + used with ``.. autocmdgroup:: `` - + can assign group and return false - + if no group is set, will try to use ``source_location`` and assign group - from path to source file - -- return value - - + true means help content added to current ``PrettyHelp`` - + false to use ``Pass::help()`` - -- adding content - - + help content is a list of ``ContentListing`` nodes, each one having a type, - body, and its own list of children ``ContentListing``\ s - + ``PrettyHelp::get_root()`` returns the root ``ContentListing`` (``type="root"``) - + ``ContentListing::{usage, option, codeblock, paragraph}`` each add a - ``ContentListing`` to the current node, with type the same as the method - - * the first argument is the body of the new node - * ``usage`` shows how to call the command (i.e. its "signature") - * ``paragraph`` content is formatted as a paragraph of text with line breaks - added automatically - * ``codeblock`` content is displayed verbatim, use line breaks as desired; - takes an optional ``language`` argument for assigning the language in RST - output for code syntax highlighting (use ``yoscrypt`` for yosys script - syntax highlighting) - * ``option`` lists a single option for the command, usually starting with a - dash (``-``); takes an optional second argument which adds a paragraph - node as a means of description - - + ``ContentListing::open_usage`` creates and returns a new usage node, can be - used to e.g. add text/options specific to a given usage of the command - + ``ContentListing::open_option`` creates and returns a new option node, can - be used to e.g. add multiple paragraphs to an option's description - + paragraphs are treated as raw RST, allowing for inline formatting and - references as if it were written in the RST file itself - -.. literalinclude:: /generated/chformal.cc - :language: c++ - :start-at: bool formatted_help() - :end-before: void execute - :caption: ``ChformalPass::formatted_help()`` from :file:`passes/cmds/chformal.cc` - -Dumping command help to json -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `help -dump-cells-json cmds.json` - - + generates a ``ContentListing`` for each command registered in Yosys - + tries to parse unformatted ``Pass::help()`` output if - ``Pass::formatted_help()`` is unimplemented or returns false - - * if a line starts with four spaces followed by the name of the command then - a space, it is parsed as a signature (usage node) - * if a line is indented and starts with a dash (``-``), it is parsed as an - option - * anything else is parsed as a codeblock and added to either the root node - or the current option depending on the indentation - - + dictionary of command name to ``ContentListing`` - - * uses ``ContentListing::to_json()`` recursively for each node in root - * root node used for source location of class definition - * includes flags set during pass constructor (e.g. ``experimental_flag`` set - by ``Pass::experimental()``) - * also title (``short_help`` argument in ``Pass::Pass``), group, and class - name - - + dictionary of group name to list of commands in that group - -- used by sphinx autodoc to generate help content - -.. literalinclude:: /generated/cmds.json - :language: json - :start-at: "chformal": { - :end-before: "chparam": { - :caption: `chformal` in generated :file:`cmds.json` - -.. note:: Synthesis command scripts are special cased - - If the final block of help output starts with the string `"The following - commands are executed by this synthesis command:\n"`, then the rest of the - code block is formatted as ``yoscrypt`` (e.g. `synth_ice40`). The caveat - here is that if the ``script()`` calls ``run()`` on any commands *prior* to - the first ``check_label`` then the auto detection will break and revert to - unformatted code (e.g. `synth_fabulous`). - -Command line rendering -~~~~~~~~~~~~~~~~~~~~~~ - -- if ``Pass::formatted_help()`` returns true, will call - ``PrettyHelp::log_help()`` - - + traverse over the children of the root node and render as plain text - + effectively the reverse of converting unformatted ``Pass::help()`` text - + lines are broken at 80 characters while maintaining indentation (controlled - by ``MAX_LINE_LEN`` in :file:`kernel/log_help.cc`) - + each line is broken into words separated by spaces, if a given word starts - and ends with backticks they will be stripped - -- if it returns false it will call ``Pass::help()`` which should call ``log()`` - directly to print and format help text - - + if ``Pass::help()`` is not overridden then a default message about missing - help will be displayed - -.. literalinclude:: /generated/chformal.log - :lines: 2- - -RST generated from autocmd -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- below is the raw RST output from ``autocmd`` (``YosysCmdDocumenter`` class in - :file:`docs/util/cmd_documenter.py`) for `chformal` command -- heading will be rendered as a subheading of the most recent heading (see - `chformal autocmd`_ above rendered under `Writing command help`_) -- ``.. cmd:def:: `` line is indexed for cross references with ``:cmd:ref:`` - directive (`chformal autocmd`_ above uses ``:noindex:`` option so that - `chformal` still links to the correct location) - - + ``:title:`` option controls text that appears when hovering over the - `chformal` link - -- commands with warning flags (experimental or internal) add a ``.. warning`` - block before any of the help content -- if a command has no ``source_location`` the ``.. note`` at the bottom will - instead link to :doc:`/cmd/index_other` - -.. autocmd_rst:: chformal diff --git a/docs/source/cmd/index_kernel.rst b/docs/source/cmd/index_kernel.rst deleted file mode 100644 index c6891b5e5..000000000 --- a/docs/source/cmd/index_kernel.rst +++ /dev/null @@ -1,5 +0,0 @@ -Yosys kernel commands ---------------------- - -.. autocmdgroup:: kernel - :members: diff --git a/docs/source/cmd/index_other.rst b/docs/source/cmd/index_other.rst deleted file mode 100644 index 540cf9e49..000000000 --- a/docs/source/cmd/index_other.rst +++ /dev/null @@ -1,9 +0,0 @@ -:orphan: - -Other commands -============== - -Unknown source location - -.. autocmdgroup:: unknown - :members: diff --git a/docs/source/cmd/index_passes.rst b/docs/source/cmd/index_passes.rst deleted file mode 100644 index b652be004..000000000 --- a/docs/source/cmd/index_passes.rst +++ /dev/null @@ -1,14 +0,0 @@ -Passes ------- - -.. toctree:: - :maxdepth: 2 - :glob: - - /cmd/index_passes_hierarchy - /cmd/index_passes_proc - /cmd/index_passes_fsm - /cmd/index_passes_memory - /cmd/index_passes_opt - /cmd/index_passes_techmap - /cmd/index_passes_* diff --git a/docs/source/cmd/index_passes_cmds.rst b/docs/source/cmd/index_passes_cmds.rst deleted file mode 100644 index d7b448f07..000000000 --- a/docs/source/cmd/index_passes_cmds.rst +++ /dev/null @@ -1,5 +0,0 @@ -Design modification -------------------- - -.. autocmdgroup:: passes/cmds - :members: diff --git a/docs/source/cmd/index_passes_equiv.rst b/docs/source/cmd/index_passes_equiv.rst deleted file mode 100644 index 6ed2c3c18..000000000 --- a/docs/source/cmd/index_passes_equiv.rst +++ /dev/null @@ -1,5 +0,0 @@ -Equivalence checking --------------------- - -.. autocmdgroup:: passes/equiv - :members: diff --git a/docs/source/cmd/index_passes_fsm.rst b/docs/source/cmd/index_passes_fsm.rst deleted file mode 100644 index 43af5dce6..000000000 --- a/docs/source/cmd/index_passes_fsm.rst +++ /dev/null @@ -1,5 +0,0 @@ -FSM handling ------------- - -.. autocmdgroup:: passes/fsm - :members: diff --git a/docs/source/cmd/index_passes_hierarchy.rst b/docs/source/cmd/index_passes_hierarchy.rst deleted file mode 100644 index 27a0faeb7..000000000 --- a/docs/source/cmd/index_passes_hierarchy.rst +++ /dev/null @@ -1,5 +0,0 @@ -Working with hierarchy ----------------------- - -.. autocmdgroup:: passes/hierarchy - :members: diff --git a/docs/source/cmd/index_passes_memory.rst b/docs/source/cmd/index_passes_memory.rst deleted file mode 100644 index b4edb88e7..000000000 --- a/docs/source/cmd/index_passes_memory.rst +++ /dev/null @@ -1,5 +0,0 @@ -Memory handling ---------------- - -.. autocmdgroup:: passes/memory - :members: diff --git a/docs/source/cmd/index_passes_opt.rst b/docs/source/cmd/index_passes_opt.rst deleted file mode 100644 index ddeb5ce10..000000000 --- a/docs/source/cmd/index_passes_opt.rst +++ /dev/null @@ -1,5 +0,0 @@ -Optimization passes -------------------- - -.. autocmdgroup:: passes/opt - :members: diff --git a/docs/source/cmd/index_passes_proc.rst b/docs/source/cmd/index_passes_proc.rst deleted file mode 100644 index 1ad8d85b4..000000000 --- a/docs/source/cmd/index_passes_proc.rst +++ /dev/null @@ -1,5 +0,0 @@ -Converting process blocks -------------------------- - -.. autocmdgroup:: passes/proc - :members: diff --git a/docs/source/cmd/index_passes_sat.rst b/docs/source/cmd/index_passes_sat.rst deleted file mode 100644 index a2571fedb..000000000 --- a/docs/source/cmd/index_passes_sat.rst +++ /dev/null @@ -1,5 +0,0 @@ -Simulating circuits -------------------- - -.. autocmdgroup:: passes/sat - :members: diff --git a/docs/source/cmd/index_passes_status.rst b/docs/source/cmd/index_passes_status.rst deleted file mode 100644 index a157ed840..000000000 --- a/docs/source/cmd/index_passes_status.rst +++ /dev/null @@ -1,5 +0,0 @@ -Design status -------------- - -.. autocmdgroup:: passes/status - :members: diff --git a/docs/source/cmd/index_passes_techmap.rst b/docs/source/cmd/index_passes_techmap.rst deleted file mode 100644 index 1682cd181..000000000 --- a/docs/source/cmd/index_passes_techmap.rst +++ /dev/null @@ -1,7 +0,0 @@ -Technology mapping ------------------- - -.. seealso:: :doc:`/cmd/index_techlibs` - -.. autocmdgroup:: passes/techmap - :members: diff --git a/docs/source/cmd/index_techlibs.rst b/docs/source/cmd/index_techlibs.rst deleted file mode 100644 index 043620a3b..000000000 --- a/docs/source/cmd/index_techlibs.rst +++ /dev/null @@ -1,11 +0,0 @@ -Technology libraries -==================== - -Listed in alphabetical order. - -.. toctree:: - :maxdepth: 2 - :glob: - - /cmd/index_techlibs_common - /cmd/index_techlibs_* diff --git a/docs/source/cmd/index_techlibs_achronix.rst b/docs/source/cmd/index_techlibs_achronix.rst deleted file mode 100644 index d4d96d24a..000000000 --- a/docs/source/cmd/index_techlibs_achronix.rst +++ /dev/null @@ -1,5 +0,0 @@ -Achronix ------------------- - -.. autocmdgroup:: techlibs/achronix - :members: diff --git a/docs/source/cmd/index_techlibs_anlogic.rst b/docs/source/cmd/index_techlibs_anlogic.rst deleted file mode 100644 index 8a2e6b577..000000000 --- a/docs/source/cmd/index_techlibs_anlogic.rst +++ /dev/null @@ -1,5 +0,0 @@ -Anlogic ------------------- - -.. autocmdgroup:: techlibs/anlogic - :members: diff --git a/docs/source/cmd/index_techlibs_common.rst b/docs/source/cmd/index_techlibs_common.rst deleted file mode 100644 index 532f4291e..000000000 --- a/docs/source/cmd/index_techlibs_common.rst +++ /dev/null @@ -1,5 +0,0 @@ -Generic ------------------- - -.. autocmdgroup:: techlibs/common - :members: diff --git a/docs/source/cmd/index_techlibs_coolrunner2.rst b/docs/source/cmd/index_techlibs_coolrunner2.rst deleted file mode 100644 index 23d91a500..000000000 --- a/docs/source/cmd/index_techlibs_coolrunner2.rst +++ /dev/null @@ -1,5 +0,0 @@ -CoolRunner-II ------------------- - -.. autocmdgroup:: techlibs/coolrunner2 - :members: diff --git a/docs/source/cmd/index_techlibs_easic.rst b/docs/source/cmd/index_techlibs_easic.rst deleted file mode 100644 index c6398ddf3..000000000 --- a/docs/source/cmd/index_techlibs_easic.rst +++ /dev/null @@ -1,5 +0,0 @@ -eASIC ------------------- - -.. autocmdgroup:: techlibs/easic - :members: diff --git a/docs/source/cmd/index_techlibs_ecp5.rst b/docs/source/cmd/index_techlibs_ecp5.rst deleted file mode 100644 index 29fd309cf..000000000 --- a/docs/source/cmd/index_techlibs_ecp5.rst +++ /dev/null @@ -1,5 +0,0 @@ -ECP5 ------------------- - -.. autocmdgroup:: techlibs/ecp5 - :members: diff --git a/docs/source/cmd/index_techlibs_fabulous.rst b/docs/source/cmd/index_techlibs_fabulous.rst deleted file mode 100644 index 96f04f40a..000000000 --- a/docs/source/cmd/index_techlibs_fabulous.rst +++ /dev/null @@ -1,5 +0,0 @@ -FABulous ------------------- - -.. autocmdgroup:: techlibs/fabulous - :members: diff --git a/docs/source/cmd/index_techlibs_gatemate.rst b/docs/source/cmd/index_techlibs_gatemate.rst deleted file mode 100644 index 951d0000b..000000000 --- a/docs/source/cmd/index_techlibs_gatemate.rst +++ /dev/null @@ -1,5 +0,0 @@ -Gatemate ------------------- - -.. autocmdgroup:: techlibs/gatemate - :members: diff --git a/docs/source/cmd/index_techlibs_gowin.rst b/docs/source/cmd/index_techlibs_gowin.rst deleted file mode 100644 index cdcb1c2ee..000000000 --- a/docs/source/cmd/index_techlibs_gowin.rst +++ /dev/null @@ -1,5 +0,0 @@ -Gowin ------------------- - -.. autocmdgroup:: techlibs/gowin - :members: diff --git a/docs/source/cmd/index_techlibs_greenpak4.rst b/docs/source/cmd/index_techlibs_greenpak4.rst deleted file mode 100644 index add1ab102..000000000 --- a/docs/source/cmd/index_techlibs_greenpak4.rst +++ /dev/null @@ -1,5 +0,0 @@ -GreenPAK4 ------------------- - -.. autocmdgroup:: techlibs/greenpak4 - :members: diff --git a/docs/source/cmd/index_techlibs_ice40.rst b/docs/source/cmd/index_techlibs_ice40.rst deleted file mode 100644 index 1c4b2d07a..000000000 --- a/docs/source/cmd/index_techlibs_ice40.rst +++ /dev/null @@ -1,5 +0,0 @@ -iCE40 ------------------- - -.. autocmdgroup:: techlibs/ice40 - :members: diff --git a/docs/source/cmd/index_techlibs_intel.rst b/docs/source/cmd/index_techlibs_intel.rst deleted file mode 100644 index 6b3a26222..000000000 --- a/docs/source/cmd/index_techlibs_intel.rst +++ /dev/null @@ -1,5 +0,0 @@ -Intel (MAX10, Cyclone IV) -------------------------- - -.. autocmdgroup:: techlibs/intel - :members: diff --git a/docs/source/cmd/index_techlibs_intel_alm.rst b/docs/source/cmd/index_techlibs_intel_alm.rst deleted file mode 100644 index afadfc13e..000000000 --- a/docs/source/cmd/index_techlibs_intel_alm.rst +++ /dev/null @@ -1,5 +0,0 @@ -Intel ALM (Cyclone V, Arria V, Cyclone 10 GX) ---------------------------------------------- - -.. autocmdgroup:: techlibs/intel_alm - :members: diff --git a/docs/source/cmd/index_techlibs_lattice.rst b/docs/source/cmd/index_techlibs_lattice.rst deleted file mode 100644 index 985bf0bbd..000000000 --- a/docs/source/cmd/index_techlibs_lattice.rst +++ /dev/null @@ -1,5 +0,0 @@ -Lattice ------------------- - -.. autocmdgroup:: techlibs/lattice - :members: diff --git a/docs/source/cmd/index_techlibs_lattice_nexus.rst b/docs/source/cmd/index_techlibs_lattice_nexus.rst deleted file mode 100644 index d5ac4184c..000000000 --- a/docs/source/cmd/index_techlibs_lattice_nexus.rst +++ /dev/null @@ -1,5 +0,0 @@ -Lattice Nexus ------------------- - -.. autocmdgroup:: techlibs/nexus - :members: diff --git a/docs/source/cmd/index_techlibs_microchip.rst b/docs/source/cmd/index_techlibs_microchip.rst deleted file mode 100644 index 06613a59c..000000000 --- a/docs/source/cmd/index_techlibs_microchip.rst +++ /dev/null @@ -1,5 +0,0 @@ -Microchip ------------------- - -.. autocmdgroup:: techlibs/microchip - :members: diff --git a/docs/source/cmd/index_techlibs_microchip_sf2.rst b/docs/source/cmd/index_techlibs_microchip_sf2.rst deleted file mode 100644 index 4ebe47f33..000000000 --- a/docs/source/cmd/index_techlibs_microchip_sf2.rst +++ /dev/null @@ -1,5 +0,0 @@ -Microchip - SmartFusion2/IGLOO2 ------------------------------------ - -.. autocmdgroup:: techlibs/sf2 - :members: diff --git a/docs/source/cmd/index_techlibs_nanoxplore.rst b/docs/source/cmd/index_techlibs_nanoxplore.rst deleted file mode 100644 index 9eff4681c..000000000 --- a/docs/source/cmd/index_techlibs_nanoxplore.rst +++ /dev/null @@ -1,5 +0,0 @@ -NanoXplore ------------------- - -.. autocmdgroup:: techlibs/nanoxplore - :members: diff --git a/docs/source/cmd/index_techlibs_quicklogic.rst b/docs/source/cmd/index_techlibs_quicklogic.rst deleted file mode 100644 index 54d199eb0..000000000 --- a/docs/source/cmd/index_techlibs_quicklogic.rst +++ /dev/null @@ -1,5 +0,0 @@ -QuickLogic ------------------- - -.. autocmdgroup:: techlibs/quicklogic - :members: diff --git a/docs/source/cmd/index_techlibs_xilinx.rst b/docs/source/cmd/index_techlibs_xilinx.rst deleted file mode 100644 index df5112b7e..000000000 --- a/docs/source/cmd/index_techlibs_xilinx.rst +++ /dev/null @@ -1,5 +0,0 @@ -Xilinx ------------------- - -.. autocmdgroup:: techlibs/xilinx - :members: diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index 668516a0b..acf2d1d41 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -1,3 +1,5 @@ +.. _cmd_ref: + ================================================================================ Command line reference ================================================================================ @@ -5,31 +7,10 @@ Command line reference .. literalinclude:: /generated/yosys :start-at: Usage -.. _cmd_ref: - -Command reference ------------------ - -.. todo:: Can we warn on command groups that aren't included anywhere? - -:ref:`List of all commands` - .. toctree:: - :maxdepth: 2 + :caption: Command reference + :maxdepth: 1 + :glob: - /appendix/env_vars - /cmd/index_frontends - /cmd/index_backends - /cmd/index_kernel - /cmd/index_formal - -.. toctree:: - :maxdepth: 3 - - /cmd/index_passes - /cmd/index_techlibs - -.. toctree:: - :maxdepth: 2 - - /cmd/index_internal + /appendix/env_vars + /cmd/* diff --git a/docs/source/code_examples/macro_commands/prep.ys b/docs/source/code_examples/macro_commands/prep.ys deleted file mode 100644 index 1bec907f6..000000000 --- a/docs/source/code_examples/macro_commands/prep.ys +++ /dev/null @@ -1,23 +0,0 @@ -#start:The following commands are executed by this synthesis command: -#end:$ -begin: - hierarchy -check [-top | -auto-top] - -coarse: - proc [-ifx] - flatten (if -flatten) - future - opt_expr -keepdc - opt_clean - check - opt -noff -keepdc - wreduce -keepdc [-memx] - memory_dff (if -rdff) - memory_memx (if -memx) - opt_clean - memory_collect - opt -noff -keepdc -fast - -check: - stat - check diff --git a/docs/source/conf.py b/docs/source/conf.py index 725ac42bb..05dcb7d5f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -43,12 +43,8 @@ html_static_path = ['_static', "_images"] # default to no highlight highlight_language = 'none' -# default single quotes to attempt auto reference, or fallback to yoscrypt +# default single quotes to attempt auto reference, or fallback to code default_role = 'autoref' -rst_prolog = """ -.. role:: yoscrypt(code) - :language: yoscrypt -""" extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex'] @@ -110,14 +106,12 @@ latex_elements = { # custom cmd-ref parsing/linking sys.path += [os.path.dirname(__file__) + "/../"] -extensions.append('util.custom_directives') +extensions.append('util.cmdref') # use autodocs extensions.append('sphinx.ext.autodoc') -extensions.append('util.cell_documenter') +extensions.append('util.cellref') cells_json = Path(__file__).parent / 'generated' / 'cells.json' -extensions.append('util.cmd_documenter') -cmds_json = Path(__file__).parent / 'generated' / 'cmds.json' from sphinx.application import Sphinx def setup(app: Sphinx) -> None: diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst index ccf0d252b..e215586cc 100644 --- a/docs/source/getting_started/example_synth.rst +++ b/docs/source/getting_started/example_synth.rst @@ -70,7 +70,7 @@ At the bottom of the `help` output for `synth_ice40` is the complete list of commands called by this script. Let's start with the section labeled ``begin``: -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: begin: :end-before: flatten: @@ -143,8 +143,8 @@ line refers to the line numbers of the start/end of the corresponding ``always @`` block. In the case of an ``initial`` block, we instead see the ``PROC`` referring to line 0. -To handle these, let us now introduce the next command: :cmd:title:`proc`. -`proc` is a macro command like `synth_ice40`. Rather than modifying the design +To handle these, let us now introduce the next command: :doc:`/cmd/proc`. `proc` +is a macro command like `synth_ice40`. Rather than modifying the design directly, it instead calls a series of other commands. In the case of `proc`, these sub-commands work to convert the behavioral logic of processes into multiplexers and registers. Let's see what happens when we run it. For now, we @@ -188,7 +188,7 @@ opt_expr `. .. note:: - :cmd:title:`clean` can also be called with two semicolons after any command, + :doc:`/cmd/clean` can also be called with two semicolons after any command, for example we could have called :yoscrypt:`opt_expr;;` instead of :yoscrypt:`opt_expr; clean`. You may notice some scripts will end each line with ``;;``. It is beneficial to run `clean` before inspecting intermediate @@ -215,8 +215,8 @@ Note that if we tried to run this command now then we would get an error. This is because we already removed all of the modules other than ``addr_gen``. We could restart our shell session, but instead let's use two new commands: -- :cmd:title:`design`, and -- :cmd:title:`read_verilog`. +- :doc:`/cmd/design`, and +- :doc:`/cmd/read_verilog`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -251,7 +251,7 @@ our design won't run into this issue, we can skip the ``-defer``. We can also run `proc` now to finish off the full :ref:`synth_begin`. Because the design schematic is quite large, we will be showing just the data path for the ``rdata`` output. If you would like to see the entire design for yourself, -you can do so with :cmd:title:`show`. Note that the `show` command only works +you can do so with :doc:`/cmd/show`. Note that the `show` command only works with a single module, so you may need to call it with :yoscrypt:`show fifo`. :ref:`show_intro` section in :doc:`/getting_started/scripting_intro` has more on how to use `show`. @@ -283,7 +283,7 @@ Flattening At this stage of a synthesis flow there are a few other commands we could run. In `synth_ice40` we get these: -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: flatten: :end-before: coarse: @@ -355,7 +355,7 @@ Part 1 In the iCE40 flow, we start with the following commands: -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: coarse: :end-before: wreduce @@ -371,7 +371,7 @@ wasting time on something we know is impossible. Next up is :yoscrypt:`opt -nodffe -nosdff` performing a set of simple optimizations on the design. This command also ensures that only a specific subset of FF types are included, in preparation for the next command: -:cmd:title:`fsm`. Both `opt` and `fsm` are macro commands which are explored in +:doc:`/cmd/fsm`. Both `opt` and `fsm` are macro commands which are explored in more detail in :doc:`/using_yosys/synthesis/opt` and :doc:`/using_yosys/synthesis/fsm` respectively. @@ -403,7 +403,7 @@ Part 2 The next group of commands performs a series of optimizations: -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-at: wreduce :end-before: t:$mul @@ -411,7 +411,7 @@ The next group of commands performs a series of optimizations: :caption: ``coarse`` section (part 2) :name: synth_coarse2 -First up is :cmd:title:`wreduce`. If we run this we get the following: +First up is :doc:`/cmd/wreduce`. If we run this we get the following: .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -432,7 +432,7 @@ the schematic and see the output of that cell has now changed. ``rdata`` output after `wreduce` -The next two (new) commands are :cmd:title:`peepopt` and :cmd:title:`share`. +The next two (new) commands are :doc:`/cmd/peepopt` and :doc:`/cmd/share`. Neither of these affect our design, and they're explored in more detail in :doc:`/using_yosys/synthesis/opt`, so let's skip over them. :yoscrypt:`techmap -map +/cmp2lut.v -D LUT_WIDTH=4` optimizes certain comparison operators by @@ -440,7 +440,7 @@ converting them to LUTs instead. The usage of `techmap` is explored more in :doc:`/using_yosys/synthesis/techmap_synth`. Our next command to run is -:cmd:title:`memory_dff`. +:doc:`/cmd/memory_dff`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -475,7 +475,7 @@ will only be performed if called with the ``-dsp`` flag: :yoscrypt:`synth_ice40 -dsp`. While our example has nothing that could be mapped to DSPs we can still take a quick look at the commands here and describe what they do. -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-at: t:$mul :end-before: alumacc @@ -514,7 +514,7 @@ Part 4 That brings us to the fourth and final part for the iCE40 synthesis flow: -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-at: alumacc :end-before: map_ram: @@ -543,7 +543,7 @@ Once these cells have been inserted, the call to `opt` can combine cells which are now identical but may have been missed due to e.g. the difference between `$add` and `$sub`. -The other new command in this part is :cmd:title:`memory`. `memory` is another +The other new command in this part is :doc:`/cmd/memory`. `memory` is another macro command which we examine in more detail in :doc:`/using_yosys/synthesis/memory`. For this document, let us focus just on the step most relevant to our example: `memory_collect`. Up until this point, @@ -594,7 +594,7 @@ Memory blocks Mapping to hard memory blocks uses a combination of `memory_libmap` and `techmap`. -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: map_ram: :end-before: map_ffram: @@ -636,7 +636,7 @@ into flip flops (the ``logic fallback``) with `memory_map`. .. |techlibs/ice40/brams_map.v| replace:: :file:`techlibs/ice40/brams_map.v` .. _techlibs/ice40/brams_map.v: https://github.com/YosysHQ/yosys/tree/main/techlibs/ice40/brams_map.v -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: map_ffram: :end-before: map_gates: @@ -671,7 +671,7 @@ an explosion in cells as multi-bit `$mux` and `$adffe` are replaced with single-bit `$_MUX_` and `$_DFFE_PP0P_` cells, while the `$alu` is replaced with primitive `$_OR_` and `$_NOT_` gates and a `$lut` cell. -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: map_gates: :end-before: map_ffs: @@ -700,7 +700,7 @@ mapped to hardware into gate-level primitives. This includes optimizing `$_MUX_` cells where one of the inputs is a constant ``1'0``, replacing it instead with an `$_AND_` cell. -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: map_ffs: :end-before: map_luts: @@ -725,7 +725,7 @@ LUTs `abc`. For more on what these do, and what the difference between these two commands are, refer to :doc:`/using_yosys/synthesis/abc`. -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: map_luts: :end-before: map_cells: @@ -742,7 +742,7 @@ commands are, refer to :doc:`/using_yosys/synthesis/abc`. Finally we use `techmap` to map the generic `$lut` cells to iCE40 ``SB_LUT4`` cells. -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: map_cells: :end-before: check: @@ -784,18 +784,19 @@ Final steps The next section of the iCE40 synth flow performs some sanity checking and final tidy up: -.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys +.. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt :start-after: check: + :end-before: blif: :dedent: :name: check :caption: ``check`` section The new commands here are: -- :cmd:title:`autoname`, -- :cmd:title:`stat`, and -- :cmd:title:`blackbox`. +- :doc:`/cmd/autoname`, +- :doc:`/cmd/stat`, and +- :doc:`/cmd/blackbox`. The output from `stat` is useful for checking resource utilization; providing a list of cells used in the design and the number of each, as well as the number @@ -834,9 +835,9 @@ Synthesis output The iCE40 synthesis flow has the following output modes available: -- `write_blif`, -- `write_edif`, and -- `write_json`. +- :doc:`/cmd/write_blif`, +- :doc:`/cmd/write_edif`, and +- :doc:`/cmd/write_json`. As an example, if we called :yoscrypt:`synth_ice40 -top fifo -json fifo.json`, our synthesized ``fifo`` design will be output as :file:`fifo.json`. We can @@ -847,4 +848,4 @@ is beyond the scope of this documentation. .. _nextpnr: https://github.com/YosysHQ/nextpnr -.. seealso:: :cmd:title:`synth_ice40` +.. seealso:: :doc:`/cmd/synth_ice40` diff --git a/docs/source/getting_started/scripting_intro.rst b/docs/source/getting_started/scripting_intro.rst index b9e2d395a..01954c661 100644 --- a/docs/source/getting_started/scripting_intro.rst +++ b/docs/source/getting_started/scripting_intro.rst @@ -122,7 +122,7 @@ module. Detailed documentation of the select framework can be found under :doc:`/using_yosys/more_scripting/selections` or in the command reference at -:cmd:title:`select`. +:doc:`/cmd/select`. .. _show_intro: @@ -219,7 +219,7 @@ those used in options, must be a single expression instead. .. _GraphViz color docs: https://graphviz.org/doc/info/colors For all of the options available to `show`, check the command reference at -:cmd:title:`show`. +:doc:`/cmd/show`. .. seealso:: :ref:`interactive_show` on the :doc:`/using_yosys/more_scripting/interactive_investigation` page. diff --git a/docs/source/index.rst b/docs/source/index.rst index 403100093..61dc114ef 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -5,7 +5,7 @@ Yosys Open SYnthesis Suite Yosys is an open source framework for RTL synthesis. To learn more about Yosys, see :doc:`/introduction`. For a quick guide on how to get started using Yosys, check out :doc:`/getting_started/index`. For the complete list of commands -available, go to :ref:`cmd_ref`. +available, go to :ref:`commandindex`. .. todo:: look into command ref improvements diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst index 5da8c04d4..e9c9bc9ac 100644 --- a/docs/source/using_yosys/more_scripting/interactive_investigation.rst +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -323,10 +323,10 @@ tools). design into an equivalent design that is easier to analyse. - Commands such as `eval` and `sat` can be used to investigate the behavior of the circuit. -- :cmd:title:`show`. -- :cmd:title:`dump`. -- :cmd:title:`add` and :cmd:title:`delete` can be used to modify and reorganize - a design dynamically. +- :doc:`/cmd/show`. +- :doc:`/cmd/dump`. +- :doc:`/cmd/add` and :doc:`/cmd/delete` can be used to modify and reorganize a + design dynamically. The code used is included in the Yosys code base under |code_examples/scrambler|_. @@ -358,7 +358,7 @@ reorganizing a module in Yosys and checking the resulting circuit. .. figure:: /_images/code_examples/scrambler/scrambler_p02.* :class: width-helper invert-helper -Analyzing the resulting circuit with :cmd:title:`eval`: +Analyzing the resulting circuit with :doc:`/cmd/eval`: .. todo:: replace inline code diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst index d1e3e1cc0..bbc55a36b 100644 --- a/docs/source/using_yosys/more_scripting/load_design.rst +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -1,11 +1,9 @@ Loading a design ~~~~~~~~~~~~~~~~ -.. TODO:: fill out this page better - keyword: Frontends -- :doc:`/cmd/index_frontends` +- :doc:`/cmd/read_verilog` .. todo:: include ``read_verilog < None: - self.name = name - self.title = title - self.content = [YosysCmdContentListing(**c) for c in content] - self.group = group - self.source_file = source_file - self.source_line = source_line - self.source_func = source_func - self.experimental_flag = experimental_flag - self.internal_flag = internal_flag - -class YosysCmdGroupDocumenter(Documenter): - objtype = 'cmdgroup' - priority = 10 - object: tuple[str, list[str]] - lib_key = 'groups' - - option_spec = Documenter.option_spec.copy() - option_spec.update({ - 'caption': autodoc.annotation_option, - 'members': autodoc.members_option, - 'source': autodoc.bool_option, - 'linenos': autodoc.bool_option, - }) - - __cmd_lib: dict[str, list[str] | dict[str]] | None = None - @property - def cmd_lib(self) -> dict[str, list[str] | dict[str]]: - if not self.__cmd_lib: - self.__cmd_lib = {} - cmds_obj: dict[str, dict[str, dict[str]]] - try: - with open(self.config.cmds_json, "r") as f: - cmds_obj = json.loads(f.read()) - except FileNotFoundError: - logger.warning( - f"unable to find cmd lib at {self.config.cmds_json}", - type = 'cmdref', - subtype = 'cmd_lib' - ) - cmds_obj = {} - for (name, obj) in cmds_obj.get(self.lib_key, {}).items(): - self.__cmd_lib[name] = obj - return self.__cmd_lib - - @classmethod - def can_document_member( - cls, - member: Any, - membername: str, - isattr: bool, - parent: Any - ) -> bool: - return False - - def parse_name(self) -> bool: - if not self.options.caption: - self.content_indent = '' - self.fullname = self.modname = self.name - return True - - def import_object(self, raiseerror: bool = False) -> bool: - # get cmd - try: - self.object = (self.modname, self.cmd_lib[self.modname]) - except KeyError: - if raiseerror: - raise - return False - - self.real_modname = self.modname - return True - - def get_sourcename(self) -> str: - return self.env.doc2path(self.env.docname) - - def format_name(self) -> str: - return self.options.caption or '' - - def format_signature(self, **kwargs: Any) -> str: - return self.modname - - def add_directive_header(self, sig: str) -> None: - pass - - def add_content(self, more_content: Any | None) -> None: - pass - - def filter_members( - self, - members: list[tuple[str, Any]], - want_all: bool - ) -> list[tuple[str, Any, bool]]: - return [(x[0], x[1], False) for x in members] - - def get_object_members( - self, - want_all: bool - ) -> tuple[bool, list[tuple[str, Any]]]: - ret: list[tuple[str, str]] = [] - - if want_all: - for member in self.object[1]: - ret.append((member, self.modname)) - else: - memberlist = self.options.members or [] - for name in memberlist: - if name in self.object: - ret.append((name, self.modname)) - else: - logger.warning(('unknown module mentioned in :members: option: ' - f'group {self.modname}, module {name}'), - type='cmdref') - - return False, ret - - def document_members(self, all_members: bool = False) -> None: - want_all = (all_members or - self.options.inherited_members or - self.options.members is autodoc.ALL) - # find out which members are documentable - members_check_module, members = self.get_object_members(want_all) - - # document non-skipped members - memberdocumenters: list[tuple[Documenter, bool]] = [] - for (mname, member, isattr) in self.filter_members(members, want_all): - classes = [cls for cls in self.documenters.values() - if cls.can_document_member(member, mname, isattr, self)] - if not classes: - # don't know how to document this member - continue - # prefer the documenter with the highest priority - classes.sort(key=lambda cls: cls.priority) - # give explicitly separated module name, so that members - # of inner classes can be documented - full_mname = self.format_signature() + '::' + mname - documenter = classes[-1](self.directive, full_mname, self.indent) - memberdocumenters.append((documenter, isattr)) - - member_order = self.options.member_order or self.config.autodoc_member_order - memberdocumenters = self.sort_members(memberdocumenters, member_order) - - for documenter, isattr in memberdocumenters: - documenter.generate( - all_members=True, real_modname=self.real_modname, - check_module=members_check_module and not isattr) - - def generate( - self, - more_content: Any | None = None, - real_modname: str | None = None, - check_module: bool = False, - all_members: bool = False - ) -> None: - if not self.parse_name(): - # need a cmd lib to import from - logger.warning( - f"don't know which cmd lib to import for autodocumenting {self.name}", - type = 'cmdref' - ) - return - - sourcename = self.get_sourcename() - - imported_object = self.import_object(); - if self.lib_key == 'groups' and self.name == 'unknown': - if imported_object: - logger.warning(f"Found commands assigned to group {self.name}: {[x[0] for x in self.object]}", type='cmdref') - else: - return - elif not imported_object: - log_msg = f"unable to load {self.name} with {type(self)}" - if self.lib_key == 'groups': - logger.info(log_msg, type = 'cmdref') - self.add_line(f'.. warning:: No commands found for group {self.name!r}', sourcename) - self.add_line('', sourcename) - self.add_line(' Documentation may have been built without ``source_location`` support.', sourcename) - self.add_line(' Try check :doc:`/cmd/index_other`.', sourcename) - else: - logger.warning(log_msg, type = 'cmdref') - return - - # check __module__ of object (for members not given explicitly) - # if check_module: - # if not self.check_module(): - # return - - self.add_line('', sourcename) - - # format the object's signature, if any - try: - sig = self.format_signature() - except Exception as exc: - logger.warning(('error while formatting signature for %s: %s'), - self.fullname, exc, type='cmdref') - return - - # generate the directive header and options, if applicable - self.add_directive_header(sig) - self.add_line('', sourcename) - - # e.g. the module directive doesn't have content - self.indent += self.content_indent - - # add all content (from docstrings, attribute docs etc.) - self.add_content(more_content) - - # document members, if possible - self.document_members(all_members) - -class YosysCmdDocumenter(YosysCmdGroupDocumenter): - objtype = 'cmd' - priority = 15 - object: YosysCmd - lib_key = 'cmds' - - @classmethod - def can_document_member( - cls, - member: Any, - membername: str, - isattr: bool, - parent: Any - ) -> bool: - if membername.startswith('$'): - return False - return isinstance(parent, YosysCmdGroupDocumenter) - - def parse_name(self) -> bool: - try: - matched = cmd_ext_sig_re.match(self.name) - group, modname, thing, attribute = matched.groups() - except AttributeError: - logger.warning(('invalid signature for auto%s (%r)') % (self.objtype, self.name), - type='cmdref') - return False - - self.modname = modname - self.groupname = group or '' - self.attribute = attribute or '' - self.fullname = ((self.modname) + (thing or '')) - - return True - - def import_object(self, raiseerror: bool = False) -> bool: - if super().import_object(raiseerror): - self.object = YosysCmd(self.modname, **self.object[1]) - return True - return False - - def get_sourcename(self) -> str: - try: - return self.object.source_file - except AttributeError: - return super().get_sourcename() - - def format_name(self) -> str: - return self.object.name - - def format_signature(self, **kwargs: Any) -> str: - return self.fullname + self.attribute - - def add_directive_header(self, sig: str) -> None: - domain = getattr(self, 'domain', self.objtype) - directive = getattr(self, 'directivetype', 'def') - source_name = self.object.source_file - source_line = self.object.source_line - - title = f'{self.object.name} - {self.object.title}' - self.add_line(title, source_name, source_line) - self.add_line('#' * len(title), source_name, source_line) - - # cmd definition - self.add_line(f'.. {domain}:{directive}:: {sig}', source_name, source_line) - if self.object.title: - self.add_line(f' :title: {self.object.title}', source_name, source_line) - - if self.options.noindex: - self.add_line(' :noindex:', source_name) - - def add_content(self, more_content: Any | None) -> None: - # set sourcename and add content from attribute documentation - domain = getattr(self, 'domain', self.objtype) - source_name = self.object.source_file - source_line = self.object.source_line - - if self.object.experimental_flag: - self.add_line(f'.. warning:: This command is experimental', source_name, source_line) - self.add_line('\n', source_name) - - if self.object.internal_flag: - self.add_line(f'.. warning:: This command is intended for internal developer use only', source_name, source_line) - self.add_line('\n', source_name) - - def render(content_list: YosysCmdContentListing, indent: int=0): - content_source = content_list.source_file or source_name - indent_str = ' '*indent - if content_list.type == 'usage': - if content_list.body: - self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::{content_list.body}', content_source) - else: - self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::', content_source) - self.add_line(f'{indent_str} :noindex:', source_name) - self.add_line('', source_name) - elif content_list.type == 'option': - self.add_line(f'{indent_str}:{content_list.type} {content_list.body}:', content_source) - elif content_list.type == 'text': - self.add_line(f'{indent_str}{content_list.body}', content_source) - self.add_line('', source_name) - elif content_list.type == 'code': - language_str = content_list.options.get('language', '') - self.add_line(f'{indent_str}.. code-block:: {language_str}', source_name) - self.add_line('', source_name) - for body_line in content_list.body.splitlines(): - self.add_line(f'{indent_str} {body_line}', content_source) - self.add_line('', source_name) - else: - logger.warning(f"unknown content type '{content_list.type}'") - for content in content_list.content: - render(content, indent+1) - - for content in self.object.content: - render(content) - - if self.get_sourcename() != 'unknown': - self.add_line('\n', source_name) - self.add_line(f'.. note:: Help text automatically generated from :file:`{source_name}:{source_line}`', source_name) - - # add additional content (e.g. from document), if present - if more_content: - for line, src in zip(more_content.data, more_content.items): - self.add_line(line, src[0], src[1]) - - def get_object_members( - self, - want_all: bool - ) -> tuple[bool, list[tuple[str, Any]]]: - - return False, [] - -class YosysCmdRstDocumenter(YosysCmdDocumenter): - objtype = 'cmd_rst' - priority = 0 - - @classmethod - def can_document_member(cls, *args) -> bool: - return False - - def add_directive_header(self, sig): - source_name = self.object.source_file - cmd = self.object.name - self.add_line(f'.. code-block:: rst', source_name) - self.add_line(f' :caption: Generated rst for ``.. autocmd:: {cmd}``', source_name) - - def add_content(self, more_content): - source_name = self.object.source_file - cmd = self.object.name - self.domain = 'cmd' - super().add_directive_header(cmd) - self.add_line('', source_name) - self.indent += self.content_indent - super().add_content(more_content) - -def setup(app: Sphinx) -> dict[str, Any]: - app.add_config_value('cmds_json', False, 'html', [Path, PosixPath, WindowsPath]) - app.setup_extension('sphinx.ext.autodoc') - app.add_autodocumenter(YosysCmdGroupDocumenter) - app.add_autodocumenter(YosysCmdDocumenter) - app.add_autodocumenter(YosysCmdRstDocumenter) - return { - 'version': '2', - 'parallel_read_safe': True, - } diff --git a/docs/util/custom_directives.py b/docs/util/cmdref.py similarity index 81% rename from docs/util/custom_directives.py rename to docs/util/cmdref.py index b90584aa7..a31b08e0d 100644 --- a/docs/util/custom_directives.py +++ b/docs/util/cmdref.py @@ -4,21 +4,20 @@ from __future__ import annotations import re from typing import cast -import warnings from docutils import nodes -from docutils.nodes import Node, Element, Text +from docutils.nodes import Node, Element, system_message from docutils.parsers.rst import directives from docutils.parsers.rst.states import Inliner from sphinx.application import Sphinx from sphinx.domains import Domain, Index from sphinx.domains.std import StandardDomain from sphinx.environment import BuildEnvironment -from sphinx.roles import XRefRole, SphinxRole +from sphinx.roles import XRefRole from sphinx.directives import ObjectDescription from sphinx.directives.code import container_wrapper from sphinx.util.nodes import make_refnode -from sphinx.util.docfields import Field, GroupedField +from sphinx.util.docfields import Field from sphinx import addnodes class TocNode(ObjectDescription): @@ -32,7 +31,7 @@ class TocNode(ObjectDescription): signode['ids'].append(idx) def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature) -> tuple[str, ...]: - if 'tocname' not in sig_node: + if 'fullname' not in sig_node: return () modname = sig_node.get('module') @@ -58,56 +57,16 @@ class TocNode(ObjectDescription): return '.'.join(parents + [name]) return '' -class NodeWithOptions(TocNode): - """A custom node with options.""" - - doc_field_types = [ - GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')), - ] - - def transform_content(self, contentnode: addnodes.desc_content) -> None: - """hack `:option -thing: desc` into a proper option list with yoscrypt highlighting""" - newchildren = [] - for node in contentnode: - newnode = node - if isinstance(node, nodes.field_list): - newnode = nodes.option_list() - for field in node: - is_option = False - option_list_item = nodes.option_list_item() - for child in field: - if isinstance(child, nodes.field_name): - option_group = nodes.option_group() - option_list_item += option_group - option = nodes.option() - option_group += option - name, text = child.rawsource.split(' ', 1) - is_option = name == 'option' - literal = nodes.literal(text=text) - literal['classes'] += ['code', 'highlight', 'yoscrypt'] - literal['language'] = 'yoscrypt' - option += literal - if not is_option: warnings.warn(f'unexpected option \'{name}\' in {field.source}') - elif isinstance(child, nodes.field_body): - description = nodes.description() - description += child.children - option_list_item += description - if is_option: - newnode += option_list_item - newchildren.append(newnode) - contentnode.children = newchildren - -class CommandNode(NodeWithOptions): +class CommandNode(TocNode): """A custom node that describes a command.""" name = 'cmd' required_arguments = 1 - option_spec = NodeWithOptions.option_spec.copy() - option_spec.update({ + option_spec = { 'title': directives.unchanged, 'tags': directives.unchanged - }) + } def handle_signature(self, sig, signode: addnodes.desc_signature): signode['fullname'] = sig @@ -134,46 +93,6 @@ class CommandNode(NodeWithOptions): idx, 0)) -class CommandUsageNode(NodeWithOptions): - """A custom node that describes command usages""" - - name = 'cmdusage' - - option_spec = NodeWithOptions.option_spec - option_spec.update({ - 'usage': directives.unchanged, - }) - - def handle_signature(self, sig: str, signode: addnodes.desc_signature): - parts = sig.split('::') - if len(parts) > 2: parts.pop(0) - use = parts[-1] - signode['fullname'] = '::'.join(parts) - usage = self.options.get('usage', use) - if usage: - signode['tocname'] = usage - signode += addnodes.desc_name(text=usage) - return signode['fullname'] - - def add_target_and_index( - self, - name: str, - sig: str, - signode: addnodes.desc_signature - ) -> None: - idx = ".".join(name.split("::")) - signode['ids'].append(idx) - if 'noindex' not in self.options: - tocname: str = signode.get('tocname', name) - objs = self.env.domaindata[self.domain]['objects'] - # (name, sig, typ, docname, anchor, prio) - objs.append((name, - tocname, - type(self).name, - self.env.docname, - idx, - 1)) - class PropNode(TocNode): name = 'prop' fieldname = 'props' @@ -474,7 +393,7 @@ class TagIndex(Index): lis.append(( dispname, 0, docname, anchor, - '', '', '' + docname, '', typ )) ret = [(k, v) for k, v in sorted(content.items())] @@ -513,19 +432,18 @@ class CommandIndex(Index): Qualifier and description are not rendered e.g. in LaTeX output. """ - content: dict[str, list[tuple]] = {} + content = {} items = ((name, dispname, typ, docname, anchor) for name, dispname, typ, docname, anchor, prio in self.domain.get_objects() if typ == self.name) items = sorted(items, key=lambda item: item[0]) for name, dispname, typ, docname, anchor in items: - title = self.domain.data['obj2title'].get(name) lis = content.setdefault(self.shortname, []) lis.append(( dispname, 0, docname, anchor, - '', '', title + '', '', typ )) ret = [(k, v) for k, v in sorted(content.items())] @@ -589,27 +507,16 @@ class PropIndex(TagIndex): return (ret, True) -class TitleRefRole(XRefRole): - """XRefRole used which has the cmd title as the displayed text.""" - pass - -class OptionRole(SphinxRole): - def run(self) -> tuple[list[Node], list]: - return self.inliner.interpreted(self.rawtext, self.text, 'yoscrypt', self.lineno) - class CommandDomain(Domain): name = 'cmd' label = 'Yosys commands' roles = { - 'ref': XRefRole(), - 'title': TitleRefRole(), - 'option': OptionRole(), + 'ref': XRefRole() } directives = { 'def': CommandNode, - 'usage': CommandUsageNode, } indices = { @@ -635,7 +542,7 @@ class CommandDomain(Domain): def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): - + match = [(docname, anchor, name) for name, sig, typ, docname, anchor, prio in self.get_objects() if sig == target] @@ -645,17 +552,9 @@ class CommandDomain(Domain): targ = match[0][1] qual_name = match[0][2] title = self.data['obj2title'].get(qual_name, targ) - - if typ == 'title': - # caller wants the title in the content of the node - cmd = contnode.astext() - contnode = Text(f'{cmd} - {title}') - return make_refnode(builder, fromdocname, todocname, - targ, contnode) - else: - # cmd title as hover text - return make_refnode(builder, fromdocname, todocname, - targ, contnode, title) + + return make_refnode(builder,fromdocname,todocname, + targ, contnode, title) else: print(f"Missing ref for {target} in {fromdocname} ") return None @@ -693,18 +592,10 @@ class CellDomain(CommandDomain): def autoref(name, rawtext: str, text: str, lineno, inliner: Inliner, options=None, content=None): - words = text.split(' ') - if len(words) == 2 and words[0] == "help": - IsLinkable = True - thing = words[1] - else: - IsLinkable = len(words) == 1 and words[0][0] != '-' - thing = words[0] - if IsLinkable: - role = 'cell:ref' if thing[0] == '$' else 'cmd:ref' - text = f'{text} <{thing}>' - else: - role = 'yoscrypt' + role = 'cell:ref' if text[0] == '$' else 'cmd:ref' + if text.startswith("help ") and text.count(' ') == 1: + _, cmd = text.split(' ', 1) + text = f'{text} <{cmd}>' return inliner.interpreted(rawtext, text, role, lineno) def setup(app: Sphinx): @@ -731,7 +622,4 @@ def setup(app: Sphinx): app.add_role('autoref', autoref) - return { - 'version': '0.3', - 'parallel_read_safe': False, - } + return {'version': '0.2'} diff --git a/kernel/json.h b/kernel/json.h index e8905c8e1..c9aa0e045 100644 --- a/kernel/json.h +++ b/kernel/json.h @@ -90,10 +90,10 @@ public: template void array(const T &&values) { - begin_array(); + begin_object(); for (auto &item : values) value(item); - end_array(); + end_object(); } }; diff --git a/kernel/log_help.cc b/kernel/log_help.cc deleted file mode 100644 index 45228b024..000000000 --- a/kernel/log_help.cc +++ /dev/null @@ -1,151 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2025 Krystine Dawn - * - * 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/log_help.h" - -USING_YOSYS_NAMESPACE - -Json ContentListing::to_json() { - Json::object object; - object["type"] = type; - if (body.length()) object["body"] = body; - if (strcmp(source_file, "unknown") != 0) object["source_file"] = source_file; - if (source_line != 0) object["source_line"] = source_line; - object["options"] = Json(options); - Json::array content_array; - for (auto child : _content) content_array.push_back(child->to_json()); - object["content"] = content_array; - return object; -} - -void ContentListing::usage(const string &text, - const source_location location) -{ - log_assert(type.compare("root") == 0); - add_content("usage", text, location); -} - -void ContentListing::option(const string &text, const string &description, - const source_location location) -{ - auto option = open_option(text); - if (description.length()) - option->add_content("text", description, location); -} - -void ContentListing::codeblock(const string &code, const string &language, - const source_location location) -{ - add_content("code", code, location); - back()->set_option("language", language); -} - -void ContentListing::paragraph(const string &text, - const source_location location) -{ - add_content("text", text, location); -} - -ContentListing* ContentListing::open_usage(const string &text, - const source_location location) -{ - usage(text, location); - return back(); -} - -ContentListing* ContentListing::open_option(const string &text, - const source_location location) -{ - log_assert(type.compare("root") == 0 || type.compare("usage") == 0); - auto option = new ContentListing("option", text, location); - add_content(option); - return option; -} - -#define MAX_LINE_LEN 80 -void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) { - if (pass_str.empty()) - return; - std::istringstream iss(pass_str); - if (leading_newline) - log("\n"); - for (std::string line; std::getline(iss, line);) { - log("%s", indent_str.c_str()); - auto curr_len = indent_str.length(); - std::istringstream lss(line); - for (std::string word; std::getline(lss, word, ' ');) { - while (word[0] == '`' && word.back() == '`') - word = word.substr(1, word.length()-2); - if (curr_len + word.length() >= MAX_LINE_LEN-1) { - curr_len = 0; - log("\n%s", indent_str.c_str()); - } - if (word.length()) { - log("%s ", word.c_str()); - curr_len += word.length() + 1; - } - } - log("\n"); - } -} -void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) { - std::string indent_str(indent*4, ' '); - log_pass_str(pass_str, indent_str, leading_newline); -} - -PrettyHelp *current_help = nullptr; - -PrettyHelp::PrettyHelp() -{ - _prior = current_help; - _root_listing = ContentListing("root", ""); - - current_help = this; -} - -PrettyHelp::~PrettyHelp() -{ - current_help = _prior; -} - -PrettyHelp *PrettyHelp::get_current() -{ - if (current_help == nullptr) - new PrettyHelp(); - return current_help; -} - -void PrettyHelp::log_help() -{ - for (auto content : _root_listing.get_content()) { - if (content->type.compare("usage") == 0) { - log_pass_str(content->body, 1, true); - log("\n"); - } else if (content->type.compare("option") == 0) { - log_pass_str(content->body, 1); - for (auto text : content->get_content()) { - log_pass_str(text->body, 2); - log("\n"); - } - } else { - log_pass_str(content->body, 0); - log("\n"); - } - } -} diff --git a/kernel/log_help.h b/kernel/log_help.h deleted file mode 100644 index 0a40fc531..000000000 --- a/kernel/log_help.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2025 Krystine Dawn - * - * 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 LOG_HELP_H -#define LOG_HELP_H - -#include "kernel/yosys_common.h" -#include "kernel/json.h" - -YOSYS_NAMESPACE_BEGIN - -class ContentListing { - vector _content; -public: - string type; - string body; - const char* source_file; - int source_line; - std::map options; - - ContentListing( - string type = "root", string body = "", - const char* source_file = "unknown", int source_line = 0 - ) : type(type), body(body), source_file(source_file), source_line(source_line) { - _content = {}; - options = {}; - } - - ContentListing(string type, string body, source_location location) : - ContentListing(type, body, location.file_name(), location.line()) { } - - void add_content(ContentListing *new_content) { - _content.push_back(new_content); - } - - void add_content(string type, string body, source_location location) { - auto new_content = new ContentListing(type, body, location); - add_content(new_content); - } - - bool has_content() { return _content.size() != 0; } - const vector get_content() { - const vector content = _content; - return content; - } - ContentListing* back() { - return _content.back(); - } - - void set_option(string key, string val = "") { - options[key] = val; - } - - void usage( - const string &text, - const source_location location = source_location::current() - ); - void option( - const string &text, - const string &description = "", - const source_location location = source_location::current() - ); - void codeblock( - const string &code, - const string &language = "none", - const source_location location = source_location::current() - ); - void paragraph( - const string &text, - const source_location location = source_location::current() - ); - - ContentListing* open_usage( - const string &text, - const source_location location = source_location::current() - ); - ContentListing* open_option( - const string &text, - const source_location location = source_location::current() - ); - - Json to_json(); -}; - -class PrettyHelp -{ -public: - string group = "unknown"; - -private: - PrettyHelp *_prior; - ContentListing _root_listing; - -public: - PrettyHelp(); - ~PrettyHelp(); - - static PrettyHelp *get_current(); - - bool has_content() { return _root_listing.has_content(); } - const vector get_content() { - return _root_listing.get_content(); - } - ContentListing* get_root() { - return &_root_listing; - } - - void set_group(const string g) { group = g; } - bool has_group() { return group.compare("unknown") != 0; } - - void log_help(); -}; - -YOSYS_NAMESPACE_END - -#endif diff --git a/kernel/register.cc b/kernel/register.cc index ea2a2624f..af1823b5b 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -21,7 +21,6 @@ #include "kernel/satgen.h" #include "kernel/json.h" #include "kernel/gzip.h" -#include "kernel/log_help.h" #include #include @@ -42,8 +41,7 @@ std::map backend_register; std::vector Frontend::next_args; -Pass::Pass(std::string name, std::string short_help, source_location location) : - pass_name(name), short_help(short_help), location(location) +Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help) { next_queued_pass = first_queued_pass; first_queued_pass = this; @@ -118,19 +116,9 @@ void Pass::post_execute(Pass::pre_post_exec_state_t state) void Pass::help() { - auto prettyHelp = PrettyHelp(); - if (formatted_help()) { - prettyHelp.log_help(); - } else { - log("\n"); - log("No help message for command `%s'.\n", pass_name.c_str()); - log("\n"); - } -} - -bool Pass::formatted_help() -{ - return false; + log("\n"); + log("No help message for command `%s'.\n", pass_name.c_str()); + log("\n"); } void Pass::clear_flags() @@ -393,8 +381,8 @@ void ScriptPass::help_script() script(); } -Frontend::Frontend(std::string name, std::string short_help, source_location location) : - Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help, location), +Frontend::Frontend(std::string name, std::string short_help) : + Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help), frontend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name) { } @@ -539,8 +527,8 @@ void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string } } -Backend::Backend(std::string name, std::string short_help, source_location location) : - Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help, location), +Backend::Backend(std::string name, std::string short_help) : + Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help), backend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name) { } @@ -693,23 +681,6 @@ static string get_cell_name(string name) { return is_code_getter(name) ? name.substr(0, name.length()-1) : name; } -static void log_warning_flags(Pass *pass) { - bool has_warnings = false; - const string name = pass->pass_name; - if (pass->experimental_flag) { - if (!has_warnings) log("\n"); - has_warnings = true; - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", name.c_str()); - } - if (pass->internal_flag) { - if (!has_warnings) log("\n"); - has_warnings = true; - log("WARNING: THE '%s' COMMAND IS INTENDED FOR INTERNAL DEVELOPER USE ONLY.\n", name.c_str()); - } - if (has_warnings) - log("\n"); -} - static struct CellHelpMessages { dict cell_help; CellHelpMessages() { @@ -735,211 +706,155 @@ struct HelpPass : public Pass { log(" help + .... print verilog code for given cell type\n"); log("\n"); } - bool dump_cmds_json(PrettyJson &json) { - // init json - json.begin_object(); - json.entry("version", "Yosys command reference"); - json.entry("generator", yosys_version_str); + void write_cmd_rst(std::string cmd, std::string title, std::string text) + { + FILE *f = fopen(stringf("docs/source/cmd/%s.rst", cmd.c_str()).c_str(), "wt"); + // make header + size_t char_len = cmd.length() + 3 + title.length(); + std::string title_line = "\n"; + title_line.insert(0, char_len, '='); + fprintf(f, "%s", title_line.c_str()); + fprintf(f, "%s - %s\n", cmd.c_str(), title.c_str()); + fprintf(f, "%s\n", title_line.c_str()); - bool raise_error = false; - std::map> groups; - - json.name("cmds"); json.begin_object(); - // iterate over commands - for (auto &it : pass_register) { - auto name = it.first; - auto pass = it.second; - auto title = pass->short_help; - - auto cmd_help = PrettyHelp(); - auto has_pretty_help = pass->formatted_help(); - - if (!has_pretty_help) { - enum PassUsageState { - PUState_none, - PUState_signature, - PUState_options, - PUState_optionbody, - }; - - source_location null_source; - string current_buffer = ""; - auto root_listing = cmd_help.get_root(); - auto current_listing = root_listing; - - // dump command help - std::ostringstream buf; - log_streams.push_back(&buf); - pass->help(); - log_streams.pop_back(); - std::stringstream ss; - ss << buf.str(); - - // parse command help - size_t def_strip_count = 0; - auto current_state = PUState_none; - auto catch_verific = false; - auto blank_lines = 2; - for (string line; std::getline(ss, line, '\n');) { - // find position of first non space character - std::size_t first_pos = line.find_first_not_of(" \t"); - std::size_t last_pos = line.find_last_not_of(" \t"); - if (first_pos == std::string::npos) { - switch (current_state) - { - case PUState_signature: - root_listing->usage(current_buffer, null_source); - current_listing = root_listing; - current_state = PUState_none; - current_buffer = ""; - break; - case PUState_none: - case PUState_optionbody: - blank_lines += 1; - break; - default: - break; - } - // skip empty lines - continue; - } - - // strip leading and trailing whitespace - std::string stripped_line = line.substr(first_pos, last_pos - first_pos +1); - bool IsDefinition = stripped_line[0] == '-'; - IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; - bool IsDedent = def_strip_count && first_pos < def_strip_count; - bool IsIndent = def_strip_count < first_pos; - - // line looks like a signature - bool IsSignature = stripped_line.find(name) == 0 && (stripped_line.length() == name.length() || stripped_line.at(name.size()) == ' '); - - if (IsSignature && first_pos <= 4 && (blank_lines >= 2 || current_state == PUState_signature)) { - if (current_state == PUState_options || current_state == PUState_optionbody) { - current_listing->codeblock(current_buffer, "none", null_source); - current_buffer = ""; - } else if (current_state == PUState_signature) { - root_listing->usage(current_buffer, null_source); - current_buffer = ""; - } else if (current_state == PUState_none && !current_buffer.empty()) { - current_listing->codeblock(current_buffer, "none", null_source); - current_buffer = ""; - } - current_listing = root_listing; - current_state = PUState_signature; - def_strip_count = first_pos; - catch_verific = false; - } else if (IsDedent) { - def_strip_count = first_pos; - if (current_state == PUState_optionbody) { - if (!current_buffer.empty()) { - current_listing->codeblock(current_buffer, "none", null_source); - current_buffer = ""; - } - if (IsIndent) { - current_state = PUState_options; - current_listing = root_listing->back(); - } else { - current_state = PUState_none; - current_listing = root_listing; - } - } else { - current_state = PUState_none; - } - } - - if (IsDefinition && !catch_verific && current_state != PUState_signature) { - if (!current_buffer.empty()) { - current_listing->codeblock(current_buffer, "none", null_source); - current_buffer = ""; - } - current_state = PUState_options; - current_listing = root_listing->open_option(stripped_line, null_source); - def_strip_count = first_pos; - } else { - if (current_state == PUState_options) { - current_state = PUState_optionbody; - } - if (current_buffer.empty()) - current_buffer = stripped_line; - else if (current_state == PUState_signature && IsIndent) - current_buffer += stripped_line; - else if (current_state == PUState_none) { - current_buffer += (blank_lines > 0 ? "\n\n" : "\n") + line; - } else - current_buffer += (blank_lines > 0 ? "\n\n" : "\n") + stripped_line; - if (stripped_line.compare("Command file parser supports following commands in file:") == 0) - catch_verific = true; - } - blank_lines = 0; - } - - if (!current_buffer.empty()) { - if (current_buffer.size() > 64 && current_buffer.substr(0, 64).compare("The following commands are executed by this synthesis command:\n\n") == 0) { - current_listing->paragraph(current_buffer.substr(0, 62), null_source); - current_listing->codeblock(current_buffer.substr(64), "yoscrypt", null_source); - } else - current_listing->codeblock(current_buffer, "none", null_source); - current_buffer = ""; - } + // render html + fprintf(f, ".. cmd:def:: %s\n", cmd.c_str()); + fprintf(f, " :title: %s\n\n", title.c_str()); + fprintf(f, " .. only:: html\n\n"); + std::stringstream ss; + std::string textcp = text; + ss << text; + bool IsUsage = true; + int blank_count = 0; + size_t def_strip_count = 0; + bool WasDefinition = false; + for (std::string line; std::getline(ss, line, '\n');) { + // find position of first non space character + std::size_t first_pos = line.find_first_not_of(" \t"); + std::size_t last_pos = line.find_last_not_of(" \t"); + if (first_pos == std::string::npos) { + // skip formatting empty lines + if (!WasDefinition) + fputc('\n', f); + blank_count += 1; + continue; } - // attempt auto group - if (!cmd_help.has_group()) { - string source_file = pass->location.file_name(); - bool has_source = source_file.compare("unknown") != 0; - if (pass->internal_flag) - cmd_help.group = "internal"; - else if (source_file.find("backends/") == 0 || (!has_source && name.find("read_") == 0)) - cmd_help.group = "backends"; - else if (source_file.find("frontends/") == 0 || (!has_source && name.find("write_") == 0)) - cmd_help.group = "frontends"; - else if (has_source) { - auto last_slash = source_file.find_last_of('/'); - if (last_slash != string::npos) { - auto parent_path = source_file.substr(0, last_slash); - cmd_help.group = parent_path; - } + // strip leading and trailing whitespace + std::string stripped_line = line.substr(first_pos, last_pos - first_pos +1); + bool IsDefinition = stripped_line[0] == '-'; + IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; + bool IsDedent = def_strip_count && first_pos <= def_strip_count; + bool IsIndent = first_pos == 2 || first_pos == 4; + if (cmd.compare(0, 7, "verific") == 0) + // verific.cc has strange and different formatting from the rest + IsIndent = false; + + // another usage block + bool NewUsage = stripped_line.find(cmd) == 0; + + if (IsUsage) { + if (stripped_line.compare(0, 4, "See ") == 0) { + // description refers to another function + fprintf(f, "\n %s\n", stripped_line.c_str()); + } else { + // usage should be the first line of help output + fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; } - // implicit !has_source - else if (name.find("equiv") == 0) - cmd_help.group = "passes/equiv"; - else if (name.find("fsm") == 0) - cmd_help.group = "passes/fsm"; - else if (name.find("memory") == 0) - cmd_help.group = "passes/memory"; - else if (name.find("opt") == 0) - cmd_help.group = "passes/opt"; - else if (name.find("proc") == 0) - cmd_help.group = "passes/proc"; + IsUsage = false; + } else if (IsIndent && NewUsage && (blank_count >= 2 || WasDefinition)) { + // another usage block + fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; + def_strip_count = 0; + } else if (IsIndent && IsDefinition && (blank_count || WasDefinition)) { + // format definition term + fprintf(f, "\n\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; + def_strip_count = first_pos; + } else { + if (IsDedent) { + fprintf(f, "\n\n ::\n"); + def_strip_count = first_pos; + } else if (WasDefinition) { + fprintf(f, "::\n"); + WasDefinition = false; + } + fprintf(f, "\n %s", line.substr(def_strip_count, std::string::npos).c_str()); } - if (groups.count(cmd_help.group) == 0) { - groups[cmd_help.group] = vector(); - } - groups[cmd_help.group].push_back(name); - - // write to json - json.name(name.c_str()); json.begin_object(); - json.entry("title", title); - json.name("content"); json.begin_array(); - for (auto content : cmd_help.get_content()) - json.value(content->to_json()); - json.end_array(); - json.entry("group", cmd_help.group); - json.entry("source_file", pass->location.file_name()); - json.entry("source_line", pass->location.line()); - json.entry("source_func", pass->location.function_name()); - json.entry("experimental_flag", pass->experimental_flag); - json.entry("internal_flag", pass->internal_flag); - json.end_object(); + blank_count = 0; } - json.end_object(); + fputc('\n', f); - json.entry("groups", groups); + // render latex + fprintf(f, ".. only:: latex\n\n"); + fprintf(f, " ::\n\n"); + std::stringstream ss2; + ss2 << textcp; + for (std::string line; std::getline(ss2, line, '\n');) { + fprintf(f, " %s\n", line.c_str()); + } + fclose(f); + } + void write_cell_rst(Yosys::SimHelper cell, Yosys::CellType ct) + { + // open + FILE *f = fopen(stringf("docs/source/cell/%s.rst", cell.filesafe_name().c_str()).c_str(), "wt"); - json.end_object(); - return raise_error; + // make header + string title_line; + if (cell.title.length()) + title_line = stringf("%s - %s", cell.name.c_str(), cell.title.c_str()); + else title_line = cell.name; + string underline = "\n"; + underline.insert(0, title_line.length(), '='); + fprintf(f, "%s\n", title_line.c_str()); + fprintf(f, "%s\n", underline.c_str()); + + // help text, with cell def for links + fprintf(f, ".. cell:def:: %s\n", cell.name.c_str()); + if (cell.title.length()) + fprintf(f, " :title: %s\n\n", cell.title.c_str()); + else + fprintf(f, " :title: %s\n\n", cell.name.c_str()); + std::stringstream ss; + ss << cell.desc; + for (std::string line; std::getline(ss, line, '\n');) { + fprintf(f, " %s\n", line.c_str()); + } + + // properties + fprintf(f, "\nProperties"); + fprintf(f, "\n----------\n\n"); + dict prop_dict = { + {"is_evaluable", ct.is_evaluable}, + {"is_combinatorial", ct.is_combinatorial}, + {"is_synthesizable", ct.is_synthesizable}, + }; + for (auto &it : prop_dict) { + fprintf(f, "- %s: %s\n", it.first.c_str(), it.second ? "true" : "false"); + } + + // source code + fprintf(f, "\nSimulation model (Verilog)"); + fprintf(f, "\n--------------------------\n\n"); + fprintf(f, ".. code-block:: verilog\n"); + fprintf(f, " :caption: %s\n\n", cell.source.c_str()); + std::stringstream ss2; + ss2 << cell.code; + for (std::string line; std::getline(ss2, line, '\n');) { + fprintf(f, " %s\n", line.c_str()); + } + + // footer + fprintf(f, "\n.. note::\n\n"); + fprintf(f, " This page was auto-generated from the output of\n"); + fprintf(f, " ``help %s``.\n", cell.name.c_str()); + + // close + fclose(f); } bool dump_cells_json(PrettyJson &json) { // init json @@ -1045,7 +960,11 @@ struct HelpPass : public Pass { log("="); log("\n"); it.second->help(); - log_warning_flags(it.second); + if (it.second->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); + log("\n"); + } } } else if (args[1] == "-cells") { @@ -1059,9 +978,44 @@ struct HelpPass : public Pass { log("\n"); return; } + // this option is undocumented as it is for internal use only + else if (args[1] == "-write-rst-command-reference-manual") { + for (auto &it : pass_register) { + std::ostringstream buf; + log_streams.push_back(&buf); + it.second->help(); + if (it.second->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); + log("\n"); + } + log_streams.pop_back(); + write_cmd_rst(it.first, it.second->short_help, buf.str()); + } + } + // this option is also undocumented as it is for internal use only + else if (args[1] == "-write-rst-cells-manual") { + bool raise_error = false; + for (auto &it : yosys_celltypes.cell_types) { + auto name = it.first.str(); + if (cell_help_messages.contains(name)) { + write_cell_rst(cell_help_messages.get(name), it.second); + } else { + log("ERROR: Missing cell help for cell '%s'.\n", name.c_str()); + raise_error |= true; + } + } + if (raise_error) { + log_error("One or more cells defined in celltypes.h are missing help documentation.\n"); + } + } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); - log_warning_flags(pass_register.at(args[1])); + if (pass_register.at(args[1])->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", args[1].c_str()); + log("\n"); + } } else if (cell_help_messages.contains(args[1])) { auto help_cell = cell_help_messages.get(args[1]); @@ -1090,17 +1044,7 @@ struct HelpPass : public Pass { log("No such command or cell type: %s\n", args[1].c_str()); return; } else if (args.size() == 3) { - // this option is undocumented as it is for internal use only - if (args[1] == "-dump-cmds-json") { - PrettyJson json; - if (!json.write_to_file(args[2])) - log_error("Can't open file `%s' for writing: %s\n", args[2].c_str(), strerror(errno)); - if (dump_cmds_json(json)) { - log_abort(); - } - } - // this option is undocumented as it is for internal use only - else if (args[1] == "-dump-cells-json") { + if (args[1] == "-dump-cells-json") { PrettyJson json; if (!json.write_to_file(args[2])) log_error("Can't open file `%s' for writing: %s\n", args[2].c_str(), strerror(errno)); @@ -1108,8 +1052,6 @@ struct HelpPass : public Pass { log_error("One or more cells defined in celltypes.h are missing help documentation.\n"); } } - else - log("Unknown help command: `%s %s'\n", args[1].c_str(), args[2].c_str()); return; } diff --git a/kernel/register.h b/kernel/register.h index e8c017c1d..f4e2127e1 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -23,62 +23,27 @@ #include "kernel/yosys_common.h" #include "kernel/yosys.h" -#include -#if __cpp_lib_source_location == 201907L - #include - using std::source_location; -#elif defined(__has_include) -# if __has_include() - #include - using std::experimental::source_location; -# else - #define SOURCE_FALLBACK -# endif -#else - #define SOURCE_FALLBACK -#endif - -#ifdef SOURCE_FALLBACK -struct source_location { // dummy placeholder - int line() const { return 0; } - int column() const { return 0; } - const char* file_name() const { return "unknown"; } - const char* function_name() const { return "unknown"; } - static const source_location current(...) { return source_location(); } -}; -#endif - YOSYS_NAMESPACE_BEGIN struct Pass { std::string pass_name, short_help; - source_location location; - Pass(std::string name, std::string short_help = "** document me **", - source_location location = source_location::current()); + Pass(std::string name, std::string short_help = "** document me **"); // Prefer overriding 'Pass::on_shutdown()' if possible virtual ~Pass(); - // Makes calls to log() to generate help message virtual void help(); - // Uses PrettyHelp::get_current() to produce a more portable formatted help message - virtual bool formatted_help(); virtual void clear_flags(); virtual void execute(std::vector args, RTLIL::Design *design) = 0; int call_counter; int64_t runtime_ns; bool experimental_flag = false; - bool internal_flag = false; void experimental() { experimental_flag = true; } - void internal() { - internal_flag = true; - } - struct pre_post_exec_state_t { Pass *parent_pass; int64_t begin_ns; @@ -116,8 +81,7 @@ struct ScriptPass : Pass RTLIL::Design *active_design; std::string active_run_from, active_run_to; - ScriptPass(std::string name, std::string short_help = "** document me **", source_location location = source_location::current()) : - Pass(name, short_help, location) { } + ScriptPass(std::string name, std::string short_help = "** document me **") : Pass(name, short_help) { } virtual void script() = 0; @@ -135,8 +99,7 @@ struct Frontend : Pass static std::string last_here_document; std::string frontend_name; - Frontend(std::string name, std::string short_help = "** document me **", - source_location location = source_location::current()); + Frontend(std::string name, std::string short_help = "** document me **"); void run_register() override; ~Frontend() override; void execute(std::vector args, RTLIL::Design *design) override final; @@ -152,8 +115,7 @@ struct Frontend : Pass struct Backend : Pass { std::string backend_name; - Backend(std::string name, std::string short_help = "** document me **", - source_location location = source_location::current()); + Backend(std::string name, std::string short_help = "** document me **"); void run_register() override; ~Backend() override; void execute(std::vector args, RTLIL::Design *design) override final; diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 8bbcb8da0..83fe781a0 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -22,18 +22,12 @@ #include "kernel/celledges.h" #include "kernel/celltypes.h" #include "kernel/utils.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct CheckPass : public Pass { CheckPass() : Pass("check", "check for obvious problems in the design") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index ccda023c0..572ed2153 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -19,7 +19,6 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -71,62 +70,62 @@ static bool is_triggered_check_cell(RTLIL::Cell * cell) } struct ChformalPass : public Pass { - ChformalPass() : Pass("chformal", "change formal constraints of the design") {} - - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - - auto content_root = help->get_root(); - - content_root->usage("chformal [types] [mode] [options] [selection]"); - content_root->paragraph( - "Make changes to the formal constraints of the design. The [types] options " - "the type of constraint to operate on. If none of the following options are " - "given, the command will operate on all constraint types:" - ); - - content_root->option("-assert", "`$assert` cells, representing ``assert(...)`` constraints"); - content_root->option("-assume", "`$assume` cells, representing ``assume(...)`` constraints"); - content_root->option("-live", "`$live` cells, representing ``assert(s_eventually ...)``"); - content_root->option("-fair", "`$fair` cells, representing ``assume(s_eventually ...)``"); - content_root->option("-cover", "`$cover` cells, representing ``cover()`` statements"); - content_root->paragraph( - "Additionally chformal will operate on `$check` cells corresponding to the " - "selected constraint types." - ); - - content_root->paragraph("Exactly one of the following modes must be specified:"); - - content_root->option("-remove", "remove the cells and thus constraints from the design"); - content_root->option("-early", - "bypass FFs that only delay the activation of a constraint. When inputs " - "of the bypassed FFs do not remain stable between clock edges, this may " - "result in unexpected behavior." - ); - content_root->option("-delay ", "delay activation of the constraint by clock cycles"); - content_root->option("-skip ", "ignore activation of the constraint in the first clock cycles"); - auto cover_option = content_root->open_option("-coverenable"); - cover_option->paragraph( - "add cover statements for the enable signals of the constraints" - ); + ChformalPass() : Pass("chformal", "change formal constraints of the design") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" chformal [types] [mode] [options] [selection]\n"); + log("\n"); + log("Make changes to the formal constraints of the design. The [types] options\n"); + log("the type of constraint to operate on. If none of the following options are\n"); + log("given, the command will operate on all constraint types:\n"); + log("\n"); + log(" -assert $assert cells, representing assert(...) constraints\n"); + log(" -assume $assume cells, representing assume(...) constraints\n"); + log(" -live $live cells, representing assert(s_eventually ...)\n"); + log(" -fair $fair cells, representing assume(s_eventually ...)\n"); + log(" -cover $cover cells, representing cover() statements\n"); + log("\n"); + log(" Additionally chformal will operate on $check cells corresponding to the\n"); + log(" selected constraint types.\n"); + log("\n"); + log("Exactly one of the following modes must be specified:\n"); + log("\n"); + log(" -remove\n"); + log(" remove the cells and thus constraints from the design\n"); + log("\n"); + log(" -early\n"); + log(" bypass FFs that only delay the activation of a constraint. When inputs\n"); + log(" of the bypassed FFs do not remain stable between clock edges, this may\n"); + log(" result in unexpected behavior.\n"); + log("\n"); + log(" -delay \n"); + log(" delay activation of the constraint by clock cycles\n"); + log("\n"); + log(" -skip \n"); + log(" ignore activation of the constraint in the first clock cycles\n"); + log("\n"); + log(" -coverenable\n"); + log(" add cover statements for the enable signals of the constraints\n"); + log("\n"); #ifdef YOSYS_ENABLE_VERIFIC - cover_option->paragraph( - "Note: For the Verific frontend it is currently not guaranteed that a " - "reachable SVA statement corresponds to an active enable signal." - ); + log(" Note: For the Verific frontend it is currently not guaranteed that a\n"); + log(" reachable SVA statement corresponds to an active enable signal.\n"); + log("\n"); #endif - content_root->option("-assert2assume"); - content_root->option("-assert2cover"); - content_root->option("-assume2assert"); - content_root->option("-live2fair"); - content_root->option("-fair2live", "change the roles of cells as indicated. these options can be combined"); - content_root->option("-lower", - "convert each $check cell into an $assert, $assume, $live, $fair or " - "$cover cell. If the $check cell contains a message, also produce a " - "$print cell." - ); - return true; + log(" -assert2assume\n"); + log(" -assert2cover\n"); + log(" -assume2assert\n"); + log(" -live2fair\n"); + log(" -fair2live\n"); + log(" change the roles of cells as indicated. these options can be combined\n"); + log("\n"); + log(" -lower\n"); + log(" convert each $check cell into an $assert, $assume, $live, $fair or\n"); + log(" $cover cell. If the $check cell contains a message, also produce a\n"); + log(" $print cell.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 47354f1d5..1db3e2ca0 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include #ifndef _WIN32 @@ -27,18 +26,15 @@ # include #endif +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct CoverPass : public Pass { - CoverPass() : Pass("cover", "print code coverage counters") { - internal(); - } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } + CoverPass() : Pass("cover", "print code coverage counters") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/dft_tag.cc b/passes/cmds/dft_tag.cc index 347c8efa4..b6afe6f89 100644 --- a/passes/cmds/dft_tag.cc +++ b/passes/cmds/dft_tag.cc @@ -22,7 +22,6 @@ #include "kernel/modtools.h" #include "kernel/sigtools.h" #include "kernel/yosys.h" -#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -953,11 +952,6 @@ struct DftTagWorker { struct DftTagPass : public Pass { DftTagPass() : Pass("dft_tag", "create tagging logic for data flow tracking") {} - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc index 933bd457f..5b53f50cc 100644 --- a/passes/cmds/edgetypes.cc +++ b/passes/cmds/edgetypes.cc @@ -19,18 +19,12 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct EdgetypePass : public Pass { EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/example_dt.cc b/passes/cmds/example_dt.cc index b10f50502..2870e062b 100644 --- a/passes/cmds/example_dt.cc +++ b/passes/cmds/example_dt.cc @@ -21,17 +21,15 @@ struct ExampleWorker struct ExampleDtPass : public Pass { - ExampleDtPass() : Pass("example_dt", "drivertools example") { - internal(); - } + ExampleDtPass() : Pass("example_dt", "drivertools example") {} - void help() override + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log("TODO: add help message\n"); log("\n"); - } + } void execute(std::vector args, RTLIL::Design *design) override diff --git a/passes/cmds/exec.cc b/passes/cmds/exec.cc index 486fa1c2b..e9cc34b30 100644 --- a/passes/cmds/exec.cc +++ b/passes/cmds/exec.cc @@ -17,8 +17,8 @@ * */ -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/register.h" +#include "kernel/log.h" #include #if defined(_WIN32) @@ -38,11 +38,6 @@ PRIVATE_NAMESPACE_BEGIN struct ExecPass : public Pass { ExecPass() : Pass("exec", "execute commands in the operating system shell") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/future.cc b/passes/cmds/future.cc index 5dcf46bcf..b03613c9b 100644 --- a/passes/cmds/future.cc +++ b/passes/cmds/future.cc @@ -24,7 +24,6 @@ #include "kernel/sigtools.h" #include "kernel/utils.h" #include "kernel/yosys.h" -#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -111,11 +110,6 @@ struct FutureWorker { struct FuturePass : public Pass { FuturePass() : Pass("future", "resolve future sampled value functions") {} - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/glift.cc b/passes/cmds/glift.cc index 0c321eba6..6a7d070d7 100644 --- a/passes/cmds/glift.cc +++ b/passes/cmds/glift.cc @@ -17,9 +17,10 @@ * */ +#include "kernel/register.h" +#include "kernel/rtlil.h" #include "kernel/utils.h" -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/log.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -424,12 +425,6 @@ public: struct GliftPass : public Pass { GliftPass() : Pass("glift", "create GLIFT models and optimization problems") {} - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } - void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/internal_stats.cc b/passes/cmds/internal_stats.cc index 00456b8f9..28822f237 100644 --- a/passes/cmds/internal_stats.cc +++ b/passes/cmds/internal_stats.cc @@ -18,6 +18,8 @@ */ #include +#include +#include #include "kernel/yosys.h" #include "kernel/celltypes.h" @@ -69,10 +71,7 @@ std::optional current_mem_bytes() { } struct InternalStatsPass : public Pass { - InternalStatsPass() : Pass("internal_stats", "print internal statistics") { - experimental(); - internal(); - } + InternalStatsPass() : Pass("internal_stats", "print internal statistics") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/logcmd.cc b/passes/cmds/logcmd.cc index 0238627d1..3b82ac48c 100644 --- a/passes/cmds/logcmd.cc +++ b/passes/cmds/logcmd.cc @@ -18,19 +18,15 @@ * */ -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct LogPass : public Pass { LogPass() : Pass("log", "print text and log files") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/logger.cc b/passes/cmds/logger.cc index 276810201..241a8799f 100644 --- a/passes/cmds/logger.cc +++ b/passes/cmds/logger.cc @@ -17,19 +17,14 @@ * */ -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/register.h" +#include "kernel/log.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct LoggerPass : public Pass { LoggerPass() : Pass("logger", "set logger properties") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/ltp.cc b/passes/cmds/ltp.cc index b3134b110..22bdaab44 100644 --- a/passes/cmds/ltp.cc +++ b/passes/cmds/ltp.cc @@ -20,7 +20,6 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -142,11 +141,6 @@ struct LtpWorker struct LtpPass : public Pass { LtpPass() : Pass("ltp", "print longest topological path") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index a653844b7..4ad7c165b 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #ifdef YOSYS_ENABLE_PLUGINS # include @@ -123,11 +122,6 @@ void load_plugin(std::string, std::vector) struct PluginPass : public Pass { PluginPass() : Pass("plugin", "load and list loaded plugins") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/portarcs.cc b/passes/cmds/portarcs.cc index 97682efbb..a6ed75de3 100644 --- a/passes/cmds/portarcs.cc +++ b/passes/cmds/portarcs.cc @@ -22,7 +22,6 @@ #include "kernel/rtlil.h" #include "kernel/utils.h" #include "kernel/celltypes.h" -#include "kernel/log_help.h" PRIVATE_NAMESPACE_BEGIN USING_YOSYS_NAMESPACE @@ -39,11 +38,6 @@ static RTLIL::SigBit canonical_bit(RTLIL::SigBit bit) struct PortarcsPass : Pass { PortarcsPass() : Pass("portarcs", "derive port arcs for propagation delay") {} - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { diff --git a/passes/cmds/portlist.cc b/passes/cmds/portlist.cc index f78d9d3b6..03048422d 100644 --- a/passes/cmds/portlist.cc +++ b/passes/cmds/portlist.cc @@ -19,18 +19,12 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct PortlistPass : public Pass { PortlistPass() : Pass("portlist", "list (top-level) ports") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/printattrs.cc b/passes/cmds/printattrs.cc index c8b0a1d1f..2a5034c13 100644 --- a/passes/cmds/printattrs.cc +++ b/passes/cmds/printattrs.cc @@ -18,18 +18,12 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct PrintAttrsPass : public Pass { PrintAttrsPass() : Pass("printattrs", "print attributes of selected objects") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index e55e63828..0f988e57a 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -21,10 +21,12 @@ // Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010 // http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm -#include "kernel/yosys.h" +#include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/log_help.h" +#include "kernel/log.h" +#include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -250,11 +252,6 @@ struct SccWorker struct SccPass : public Pass { SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/scratchpad.cc b/passes/cmds/scratchpad.cc index 4a63f2f60..aecc4c17d 100644 --- a/passes/cmds/scratchpad.cc +++ b/passes/cmds/scratchpad.cc @@ -18,19 +18,15 @@ * */ -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct ScratchpadPass : public Pass { ScratchpadPass() : Pass("scratchpad", "get/set values in the scratchpad") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 901f923f8..1d75091af 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -20,7 +20,8 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/log_help.h" +#include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -1084,11 +1085,6 @@ PRIVATE_NAMESPACE_BEGIN struct SelectPass : public Pass { SelectPass() : Pass("select", "modify and view the list of selected objects") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -1668,11 +1664,6 @@ struct SelectPass : public Pass { struct CdPass : public Pass { CdPass() : Pass("cd", "a shortcut for 'select -module '") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -1785,11 +1776,6 @@ static void log_matches(const char *title, Module *module, const T &list) struct LsPass : public Pass { LsPass() : Pass("ls", "list modules or objects in modules") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/setenv.cc b/passes/cmds/setenv.cc index 850d7c961..27f2eea28 100644 --- a/passes/cmds/setenv.cc +++ b/passes/cmds/setenv.cc @@ -17,18 +17,15 @@ * */ -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SetenvPass : public Pass { SetenvPass() : Pass("setenv", "set an environment variable") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 4eb6569e6..8a1bd58c4 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -17,9 +17,10 @@ * */ -#include "kernel/yosys.h" +#include "kernel/register.h" #include "kernel/celltypes.h" -#include "kernel/log_help.h" +#include "kernel/log.h" +#include #ifndef _WIN32 # include @@ -657,11 +658,6 @@ struct ShowWorker struct ShowPass : public Pass { ShowPass() : Pass("show", "generate schematics using graphviz") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/sta.cc b/passes/cmds/sta.cc index 5dfac1575..4ad0e96be 100644 --- a/passes/cmds/sta.cc +++ b/passes/cmds/sta.cc @@ -21,7 +21,6 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/timinginfo.h" -#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -276,11 +275,6 @@ struct StaWorker struct StaPass : public Pass { StaPass() : Pass("sta", "perform static timing analysis") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index af7023bdd..6b93621f1 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -25,7 +25,6 @@ #include "kernel/cost.h" #include "kernel/gzip.h" #include "libs/json11/json11.hpp" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -368,11 +367,6 @@ void read_liberty_cellarea(dict &cell_area, string libert struct StatPass : public Pass { StatPass() : Pass("stat", "print some statistics") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index fbd42e311..853f1bad3 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -18,19 +18,15 @@ * */ -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct TeePass : public Pass { TeePass() : Pass("tee", "redirect command output to file") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc index 537b6793d..1620c0bca 100644 --- a/passes/cmds/torder.cc +++ b/passes/cmds/torder.cc @@ -21,18 +21,12 @@ #include "kernel/celltypes.h" #include "kernel/sigtools.h" #include "kernel/utils.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct TorderPass : public Pass { TorderPass() : Pass("torder", "print cells in topological order") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc index 39ed8e60e..400542776 100644 --- a/passes/cmds/trace.cc +++ b/passes/cmds/trace.cc @@ -19,7 +19,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -61,11 +60,6 @@ struct TraceMonitor : public RTLIL::Monitor struct TracePass : public Pass { TracePass() : Pass("trace", "redirect command output to file") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -102,11 +96,6 @@ struct TracePass : public Pass { struct DebugPass : public Pass { DebugPass() : Pass("debug", "run command with debug log messages enabled") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/viz.cc b/passes/cmds/viz.cc index 4c73b4d71..131e799ab 100644 --- a/passes/cmds/viz.cc +++ b/passes/cmds/viz.cc @@ -19,7 +19,6 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" -#include "kernel/log_help.h" #ifndef _WIN32 # include @@ -818,11 +817,6 @@ struct VizWorker struct VizPass : public Pass { VizPass() : Pass("viz", "visualize data flow graph") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index a22fdaf2a..ea9b3f556 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -19,18 +19,12 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct WriteFileFrontend : public Frontend { WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/status"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/xprop.cc b/passes/cmds/xprop.cc index d2d0c4d8e..dc5befc27 100644 --- a/passes/cmds/xprop.cc +++ b/passes/cmds/xprop.cc @@ -24,7 +24,6 @@ #include "kernel/sigtools.h" #include "kernel/utils.h" #include "kernel/yosys.h" -#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -1101,11 +1100,6 @@ struct XpropWorker struct XpropPass : public Pass { XpropPass() : Pass("xprop", "formal x propagation") {} - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index ff1a2fcc5..3cd1b6180 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -1,5 +1,4 @@ -OBJS += passes/hierarchy/flatten.o OBJS += passes/hierarchy/hierarchy.o OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc index 0ac391790..9fa9f5c2d 100644 --- a/passes/opt/rmports.cc +++ b/passes/opt/rmports.cc @@ -17,20 +17,17 @@ * */ +#include "kernel/register.h" #include "kernel/sigtools.h" -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/log.h" +#include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct RmportsPassPass : public Pass { RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("techlibs/greenpak4"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index 892500850..fc41848f7 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -117,9 +117,7 @@ void opt_eqpmux(test_pmgen_pm &pm) } struct TestPmgenPass : public Pass { - TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { - internal(); - } + TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc index 7b3357f82..991977ab9 100644 --- a/passes/sat/assertpmux.cc +++ b/passes/sat/assertpmux.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/utils.h" @@ -183,11 +182,6 @@ struct AssertpmuxWorker struct AssertpmuxPass : public Pass { AssertpmuxPass() : Pass("assertpmux", "adds asserts for parallel muxes") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index e86a78d81..93c7e96c8 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -28,11 +27,6 @@ PRIVATE_NAMESPACE_BEGIN struct Async2syncPass : public Pass { Async2syncPass() : Pass("async2sync", "convert async FF inputs to sync circuits") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index 6b94cbe19..348bab727 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -34,11 +33,6 @@ struct SampledSig { struct Clk2fflogicPass : public Pass { Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 485e44fd6..dcd399a57 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -26,11 +25,6 @@ PRIVATE_NAMESPACE_BEGIN struct CutpointPass : public Pass { CutpointPass() : Pass("cutpoint", "adds formal cut points to the design") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 1e975db0f..9afe00d5c 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -17,10 +17,11 @@ * */ -#include "kernel/yosys.h" +#include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/log_help.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -216,11 +217,6 @@ RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width struct ExposePass : public Pass { ExposePass() : Pass("expose", "convert internal signals to module ports") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/cmds"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc index 575b2f40d..220cf5c52 100644 --- a/passes/sat/fmcombine.cc +++ b/passes/sat/fmcombine.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" @@ -238,11 +237,6 @@ struct FmcombineWorker struct FmcombinePass : public Pass { FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/fminit.cc b/passes/sat/fminit.cc index 547082164..5f4ec0068 100644 --- a/passes/sat/fminit.cc +++ b/passes/sat/fminit.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -26,11 +25,6 @@ PRIVATE_NAMESPACE_BEGIN struct FminitPass : public Pass { FminitPass() : Pass("fminit", "set init values/sequences for formal") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/formalff.cc b/passes/sat/formalff.cc index 286bf2976..1d87fcc3b 100644 --- a/passes/sat/formalff.cc +++ b/passes/sat/formalff.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -487,11 +486,6 @@ void HierarchyWorker::propagate() struct FormalFfPass : public Pass { FormalFfPass() : Pass("formalff", "prepare FFs for formal") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index 6063c5ab9..52e80f667 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -17,12 +17,17 @@ * */ +#include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/consteval.h" #include "kernel/sigtools.h" +#include "kernel/log.h" #include "kernel/satgen.h" -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include +#include +#include +#include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -756,11 +761,6 @@ struct FreduceWorker struct FreducePass : public Pass { FreducePass() : Pass("freduce", "perform functional reduction") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 9bcf25547..8f27c4c6f 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -17,8 +17,9 @@ * */ -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -394,11 +395,6 @@ void create_miter_assert(struct Pass *that, std::vector args, RTLIL struct MiterPass : public Pass { MiterPass() : Pass("miter", "automatically create a miter circuit") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc index 43373ce0e..3075ef3f0 100644 --- a/passes/sat/mutate.cc +++ b/passes/sat/mutate.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -729,11 +728,6 @@ void mutate_cnot(Design *design, const mutate_opts_t &opts, bool one) struct MutatePass : public Pass { MutatePass() : Pass("mutate", "generate or apply design mutations") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc index 7a7a31806..db6836ea1 100644 --- a/passes/sat/qbfsat.cc +++ b/passes/sat/qbfsat.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/consteval.h" #include "qbfsat.h" @@ -505,11 +504,6 @@ QbfSolveOptions parse_args(const std::vector &args) { struct QbfSatPass : public Pass { QbfSatPass() : Pass("qbfsat", "solve a 2QBF-SAT problem in the circuit") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/recover_names.cc b/passes/sat/recover_names.cc index 7ed8b1304..cddd8771c 100644 --- a/passes/sat/recover_names.cc +++ b/passes/sat/recover_names.cc @@ -23,7 +23,6 @@ #include "kernel/celltypes.h" #include "kernel/utils.h" #include "kernel/satgen.h" -#include "kernel/log_help.h" #include #include @@ -691,11 +690,6 @@ struct RecoverNamesWorker { struct RecoverNamesPass : public Pass { RecoverNamesPass() : Pass("recover_names", "Execute a lossy mapping command and recover original netnames") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("passes/opt"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 2f20880cb..0c2143fc9 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -21,12 +21,17 @@ // Niklas Een and Niklas Sörensson (2003) // http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.8161 +#include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/consteval.h" #include "kernel/sigtools.h" +#include "kernel/log.h" #include "kernel/satgen.h" -#include "kernel/yosys.h" -#include "kernel/log_help.h" +#include +#include +#include +#include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -897,11 +902,6 @@ void print_qed() struct SatPass : public Pass { SatPass() : Pass("sat", "solve a SAT problem in the circuit") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/supercover.cc b/passes/sat/supercover.cc index f1b3ad09c..38dbd3cf9 100644 --- a/passes/sat/supercover.cc +++ b/passes/sat/supercover.cc @@ -18,7 +18,6 @@ */ #include "kernel/yosys.h" -#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -26,11 +25,6 @@ PRIVATE_NAMESPACE_BEGIN struct SupercoverPass : public Pass { SupercoverPass() : Pass("supercover", "add hi/lo cover cells for each wire bit") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/synthprop.cc b/passes/sat/synthprop.cc index 4703e4ad7..5553abec2 100644 --- a/passes/sat/synthprop.cc +++ b/passes/sat/synthprop.cc @@ -18,9 +18,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ - #include "kernel/yosys.h" -#include "kernel/log_help.h" YOSYS_NAMESPACE_BEGIN @@ -181,13 +179,7 @@ void SynthPropWorker::run() struct SyntProperties : public Pass { SyntProperties() : Pass("synthprop", "synthesize SVA properties") { } - bool formatted_help() override { - auto *help = PrettyHelp::get_current(); - help->set_group("formal"); - return false; - } - - void help() override + virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -216,7 +208,7 @@ struct SyntProperties : public Pass { log("\n"); } - void execute(std::vector args, RTLIL::Design* design) override + virtual void execute(std::vector args, RTLIL::Design* design) { log_header(design, "Executing SYNTHPROP pass.\n"); SynthPropWorker worker(design); diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 91b3b563a..c9fe98a74 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -1,4 +1,5 @@ +OBJS += passes/techmap/flatten.o OBJS += passes/techmap/techmap.o OBJS += passes/techmap/simplemap.o OBJS += passes/techmap/dfflibmap.o diff --git a/passes/hierarchy/flatten.cc b/passes/techmap/flatten.cc similarity index 100% rename from passes/hierarchy/flatten.cc rename to passes/techmap/flatten.cc diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc index ed54ce164..9e7adaab1 100644 --- a/passes/tests/test_abcloop.cc +++ b/passes/tests/test_abcloop.cc @@ -243,9 +243,7 @@ static void test_abcloop() } struct TestAbcloopPass : public Pass { - TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { - internal(); - } + TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index 306e760ee..404d1e48d 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -326,9 +326,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int s } struct TestAutotbBackend : public Backend { - TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { - internal(); - } + TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index b6385766c..a34eafc2f 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -705,9 +705,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: } struct TestCellPass : public Pass { - TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { - internal(); - } + TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From bf9aaac0fe7220ff14290c8925980a6a97cfcc7f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 00:26:42 +0000 Subject: [PATCH 108/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d7b38c80f..083805fbc 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+109 +YOSYS_VER := 0.55+112 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 0314db80eafcb81be0d04c5b92986bf748b020bf Mon Sep 17 00:00:00 2001 From: Mike Inouye Date: Fri, 25 Jul 2025 19:15:01 +0000 Subject: [PATCH 109/181] Correctly reset Verific flags to Yosys defaults after -import and warn this has occurred. Co-authored-by: Chris Pearce Signed-off-by: Mike Inouye --- frontends/verific/verific.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 241b2db30..cf8fad8b0 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -2978,6 +2978,9 @@ std::set import_tops(const char* work, std::map args, RTLIL::Design *design) override { - static bool set_verific_global_flags = true; if (check_noverific_env()) log_cmd_error("This version of Yosys is built without Verific support.\n" @@ -4127,6 +4130,9 @@ struct VerificPass : public Pass { if ((unsigned long)verific_sva_fsm_limit >= sizeof(1ull)*8) log_cmd_error("-L %d: limit too large; maximum allowed value is %zu.\n", verific_sva_fsm_limit, sizeof(1ull)*8-1); + if (already_imported) + log_warning("Note that all Verific flags were reset to defaults after last -import.\n"); + std::set top_mod_names; if (mode_all) @@ -4204,6 +4210,7 @@ struct VerificPass : public Pass { } verific_cleanup(); + already_imported = true; goto check_error; } From 902cbda4f92f3bfae19bd32b647a9cee6c6bf991 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:49 +1200 Subject: [PATCH 110/181] bugpoint: Document -wires flag --- passes/cmds/bugpoint.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index da06acedf..e1f3544b4 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -89,6 +89,10 @@ struct BugpointPass : public Pass { log(" -updates\n"); log(" try to remove process updates from syncs.\n"); log("\n"); + log(" -wires\n"); + log(" try to remove wires. wires with a (* bugpoint_keep *) attribute will be\n"); + log(" skipped.\n"); + log("\n"); log(" -runner \"\"\n"); log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n"); log("\n"); From 8a732080e02e765f97c1a1dd8f4f3eff619f3a77 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:50 +1200 Subject: [PATCH 111/181] bugpoint: Add -expect-return Allows checking return value from crashing design. Makes it possible to only accept designs that crash with e.g. SEGFAULT. Based on `exec -expect-return`. --- passes/cmds/bugpoint.cc | 62 +++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index e1f3544b4..24c778fdb 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -50,6 +50,10 @@ struct BugpointPass : public Pass { log(" -grep \"\"\n"); log(" only consider crashes that place this string in the log file.\n"); log("\n"); + log(" -expect-return \n"); + log(" only consider crashes that return the specified value. e.g. SEGFAULT\n"); + log(" returns a value of 139.\n"); + log("\n"); log(" -fast\n"); log(" run `proc_clean; clean -purge` after each minimization step. converges\n"); log(" faster, but produces larger testcases, and may fail to produce any\n"); @@ -98,7 +102,7 @@ struct BugpointPass : public Pass { log("\n"); } - bool run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg) + int run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg) { design->sort(); @@ -107,7 +111,16 @@ struct BugpointPass : public Pass { f.close(); string yosys_cmdline = stringf("%s %s -qq -L bugpoint-case.log %s bugpoint-case.il", runner.c_str(), yosys_cmd.c_str(), yosys_arg.c_str()); - return run_command(yosys_cmdline) == 0; + auto status = run_command(yosys_cmdline); + // we're not processing lines, which means we're getting raw system() returns + if(WIFEXITED(status)) + return WEXITSTATUS(status); + else if(WIFSIGNALED(status)) + return WTERMSIG(status); + else if(WIFSTOPPED(status)) + return WSTOPSIG(status); + else + return 0; } bool check_logfile(string grep) @@ -404,6 +417,8 @@ struct BugpointPass : public Pass { void execute(std::vector args, RTLIL::Design *design) override { string yosys_cmd = "yosys", yosys_arg, grep, runner; + bool flag_expect_return = false, has_check = false; + int expect_return_value = 0; bool fast = false, clean = false; bool modules = false, ports = false, cells = false, connections = false, processes = false, assigns = false, updates = false, wires = false, has_part = false; @@ -430,9 +445,19 @@ struct BugpointPass : public Pass { continue; } if (args[argidx] == "-grep" && argidx + 1 < args.size()) { + has_check = true; grep = args[++argidx]; continue; } + if (args[argidx] == "-expect-return") { + flag_expect_return = true; + ++argidx; + if (argidx >= args.size()) + log_cmd_error("No expected return value specified.\n"); + + expect_return_value = atoi(args[argidx].c_str()); + continue; + } if (args[argidx] == "-fast") { fast = true; continue; @@ -496,6 +521,9 @@ struct BugpointPass : public Pass { if (yosys_arg.empty()) log_cmd_error("Missing -script or -command option.\n"); + if (flag_expect_return && expect_return_value == 0 && !has_check) + log_cmd_error("Nothing to match on for -expect-return 0; change value or use -grep.\n"); + if (!has_part) { modules = true; @@ -512,7 +540,10 @@ struct BugpointPass : public Pass { log_cmd_error("This command only operates on fully selected designs!\n"); RTLIL::Design *crashing_design = clean_design(design, clean); - if (run_yosys(crashing_design, runner, yosys_cmd, yosys_arg)) + int retval = run_yosys(crashing_design, runner, yosys_cmd, yosys_arg); + if (flag_expect_return && retval != expect_return_value) + log_cmd_error("The provided script file or command and Yosys binary returned value %d instead of expected %d on this design!\n", retval, expect_return_value); + if (!flag_expect_return && retval == 0) log_cmd_error("The provided script file or command and Yosys binary do not crash on this design!\n"); if (!check_logfile(grep)) log_cmd_error("The provided grep string is not found in the log file!\n"); @@ -525,21 +556,37 @@ struct BugpointPass : public Pass { { simplified = clean_design(simplified, fast, /*do_delete=*/true); - bool crashes; if (clean) { RTLIL::Design *testcase = clean_design(simplified); - crashes = !run_yosys(testcase, runner, yosys_cmd, yosys_arg); + retval = run_yosys(testcase, runner, yosys_cmd, yosys_arg); delete testcase; } else { - crashes = !run_yosys(simplified, runner, yosys_cmd, yosys_arg); + retval = run_yosys(simplified, runner, yosys_cmd, yosys_arg); } - if (crashes && check_logfile(grep)) + bool crashes = false; + if (flag_expect_return && retval == expect_return_value && check_logfile(grep)) + { + log("Testcase matches expected crash.\n"); + crashes = true; + } + else if (!flag_expect_return && retval == 0) + log("Testcase does not crash.\n"); + else if (!flag_expect_return && check_logfile(grep)) { log("Testcase crashes.\n"); + crashes = true; + } + else + // flag_expect_return && !(retval == expect_return_value && check_logfile(grep)) + // !flag_expect_return && !(retval == 0 && check_logfile(grep)) + log("Testcase does not match expected crash.\n"); + + if (crashes) + { if (crashing_design != design) delete crashing_design; crashing_design = simplified; @@ -547,7 +594,6 @@ struct BugpointPass : public Pass { } else { - log("Testcase does not crash.\n"); delete simplified; seed++; } From a7a926b24701faeb62e1e7e0b35bff5679def157 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:50 +1200 Subject: [PATCH 112/181] bugpoint.cc: WIN32 exit signals --- passes/cmds/bugpoint.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 24c778fdb..06aae1885 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -20,6 +20,15 @@ #include "kernel/yosys.h" #include "backends/rtlil/rtlil_backend.h" +#if defined(_WIN32) +# define WIFEXITED(x) 1 +# define WIFSIGNALED(x) 0 +# define WIFSTOPPED(x) 0 +# define WEXITSTATUS(x) ((x) & 0xff) +# define WTERMSIG(x) SIGTERM +# define WSTOPSIG(x) 0 +#endif + USING_YOSYS_NAMESPACE using namespace RTLIL_BACKEND; PRIVATE_NAMESPACE_BEGIN From 134da811f7f7c7688c3871ad93f7b480cc8c1274 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:50 +1200 Subject: [PATCH 113/181] Add raise_error pass Raise errors from attributes for testing. I want it for bugpoint tests but it could be useful elsewhere. --- kernel/constids.inc | 1 + passes/tests/Makefile.inc | 1 + passes/tests/raise_error.cc | 61 +++++++++++++++++++++++++++++++++++++ tests/bugpoint/.gitignore | 2 ++ tests/bugpoint/err.ys | 27 ++++++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 passes/tests/raise_error.cc create mode 100644 tests/bugpoint/.gitignore create mode 100644 tests/bugpoint/err.ys diff --git a/kernel/constids.inc b/kernel/constids.inc index c77a25c01..29872d45e 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -286,3 +286,4 @@ X(A_WIDTHS) X(B_WIDTHS) X(C_WIDTHS) X(C_SIGNED) +X(raise_error) diff --git a/passes/tests/Makefile.inc b/passes/tests/Makefile.inc index 531943d78..25e11967c 100644 --- a/passes/tests/Makefile.inc +++ b/passes/tests/Makefile.inc @@ -2,4 +2,5 @@ OBJS += passes/tests/test_autotb.o OBJS += passes/tests/test_cell.o OBJS += passes/tests/test_abcloop.o +OBJS += passes/tests/raise_error.o diff --git a/passes/tests/raise_error.cc b/passes/tests/raise_error.cc new file mode 100644 index 000000000..edc42de65 --- /dev/null +++ b/passes/tests/raise_error.cc @@ -0,0 +1,61 @@ +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct RaiseErrorPass : public Pass { + RaiseErrorPass() : Pass("raise_error", "raise errors from attributes") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" raise_error [selection]\n"); + log("\n"); + log("Test error handling by raising arbitrary errors. This pass iterates over the\n"); + log("design (or selection of it) checking for objects with the 'raise_error'\n"); + log("attribute set.\n"); + log("\n"); + } + void execute(vector args, RTLIL::Design *design) override + { + log_header(design, "Executing RAISE_ERROR pass.\n"); + + extra_args(args, 1, design, true); + + RTLIL::NamedObject *err_obj = nullptr; + + for (auto mod : design->all_selected_modules()) { + if (mod->has_attribute(ID::raise_error)) { + err_obj = mod->clone(); + break; + } + for (auto memb : mod->selected_members()) { + if (memb->has_attribute(ID::raise_error)) { + err_obj = memb; + break; + } + } + if (err_obj != nullptr) break; + } + + if (err_obj != nullptr) { + log("Raising error from '%s'.\n", log_id(err_obj)); + auto err_no = err_obj->attributes[ID::raise_error].as_int(); + if (err_no < 256) { + log_flush(); + #if defined(_MSC_VER) + _exit(err_no); + #else + _Exit(err_no); + #endif + } else { + auto err_msg = err_obj->get_string_attribute(ID::raise_error); + log_error("%s\n", err_msg.c_str()); + } + } else { + log("'raise_error' attribute not found\n"); + } + } +} RaiseErrorPass; + +PRIVATE_NAMESPACE_END diff --git a/tests/bugpoint/.gitignore b/tests/bugpoint/.gitignore new file mode 100644 index 000000000..072ed1097 --- /dev/null +++ b/tests/bugpoint/.gitignore @@ -0,0 +1,2 @@ +*.il +*.log \ No newline at end of file diff --git a/tests/bugpoint/err.ys b/tests/bugpoint/err.ys new file mode 100644 index 000000000..d6a152144 --- /dev/null +++ b/tests/bugpoint/err.ys @@ -0,0 +1,27 @@ +read_verilog -noblackbox << EOF +(* raise_error=7 *) +module top(); +endmodule + +(* raise_error="help me" *) +module other(); +endmodule + +module zzy(); +endmodule +EOF +select -assert-mod-count 3 =* +design -stash read + +# raise_error with int exits with status +design -load read +bugpoint -yosys ../../yosys -command raise_error -expect-return 7 +select -assert-mod-count 1 =* +select -assert-mod-count 1 top + +# raise_error with string prints message +design -load read +rename top abc +bugpoint -yosys ../../yosys -command raise_error -grep "help me" +select -assert-mod-count 1 =* +select -assert-mod-count 1 other From 8d5dbae06e33fede98fd3041f2fdf3b04137bddc Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:50 +1200 Subject: [PATCH 114/181] raise_error.cc: Option for direct to stderr Add more to help text to describe usage. Add test for no value (should `exit(1)`). --- passes/tests/raise_error.cc | 40 ++++++++++++++++++++++++++++--------- tests/bugpoint/err.ys | 27 ++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/passes/tests/raise_error.cc b/passes/tests/raise_error.cc index edc42de65..f9055e6d2 100644 --- a/passes/tests/raise_error.cc +++ b/passes/tests/raise_error.cc @@ -9,18 +9,35 @@ struct RaiseErrorPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" raise_error [selection]\n"); + log(" raise_error [options] [selection]\n"); log("\n"); log("Test error handling by raising arbitrary errors. This pass iterates over the\n"); log("design (or selection of it) checking for objects with the 'raise_error'\n"); - log("attribute set.\n"); + log("attribute set. Assigning 'raise_error' to a string more than one character long\n"); + log("will log that string as an error message before exiting. Assigning 'raise_error'\n"); + log("to an integer (less than 256) will exit with that value as the exit code.\n"); + log("\n"); + log(" -stderr\n"); + log(" Log error messages directly to stderr instead of using 'log_error'.\n"); log("\n"); } void execute(vector args, RTLIL::Design *design) override { log_header(design, "Executing RAISE_ERROR pass.\n"); - extra_args(args, 1, design, true); + bool use_stderr = false; + + int argidx; + for (argidx = 1; argidx < GetSize(args); argidx++) + { + if (args[argidx] == "-stderr") { + use_stderr = true; + continue; + } + break; + } + + extra_args(args, argidx, design, true); RTLIL::NamedObject *err_obj = nullptr; @@ -43,15 +60,20 @@ struct RaiseErrorPass : public Pass { auto err_no = err_obj->attributes[ID::raise_error].as_int(); if (err_no < 256) { log_flush(); - #if defined(_MSC_VER) - _exit(err_no); - #else - _Exit(err_no); - #endif } else { auto err_msg = err_obj->get_string_attribute(ID::raise_error); - log_error("%s\n", err_msg.c_str()); + if (use_stderr) { + std::cerr << err_msg << std::endl; + err_no = 1; + } else { + log_error("%s\n", err_msg.c_str()); + } } + #if defined(_MSC_VER) + _exit(err_no); + #else + _Exit(err_no); + #endif } else { log("'raise_error' attribute not found\n"); } diff --git a/tests/bugpoint/err.ys b/tests/bugpoint/err.ys index d6a152144..7b2fbcc81 100644 --- a/tests/bugpoint/err.ys +++ b/tests/bugpoint/err.ys @@ -7,7 +7,8 @@ endmodule module other(); endmodule -module zzy(); +(* raise_error *) +module def(); endmodule EOF select -assert-mod-count 3 =* @@ -25,3 +26,27 @@ rename top abc bugpoint -yosys ../../yosys -command raise_error -grep "help me" select -assert-mod-count 1 =* select -assert-mod-count 1 other + +# raise_error with no value exits with 1 +design -load read +rename def zzy +bugpoint -yosys ../../yosys -command raise_error -expect-return 1 +select -assert-mod-count 1 =* +select -assert-mod-count 1 zzy + +# raise_error -stderr exits with 1 +design -load read +rename top abc +delete def +bugpoint -yosys ../../yosys -command "raise_error -stderr" -expect-return 1 +select -assert-mod-count 1 =* +select -assert-mod-count 1 other + +#TODO +# raise_error -stderr prints to stderr +design -load read +rename top abc +delete def +# bugpoint -yosys ../../yosys -command "raise_error -stderr" -grep "help me" +# select -assert-mod-count 1 =* +# select -assert-mod-count 1 other From fb92eabdcd7d55ed371f49ffdf53a88bc05e69fe Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:51 +1200 Subject: [PATCH 115/181] bugpoint: Add -greperr option `-greperr ` redirects stderr to 'bugpoint-case.err', and then searches that file for ``. Move `-runner` option up with the other options to reduce ambiguity (i.e. so it doesn't look like it's another design parts constraint). Also some shuffling of `err.ys`. --- passes/cmds/bugpoint.cc | 53 ++++++++++++++++++++++++++++----------- tests/bugpoint/.gitignore | 3 ++- tests/bugpoint/err.ys | 19 ++++---------- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 06aae1885..11f6de35b 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -73,6 +73,13 @@ struct BugpointPass : public Pass { log(" finishing. produces smaller and more useful testcases, but may fail to\n"); log(" produce any testcase at all if the crash is related to dangling wires.\n"); log("\n"); + log(" -runner \"\"\n"); + log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n"); + log("\n"); + log(" -greperr \"\"\n"); + log(" only consider crashes that print this string on stderr. useful for\n"); + log(" errors outside of yosys.\n"); + log("\n"); log("It is possible to constrain which parts of the design will be considered for\n"); log("removal. Unless one or more of the following options are specified, all parts\n"); log("will be considered.\n"); @@ -106,12 +113,9 @@ struct BugpointPass : public Pass { log(" try to remove wires. wires with a (* bugpoint_keep *) attribute will be\n"); log(" skipped.\n"); log("\n"); - log(" -runner \"\"\n"); - log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n"); - log("\n"); } - int run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg) + int run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg, bool catch_err) { design->sort(); @@ -120,6 +124,7 @@ struct BugpointPass : public Pass { f.close(); string yosys_cmdline = stringf("%s %s -qq -L bugpoint-case.log %s bugpoint-case.il", runner.c_str(), yosys_cmd.c_str(), yosys_arg.c_str()); + if (catch_err) yosys_cmdline += " 2>bugpoint-case.err"; auto status = run_command(yosys_cmdline); // we're not processing lines, which means we're getting raw system() returns if(WIFEXITED(status)) @@ -132,7 +137,7 @@ struct BugpointPass : public Pass { return 0; } - bool check_logfile(string grep) + bool check_logfile(string grep, bool err=false) { if (grep.empty()) return true; @@ -140,7 +145,12 @@ struct BugpointPass : public Pass { if (grep.size() > 2 && grep.front() == '"' && grep.back() == '"') grep = grep.substr(1, grep.size() - 2); - std::ifstream f("bugpoint-case.log"); + std::ifstream f; + if (err) + f = std::ifstream("bugpoint-case.err"); + else + f = std::ifstream("bugpoint-case.log"); + while (!f.eof()) { string line; @@ -151,6 +161,11 @@ struct BugpointPass : public Pass { return false; } + bool check_logfiles(string grep, string greperr) + { + return check_logfile(grep) && check_logfile(greperr, true); + } + RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false) { if (!do_clean) @@ -425,8 +440,8 @@ struct BugpointPass : public Pass { void execute(std::vector args, RTLIL::Design *design) override { - string yosys_cmd = "yosys", yosys_arg, grep, runner; - bool flag_expect_return = false, has_check = false; + string yosys_cmd = "yosys", yosys_arg, grep, greperr, runner; + bool flag_expect_return = false, has_check = false, check_err = false; int expect_return_value = 0; bool fast = false, clean = false; bool modules = false, ports = false, cells = false, connections = false, processes = false, assigns = false, updates = false, wires = false, has_part = false; @@ -458,6 +473,12 @@ struct BugpointPass : public Pass { grep = args[++argidx]; continue; } + if (args[argidx] == "-greperr" && argidx + 1 < args.size()) { + has_check = true; + check_err = true; + greperr = args[++argidx]; + continue; + } if (args[argidx] == "-expect-return") { flag_expect_return = true; ++argidx; @@ -549,13 +570,15 @@ struct BugpointPass : public Pass { log_cmd_error("This command only operates on fully selected designs!\n"); RTLIL::Design *crashing_design = clean_design(design, clean); - int retval = run_yosys(crashing_design, runner, yosys_cmd, yosys_arg); + int retval = run_yosys(crashing_design, runner, yosys_cmd, yosys_arg, check_err); if (flag_expect_return && retval != expect_return_value) log_cmd_error("The provided script file or command and Yosys binary returned value %d instead of expected %d on this design!\n", retval, expect_return_value); if (!flag_expect_return && retval == 0) log_cmd_error("The provided script file or command and Yosys binary do not crash on this design!\n"); if (!check_logfile(grep)) log_cmd_error("The provided grep string is not found in the log file!\n"); + if (!check_logfile(greperr, true)) + log_cmd_error("The provided grep string is not found in stderr log!\n"); int seed = 0; bool found_something = false, stage2 = false; @@ -568,30 +591,30 @@ struct BugpointPass : public Pass { if (clean) { RTLIL::Design *testcase = clean_design(simplified); - retval = run_yosys(testcase, runner, yosys_cmd, yosys_arg); + retval = run_yosys(testcase, runner, yosys_cmd, yosys_arg, check_err); delete testcase; } else { - retval = run_yosys(simplified, runner, yosys_cmd, yosys_arg); + retval = run_yosys(simplified, runner, yosys_cmd, yosys_arg, check_err); } bool crashes = false; - if (flag_expect_return && retval == expect_return_value && check_logfile(grep)) + if (flag_expect_return && retval == expect_return_value && check_logfiles(grep, greperr)) { log("Testcase matches expected crash.\n"); crashes = true; } else if (!flag_expect_return && retval == 0) log("Testcase does not crash.\n"); - else if (!flag_expect_return && check_logfile(grep)) + else if (!flag_expect_return && check_logfiles(grep, greperr)) { log("Testcase crashes.\n"); crashes = true; } else - // flag_expect_return && !(retval == expect_return_value && check_logfile(grep)) - // !flag_expect_return && !(retval == 0 && check_logfile(grep)) + // flag_expect_return && !(retval == expect_return_value && check_logfiles(grep, greperr)) + // !flag_expect_return && !(retval == 0 && check_logfiles(grep, greperr)) log("Testcase does not match expected crash.\n"); if (crashes) diff --git a/tests/bugpoint/.gitignore b/tests/bugpoint/.gitignore index 072ed1097..818575593 100644 --- a/tests/bugpoint/.gitignore +++ b/tests/bugpoint/.gitignore @@ -1,2 +1,3 @@ *.il -*.log \ No newline at end of file +*.log +*.err diff --git a/tests/bugpoint/err.ys b/tests/bugpoint/err.ys index 7b2fbcc81..c74e34b19 100644 --- a/tests/bugpoint/err.ys +++ b/tests/bugpoint/err.ys @@ -20,33 +20,24 @@ bugpoint -yosys ../../yosys -command raise_error -expect-return 7 select -assert-mod-count 1 =* select -assert-mod-count 1 top -# raise_error with string prints message +# raise_error with string prints message and exits with 1 design -load read rename top abc -bugpoint -yosys ../../yosys -command raise_error -grep "help me" +bugpoint -yosys ../../yosys -command raise_error -grep "help me" -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 other # raise_error with no value exits with 1 design -load read rename def zzy +delete other bugpoint -yosys ../../yosys -command raise_error -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 zzy -# raise_error -stderr exits with 1 +# raise_error -stderr prints to stderr and exits with 1 design -load read rename top abc -delete def -bugpoint -yosys ../../yosys -command "raise_error -stderr" -expect-return 1 +bugpoint -yosys ../../yosys -command "raise_error -stderr" -greperr "help me" -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 other - -#TODO -# raise_error -stderr prints to stderr -design -load read -rename top abc -delete def -# bugpoint -yosys ../../yosys -command "raise_error -stderr" -grep "help me" -# select -assert-mod-count 1 =* -# select -assert-mod-count 1 other From 65147670a67e759d7e08c5bc4fb14520b5221398 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:51 +1200 Subject: [PATCH 116/181] bugpoint.cc: Include csignal for windows --- passes/cmds/bugpoint.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 11f6de35b..101d82bd6 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -21,6 +21,7 @@ #include "backends/rtlil/rtlil_backend.h" #if defined(_WIN32) +# include # define WIFEXITED(x) 1 # define WIFSIGNALED(x) 0 # define WIFSTOPPED(x) 0 From b5a13ae95b9b9350d726e1ec2ad5eec9cc544b0c Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:51 +1200 Subject: [PATCH 117/181] bugpoint.cc: Rename to -err_grep --- passes/cmds/bugpoint.cc | 22 +++++++++++----------- tests/bugpoint/err.ys | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 101d82bd6..f740c2bcd 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -77,7 +77,7 @@ struct BugpointPass : public Pass { log(" -runner \"\"\n"); log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n"); log("\n"); - log(" -greperr \"\"\n"); + log(" -err_grep \"\"\n"); log(" only consider crashes that print this string on stderr. useful for\n"); log(" errors outside of yosys.\n"); log("\n"); @@ -162,9 +162,9 @@ struct BugpointPass : public Pass { return false; } - bool check_logfiles(string grep, string greperr) + bool check_logfiles(string grep, string err_grep) { - return check_logfile(grep) && check_logfile(greperr, true); + return check_logfile(grep) && check_logfile(err_grep, true); } RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false) @@ -441,7 +441,7 @@ struct BugpointPass : public Pass { void execute(std::vector args, RTLIL::Design *design) override { - string yosys_cmd = "yosys", yosys_arg, grep, greperr, runner; + string yosys_cmd = "yosys", yosys_arg, grep, err_grep, runner; bool flag_expect_return = false, has_check = false, check_err = false; int expect_return_value = 0; bool fast = false, clean = false; @@ -474,10 +474,10 @@ struct BugpointPass : public Pass { grep = args[++argidx]; continue; } - if (args[argidx] == "-greperr" && argidx + 1 < args.size()) { + if (args[argidx] == "-err_grep" && argidx + 1 < args.size()) { has_check = true; check_err = true; - greperr = args[++argidx]; + err_grep = args[++argidx]; continue; } if (args[argidx] == "-expect-return") { @@ -578,7 +578,7 @@ struct BugpointPass : public Pass { log_cmd_error("The provided script file or command and Yosys binary do not crash on this design!\n"); if (!check_logfile(grep)) log_cmd_error("The provided grep string is not found in the log file!\n"); - if (!check_logfile(greperr, true)) + if (!check_logfile(err_grep, true)) log_cmd_error("The provided grep string is not found in stderr log!\n"); int seed = 0; @@ -601,21 +601,21 @@ struct BugpointPass : public Pass { } bool crashes = false; - if (flag_expect_return && retval == expect_return_value && check_logfiles(grep, greperr)) + if (flag_expect_return && retval == expect_return_value && check_logfiles(grep, err_grep)) { log("Testcase matches expected crash.\n"); crashes = true; } else if (!flag_expect_return && retval == 0) log("Testcase does not crash.\n"); - else if (!flag_expect_return && check_logfiles(grep, greperr)) + else if (!flag_expect_return && check_logfiles(grep, err_grep)) { log("Testcase crashes.\n"); crashes = true; } else - // flag_expect_return && !(retval == expect_return_value && check_logfiles(grep, greperr)) - // !flag_expect_return && !(retval == 0 && check_logfiles(grep, greperr)) + // flag_expect_return && !(retval == expect_return_value && check_logfiles(grep, err_grep)) + // !flag_expect_return && !(retval == 0 && check_logfiles(grep, err_grep)) log("Testcase does not match expected crash.\n"); if (crashes) diff --git a/tests/bugpoint/err.ys b/tests/bugpoint/err.ys index c74e34b19..08bf0a9c7 100644 --- a/tests/bugpoint/err.ys +++ b/tests/bugpoint/err.ys @@ -38,6 +38,6 @@ select -assert-mod-count 1 zzy # raise_error -stderr prints to stderr and exits with 1 design -load read rename top abc -bugpoint -yosys ../../yosys -command "raise_error -stderr" -greperr "help me" -expect-return 1 +bugpoint -yosys ../../yosys -command "raise_error -stderr" -err_grep "help me" -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 other From 93f7429f4fd9feaef054e61ddc4179a8756e5719 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:51 +1200 Subject: [PATCH 118/181] tests: Add bugpoint to MK_TEST_DIRS Also change `-err_grep` to `-err-grep` for consistency with `-expect-return`. --- Makefile | 1 + passes/cmds/bugpoint.cc | 4 ++-- tests/bugpoint/err.ys | 2 +- tests/bugpoint/run-test.sh | 4 ++++ 4 files changed, 8 insertions(+), 3 deletions(-) create mode 100755 tests/bugpoint/run-test.sh diff --git a/Makefile b/Makefile index 083805fbc..3f17d471c 100644 --- a/Makefile +++ b/Makefile @@ -868,6 +868,7 @@ MK_TEST_DIRS += tests/arch/nexus MK_TEST_DIRS += tests/arch/quicklogic/pp3 MK_TEST_DIRS += tests/arch/quicklogic/qlf_k6n10f MK_TEST_DIRS += tests/arch/xilinx +MK_TEST_DIRS += tests/bugpoint MK_TEST_DIRS += tests/opt MK_TEST_DIRS += tests/sat MK_TEST_DIRS += tests/sim diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index f740c2bcd..683430372 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -77,7 +77,7 @@ struct BugpointPass : public Pass { log(" -runner \"\"\n"); log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n"); log("\n"); - log(" -err_grep \"\"\n"); + log(" -err-grep \"\"\n"); log(" only consider crashes that print this string on stderr. useful for\n"); log(" errors outside of yosys.\n"); log("\n"); @@ -474,7 +474,7 @@ struct BugpointPass : public Pass { grep = args[++argidx]; continue; } - if (args[argidx] == "-err_grep" && argidx + 1 < args.size()) { + if (args[argidx] == "-err-grep" && argidx + 1 < args.size()) { has_check = true; check_err = true; err_grep = args[++argidx]; diff --git a/tests/bugpoint/err.ys b/tests/bugpoint/err.ys index 08bf0a9c7..42e84241c 100644 --- a/tests/bugpoint/err.ys +++ b/tests/bugpoint/err.ys @@ -38,6 +38,6 @@ select -assert-mod-count 1 zzy # raise_error -stderr prints to stderr and exits with 1 design -load read rename top abc -bugpoint -yosys ../../yosys -command "raise_error -stderr" -err_grep "help me" -expect-return 1 +bugpoint -yosys ../../yosys -command "raise_error -stderr" -err-grep "help me" -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 other diff --git a/tests/bugpoint/run-test.sh b/tests/bugpoint/run-test.sh new file mode 100755 index 000000000..006c731e3 --- /dev/null +++ b/tests/bugpoint/run-test.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eu +source ../gen-tests-makefile.sh +generate_mk --yosys-scripts From d3ff803b8176594be47ca2b83ab138b16c5a11fc Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:52 +1200 Subject: [PATCH 119/181] bugpoint: Add -suffix option Allows for adding a suffix to the `bugpoint-case` file name so that multiple `bugpoint`s can run in the same directory, e.g. during a `make test -j4`. --- passes/cmds/bugpoint.cc | 61 ++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 683430372..08dc76dda 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -81,6 +81,10 @@ struct BugpointPass : public Pass { log(" only consider crashes that print this string on stderr. useful for\n"); log(" errors outside of yosys.\n"); log("\n"); + log(" -suffix \"\"\n"); + log(" add suffix to generated file names. useful when running more than one\n"); + log(" instance of bugpoint in the same directory. limited to 8 characters.\n"); + log("\n"); log("It is possible to constrain which parts of the design will be considered for\n"); log("removal. Unless one or more of the following options are specified, all parts\n"); log("will be considered.\n"); @@ -116,16 +120,20 @@ struct BugpointPass : public Pass { log("\n"); } - int run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg, bool catch_err) + int run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg, string suffix, bool catch_err) { design->sort(); - std::ofstream f("bugpoint-case.il"); + string bugpoint_file = "bugpoint-case"; + if (suffix.size()) + bugpoint_file += stringf(".%.8s", suffix.c_str()); + + std::ofstream f(bugpoint_file + ".il"); RTLIL_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false); f.close(); - string yosys_cmdline = stringf("%s %s -qq -L bugpoint-case.log %s bugpoint-case.il", runner.c_str(), yosys_cmd.c_str(), yosys_arg.c_str()); - if (catch_err) yosys_cmdline += " 2>bugpoint-case.err"; + string yosys_cmdline = stringf("%s %s -qq -L %s.log %s %s.il", runner.c_str(), yosys_cmd.c_str(), bugpoint_file.c_str(), yosys_arg.c_str(), bugpoint_file.c_str()); + if (catch_err) yosys_cmdline += stringf(" 2>%s.err", bugpoint_file.c_str()); auto status = run_command(yosys_cmdline); // we're not processing lines, which means we're getting raw system() returns if(WIFEXITED(status)) @@ -138,7 +146,7 @@ struct BugpointPass : public Pass { return 0; } - bool check_logfile(string grep, bool err=false) + bool check_logfile(string grep, string suffix, bool err=false) { if (grep.empty()) return true; @@ -146,11 +154,12 @@ struct BugpointPass : public Pass { if (grep.size() > 2 && grep.front() == '"' && grep.back() == '"') grep = grep.substr(1, grep.size() - 2); - std::ifstream f; - if (err) - f = std::ifstream("bugpoint-case.err"); - else - f = std::ifstream("bugpoint-case.log"); + string bugpoint_file = "bugpoint-case"; + if (suffix.size()) + bugpoint_file += stringf(".%.8s", suffix.c_str()); + bugpoint_file += err ? ".err" : ".log"; + + std::ifstream f(bugpoint_file); while (!f.eof()) { @@ -162,9 +171,9 @@ struct BugpointPass : public Pass { return false; } - bool check_logfiles(string grep, string err_grep) + bool check_logfiles(string grep, string err_grep, string suffix) { - return check_logfile(grep) && check_logfile(err_grep, true); + return check_logfile(grep, suffix) && check_logfile(err_grep, suffix, true); } RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false) @@ -441,7 +450,7 @@ struct BugpointPass : public Pass { void execute(std::vector args, RTLIL::Design *design) override { - string yosys_cmd = "yosys", yosys_arg, grep, err_grep, runner; + string yosys_cmd = "yosys", yosys_arg, grep, err_grep, runner, suffix; bool flag_expect_return = false, has_check = false, check_err = false; int expect_return_value = 0; bool fast = false, clean = false; @@ -545,6 +554,14 @@ struct BugpointPass : public Pass { } continue; } + if (args[argidx] == "-suffix" && argidx + 1 < args.size()) { + suffix = args[++argidx]; + if (suffix.size() && suffix.at(0) == '"') { + log_assert(suffix.back() == '"'); + suffix = suffix.substr(1, suffix.size() - 2); + } + continue; + } break; } extra_args(args, argidx, design); @@ -571,14 +588,14 @@ struct BugpointPass : public Pass { log_cmd_error("This command only operates on fully selected designs!\n"); RTLIL::Design *crashing_design = clean_design(design, clean); - int retval = run_yosys(crashing_design, runner, yosys_cmd, yosys_arg, check_err); + int retval = run_yosys(crashing_design, runner, yosys_cmd, yosys_arg, suffix, check_err); if (flag_expect_return && retval != expect_return_value) log_cmd_error("The provided script file or command and Yosys binary returned value %d instead of expected %d on this design!\n", retval, expect_return_value); if (!flag_expect_return && retval == 0) log_cmd_error("The provided script file or command and Yosys binary do not crash on this design!\n"); - if (!check_logfile(grep)) + if (!check_logfile(grep, suffix)) log_cmd_error("The provided grep string is not found in the log file!\n"); - if (!check_logfile(err_grep, true)) + if (!check_logfile(err_grep, suffix, true)) log_cmd_error("The provided grep string is not found in stderr log!\n"); int seed = 0; @@ -592,30 +609,30 @@ struct BugpointPass : public Pass { if (clean) { RTLIL::Design *testcase = clean_design(simplified); - retval = run_yosys(testcase, runner, yosys_cmd, yosys_arg, check_err); + retval = run_yosys(testcase, runner, yosys_cmd, yosys_arg, suffix, check_err); delete testcase; } else { - retval = run_yosys(simplified, runner, yosys_cmd, yosys_arg, check_err); + retval = run_yosys(simplified, runner, yosys_cmd, yosys_arg, suffix, check_err); } bool crashes = false; - if (flag_expect_return && retval == expect_return_value && check_logfiles(grep, err_grep)) + if (flag_expect_return && retval == expect_return_value && check_logfiles(grep, err_grep, suffix)) { log("Testcase matches expected crash.\n"); crashes = true; } else if (!flag_expect_return && retval == 0) log("Testcase does not crash.\n"); - else if (!flag_expect_return && check_logfiles(grep, err_grep)) + else if (!flag_expect_return && check_logfiles(grep, err_grep, suffix)) { log("Testcase crashes.\n"); crashes = true; } else - // flag_expect_return && !(retval == expect_return_value && check_logfiles(grep, err_grep)) - // !flag_expect_return && !(retval == 0 && check_logfiles(grep, err_grep)) + // flag_expect_return && !(retval == expect_return_value && check_logfiles(grep, err_grep, suffix)) + // !flag_expect_return && !(retval == 0 && check_logfiles(grep, err_grep, suffix)) log("Testcase does not match expected crash.\n"); if (crashes) From fe07d390f1b5b80aaeeb295bd74978d617e4bbc0 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:39:52 +1200 Subject: [PATCH 120/181] tests/bugpoint: More tests More coverage. --- tests/bugpoint/.gitignore | 4 +- tests/bugpoint/failures.ys | 29 ++++++++ tests/bugpoint/mod_constraints.ys | 83 +++++++++++++++++++++++ tests/bugpoint/mods.il | 38 +++++++++++ tests/bugpoint/proc_constraints.ys | 49 +++++++++++++ tests/bugpoint/procs.il | 42 ++++++++++++ tests/bugpoint/{err.ys => raise_error.ys} | 8 +-- 7 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 tests/bugpoint/failures.ys create mode 100644 tests/bugpoint/mod_constraints.ys create mode 100644 tests/bugpoint/mods.il create mode 100644 tests/bugpoint/proc_constraints.ys create mode 100644 tests/bugpoint/procs.il rename tests/bugpoint/{err.ys => raise_error.ys} (71%) diff --git a/tests/bugpoint/.gitignore b/tests/bugpoint/.gitignore index 818575593..1012c66cc 100644 --- a/tests/bugpoint/.gitignore +++ b/tests/bugpoint/.gitignore @@ -1,3 +1,5 @@ -*.il +bugpoint-case.* *.log *.err +*.temp +run-test.mk diff --git a/tests/bugpoint/failures.ys b/tests/bugpoint/failures.ys new file mode 100644 index 000000000..ce8daa8cc --- /dev/null +++ b/tests/bugpoint/failures.ys @@ -0,0 +1,29 @@ +write_file fail.temp << EOF +logger -expect error "Missing -script or -command option." 1 +bugpoint -suffix fail -yosys ../../yosys +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp + +write_file fail.temp << EOF +logger -expect error "do not crash on this design" 1 +bugpoint -suffix fail -yosys ../../yosys -command "dump" +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp + +write_file fail.temp << EOF +logger -expect error "returned value 3 instead of expected 7" 1 +bugpoint -suffix fail -yosys ../../yosys -command raise_error -expect-return 7 +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp + +write_file fail.temp << EOF +logger -expect error "not found in the log file!" 1 +bugpoint -suffix fail -yosys ../../yosys -command raise_error -grep "nope" +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp + +write_file fail.temp << EOF +logger -expect error "not found in stderr log!" 1 +bugpoint -suffix fail -yosys ../../yosys -command raise_error -err-grep "nope" +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp diff --git a/tests/bugpoint/mod_constraints.ys b/tests/bugpoint/mod_constraints.ys new file mode 100644 index 000000000..f35095510 --- /dev/null +++ b/tests/bugpoint/mod_constraints.ys @@ -0,0 +1,83 @@ +read_rtlil mods.il +select -assert-count 7 w:* +select -assert-mod-count 3 =* +select -assert-count 4 c:* +design -stash base + +# everything is removed by default +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 1 w:* +select -assert-mod-count 1 =* +select -assert-none c:* + +# don't remove wires +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 -modules -cells +select -assert-count 3 w:* +select -assert-mod-count 1 =* +select -assert-none c:* + +# don't remove cells or their connections +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 -wires -modules +select -assert-count 5 w:* +select -assert-mod-count 1 =* +select -assert-count 4 c:* + +# don't remove cells but do remove their connections +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 -wires -modules -connections +select -assert-count 1 w:* +select -assert-mod-count 1 =* +select -assert-count 4 c:* + +# don't remove modules +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 -wires -cells +select -assert-count 1 w:* +select -assert-mod-count 3 =* +select -assert-none c:* + +# can keep wires +design -load base +setattr -set bugpoint_keep 1 w:w_b +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 2 w:* +select -assert-mod-count 1 =* +select -assert-none c:* + +# a wire with keep won't keep the cell/module containing it +design -load base +setattr -set bugpoint_keep 1 w:w_o +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 1 w:* +select -assert-mod-count 1 =* +select -assert-none c:* + +# can keep cells (and do it without the associated module) +design -load base +setattr -set bugpoint_keep 1 c:c_a +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 1 w:* +select -assert-mod-count 1 =* +select -assert-count 1 c:* + +# can keep modules +design -load base +setattr -mod -set bugpoint_keep 1 m_a +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 1 w:* +select -assert-mod-count 2 =* +select -assert-none c:* + +# minimize to just the path connecting w_a and w_c +# which happens via w_b, w_i, w_o, m_a, c_a and c_b +write_file script.temp << EOF +select -assert-none w:w_a %co* w:w_c %ci* %i +EOF +design -load base +bugpoint -suffix mods -yosys ../../yosys -script script.temp -grep "Assertion failed" +select -assert-count 5 w:* +select -assert-mod-count 2 =* +select -assert-count 2 c:* diff --git a/tests/bugpoint/mods.il b/tests/bugpoint/mods.il new file mode 100644 index 000000000..fe3ce6522 --- /dev/null +++ b/tests/bugpoint/mods.il @@ -0,0 +1,38 @@ +module \m_a + wire input 1 \w_i + wire output 2 \w_o + connect \w_o \w_i +end + +module \m_b + wire input 1 \w_i + wire output 2 \w_o +end + +attribute \top 1 +module \top + attribute \raise_error 3 + wire \w_a + wire \w_b + wire \w_c + + cell \m_a \c_a + connect \w_i \w_a + connect \w_o \w_b + end + + cell \m_a \c_b + connect \w_i \w_b + connect \w_o \w_c + end + + cell \m_b \c_c + connect \w_i \w_c + connect \w_o \w_a + end + + cell \m_b \c_d + connect \w_i 1'0 + connect \w_o 1'1 + end +end diff --git a/tests/bugpoint/proc_constraints.ys b/tests/bugpoint/proc_constraints.ys new file mode 100644 index 000000000..22b8b3c60 --- /dev/null +++ b/tests/bugpoint/proc_constraints.ys @@ -0,0 +1,49 @@ +read_rtlil procs.il +select -assert-count 2 p:* +design -stash err_q + +# processes get removed by default +design -load err_q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 +select -assert-none p:* + +# individual processes can be kept +design -load err_q +setattr -set bugpoint_keep 1 p:proc_a +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 +select -assert-count 1 p:* + +# all processes can be kept +design -load err_q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -wires +select -assert-count 2 p:* + +# d and clock are connected after proc +design -load err_q +proc +select -assert-count 3 w:d %co +select -assert-count 3 w:clock %co + +# no assigns means no d +design -load err_q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -assigns +proc +select -assert-count 1 w:d %co + +# no updates means no clock +design -load err_q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -updates +proc +select -assert-count 1 w:clock %co + +# can remove ports +design -load err_q +select -assert-count 5 x:* +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -ports +select -assert-none x:* + +# can keep ports +design -load err_q +setattr -set bugpoint_keep 1 i:d o:q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -ports +select -assert-count 2 x:* diff --git a/tests/bugpoint/procs.il b/tests/bugpoint/procs.il new file mode 100644 index 000000000..cb9f7c8dd --- /dev/null +++ b/tests/bugpoint/procs.il @@ -0,0 +1,42 @@ +module \ff_with_en_and_sync_reset + wire $0\q[1:1] + wire $0\q[0:0] + attribute \raise_error 4 + wire width 2 output 5 \q + wire width 2 input 4 \d + wire input 3 \enable + wire input 2 \reset + wire input 1 \clock + + process \proc_a + assign $0\q[0:0] \q [0] + switch \reset + case 1'1 + assign $0\q[0:0] 1'0 + case + switch \enable + case 1'1 + assign $0\q[0:0] \d [0] + case + end + end + sync posedge \clock + update \q [0] $0\q[0:0] + end + + process \proc_b + assign $0\q[1:1] \q [1] + switch \reset + case 1'1 + assign $0\q[1:1] 1'0 + case + switch \enable + case 1'1 + assign $0\q[1:1] \d [1] + case + end + end + sync posedge \clock + update \q [1] $0\q[1:1] + end +end diff --git a/tests/bugpoint/err.ys b/tests/bugpoint/raise_error.ys similarity index 71% rename from tests/bugpoint/err.ys rename to tests/bugpoint/raise_error.ys index 42e84241c..a0a03f447 100644 --- a/tests/bugpoint/err.ys +++ b/tests/bugpoint/raise_error.ys @@ -16,14 +16,14 @@ design -stash read # raise_error with int exits with status design -load read -bugpoint -yosys ../../yosys -command raise_error -expect-return 7 +bugpoint -suffix error -yosys ../../yosys -command raise_error -expect-return 7 select -assert-mod-count 1 =* select -assert-mod-count 1 top # raise_error with string prints message and exits with 1 design -load read rename top abc -bugpoint -yosys ../../yosys -command raise_error -grep "help me" -expect-return 1 +bugpoint -suffix error -yosys ../../yosys -command raise_error -grep "help me" -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 other @@ -31,13 +31,13 @@ select -assert-mod-count 1 other design -load read rename def zzy delete other -bugpoint -yosys ../../yosys -command raise_error -expect-return 1 +bugpoint -suffix error -yosys ../../yosys -command raise_error -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 zzy # raise_error -stderr prints to stderr and exits with 1 design -load read rename top abc -bugpoint -yosys ../../yosys -command "raise_error -stderr" -err-grep "help me" -expect-return 1 +bugpoint -suffix error -yosys ../../yosys -command "raise_error -stderr" -err-grep "help me" -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 other From 8f6d7a3043d4ca89b1fab25d2b4f45a56b9e5efa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:29:04 +0000 Subject: [PATCH 121/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 083805fbc..36b4d0491 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+112 +YOSYS_VER := 0.55+115 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 6ee3cd8ffdb636301b5600513f549e3e319f1ac6 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 9 Jul 2025 02:31:33 +0000 Subject: [PATCH 122/181] Replace `stringf()` with a templated function which does compile-time format string checking. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking only happens at compile time if -std=c++20 (or greater) is enabled. Otherwise the checking happens at run time. This requires the format string to be a compile-time constant (when compiling with C++20), so fix a few places where that isn't true. The format string behavior is a bit more lenient than C printf. For %d/%u you can pass any integer type and it will be converted and output without truncating bits, i.e. any length specifier is ignored and the conversion is always treated as 'll'. Any truncation needs to be done by casting the argument itself. For %f/%g you can pass anything that converts to double, including integers. Performance results with clang 19 -O3 on Linux: ``` hyperfine './yosys -dp "read_rtlil /usr/local/google/home/rocallahan/Downloads/jpeg.synth.il; dump"' ``` C++17 before: Time (mean ± σ): 101.3 ms ± 0.8 ms [User: 85.6 ms, System: 15.6 ms] C++17 after: Time (mean ± σ): 98.4 ms ± 1.2 ms [User: 82.1 ms, System: 16.1 ms] C++20 before: Time (mean ± σ): 100.9 ms ± 1.1 ms [User: 87.0 ms, System: 13.8 ms] C++20 after: Time (mean ± σ): 97.8 ms ± 1.4 ms [User: 83.1 ms, System: 14.7 ms] The generated code is reasonably efficient. E.g. with clang 19, `stringf()` with a format with no %% escapes and no other parameters (a weirdly common case) often compiles to a fully inlined `std::string` construction. In general the format string parsing is often (not always) compiled away. --- backends/verilog/verilog_backend.cc | 20 +- kernel/io.cc | 149 +++++++++++ kernel/io.h | 392 +++++++++++++++++++++++++++- kernel/yosys_common.h | 9 + 4 files changed, 551 insertions(+), 19 deletions(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 1cef7be60..44f8f05ea 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1163,9 +1163,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = ~(("); dump_cell_expr_port(f, cell, "A", false); - f << stringf(cell->type == ID($_AOI3_) ? " & " : " | "); + f << (cell->type == ID($_AOI3_) ? " & " : " | "); dump_cell_expr_port(f, cell, "B", false); - f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &"); + f << (cell->type == ID($_AOI3_) ? ") |" : ") &"); dump_attributes(f, "", cell->attributes, " "); f << stringf(" "); dump_cell_expr_port(f, cell, "C", false); @@ -1178,13 +1178,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = ~(("); dump_cell_expr_port(f, cell, "A", false); - f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + f << (cell->type == ID($_AOI4_) ? " & " : " | "); dump_cell_expr_port(f, cell, "B", false); - f << stringf(cell->type == ID($_AOI4_) ? ") |" : ") &"); + f << (cell->type == ID($_AOI4_) ? ") |" : ") &"); dump_attributes(f, "", cell->attributes, " "); f << stringf(" ("); dump_cell_expr_port(f, cell, "C", false); - f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + f << (cell->type == ID($_AOI4_) ? " & " : " | "); dump_cell_expr_port(f, cell, "D", false); f << stringf("));\n"); return true; @@ -1395,10 +1395,10 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) int s_width = cell->getPort(ID::S).size(); std::string func_name = cellname(cell); - f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str()); - f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1); - f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1); - f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1); + f << stringf("%s" "function [%d:0] %s;\n", indent, width-1, func_name); + f << stringf("%s" " input [%d:0] a;\n", indent, width-1); + f << stringf("%s" " input [%d:0] b;\n", indent, s_width*width-1); + f << stringf("%s" " input [%d:0] s;\n", indent, s_width-1); dump_attributes(f, indent + " ", cell->attributes); if (noparallelcase) @@ -1407,7 +1407,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (!noattr) f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); f << stringf("%s" " casez (s)", indent.c_str()); - f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); + f << (noattr ? " // synopsys parallel_case\n" : "\n"); } for (int i = 0; i < s_width; i++) diff --git a/kernel/io.cc b/kernel/io.cc index d7d126c4c..3deb54d86 100644 --- a/kernel/io.cc +++ b/kernel/io.cc @@ -384,4 +384,153 @@ std::string escape_filename_spaces(const std::string& filename) return out; } +void format_emit_unescaped(std::string &result, std::string_view fmt) +{ + result.reserve(result.size() + fmt.size()); + for (size_t i = 0; i < fmt.size(); ++i) { + char ch = fmt[i]; + result.push_back(ch); + if (ch == '%' && i + 1 < fmt.size() && fmt[i + 1] == '%') { + ++i; + } + } +} + +std::string unescape_format_string(std::string_view fmt) +{ + std::string result; + format_emit_unescaped(result, fmt); + return result; +} + +static std::string string_view_stringf(std::string_view spec, ...) +{ + std::string fmt(spec); + char format_specifier = fmt[fmt.size() - 1]; + switch (format_specifier) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': { + // Strip any length modifier off `fmt` + std::string long_fmt; + for (size_t i = 0; i + 1 < fmt.size(); ++i) { + char ch = fmt[i]; + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { + break; + } + long_fmt.push_back(ch); + } + // Add `lld` or whatever + long_fmt += "ll"; + long_fmt.push_back(format_specifier); + fmt = long_fmt; + break; + } + default: + break; + } + + va_list ap; + va_start(ap, spec); + std::string result = vstringf(fmt.c_str(), ap); + va_end(ap); + return result; +} + +template +static void format_emit_stringf(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, Arg arg) +{ + // Delegate nontrivial formats to the C library. + switch (num_dynamic_ints) { + case DynamicIntCount::NONE: + result += string_view_stringf(spec, arg); + return; + case DynamicIntCount::ONE: + result += string_view_stringf(spec, dynamic_ints[0], arg); + return; + case DynamicIntCount::TWO: + result += string_view_stringf(spec, dynamic_ints[0], dynamic_ints[1], arg); + return; + } + YOSYS_ABORT("Internal error"); +} + +void format_emit_long_long(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, long long arg) +{ + if (spec == "%d") { + // Format checking will have guaranteed num_dynamic_ints == 0. + result += std::to_string(arg); + return; + } + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + +void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, unsigned long long arg) +{ + if (spec == "%u") { + // Format checking will have guaranteed num_dynamic_ints == 0. + result += std::to_string(arg); + return; + } + if (spec == "%c") { + result += static_cast(arg); + return; + } + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + +void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, double arg) +{ + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + +void format_emit_char_ptr(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const char *arg) +{ + if (spec == "%s") { + // Format checking will have guaranteed num_dynamic_ints == 0. + result += arg; + return; + } + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + +void format_emit_string(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const std::string &arg) +{ + if (spec == "%s") { + // Format checking will have guaranteed num_dynamic_ints == 0. + result += arg; + return; + } + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg.c_str()); +} + +void format_emit_string_view(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, std::string_view arg) +{ + if (spec == "%s") { + // Format checking will have guaranteed num_dynamic_ints == 0. + // We can output the string without creating a temporary copy. + result += arg; + return; + } + // Delegate nontrivial formats to the C library. We need to construct + // a temporary string to ensure null termination. + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, std::string(arg).c_str()); +} + +void format_emit_void_ptr(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const void *arg) +{ + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + YOSYS_NAMESPACE_END diff --git a/kernel/io.h b/kernel/io.h index 91699d775..ea2499e43 100644 --- a/kernel/io.h +++ b/kernel/io.h @@ -1,5 +1,6 @@ #include #include +#include #include "kernel/yosys_common.h" #ifndef YOSYS_IO_H @@ -50,18 +51,391 @@ inline std::string vstringf(const char *fmt, va_list ap) #endif } -std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); - -inline std::string stringf(const char *fmt, ...) +enum ConversionSpecifier : uint8_t { - std::string string; - va_list ap; + CONVSPEC_NONE, + // Specifier not understood/supported + CONVSPEC_ERROR, + // Consumes a "long long" + CONVSPEC_SIGNED_INT, + // Consumes a "unsigned long long" + CONVSPEC_UNSIGNED_INT, + // Consumes a "double" + CONVSPEC_DOUBLE, + // Consumes a "const char*" + CONVSPEC_CHAR_PTR, + // Consumes a "void*" + CONVSPEC_VOID_PTR, +}; - va_start(ap, fmt); - string = vstringf(fmt, ap); - va_end(ap); +constexpr ConversionSpecifier parse_conversion_specifier(char ch, char prev_ch) +{ + switch (ch) { + case 'd': + case 'i': + return CONVSPEC_SIGNED_INT; + case 'o': + case 'u': + case 'x': + case 'X': + case 'm': + return CONVSPEC_UNSIGNED_INT; + case 'c': + if (prev_ch == 'l' || prev_ch == 'q' || prev_ch == 'L') { + // wchar not supported + return CONVSPEC_ERROR; + } + return CONVSPEC_UNSIGNED_INT; + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + return CONVSPEC_DOUBLE; + case 's': + if (prev_ch == 'l' || prev_ch == 'q' || prev_ch == 'L') { + // wchar not supported + return CONVSPEC_ERROR; + } + return CONVSPEC_CHAR_PTR; + case 'p': + return CONVSPEC_VOID_PTR; + case '$': // positional parameters + case 'n': + case 'S': + return CONVSPEC_ERROR; + default: + return CONVSPEC_NONE; + } +} - return string; +enum class DynamicIntCount : uint8_t { + NONE = 0, + ONE = 1, + TWO = 2, +}; + +// Describes a printf-style format conversion specifier found in a format string. +struct FoundFormatSpec +{ + // The start offset of the conversion spec in the format string. + int start; + // The end offset of the conversion spec in the format string. + int end; + ConversionSpecifier spec; + // Number of int args consumed by '*' dynamic width/precision args. + DynamicIntCount num_dynamic_ints; +}; + +// Ensure there is no format spec. +constexpr void ensure_no_format_spec(std::string_view fmt, int index, bool *has_escapes) +{ + int fmt_size = static_cast(fmt.size()); + // A trailing '%' is not a format spec. + while (index + 1 < fmt_size) { + if (fmt[index] != '%') { + ++index; + continue; + } + if (fmt[index + 1] != '%') { + YOSYS_ABORT("More format conversion specifiers than arguments"); + } + *has_escapes = true; + index += 2; + } +} + +// Returns the next format conversion specifier (starting with '%'). +// Returns CONVSPEC_NONE if there isn't a format conversion specifier. +constexpr FoundFormatSpec find_next_format_spec(std::string_view fmt, int fmt_start, bool *has_escapes) +{ + int index = fmt_start; + int fmt_size = static_cast(fmt.size()); + while (index < fmt_size) { + if (fmt[index] != '%') { + ++index; + continue; + } + int p = index + 1; + uint8_t num_dynamic_ints = 0; + while (true) { + if (p == fmt_size) { + return {0, 0, CONVSPEC_NONE, DynamicIntCount::NONE}; + } + if (fmt[p] == '%') { + *has_escapes = true; + index = p + 1; + break; + } + if (fmt[p] == '*') { + if (num_dynamic_ints >= 2) { + return {0, 0, CONVSPEC_ERROR, DynamicIntCount::NONE}; + } + ++num_dynamic_ints; + } + ConversionSpecifier spec = parse_conversion_specifier(fmt[p], fmt[p - 1]); + if (spec != CONVSPEC_NONE) { + return {index, p + 1, spec, static_cast(num_dynamic_ints)}; + } + ++p; + } + } + return {0, 0, CONVSPEC_NONE, DynamicIntCount::NONE}; +} + +template +constexpr typename std::enable_if::type +check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormatSpec*, DynamicIntCount) +{ + ensure_no_format_spec(fmt, fmt_start, has_escapes); +} + +// Check that the format string `fmt.substr(fmt_start)` is valid for the given type arguments. +// Fills `specs` with the FoundFormatSpecs found in the format string. +// `int_args_consumed` is the number of int arguments already consumed to satisfy the +// dynamic width/precision args for the next format conversion specifier. +template +constexpr void check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormatSpec* specs, + DynamicIntCount int_args_consumed) +{ + FoundFormatSpec found = find_next_format_spec(fmt, fmt_start, has_escapes); + if (found.num_dynamic_ints > int_args_consumed) { + // We need to consume at least one more int for the dynamic + // width/precision of this format conversion specifier. + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected dynamic int argument"); + } + check_format(fmt, fmt_start, has_escapes, specs, + static_cast(static_cast(int_args_consumed) + 1)); + return; + } + switch (found.spec) { + case CONVSPEC_NONE: + YOSYS_ABORT("Expected format conversion specifier for argument"); + break; + case CONVSPEC_ERROR: + YOSYS_ABORT("Found unsupported format conversion specifier"); + break; + case CONVSPEC_SIGNED_INT: + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected type convertible to signed integer"); + } + *specs = found; + break; + case CONVSPEC_UNSIGNED_INT: + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected type convertible to unsigned integer"); + } + *specs = found; + break; + case CONVSPEC_DOUBLE: + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected type convertible to double"); + } + *specs = found; + break; + case CONVSPEC_CHAR_PTR: + if constexpr (!std::is_convertible_v && + !std::is_convertible_v && + !std::is_convertible_v) { + YOSYS_ABORT("Expected type convertible to char *"); + } + *specs = found; + break; + case CONVSPEC_VOID_PTR: + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected pointer type"); + } + *specs = found; + break; + } + check_format(fmt, found.end, has_escapes, specs + 1, DynamicIntCount::NONE); +} + +// Emit the string representation of `arg` that has been converted to a `long long'. +void format_emit_long_long(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, long long arg); + +// Emit the string representation of `arg` that has been converted to a `unsigned long long'. +void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, unsigned long long arg); + +// Emit the string representation of `arg` that has been converted to a `double'. +void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, double arg); + +// Emit the string representation of `arg` that has been converted to a `const char*'. +void format_emit_char_ptr(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const char *arg); + +// Emit the string representation of `arg` that has been converted to a `std::string'. +void format_emit_string(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const std::string &arg); + +// Emit the string representation of `arg` that has been converted to a `std::string_view'. +void format_emit_string_view(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, std::string_view arg); + +// Emit the string representation of `arg` that has been converted to a `double'. +void format_emit_void_ptr(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const void *arg); + +// Emit the string representation of `arg` according to the given `FoundFormatSpec`, +// appending it to `result`. +template +inline void format_emit_one(std::string &result, std::string_view fmt, const FoundFormatSpec &ffspec, + int *dynamic_ints, const Arg& arg) +{ + std::string_view spec = fmt.substr(ffspec.start, ffspec.end - ffspec.start); + DynamicIntCount num_dynamic_ints = ffspec.num_dynamic_ints; + switch (ffspec.spec) { + case CONVSPEC_SIGNED_INT: + if constexpr (std::is_convertible_v) { + long long s = arg; + format_emit_long_long(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + case CONVSPEC_UNSIGNED_INT: + if constexpr (std::is_convertible_v) { + unsigned long long s = arg; + format_emit_unsigned_long_long(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + case CONVSPEC_DOUBLE: + if constexpr (std::is_convertible_v) { + double s = arg; + format_emit_double(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + case CONVSPEC_CHAR_PTR: + if constexpr (std::is_convertible_v) { + const char *s = arg; + format_emit_char_ptr(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + if constexpr (std::is_convertible_v) { + const std::string &s = arg; + format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + if constexpr (std::is_convertible_v) { + const std::string_view &s = arg; + format_emit_string_view(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + case CONVSPEC_VOID_PTR: + if constexpr (std::is_convertible_v) { + const void *s = arg; + format_emit_void_ptr(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + default: + break; + } + YOSYS_ABORT("Internal error"); +} + +// Append the format string `fmt` to `result`, assuming there are no format conversion +// specifiers other than "%%" and therefore no arguments. Unescape "%%". +void format_emit_unescaped(std::string &result, std::string_view fmt); +std::string unescape_format_string(std::string_view fmt); + +inline void format_emit(std::string &result, std::string_view fmt, int fmt_start, + bool has_escapes, const FoundFormatSpec*, int*, DynamicIntCount) +{ + fmt = fmt.substr(fmt_start); + if (has_escapes) { + format_emit_unescaped(result, fmt); + } else { + result += fmt; + } +} +// Format `args` according to `fmt` (starting at `fmt_start`) and `specs` and append to `result`. +// `num_dynamic_ints` in `dynamic_ints[]` have already been collected to provide as +// dynamic width/precision args for the next format conversion specifier. +template +inline void format_emit(std::string &result, std::string_view fmt, int fmt_start, bool has_escapes, + const FoundFormatSpec* specs, int *dynamic_ints, DynamicIntCount num_dynamic_ints, + const Arg &arg, const Args &... args) +{ + if (specs->num_dynamic_ints > num_dynamic_ints) { + // Collect another int for the dynamic width precision/args + // for the next format conversion specifier. + if constexpr (std::is_convertible_v) { + dynamic_ints[static_cast(num_dynamic_ints)] = arg; + } else { + YOSYS_ABORT("Internal error"); + } + format_emit(result, fmt, fmt_start, has_escapes, specs, dynamic_ints, + static_cast(static_cast(num_dynamic_ints) + 1), args...); + return; + } + std::string_view str = fmt.substr(fmt_start, specs->start - fmt_start); + if (has_escapes) { + format_emit_unescaped(result, str); + } else { + result += str; + } + format_emit_one(result, fmt, *specs, dynamic_ints, arg); + format_emit(result, fmt, specs->end, has_escapes, specs + 1, dynamic_ints, DynamicIntCount::NONE, args...); +} + +template +inline std::string format_emit_toplevel(std::string_view fmt, bool has_escapes, const FoundFormatSpec* specs, const Args &... args) +{ + std::string result; + int dynamic_ints[2] = { 0, 0 }; + format_emit(result, fmt, 0, has_escapes, specs, dynamic_ints, DynamicIntCount::NONE, args...); + return result; +} +template <> +inline std::string format_emit_toplevel(std::string_view fmt, bool has_escapes, const FoundFormatSpec*) +{ + if (!has_escapes) { + return std::string(fmt); + } + return unescape_format_string(fmt); +} + +// This class parses format strings to build a list of `FoundFormatSpecs` in `specs`. +// When the compiler supports `consteval` (C++20), this parsing happens at compile time and +// type errors will be reported at compile time. Otherwise the parsing happens at +// runtime and type errors will trigger an `abort()` at runtime. +template +class FmtString +{ +public: + // Implicit conversion from const char * means that users can pass + // C string constants which are automatically parsed. + YOSYS_CONSTEVAL FmtString(const char *p) : fmt(p) + { + check_format(fmt, 0, &has_escapes, specs, DynamicIntCount::NONE); + } + std::string format(const Args &... args) + { + return format_emit_toplevel(fmt, has_escapes, specs, args...); + } +private: + std::string_view fmt; + bool has_escapes = false; + FoundFormatSpec specs[sizeof...(Args)] = {}; +}; + +template struct WrapType { using type = T; }; +template using TypeIdentity = typename WrapType::type; + +template +inline std::string stringf(FmtString...> fmt, Args... args) +{ + return fmt.format(args...); } int readsome(std::istream &f, char *s, int n); diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index e84676bc0..ecc8ce623 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -134,6 +134,15 @@ # define YS_COLD #endif +#ifdef __cpp_consteval +#define YOSYS_CONSTEVAL consteval +#else +// If we can't use consteval we can at least make it constexpr. +#define YOSYS_CONSTEVAL constexpr +#endif + +#define YOSYS_ABORT(s) abort() + #include "kernel/io.h" YOSYS_NAMESPACE_BEGIN From 2c8b4d7ad13e4817a36b3c20f20a47b658dd5b9e Mon Sep 17 00:00:00 2001 From: Drew Lewis Date: Tue, 22 Jul 2025 19:49:10 +0000 Subject: [PATCH 123/181] Use unordered_map instead of dict for IdString char* to index storage. dict is pretty slow when you don't ever need to iterate the container in order. And the hashfunction for char* in dict hashes for every single byte in the string, likely doing significantly more work than std::hash. --- kernel/rtlil.cc | 2 +- kernel/rtlil.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 8a0080dbf..d24ab4d1e 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN bool RTLIL::IdString::destruct_guard_ok = false; RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard; std::vector RTLIL::IdString::global_id_storage_; -dict RTLIL::IdString::global_id_index_; +std::unordered_map RTLIL::IdString::global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT std::vector RTLIL::IdString::global_refcount_storage_; std::vector RTLIL::IdString::global_free_idx_list_; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 504fa0062..745b8fcaf 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -23,6 +23,9 @@ #include "kernel/yosys_common.h" #include "kernel/yosys.h" +#include +#include + YOSYS_NAMESPACE_BEGIN namespace RTLIL @@ -122,7 +125,7 @@ struct RTLIL::IdString } destruct_guard; static std::vector global_id_storage_; - static dict global_id_index_; + static std::unordered_map global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT static std::vector global_refcount_storage_; static std::vector global_free_idx_list_; From d9b3a3f7bb6f054181d587298b9ca3f19fa816b1 Mon Sep 17 00:00:00 2001 From: Drew Lewis Date: Tue, 22 Jul 2025 15:38:02 +0000 Subject: [PATCH 124/181] Improve the performance of `concat_name` in the flattening pass - Make IdString parameter pass by const ref to avoid IdString ref counting in the constructor - Remove extra std::string allocation to remove prefix - Avoid using `stringf` for concatenation --- passes/techmap/flatten.cc | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc index 6363b3432..299b18006 100644 --- a/passes/techmap/flatten.cc +++ b/passes/techmap/flatten.cc @@ -24,20 +24,36 @@ #include #include #include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -IdString concat_name(RTLIL::Cell *cell, IdString object_name, const std::string &separator = ".") +template +[[nodiscard]] std::string concat_views(const Args&... views) { + static_assert((std::is_convertible_v && ...), + "All arguments must be convertible to std::string_view."); + const std::size_t total_size = (std::string_view(views).size() + ... + 0); + + std::string result; + result.reserve(total_size); + + (result.append(views), ...); + return result; +} + +IdString concat_name(RTLIL::Cell *cell, IdString const &object_name, const std::string &separator = ".") { - if (object_name[0] == '\\') - return stringf("%s%s%s", cell->name.c_str(), separator.c_str(), object_name.c_str() + 1); - else { - std::string object_name_str = object_name.str(); - if (object_name_str.substr(0, 8) == "$flatten") - object_name_str.erase(0, 8); - return stringf("$flatten%s%s%s", cell->name.c_str(), separator.c_str(), object_name_str.c_str()); + std::string_view object_name_view(object_name.c_str()); + if (object_name_view[0] == '\\'){ + return concat_views(cell->name.c_str(), separator, object_name_view.substr(1)); } + + constexpr std::string_view prefix = "$flatten"; + if (object_name_view.substr(0, prefix.size()) == prefix){ + object_name_view.remove_prefix(prefix.size()); + } + return concat_views(prefix, cell->name.c_str(), separator, object_name_view); } template From 5392a42a9772c049a21efc0751bf3a65fd8841f3 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 29 Jul 2025 16:11:13 +0200 Subject: [PATCH 125/181] Update ABC to latest --- abc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abc b/abc index e55d316cc..fcd8ac34d 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit e55d316cc9a7f72a84a76eda555aa6ec083c9d0d +Subproject commit fcd8ac34d6105b48f32dfaf31983b071c46fb7b9 From b64484795d0f0c304494e28c0b3b40344cb9f21d Mon Sep 17 00:00:00 2001 From: Fred Tombs Date: Tue, 29 Jul 2025 15:27:59 -0400 Subject: [PATCH 126/181] Fix typos in memlib --- passes/memory/memlib.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/passes/memory/memlib.md b/passes/memory/memlib.md index 855aa1345..f3c0dd937 100644 --- a/passes/memory/memlib.md +++ b/passes/memory/memlib.md @@ -148,7 +148,7 @@ The rules for this property are as follows: - for every available width, the width needs to be a multiple of the byte size, or the byte size needs to be larger than the width -- if the byte size is larger than the width, the byte enable signel is assumed +- if the byte size is larger than the width, the byte enable signal is assumed to be one bit wide and cover the whole port - otherwise, the byte enable signal has one bit for every `byte` bits of the data port @@ -176,7 +176,7 @@ Eg. for the following properties: The cost of a given cell will be assumed to be `(8 - 7) + 7 * (used_bits / 14)`. If `widthscale` is used, The pass will attach a `BITS_USED` parameter to mapped -calls, with a bitmask of which data bits of the memory are actually in use. +cells, with a bitmask of which data bits of the memory are actually in use. The parameter width will be the widest width in the `widths` property, and the bit correspondence is defined accordingly. @@ -193,7 +193,7 @@ one of the following values: - `zero`: the memory contents are zero, memories can be mapped to this cell iff their initialization value is entirely zero or undef - `any`: the memory contents can be arbitrarily selected, and the initialization - will be passes as the `INIT` parameter to the mapped cell + will be passed as the `INIT` parameter to the mapped cell - `no_undef`: like `any`, but only 0 and 1 bit values are supported (the pass will convert any x bits to 0) @@ -234,7 +234,7 @@ Ports come in 5 kinds: - `ar`: asynchronous read port - `sr`: synchronous read port - `sw`: synchronous write port -- `arsw`: simultanous synchronous write + asynchronous read with common address (commonly found in LUT RAMs) +- `arsw`: simultaneous synchronous write + asynchronous read with common address (commonly found in LUT RAMs) - `srsw`: synchronous write + synchronous read with common address The port properties available are: @@ -419,7 +419,7 @@ If not provided, `none` is assumed for all three properties. The `wrprio` property is only allowed on write ports and defines a priority relationship between port — when `wrprio "B";` is used in definition of port `"A"`, and both ports -simultanously write to the same memory cell, the value written by port `"A"` will have +simultaneously write to the same memory cell, the value written by port `"A"` will have precedence. This property is optional, and can be used multiple times as necessary. If no relationship @@ -493,7 +493,7 @@ will disallow combining the RAM option `ABC = 2` with port option `DEF = "GHI"`. ## Ifdefs -To allow reusing a library for multiple FPGA families with slighly differing +To allow reusing a library for multiple FPGA families with slightly differing capabilities, `ifdef` (and `ifndef`) blocks are provided: ifdef IS_FANCY_FPGA_WITH_CONFIGURABLE_ASYNC_RESET { From 6440499e572d4d75ca5f857023c77b27ee1a69f8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 00:26:56 +0000 Subject: [PATCH 127/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bcb855940..2dcc7ed72 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+115 +YOSYS_VER := 0.55+144 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From f92a53ec31edb44e985546dcb4c8d6d8c275959c Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 30 Jul 2025 10:51:54 +0200 Subject: [PATCH 128/181] verific: handle nullptr for message_id --- frontends/verific/verific.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index cf8fad8b0..7284c7cd9 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -123,7 +123,7 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil msg_type == VERIFIC_IGNORE ? "IGNORE" : msg_type == VERIFIC_INFO ? "INFO" : msg_type == VERIFIC_COMMENT ? "COMMENT" : - msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id); + msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id ? message_id : ""); string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : ""; message += vstringf(msg, args); From 206d2a455374c1ff25402a6efdcf7e4b47d6d093 Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Wed, 30 Jul 2025 21:31:34 +0300 Subject: [PATCH 129/181] opt_dff: refactor simplify_patterns --- passes/opt/opt_dff.cc | 53 ++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index def177133..036c1b917 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -172,60 +172,57 @@ struct OptDffWorker void simplify_patterns(patterns_t& patterns) { - // remove complimentary patterns - auto new_patterns = patterns; + auto find_comp = [](const auto& left, const auto& right) -> std::optional { + std::optional ret; + for (const auto &pt: left) + if (right.count(pt.first) == 0) + return {}; + else if (right.at(pt.first) == pt.second) + continue; + else + if (ret) + return {}; + else + ret = pt.first; + return ret; + }; + + // remove complimentary patterns bool optimized; do { optimized = false; for (auto i = patterns.begin(); i != patterns.end(); i++) { for (auto j = std::next(i, 1); j != patterns.end(); j++) { - const auto& smaller = (GetSize(*j) <= GetSize(*i)) ? *j : *i; - const auto& larger = (GetSize(*i) < GetSize(*j)) ? *j : *i; - auto left = std::move(smaller); - auto right = std::move(larger); + const auto& left = (GetSize(*j) <= GetSize(*i)) ? *j : *i; + auto right = (GetSize(*i) < GetSize(*j)) ? *j : *i; - std::optional complimentary_var; + const auto complimentary_var = find_comp(left, right); - for (const auto &pt : left) - if (right.count(pt.first) == 0) - goto next; - else if (right[pt.first] == pt.second) - continue; - else - if (complimentary_var) - goto next; - else - complimentary_var = pt.first; if (complimentary_var) { new_patterns.erase(right); right.erase(complimentary_var.value()); new_patterns.insert(right); optimized = true; } - next: - continue; } } patterns = new_patterns; } while(optimized); // remove redundant patterns - for (auto i = patterns.begin(); i != patterns.end(); ++i) { - for (auto j = std::next(i, 1); j != patterns.end(); ++j) { - const auto& smaller = (GetSize(*j) <= GetSize(*i)) ? *j : *i; - const auto& larger = (GetSize(*i) < GetSize(*j)) ? *j : *i; - auto left = std::move(smaller); - auto right = std::move(larger); + for (auto j = std::next(i, 1); j != patterns.end(); ++j) { + const auto& left = (GetSize(*j) <= GetSize(*i)) ? *j : *i; + const auto& right = (GetSize(*i) < GetSize(*j)) ? *j : *i; bool redundant = true; - for (const auto& pt : left) - if (right.count(pt.first) == 0 || right[pt.first] != pt.second) + for (const auto& pt : smaller) + if (larger.count(pt.first) == 0 || larger[pt.first] != pt.second) redundant = false; if (redundant) - new_patterns.erase(right); + new_patterns.erase(larger); } } patterns = std::move(new_patterns); From bfff7a47f17033e2d61c75a779d0078bc0f8cfcf Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Wed, 30 Jul 2025 21:34:42 +0300 Subject: [PATCH 130/181] typo --- passes/opt/opt_dff.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 036c1b917..2a9d67dd6 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -222,7 +222,7 @@ struct OptDffWorker if (larger.count(pt.first) == 0 || larger[pt.first] != pt.second) redundant = false; if (redundant) - new_patterns.erase(larger); + new_patterns.erase(right); } } patterns = std::move(new_patterns); From 85e0e8ca670333898e9cbbbd4f1bf94dc421ae9c Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Wed, 30 Jul 2025 21:40:20 +0300 Subject: [PATCH 131/181] typo --- passes/opt/opt_dff.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 2a9d67dd6..210c4828f 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -218,8 +218,8 @@ struct OptDffWorker bool redundant = true; - for (const auto& pt : smaller) - if (larger.count(pt.first) == 0 || larger[pt.first] != pt.second) + for (const auto& pt : left) + if (right.count(pt.first) == 0 || right.at(pt.first) != pt.second) redundant = false; if (redundant) new_patterns.erase(right); From 262b00d5e5fe6a1c60c047dcbabd522309e4d1ef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 00:26:33 +0000 Subject: [PATCH 132/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2dcc7ed72..d509b9234 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+144 +YOSYS_VER := 0.55+146 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From feabfd5703e2cba0ff29588e31b7902127d8d173 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Thu, 31 Jul 2025 16:39:27 +1000 Subject: [PATCH 133/181] docs: mention Yosys Discourse group and blog --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 71d47d76c..6c576f682 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ Web Site and Other Resources More information and documentation can be found on the Yosys web site: - https://yosyshq.net/yosys/ +If you have any Yosys-related questions, please post them on the Discourse group: +- https://yosyshq.discourse.group + Documentation from this repository is automatically built and available on Read the Docs: - https://yosyshq.readthedocs.io/projects/yosys @@ -34,6 +37,9 @@ verification front-end for Yosys, SBY: - https://yosyshq.readthedocs.io/projects/sby/ - https://github.com/YosysHQ/sby +The Yosys blog has news and articles from users: +- https://blog.yosyshq.com + Installation ============ @@ -242,7 +248,7 @@ Note that there is no need to build the manual if you just want to read it. Simply visit https://yosys.readthedocs.io/en/latest/ instead. In addition to those packages listed above for building Yosys from source, the -following are used for building the website: +following are used for building the website: $ sudo apt install pdf2svg faketime @@ -258,7 +264,7 @@ build process for the website. Or, run the following: Or for MacOS, using homebrew: $ brew install basictex - $ sudo tlmgr update --self + $ sudo tlmgr update --self $ sudo tlmgr install collection-latexextra latexmk tex-gyre The Python package, Sphinx, is needed along with those listed in @@ -268,5 +274,5 @@ The Python package, Sphinx, is needed along with those listed in From the root of the repository, run `make docs`. This will build/rebuild yosys as necessary before generating the website documentation from the yosys help -commands. To build for pdf instead of html, call +commands. To build for pdf instead of html, call `make docs DOC_TARGET=latexpdf`. From ffd52a0d8e43aba34d8d87def77b073c9fd90a07 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Thu, 31 Jul 2025 10:19:44 +0000 Subject: [PATCH 134/181] Making `stringf()` use the format conversion specs as-is without widening them. And make sure our fast-path for `%d` and `%u` narrows to `int` correctly. Resolves #5260 --- kernel/io.cc | 34 ++---------------- kernel/io.h | 2 +- tests/unit/kernel/ioTest.cc | 72 +++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 tests/unit/kernel/ioTest.cc diff --git a/kernel/io.cc b/kernel/io.cc index 3deb54d86..9811919ba 100644 --- a/kernel/io.cc +++ b/kernel/io.cc @@ -405,37 +405,9 @@ std::string unescape_format_string(std::string_view fmt) static std::string string_view_stringf(std::string_view spec, ...) { - std::string fmt(spec); - char format_specifier = fmt[fmt.size() - 1]; - switch (format_specifier) { - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': { - // Strip any length modifier off `fmt` - std::string long_fmt; - for (size_t i = 0; i + 1 < fmt.size(); ++i) { - char ch = fmt[i]; - if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { - break; - } - long_fmt.push_back(ch); - } - // Add `lld` or whatever - long_fmt += "ll"; - long_fmt.push_back(format_specifier); - fmt = long_fmt; - break; - } - default: - break; - } - va_list ap; va_start(ap, spec); - std::string result = vstringf(fmt.c_str(), ap); + std::string result = vstringf(std::string(spec).c_str(), ap); va_end(ap); return result; } @@ -464,7 +436,7 @@ void format_emit_long_long(std::string &result, std::string_view spec, int *dyna { if (spec == "%d") { // Format checking will have guaranteed num_dynamic_ints == 0. - result += std::to_string(arg); + result += std::to_string(static_cast(arg)); return; } format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); @@ -475,7 +447,7 @@ void format_emit_unsigned_long_long(std::string &result, std::string_view spec, { if (spec == "%u") { // Format checking will have guaranteed num_dynamic_ints == 0. - result += std::to_string(arg); + result += std::to_string(static_cast(arg)); return; } if (spec == "%c") { diff --git a/kernel/io.h b/kernel/io.h index ea2499e43..dafef8bfa 100644 --- a/kernel/io.h +++ b/kernel/io.h @@ -62,7 +62,7 @@ enum ConversionSpecifier : uint8_t CONVSPEC_UNSIGNED_INT, // Consumes a "double" CONVSPEC_DOUBLE, - // Consumes a "const char*" + // Consumes a "const char*" or other string type CONVSPEC_CHAR_PTR, // Consumes a "void*" CONVSPEC_VOID_PTR, diff --git a/tests/unit/kernel/ioTest.cc b/tests/unit/kernel/ioTest.cc new file mode 100644 index 000000000..7cb8498cb --- /dev/null +++ b/tests/unit/kernel/ioTest.cc @@ -0,0 +1,72 @@ +#include + +#include "kernel/io.h" + +YOSYS_NAMESPACE_BEGIN + +TEST(KernelStringfTest, integerTruncation) +{ + EXPECT_EQ(stringf("%d", 1LL << 32), "0"); + EXPECT_EQ(stringf("%u", 1LL << 32), "0"); + EXPECT_EQ(stringf("%x", 0xff12345678LL), "12345678"); + EXPECT_EQ(stringf("%hu", 0xff12345678LL), "22136"); +} + +TEST(KernelStringfTest, charFormat) +{ + EXPECT_EQ(stringf("%c", 256), std::string_view("\0", 1)); + EXPECT_EQ(stringf("%c", -1), "\377"); +} + +TEST(KernelStringfTest, floatFormat) +{ + EXPECT_EQ(stringf("%g", 1.0), "1"); +} + +TEST(KernelStringfTest, intToFloat) +{ + EXPECT_EQ(stringf("%g", 1), "1"); +} + +TEST(KernelStringfTest, floatToInt) +{ + EXPECT_EQ(stringf("%d", 1.0), "1"); + EXPECT_EQ(stringf("%d", -1.6), "-1"); +} + +TEST(KernelStringfTest, stringParam) +{ + EXPECT_EQ(stringf("%s", std::string("hello")), "hello"); +} + +TEST(KernelStringfTest, stringViewParam) +{ + EXPECT_EQ(stringf("%s", std::string_view("hello")), "hello"); +} + +TEST(KernelStringfTest, escapePercent) +{ + EXPECT_EQ(stringf("%%"), "%"); +} + +TEST(KernelStringfTest, trailingPercent) +{ + EXPECT_EQ(stringf("%"), "%"); +} + +TEST(KernelStringfTest, dynamicWidth) +{ + EXPECT_EQ(stringf("%*s", 8, "hello"), " hello"); +} + +TEST(KernelStringfTest, dynamicPrecision) +{ + EXPECT_EQ(stringf("%.*f", 4, 1.0), "1.0000"); +} + +TEST(KernelStringfTest, dynamicWidthAndPrecision) +{ + EXPECT_EQ(stringf("%*.*f", 8, 4, 1.0), " 1.0000"); +} + +YOSYS_NAMESPACE_END From 555c08a98a6805e9752ab4b93439d28b73d1e2e3 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Fri, 1 Aug 2025 11:30:42 +1000 Subject: [PATCH 135/181] docs: update issues template with Discourse link --- .github/ISSUE_TEMPLATE/config.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index bef410a3c..c758bf1f6 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,8 @@ contact_links: - - name: Discussions - url: https://github.com/YosysHQ/yosys/discussions - about: "Have a question? Ask it on our discussions page!" - - name: Community Slack - url: https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA - about: "Yosys Community Slack" + - name: Discourse + url: https://yosyshq.discourse.group + about: "Have a question? Ask it on our Discourse group!" - name: IRC Channel url: https://web.libera.chat/#yosys about: "#yosys on irc.libera.chat" - + From 895dfd963f6597a5d4bd5fe352c94d71a032aa04 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:47:52 +1200 Subject: [PATCH 136/181] raise_error: Add -always --- passes/tests/raise_error.cc | 48 +++++++++++++++++++++++++---------- tests/bugpoint/raise_error.ys | 11 ++++++++ 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/passes/tests/raise_error.cc b/passes/tests/raise_error.cc index f9055e6d2..588a40806 100644 --- a/passes/tests/raise_error.cc +++ b/passes/tests/raise_error.cc @@ -20,12 +20,15 @@ struct RaiseErrorPass : public Pass { log(" -stderr\n"); log(" Log error messages directly to stderr instead of using 'log_error'.\n"); log("\n"); + log(" -always\n"); + log(" Raise an error even if the 'raise_error' attribute is missing.\n"); + log("\n"); } void execute(vector args, RTLIL::Design *design) override { log_header(design, "Executing RAISE_ERROR pass.\n"); - bool use_stderr = false; + bool use_stderr = false, always = false; int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -34,6 +37,10 @@ struct RaiseErrorPass : public Pass { use_stderr = true; continue; } + if (args[argidx] == "-always") { + always = true; + continue; + } break; } @@ -55,27 +62,40 @@ struct RaiseErrorPass : public Pass { if (err_obj != nullptr) break; } + + if (err_obj == nullptr && !always) { + log("'raise_error' attribute not found\n"); + return; + } + + int err_no = 1; + string err_msg = ""; if (err_obj != nullptr) { log("Raising error from '%s'.\n", log_id(err_obj)); - auto err_no = err_obj->attributes[ID::raise_error].as_int(); - if (err_no < 256) { - log_flush(); - } else { - auto err_msg = err_obj->get_string_attribute(ID::raise_error); - if (use_stderr) { - std::cerr << err_msg << std::endl; - err_no = 1; - } else { - log_error("%s\n", err_msg.c_str()); - } + err_no = err_obj->attributes[ID::raise_error].as_int(); + if (err_no > 256) { + err_msg = err_obj->get_string_attribute(ID::raise_error); + err_no = 1; } + } else { + err_msg = "No 'raise_error' attribute found"; + } + + if (err_msg.size() > 0) { + if (use_stderr) { + std::cerr << err_msg << std::endl; + } else { + log_error("%s\n", err_msg.c_str()); + } + } + + if (err_no < 256) { + log_flush(); #if defined(_MSC_VER) _exit(err_no); #else _Exit(err_no); #endif - } else { - log("'raise_error' attribute not found\n"); } } } RaiseErrorPass; diff --git a/tests/bugpoint/raise_error.ys b/tests/bugpoint/raise_error.ys index a0a03f447..66db2916a 100644 --- a/tests/bugpoint/raise_error.ys +++ b/tests/bugpoint/raise_error.ys @@ -14,6 +14,12 @@ EOF select -assert-mod-count 3 =* design -stash read +# empty design does not raise_error +design -reset +logger -expect log "'raise_error' attribute not found" 1 +raise_error +logger -check-expected + # raise_error with int exits with status design -load read bugpoint -suffix error -yosys ../../yosys -command raise_error -expect-return 7 @@ -41,3 +47,8 @@ rename top abc bugpoint -suffix error -yosys ../../yosys -command "raise_error -stderr" -err-grep "help me" -expect-return 1 select -assert-mod-count 1 =* select -assert-mod-count 1 other + +# empty design can raise_error -always +design -reset +bugpoint -suffix error -yosys ../../yosys -command "raise_error -always" -grep "ERROR: No 'raise_error' attribute found" -expect-return 1 +bugpoint -suffix error -yosys ../../yosys -command "raise_error -always -stderr" -err-grep "No 'raise_error' attribute found" -expect-return 1 From f78cd9d13ff50f25beebb056a8558413bfabf459 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:54:32 +1200 Subject: [PATCH 137/181] raise_error: Extra test --- tests/bugpoint/raise_error.ys | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/bugpoint/raise_error.ys b/tests/bugpoint/raise_error.ys index 66db2916a..8fb10eb96 100644 --- a/tests/bugpoint/raise_error.ys +++ b/tests/bugpoint/raise_error.ys @@ -26,6 +26,12 @@ bugpoint -suffix error -yosys ../../yosys -command raise_error -expect-return 7 select -assert-mod-count 1 =* select -assert-mod-count 1 top +# raise_error -always still uses 'raise_error' attribute if possible +design -load read +bugpoint -suffix error -yosys ../../yosys -command "raise_error -always" -expect-return 7 +select -assert-mod-count 1 =* +select -assert-mod-count 1 top + # raise_error with string prints message and exits with 1 design -load read rename top abc From 15b4716d1856d03ee0fe1dddbcc24c67f6d32729 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 3 Aug 2025 00:29:54 +0000 Subject: [PATCH 138/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d509b9234..c828aa2d5 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+146 +YOSYS_VER := 0.55+150 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 75b62d01645bfb454a2e0452020938b598c0a02f Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Mon, 4 Aug 2025 15:38:19 +0200 Subject: [PATCH 139/181] verificsva: Fix typo in the cover only followed-by operator support --- frontends/verific/verificsva.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 6e87bd267..60d8292b8 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -1616,7 +1616,7 @@ struct VerificSvaImporter inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION || (mode_cover && ( inst->Type() == PRIM_SVA_OVERLAPPED_FOLLOWED_BY || - inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION))) + inst->Type() == PRIM_SVA_NON_OVERLAPPED_FOLLOWED_BY))) { Net *antecedent_net = inst->GetInput1(); Net *consequent_net = inst->GetInput2(); From 86ef7f7edee18c075197ba80a4d03cb45c641003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Milanovi=C4=87?= Date: Mon, 4 Aug 2025 17:43:03 +0200 Subject: [PATCH 140/181] Update wheels to Trusted Publisher --- .github/workflows/wheels.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b01ce6b3a..d81e340aa 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -119,6 +119,11 @@ jobs: upload_wheels: name: Upload Wheels runs-on: ubuntu-latest + # Specifying a GitHub environment is optional, but strongly encouraged + environment: pypi + permissions: + # IMPORTANT: this permission is mandatory for Trusted Publishing + id-token: write needs: build_wheels steps: - uses: actions/download-artifact@v4 @@ -132,6 +137,3 @@ jobs: mv *.whl ./dist - name: Publish uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_TOKEN }} - repository-url: ${{ vars.PYPI_INDEX || 'https://upload.pypi.org/legacy/' }} From 074f5e7ea606564f782e78812d1e5f6a7ee45627 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:56 +1200 Subject: [PATCH 141/181] Docs: Initial outline of minimizing designs How to guide for using bugpoint, minimizing yosys scripts, and minimizing verilog code. AKA how to MVCE. --- docs/source/using_yosys/bugpoint.rst | 210 +++++++++++++++++++++++++++ docs/source/using_yosys/index.rst | 1 + 2 files changed, 211 insertions(+) create mode 100644 docs/source/using_yosys/bugpoint.rst diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst new file mode 100644 index 000000000..8c685c35c --- /dev/null +++ b/docs/source/using_yosys/bugpoint.rst @@ -0,0 +1,210 @@ +Minimizing failing (or bugged) designs +====================================== + +- how to guide +- assumes knowledge and familiarity with Yosys +- something is wrong with your design OR something is wrong with Yosys + + + how to work out which + +- *read* the error message +- is it a Yosys error? (starts with ERROR:) + + + does it give you a line number from your design + +- is it a runtime error, e.g. SEGFAULT +- are you using the latest release of Yosys + + + has your problem already been fixed + +- is your input design valid? + + + if you're using Verilog, try load it with `iverilog`_ or `verilator`_ + +.. _iverilog: https://steveicarus.github.io/iverilog/ +.. _verilator: https://www.veripool.org/verilator/ + +- make sure to back up your code (design source and yosys script(s)) before + making any modifications + + + even if the code itself isn't important, this can help avoid "losing" the + error while trying to debug it + + +.. _minimize your RTLIL: + +Minimizing RTLIL designs with bugpoint +-------------------------------------- + +- `bugpoint`, only usable on platforms where Yosys can spawn executables + + + unavailable on emscripten and wasm + + can test by running e.g. ``yosys -qqp '!echo test'`` + + * the ``-qq`` prevents Yosys from outputting non-error messages to the + console, so this will either display the text ``test``, or an error + message about ``Shell`` being unavailable + * be careful about using ``!`` in bash as it will perform a history + substitution if not escaped with single quotes (double quotes will not + escape it) + * ``!`` does not need to be escaped in *Yosys* scripts or when called within + the interactive Yosys shell, *only* when called on the command line with + ``-p`` + +- single command (``yosys -p "" design.il``) +- *or* multiple commands in a separate script file + + + script shouldn't load the design + + ``yosys -s design.il`` + + `minimize your script`_ to reduce the time needed by `bugpoint` + +- doesn't require design to be in RTLIL format + + + can e.g. ``read_verilog ; prep -top ;`` before `bugpoint` + + this method may be more useful if you are trying to find a bug in your + design rather than Yosys + + but, `bugpoint` itself calls the command/script with an RTLIL dump, so if it + isn't reproducible from RTLIL then `bugpoint` won't work + +- follow `bugpoint` instructions + + + output design after `bugpoint` with `write_rtlil` + + use ``-grep ""`` to only accept a minimized design that crashes + with the ```` in the log file + + ``-modules``, ``-ports``, ``-cells``, and ``-processes`` will enable those + parts of the design to be removed + + * use the ``bugpoint_keep`` attribute on objects you don't want to be + removed, usually because you already know they are related to the failure + * ``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in + RTLIL, or ``setattr -set bugpoint_keep 1 [selection]`` from script + + + ``-runner ""`` can allow running ``yosys`` wrapped by another + command + + can also use `setenv` before `bugpoint` to set environment variables for + the spawned processes (e.g. ``setenv UBSAN_OPTIONS halt_on_error=1``) + +.. note:: + + Using `setenv` in this way **does not affect the current process**. Only + child processes will respect the assigned ``halt_on_error``. + + +.. _minimize your script: + +Minimizing scripts +------------------ + +- reminder to back up original code before modifying it +- if you're using command line, convert it to a script +- if you're using one of the :doc:`/using_yosys/synthesis/synth`, replace it + with its contents +- remove everything *after* the error occurs +- can use `log` command to print messages to help locate the failure point +- `echo` can also help (``echo on``) +- try ``write_rtlil ; design -reset; read_rtlil ;`` before + the failure point + + + ideally you now have a single command that is producing an error and can + `minimize your RTLIL`_ with the ```` output + + if not, try to move the write/reset/read earlier in the script until you can + reproduce the error + + if you have no idea where exactly you should put the reset, the best way is + to use a "binary search" type approach, reducing the possible options by + half after each attempt + + * for example, your script has 16 commands in it before failing on the 17th + * if resetting immediately before the 17th doesn't reproduce the error, try + between the 8th and 9th (8 is half of the total 16) + * if that produces the error then you can remove everything before the + `read_rtlil` and try reset again in the middle of what's left, making sure + to use a different name for the output file so that you don't overwrite + what you've already got + * if the error isn't produced then you need to go earlier still, so in this + case you would do between the 4th and 5th (4 is half of the previous 8) + * repeat this until you can't reduce the remaining commands any further + +.. TODO:: is it possible to dump scratchpad? + + is there anything else in the yosys/design state that doesn't get included in + `write_rtlil`? + +- you can also try to remove or comment out commands prior to the failing + command; just because the first and last commands are needed doesn't mean that + every command between them is + + +Minimizing Verilog designs +-------------------------- + +- manual process +- made easier if the error message is able to identify the source line or name + of the object +- reminder to back up original code before modifying it +- if a specific module is causing the problem, try to set that as the top + module, you can then remove + + + if the problem is parameter specific you may be able to change the default + parameters so that they match the problematic configuration + +- as with `minimize your script`_, if you have no idea what is or is not + relevant, try to follow a "binary search" type approach where you remove (or + comment out) roughly half of what's left at a time +- focusing on one type of object at a time simplifies the process, removing as + many as you can until the error disappears if any of the remaining objects are + removed +- periodically check if anything is totally disconnected (ports, wires, etc), if + it is then it can be removed too +- start by removing cells (instances of modules) + + + if a module has no more instances, remove it entirely + +- then processes +- try to remove or reduce assignments and operations + + + are there any wires/registers which get read but never written? + + * try removing the signal declaration and replacing references to it with + ``'0`` or ``'x`` + * try this with constants too + + + can you replace strings with numeric values? + + are you able to simplify any operations? like replacing ``a & '0`` with + ``'0`` + + if you have enable or reset logic, does the error still happen without that? + + can you reduce an ``if .. else`` to a single case? + +- if you're planning to share the minimized code: + + + make sure there is no sensitive or proprietary data in the design + + instead of a long string of numbers and letters that had some meaning (or + were randomly or sequentially generated), can you give it a single character + name like ``a`` or ``x`` + + please try to keep things in English, using the letters a-z and numbers 0-9 + (unless the error is arising because of the names used) + + +Creating an issue on GitHub +--------------------------- + +- "Reproduction Steps" is ideally a single code-block (starting and ending with + triple backquotes), containing the minimized yosys script file, which includes + the minimized design as a "here document" followed by the sequence of commands + which reproduce the error + +.. TODO:: https://tldp.org/LDP/abs/html/here-docs.html + + Actually fill out :doc:`/using_yosys/more_scripting/load_design` with here + docs info and then link to it from here + +.. code-block:: markdown + + ``` + read_rtlil < Date: Tue, 5 Aug 2025 09:53:56 +1200 Subject: [PATCH 142/181] docs: Outline loading a design page Talk about input files coming from command line, the `read` command, and features provided by `RTLIL::Frontend` (making note that `read_slang` is a subclass but `ghdl` isn't). --- .../more_scripting/load_design.rst | 119 +++++++++++++++--- 1 file changed, 101 insertions(+), 18 deletions(-) diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst index bbc55a36b..964e9a7df 100644 --- a/docs/source/using_yosys/more_scripting/load_design.rst +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -1,11 +1,88 @@ Loading a design -~~~~~~~~~~~~~~~~ +---------------- -keyword: Frontends +.. _input files: + +Input files on the command line +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- guesses frontend based on file extension + + + ``.v`` -> ``read -vlog2k`` + + ``.sv`` -> ``read -sv`` + + ``.vhd`` and ``.vhdl`` -> ``read -vhdl`` + + ``.blif`` and ``.eblif`` -> `read_blif` + + ``.json`` -> `read_json` + + ``.il`` -> `read_rtlil` (direct textual representation of Yosys internal + state) + +- command line also supports + + + ``.ys`` -> `script` + + ``.tcl`` -> `tcl` + + ``-`` -> reads stdin and treats it as a script + +The `read` command +~~~~~~~~~~~~~~~~~~ + +- standard method of loading designs +- also for defining macros and include directories +- uses `verific` command if available + + + ``-verific`` and ``-noverific`` options to enforce with/without Verific + + check ``help read`` for more about the options available and the filetypes + supported + +- fallback to `read_verilog` + +.. note:: + + The Verific frontend for Yosys, which provides the :cmd:ref:`verific` + command, requires Yosys to be built with Verific. For full functionality, + custom modifications to the Verific source code from YosysHQ are required, + but limited useability can be achieved with some stock Verific builds. Check + :doc:`/yosys_internals/extending_yosys/build_verific` for more. + +.. _Frontend: + +Yosys frontends +~~~~~~~~~~~~~~~ + +- typically start with ``read_`` +- built-in support for heredocs + + + in-line code with ``< Date: Tue, 5 Aug 2025 09:53:57 +1200 Subject: [PATCH 143/181] Docs: Bugpoint fixups from JF Also dropping the `autosectionlabel_maxdepth = 1` so that I can actually use the auto section labels. Adds warning on bash substitution on scripting intro page when talking about `yosys -p`. --- docs/source/conf.py | 1 - .../getting_started/scripting_intro.rst | 9 ++- docs/source/using_yosys/bugpoint.rst | 79 ++++++++++++------- .../more_scripting/load_design.rst | 18 ++++- 4 files changed, 74 insertions(+), 33 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 05dcb7d5f..e587b8d31 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -64,7 +64,6 @@ if os.getenv("READTHEDOCS"): # Ensure that autosectionlabel will produce unique names autosectionlabel_prefix_document = True -autosectionlabel_maxdepth = 1 # include todos for previews extensions.append('sphinx.ext.todo') diff --git a/docs/source/getting_started/scripting_intro.rst b/docs/source/getting_started/scripting_intro.rst index 01954c661..6a6e4ff51 100644 --- a/docs/source/getting_started/scripting_intro.rst +++ b/docs/source/getting_started/scripting_intro.rst @@ -26,7 +26,7 @@ of the comment is a semicolon ``;`` or a new line. .. code-block:: :caption: Using the ``-p`` option - $ yosys -p "read_verilog fifo.v; :this is a comment; prep" + $ yosys -p 'read_verilog fifo.v; :this is a comment; prep' .. warning:: @@ -42,6 +42,13 @@ will be raised by Yosys. `exec` provides a much more flexible way of executing commands, allowing the output to be logged and more control over when to generate errors. +.. warning:: + + Take care when using the ``yosys -p`` option. Some shells such as bash will + perform substitution options inside of a double quoted string, such as ``!`` + for history substitution and ``$`` for variable substitution; single quotes + should be used instead to pass the string to Yosys without substitution. + The synthesis starter script ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 8c685c35c..9b402858e 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -44,14 +44,10 @@ Minimizing RTLIL designs with bugpoint * the ``-qq`` prevents Yosys from outputting non-error messages to the console, so this will either display the text ``test``, or an error message about ``Shell`` being unavailable - * be careful about using ``!`` in bash as it will perform a history - substitution if not escaped with single quotes (double quotes will not - escape it) - * ``!`` does not need to be escaped in *Yosys* scripts or when called within - the interactive Yosys shell, *only* when called on the command line with - ``-p`` + * check :ref:`getting_started/scripting_intro:script parsing` for more about + the ``-p`` option and common pitfalls -- single command (``yosys -p "" design.il``) +- single command (``yosys -p '' design.il``) - *or* multiple commands in a separate script file + script shouldn't load the design @@ -68,26 +64,30 @@ Minimizing RTLIL designs with bugpoint - follow `bugpoint` instructions - + output design after `bugpoint` with `write_rtlil` - + use ``-grep ""`` to only accept a minimized design that crashes - with the ```` in the log file - + ``-modules``, ``-ports``, ``-cells``, and ``-processes`` will enable those - parts of the design to be removed + + output design after `bugpoint` with `write_rtlil` + + use ``-grep ""`` to only accept a minimized design that crashes + with the ```` in the log file + + ``-modules``, ``-ports``, ``-cells``, and ``-processes`` will enable those + parts of the design to be removed - * use the ``bugpoint_keep`` attribute on objects you don't want to be - removed, usually because you already know they are related to the failure - * ``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in - RTLIL, or ``setattr -set bugpoint_keep 1 [selection]`` from script + * use the ``bugpoint_keep`` attribute on objects you don't want to be + removed, usually because you already know they are related to the failure + * ``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in + RTLIL, or ``setattr -set bugpoint_keep 1 [selection]`` from script - + ``-runner ""`` can allow running ``yosys`` wrapped by another - command - + can also use `setenv` before `bugpoint` to set environment variables for - the spawned processes (e.g. ``setenv UBSAN_OPTIONS halt_on_error=1``) + + ``-runner ""`` can allow running ``yosys`` wrapped by another + command + + can also use `setenv` before `bugpoint` to set environment variables for + the spawned processes (e.g. ``setenv UBSAN_OPTIONS halt_on_error=1``) .. note:: - Using `setenv` in this way **does not affect the current process**. Only - child processes will respect the assigned ``halt_on_error``. + Using `setenv` in this way may not affect the current process as some + environment variables are only read on start up. For instance the + ``UBSAN_OPTIONS halt_on_error`` here only affects child processes, as does + the :doc:`Yosys environment variable` ``ABC``. While + others such as ``YOSYS_NOVERIFIC`` and ``HOME`` are evaluated each time they + are used. .. _minimize your script: @@ -187,15 +187,34 @@ Minimizing Verilog designs Creating an issue on GitHub --------------------------- -- "Reproduction Steps" is ideally a single code-block (starting and ending with - triple backquotes), containing the minimized yosys script file, which includes - the minimized design as a "here document" followed by the sequence of commands - which reproduce the error +- "Reproduction Steps" is ideally a code-block (starting and ending with triple + backquotes) containing the minimized design (Verilog or RTLIL), followed by a + code-block containing the minimized yosys script OR a command line call to + yosys with code-formatting (starting and ending with single backquotes) -.. TODO:: https://tldp.org/LDP/abs/html/here-docs.html +.. code-block:: markdown - Actually fill out :doc:`/using_yosys/more_scripting/load_design` with here - docs info and then link to it from here + min.v + ```verilog + // minimized Verilog design + ``` + + min.ys + ``` + read_verilog min.v + # minimum sequence of commands to reproduce error + ``` + + OR + + `yosys -p ': minimum sequence of commands;' min.v` + + +- alternatively can provide a single code-block which includes the minimized + design as a "here document" followed by the sequence of commands which + reproduce the error + + + see :doc:`/using_yosys/more_scripting/load_design` for more on heredocs. .. code-block:: markdown @@ -203,7 +222,7 @@ Creating an issue on GitHub read_rtlil <`` (or use + `hierarchy`) -- fallback to `read_verilog` +- fallback to `read_verilog` with ``-defer`` option + + + does not compile design until `hierarchy` command as discussed in + :doc:`/getting_started/example_synth` + + more similar to `verific` behaviour + +- ``read -define`` et al mapped to `verific` or `verilog_defines` +- similarly, ``read -incdir`` et al mapped to `verific` or `verilog_defaults` .. note:: @@ -129,3 +138,10 @@ Externally maintained plugins - both plugins above are included in `OSS CAD Suite`_ .. _OSS CAD Suite: https://github.com/YosysHQ/oss-cad-suite-build + +- `Synlig`_, which uses `Surelog`_ to provide SystemVerilog support + + + also implemented as a '`Frontend`_' + +.. _Synlig: https://github.com/chipsalliance/synlig +.. _Surelog: https://github.com/chipsalliance/Surelog From db3dc45bc68f97b244d0732260dbcd31094b1fc2 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:57 +1200 Subject: [PATCH 144/181] Docs: Tidying Fix error on duplicated heading. Drop `cmd_ref`_ link (everything already uses :doc:`cmd_ref`). --- docs/source/cmd_ref.rst | 2 -- .../using_yosys/more_scripting/interactive_investigation.rst | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index acf2d1d41..d2d59ba54 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -1,5 +1,3 @@ -.. _cmd_ref: - ================================================================================ Command line reference ================================================================================ diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst index e9c9bc9ac..ca4c76ee7 100644 --- a/docs/source/using_yosys/more_scripting/interactive_investigation.rst +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -311,8 +311,8 @@ cells, as the net-names are usually suppressed in the circuit diagram if they are auto-generated. Note that the output is in the RTLIL representation, described in :doc:`/yosys_internals/formats/rtlil_rep`. -Interactive Design Investigation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Design Investigation +~~~~~~~~~~~~~~~~~~~~ Yosys can also be used to investigate designs (or netlists created from other tools). From 6e1cc9c0cd278f30c1593e5d4a021de8ab578a53 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:57 +1200 Subject: [PATCH 145/181] docs: Some extra bugpoint bullets --- docs/source/using_yosys/bugpoint.rst | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 9b402858e..d292c2d32 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -67,8 +67,11 @@ Minimizing RTLIL designs with bugpoint + output design after `bugpoint` with `write_rtlil` + use ``-grep ""`` to only accept a minimized design that crashes with the ```` in the log file + + * only checks log file, will not match runtime errors + + ``-modules``, ``-ports``, ``-cells``, and ``-processes`` will enable those - parts of the design to be removed + parts of the design to be removed (default is allow removing all) * use the ``bugpoint_keep`` attribute on objects you don't want to be removed, usually because you already know they are related to the failure @@ -89,6 +92,23 @@ Minimizing RTLIL designs with bugpoint others such as ``YOSYS_NOVERIFIC`` and ``HOME`` are evaluated each time they are used. +- check minimized design still fails, especially if not using `write_rtlil` + + + e.g. if you ran :ref:`bugpoint_script` below, then calling ``yosys -s + min.v`` should still fail in the same way + + `write_rtlil` is more reliable since `bugpoint` will have run that exact + code through the failing script; other ``write_*`` commands convert from the + RTLIL and then back again during the ``read_*`` which can result in + differences which mean the design no longer fails + +.. code-block:: yoscrypt + :caption: example `bugpoint` minimizer + :name: bugpoint_script + + read_verilog design.v + bugpoint -script + write_verilog min.v + .. _minimize your script: From aefe3443aac9fbeb5549502893d12afdebc9f9a3 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:57 +1200 Subject: [PATCH 146/181] docs: Minimizing synth with -run bullets --- docs/source/using_yosys/bugpoint.rst | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index d292c2d32..804787df8 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -119,9 +119,44 @@ Minimizing scripts - if you're using command line, convert it to a script - if you're using one of the :doc:`/using_yosys/synthesis/synth`, replace it with its contents + + + can also do this piece-wise with the ``-run`` option + + e.g. replacing ``synth -top -lut`` with :ref:`replace_synth` + + the options ``-top -lut`` can be provided to each `synth` step, or + to just the step(s) where it is relevant, as done here + +.. code-block:: yoscrypt + :caption: example replacement script for `synth` command + :name: replace_synth + + synth -top -run :coarse + synth -lut -run coarse:fine + synth -lut -run fine:check + synth -run check: + - remove everything *after* the error occurs - can use `log` command to print messages to help locate the failure point - `echo` can also help (``echo on``) + + + if you used a ``-run`` option like in :ref:`replace_synth` above, you can + now replace the failing step with its contents and repeat the above if + needed + + checking the log should tell you the last command that ran which can make + this easier + + say we ran :ref:`replace_synth` and were able to remove the ``synth -run + check:`` and still got our error, then we check the log and we see the last + thing before the error was `7.2. Executing MEMORY_MAP pass (converting + memories to logic and flip-flops).` + + we can then update our script to the following: + +.. code-block:: yoscrypt + :caption: example replacement script for `synth` when `memory_map` is failing + + synth -top -run :fine + opt -fast -full + memory_map + + - try ``write_rtlil ; design -reset; read_rtlil ;`` before the failure point From 8ec3e3a1025943b45772644b0a2387e1c3a1f238 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:58 +1200 Subject: [PATCH 147/181] docs: Bullets for identifying issues Add a note on fuzzers, with a polite suggestion that if you're fuzzing you should put in the work of identifying the underlying issue so that you (and we) are confident you're not raising multiple issues for the same bug. --- docs/source/using_yosys/bugpoint.rst | 113 +++++++++++++++++++++------ 1 file changed, 89 insertions(+), 24 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 804787df8..abc6094a6 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -36,6 +36,11 @@ Minimizing failing (or bugged) designs Minimizing RTLIL designs with bugpoint -------------------------------------- +- what is `bugpoint` + +Can I use bugpoint? +~~~~~~~~~~~~~~~~~~~ + - `bugpoint`, only usable on platforms where Yosys can spawn executables + unavailable on emscripten and wasm @@ -62,26 +67,29 @@ Minimizing RTLIL designs with bugpoint + but, `bugpoint` itself calls the command/script with an RTLIL dump, so if it isn't reproducible from RTLIL then `bugpoint` won't work + +How do I use bugpoint? +~~~~~~~~~~~~~~~~~~~~~~ + - follow `bugpoint` instructions +- output design after `bugpoint` with `write_rtlil` +- use ``-grep ""`` to only accept a minimized design that crashes +- with the ```` in the log file - + output design after `bugpoint` with `write_rtlil` - + use ``-grep ""`` to only accept a minimized design that crashes - with the ```` in the log file + + only checks log file, will not match runtime errors - * only checks log file, will not match runtime errors +- ``-modules``, ``-ports``, ``-cells``, and ``-processes`` will enable those +- parts of the design to be removed (default is allow removing all) - + ``-modules``, ``-ports``, ``-cells``, and ``-processes`` will enable those - parts of the design to be removed (default is allow removing all) + + use the ``bugpoint_keep`` attribute on objects you don't want to be + removed, usually because you already know they are related to the failure + + ``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in + RTLIL, or ``setattr -set bugpoint_keep 1 [selection]`` from script - * use the ``bugpoint_keep`` attribute on objects you don't want to be - removed, usually because you already know they are related to the failure - * ``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in - RTLIL, or ``setattr -set bugpoint_keep 1 [selection]`` from script - - + ``-runner ""`` can allow running ``yosys`` wrapped by another - command - + can also use `setenv` before `bugpoint` to set environment variables for - the spawned processes (e.g. ``setenv UBSAN_OPTIONS halt_on_error=1``) +- ``-runner ""`` can allow running ``yosys`` wrapped by another +- command +- can also use `setenv` before `bugpoint` to set environment variables for +- the spawned processes (e.g. ``setenv UBSAN_OPTIONS halt_on_error=1``) .. note:: @@ -92,14 +100,13 @@ Minimizing RTLIL designs with bugpoint others such as ``YOSYS_NOVERIFIC`` and ``HOME`` are evaluated each time they are used. -- check minimized design still fails, especially if not using `write_rtlil` - + e.g. if you ran :ref:`bugpoint_script` below, then calling ``yosys -s - min.v`` should still fail in the same way - + `write_rtlil` is more reliable since `bugpoint` will have run that exact - code through the failing script; other ``write_*`` commands convert from the - RTLIL and then back again during the ``read_*`` which can result in - differences which mean the design no longer fails +What do I do with the minimized design? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- check minimized design still fails, especially if not using `write_rtlil` +- e.g. if you ran :ref:`bugpoint_script` below, then calling ``yosys -s + min.v`` should still fail in the same way .. code-block:: yoscrypt :caption: example `bugpoint` minimizer @@ -109,6 +116,12 @@ Minimizing RTLIL designs with bugpoint bugpoint -script write_verilog min.v +- `write_rtlil` is more reliable since `bugpoint` will have run that exact + code through the failing script; other ``write_*`` commands convert from the + RTLIL and then back again during the ``read_*`` which can result in + differences which mean the design no longer fails +- check out :ref:`using_yosys/bugpoint:identifying issues` for more on what to + do next .. _minimize your script: @@ -145,8 +158,8 @@ Minimizing scripts this easier + say we ran :ref:`replace_synth` and were able to remove the ``synth -run check:`` and still got our error, then we check the log and we see the last - thing before the error was `7.2. Executing MEMORY_MAP pass (converting - memories to logic and flip-flops).` + thing before the error was ``7.2. Executing MEMORY_MAP pass (converting + memories to logic and flip-flops).`` + we can then update our script to the following: .. code-block:: yoscrypt @@ -239,6 +252,58 @@ Minimizing Verilog designs (unless the error is arising because of the names used) +Identifying issues +------------------ + +- does the failing command indicate limited support, or does it mention some + other command that needs to be run first? +- if you're able to, try to match the minimized design back to its original + context + + + could you achieve the same thing a different way? + + and if so, does this other method have the same issue? + +- try to change the design in small ways and see what happens + + + `bugpoint` can reduce and simplify a design, but it doesn't *change* much + + what happens if you change operators, for example a left shift (or `$shl`) + to a right shift (or `$shr`)? + + is the issue tied to specific parameters, widths, or values? + +- if you're familiar with C/C++ you might try to have a look at the source + code of the command that's failing + + + even if you can't fix the problem yourself, it can be very helpful for + anyone else investigating if you're able to identify where exactly the + issue is + + if you're using a fuzzer to find issues in Yosys, you should be prepared to + do this step + +.. warning:: + + In the event that you are unable to identify the root cause of a fuzzer + generated issue, **do not** open more than one issue at a time. You have no + way of being able to tell if multiple fuzzer generated issues are simply + different cases of the same problem, and opening multiple issues for the same + problem means more time is spent on triaging and diagnosing bug reports and + less on fixing the problem. If you are found to be doing this, your issues + may be closed without further investigation. + +- search `the existing issues`_ and see if someone has already made a bug report + + + this is where changing the design and finding the limits of what causes the + failure really comes in handy + + if you're more familiar with how the problem can arise, you may be able to + find a related issue more easily + + if an issue already exists for one case of the problem but you've found + other cases, you can comment on the issue and help get it solved + +.. _the existing issues: https://github.com/YosysHQ/yosys/issues + +- if there are no existing or related issues already, the check out the steps + for :ref:`using_yosys/bugpoint:creating an issue on github` + + Creating an issue on GitHub --------------------------- From 385d58562d171f86884247a1ae1971666362df82 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:58 +1200 Subject: [PATCH 148/181] Docs: Move verilog.rst to using_yosys Was previously in yosys_internals which is more developer focused, rather than user focused. --- docs/source/using_yosys/index.rst | 1 + docs/source/{yosys_internals => using_yosys}/verilog.rst | 2 -- docs/source/yosys_internals/index.rst | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) rename docs/source/{yosys_internals => using_yosys}/verilog.rst (99%) diff --git a/docs/source/using_yosys/index.rst b/docs/source/using_yosys/index.rst index 0ea91fae7..b243d431e 100644 --- a/docs/source/using_yosys/index.rst +++ b/docs/source/using_yosys/index.rst @@ -16,3 +16,4 @@ ways Yosys can interact with designs for a deeper investigation. synthesis/index more_scripting/index bugpoint + verilog diff --git a/docs/source/yosys_internals/verilog.rst b/docs/source/using_yosys/verilog.rst similarity index 99% rename from docs/source/yosys_internals/verilog.rst rename to docs/source/using_yosys/verilog.rst index d67553aa9..1701458f7 100644 --- a/docs/source/yosys_internals/verilog.rst +++ b/docs/source/using_yosys/verilog.rst @@ -1,8 +1,6 @@ Notes on Verilog support in Yosys ================================= -.. TODO:: how much of this is specific to the read_verilog and should be in :doc:`/yosys_internals/flow/verilog_frontend`? - Unsupported Verilog-2005 Features --------------------------------- diff --git a/docs/source/yosys_internals/index.rst b/docs/source/yosys_internals/index.rst index 3dd4224fa..483cc2bf8 100644 --- a/docs/source/yosys_internals/index.rst +++ b/docs/source/yosys_internals/index.rst @@ -38,5 +38,4 @@ as reference to implement a similar system in any language. formats/index extending_yosys/index techmap - verilog hashing From be999219a2972acb6d1931c6c589d8dd5f02383a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:58 +1200 Subject: [PATCH 149/181] docs: User-defined failures in bugpoint Also some other tidy up and clarifications. --- docs/source/using_yosys/bugpoint.rst | 47 +++++++++++++++++++--------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index abc6094a6..a74693fa3 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -36,12 +36,16 @@ Minimizing failing (or bugged) designs Minimizing RTLIL designs with bugpoint -------------------------------------- -- what is `bugpoint` +Yosys provides the `bugpoint` command for reducing a failing design to the +smallest portion of that design which still results in failure. While initially +developed for Yosys crashes, `bugpoint` can also be used for designs that lead +to non-fatal errors, or even failures in other tools that use the output of a +Yosys script. Can I use bugpoint? ~~~~~~~~~~~~~~~~~~~ -- `bugpoint`, only usable on platforms where Yosys can spawn executables +- only usable on platforms where Yosys can spawn executables + unavailable on emscripten and wasm + can test by running e.g. ``yosys -qqp '!echo test'`` @@ -67,6 +71,18 @@ Can I use bugpoint? + but, `bugpoint` itself calls the command/script with an RTLIL dump, so if it isn't reproducible from RTLIL then `bugpoint` won't work +- works with user-defined failures in scripts + + + e.g. `select` command with ``-assert-*`` option + + or `equiv_opt` + + can even call another tool with `exec` + + * useful for when Yosys is outputting an invalid design + * use the ``-expect-*`` options to ensure the script correctly returns the + failure state to `bugpoint` + * can call shell scripts with e.g. ``exec -expect-return 1 -- bash + `` + How do I use bugpoint? ~~~~~~~~~~~~~~~~~~~~~~ @@ -74,31 +90,34 @@ How do I use bugpoint? - follow `bugpoint` instructions - output design after `bugpoint` with `write_rtlil` - use ``-grep ""`` to only accept a minimized design that crashes -- with the ```` in the log file + with the ```` in the log file + only checks log file, will not match runtime errors + + can be particularly important for scripts with multiple commands to avoid + unrelated failures + + call e.g. ``yosys -qqp '' design.il`` or ``yosys -qqs + design.il`` to print only the error message(s) and use that (or a portion of + that) as the ```` to search for - ``-modules``, ``-ports``, ``-cells``, and ``-processes`` will enable those -- parts of the design to be removed (default is allow removing all) + parts of the design to be removed (default is to allow removing all) + use the ``bugpoint_keep`` attribute on objects you don't want to be removed, usually because you already know they are related to the failure + ``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in RTLIL, or ``setattr -set bugpoint_keep 1 [selection]`` from script -- ``-runner ""`` can allow running ``yosys`` wrapped by another -- command +- ``-runner ""`` can allow running ``yosys`` wrapped by another command - can also use `setenv` before `bugpoint` to set environment variables for -- the spawned processes (e.g. ``setenv UBSAN_OPTIONS halt_on_error=1``) + the spawned processes (e.g. ``setenv UBSAN_OPTIONS halt_on_error=1``) .. note:: - Using `setenv` in this way may not affect the current process as some - environment variables are only read on start up. For instance the - ``UBSAN_OPTIONS halt_on_error`` here only affects child processes, as does - the :doc:`Yosys environment variable` ``ABC``. While - others such as ``YOSYS_NOVERIFIC`` and ``HOME`` are evaluated each time they - are used. + Using `setenv` in this way may or may not affect the current process. For + instance the ``UBSAN_OPTIONS halt_on_error`` here only affects child + processes, as does the :doc:`Yosys environment variable` + ``ABC`` because they are only read on start-up. While others, such as + ``YOSYS_NOVERIFIC`` and ``HOME``, are evaluated each time they are used. What do I do with the minimized design? @@ -300,7 +319,7 @@ Identifying issues .. _the existing issues: https://github.com/YosysHQ/yosys/issues -- if there are no existing or related issues already, the check out the steps +- if there are no existing or related issues already, then check out the steps for :ref:`using_yosys/bugpoint:creating an issue on github` From 47c89a61df5c85f60ff738e0340554e583597570 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:58 +1200 Subject: [PATCH 150/181] Docs: What is bugpoint in paragraphs --- docs/source/using_yosys/bugpoint.rst | 82 +++++++++++++++++----------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index a74693fa3..ffefcb144 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -45,43 +45,64 @@ Yosys script. Can I use bugpoint? ~~~~~~~~~~~~~~~~~~~ -- only usable on platforms where Yosys can spawn executables +The first thing to be aware of is that `bugpoint` is not available in every +build of Yosys. Because the command works by invoking external processes, it +requires that Yosys can spawn executables. Notably this means `bugpoint` is not +able to be used in WebAssembly builds such as that available via YoWASP. The +easiest way to check your build of Yosys is by running ``yosys -qq -p '!echo +test'``. If this echoes ``test`` in the console, then `bugpoint` will work as +expected. If instead if it displays the text ``ERROR: Shell is not available.`` +then `bugpoint` will not work either. - + unavailable on emscripten and wasm - + can test by running e.g. ``yosys -qqp '!echo test'`` +.. note:: - * the ``-qq`` prevents Yosys from outputting non-error messages to the - console, so this will either display the text ``test``, or an error - message about ``Shell`` being unavailable - * check :ref:`getting_started/scripting_intro:script parsing` for more about - the ``-p`` option and common pitfalls + The console command ``yosys -qq -p '!echo test'`` uses the ``-qq`` flag to + prevent Yosys from outputting non-error messages to the console. The ``-p`` + option executes ``!echo test`` as a Yosys command, attempting to pass ``echo + test`` to the shell for execution. For more about the ``-p`` option and + common pitfalls, check out :ref:`getting_started/scripting_intro:script + parsing` in the :doc:`/getting_started/index` section. -- single command (``yosys -p '' design.il``) -- *or* multiple commands in a separate script file +.. TODO:: Add ``YOSYS_DISABLE_SPAWN`` check in ``bugpoint.cc``. - + script shouldn't load the design - + ``yosys -s design.il`` - + `minimize your script`_ to reduce the time needed by `bugpoint` + At least in the help text, so that ``yosys -h bugpoint`` will correctly + indicate if the command will work instead of this roundabout method. -- doesn't require design to be in RTLIL format +Next you need to separate loading the design from the failure point; you should +be aiming to reproduce the failure by running ``yosys -s -s +``. If the failure occurs while loading the design, such as during +`read_verilog` you will instead have to minimize the input design yourself. +Check out the instructions for :ref:`using_yosys/bugpoint:minimizing verilog +designs` below. - + can e.g. ``read_verilog ; prep -top ;`` before `bugpoint` - + this method may be more useful if you are trying to find a bug in your - design rather than Yosys - + but, `bugpoint` itself calls the command/script with an RTLIL dump, so if it - isn't reproducible from RTLIL then `bugpoint` won't work +The commands in ```` only need to be run once, while those in +```` will be run on each iteration of `bugpoint`. If you haven't +already, following the instructions for :ref:`using_yosys/bugpoint:minimizing +scripts` will also help with identifying exactly which commands are needed to +produce the failure and which can be safely moved to the loading script. -- works with user-defined failures in scripts +.. note:: - + e.g. `select` command with ``-assert-*`` option - + or `equiv_opt` - + can even call another tool with `exec` - - * useful for when Yosys is outputting an invalid design - * use the ``-expect-*`` options to ensure the script correctly returns the - failure state to `bugpoint` - * can call shell scripts with e.g. ``exec -expect-return 1 -- bash - `` + You should also be able to run the two scripts separately, calling first + ``yosys -s -p 'write_rtlil design.il'`` and then ``yosys -s + design.il``. If this doesn't work then it may mean that the + failure isn't reproducible from RTLIL and `bugpoint` won't work either. + +When we talk about failure points here, it doesn't just mean crashes or errors +in Yosys. The ```` script can also be a user-defined failure such +as the `select` command with one of the ``-assert-*`` options; an example where +this might be useful is when a pass is supposed to remove a certain kind of +cell, but there is some edge case where the cell is not removed. Another +use-case would be minimizing a design which fails with the `equiv_opt` command, +suggesting that the optimization in question alters the circuit in some way. + +It is even possible to use `bugpoint` with failures *external* to Yosys, by +making use of the `exec` command in ````. This is especially useful +when Yosys is outputting an invalid design, or when some other tool is +incompatible with the design. Be sure to use the ``exec -expect-*`` options so +that the pass/fail can be detected correctly. Multiple calls to `exec` can be +made, or even entire shell scripts (e.g. ``exec -expect-return 1 -- bash +``). How do I use bugpoint? @@ -142,6 +163,7 @@ What do I do with the minimized design? - check out :ref:`using_yosys/bugpoint:identifying issues` for more on what to do next + .. _minimize your script: Minimizing scripts @@ -188,7 +210,6 @@ Minimizing scripts opt -fast -full memory_map - - try ``write_rtlil ; design -reset; read_rtlil ;`` before the failure point @@ -348,7 +369,6 @@ Creating an issue on GitHub `yosys -p ': minimum sequence of commands;' min.v` - - alternatively can provide a single code-block which includes the minimized design as a "here document" followed by the sequence of commands which reproduce the error From 2c534c88282e6d04760b0a09a7b76b697d6530f4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:59 +1200 Subject: [PATCH 151/181] Docs: How to use bugpoint paragraphs --- docs/source/using_yosys/bugpoint.rst | 98 ++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index ffefcb144..7d38124bf 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -104,33 +104,75 @@ that the pass/fail can be detected correctly. Multiple calls to `exec` can be made, or even entire shell scripts (e.g. ``exec -expect-return 1 -- bash ``). +Our final failure we can use with `bugpoint` is one returned by a wrapper +process, such as ``valgrind`` or ``timeout``. In this case you will be calling +something like `` yosys -s design.il``. Here, Yosys is +run under a wrapper process which checks for some failure state, like a memory +leak or excessive runtime. Note however that unlike the `exec` command, there +is currently no way to check the return status or messages from the wrapper +process; only a binary pass/fail. + How do I use bugpoint? ~~~~~~~~~~~~~~~~~~~~~~ -- follow `bugpoint` instructions -- output design after `bugpoint` with `write_rtlil` -- use ``-grep ""`` to only accept a minimized design that crashes - with the ```` in the log file +At this point you should have: - + only checks log file, will not match runtime errors - + can be particularly important for scripts with multiple commands to avoid - unrelated failures - + call e.g. ``yosys -qqp '' design.il`` or ``yosys -qqs - design.il`` to print only the error message(s) and use that (or a portion of - that) as the ```` to search for +1. either an RTLIL file containing the design to minimize (referred to here as + ``design.il``), or a Yosys script, ````, which loads it; and +2. a Yosys script, ````, which produces the failure and returns a + non-zero return status. -- ``-modules``, ``-ports``, ``-cells``, and ``-processes`` will enable those - parts of the design to be removed (default is to allow removing all) +Now call ``yosys -qq -s design.il`` and take note of the error(s) +that get printed. A template script, ````, is provided here which +you can use. Make sure to configure it with the correct filenames and use only +one of the methods to load the design. Fill in the ``-grep`` option with the +error message printed just before. If you are using a wrapper process for your +failure state, add the ``-runner ""`` option to the `bugpoint` call. +For more about the options available, check ``help bugpoint`` or +:doc:`/cmd/bugpoint`. - + use the ``bugpoint_keep`` attribute on objects you don't want to be - removed, usually because you already know they are related to the failure - + ``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in - RTLIL, or ``setattr -set bugpoint_keep 1 [selection]`` from script +.. code-block:: yoscrypt + :caption: ```` template script -- ``-runner ""`` can allow running ``yosys`` wrapped by another command -- can also use `setenv` before `bugpoint` to set environment variables for - the spawned processes (e.g. ``setenv UBSAN_OPTIONS halt_on_error=1``) + # Load design + read_rtlil design.il + ## OR + script + + # Call bugpoint with failure + bugpoint -script -grep "" + + # Save minimized design + write_rtlil min.il + +.. note:: + + Using ``-grep ""`` with `bugpoint` is optional, but helps to ensure + that the minimized design is reproducing the right error, especially when + ```` contains more than one command. Unfortunately this does not + work with runtime errors such as a ``SEGFAULT`` as it is only able to match + strings from the log file. + +.. TODO:: Consider checking ``run_command`` return value for runtime errors. + + Currently ``BugpointPass::run_yosys`` returns ``run_command(yosys_cmdline) == + 0``, so it shouldn't be too hard to add an option for it. Could also be + used with the ``-runner`` option, which might give it a bit more flexibility. + +By default, `bugpoint` is able to remove any part of the design. In order to +keep certain parts, for instance because you already know they are related to +the failure, you can use the ``bugpoint_keep`` attribute. This can be done with +``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in RTLIL, or +``setattr -set bugpoint_keep 1 [selection]`` from a Yosys script. It is also +possible to limit `bugpoint` to only removing certain *kinds* of objects, such +as only removing entire modules or cells (instances of modules). For more about +the options available, check ``help bugpoint`` or :doc:`/cmd/bugpoint`. + +In some situations, it may also be helpful to use `setenv` before `bugpoint` to +set environment variables for the spawned processes. An example of this is +``setenv UBSAN_OPTIONS halt_on_error=1`` for where you are trying to raise an +error on undefined behaviour but only want the child process to halt on error. .. note:: @@ -140,6 +182,24 @@ How do I use bugpoint? ``ABC`` because they are only read on start-up. While others, such as ``YOSYS_NOVERIFIC`` and ``HOME``, are evaluated each time they are used. +Once you have finished configuration, you can now run ``yosys ``. +The first thing `bugpoint` will do is test the input design fails. If it +doesn't, make sure you are using the right ``yosys`` executable; unless the +``-yosys`` option is provided, it will use whatever the shell defaults to. If +you are using the ``-runner`` option, try replacing the `bugpoint` command with +``write_rtlil test.il`` and then on a new line, ``! yosys -s + test.il`` to check it works as expected and returns a non-zero +status. + +Depending on the size of your design, and the length of your ````, +`bugpoint` may take some time; remember, it will run ``yosys -s `` +on each iteration of the design. The bigger the design, the more iterations. +The longer the ````, the longer each iteration will take. As the +design shrinks and `bugpoint` converges, each iteration should take less and +less time. Once all simplifications are exhausted and there are no more objects +that can be removed, the script will continue and the minimized design can be +saved. + What do I do with the minimized design? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 9fa1f76cf2b364a825d98a0b9ef672f059965a94 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:59 +1200 Subject: [PATCH 152/181] bugpoint.rst: Why context matters (bullets) --- docs/source/using_yosys/bugpoint.rst | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 7d38124bf..54559de32 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -230,6 +230,10 @@ Minimizing scripts ------------------ - reminder to back up original code before modifying it + + + sometimes over-minimizing scripts can hide underlying issues, so maintaining + the original context is important + - if you're using command line, convert it to a script - if you're using one of the :doc:`/using_yosys/synthesis/synth`, replace it with its contents @@ -370,6 +374,18 @@ Identifying issues to a right shift (or `$shr`)? + is the issue tied to specific parameters, widths, or values? +- if the failing command was part of a larger script, such as one of the + :doc:`/using_yosys/synthesis/synth`, you could try to follow the design + through the script + + + sometimes when a command is raising an error, you're seeing a symptom rather + than the underlying issue + + an earlier command may be putting the design in an invalid state which isn't + picked up until the error is raised + + check out :ref:`using_yosys/bugpoint:Why context matters` + + if you're using a fuzzer to find issues in Yosys, you should be prepared to + do this step + - if you're familiar with C/C++ you might try to have a look at the source code of the command that's failing @@ -404,6 +420,40 @@ Identifying issues for :ref:`using_yosys/bugpoint:creating an issue on github` +Why context matters +------------------- + +- if you did `minimize your script`_, and removed commands prior to the failure + to get a smaller design, try to work backwards and find which commands may + have contributed to the design failing +- especially important when the bug is happening inside of a ``synth_*`` script +- example (#4590) + + + say you did all the minimization and found that the error occurs when a call + to ``techmap -map +/xilinx/cells_map.v`` with ``MIN_MUX_INPUTS`` defined + parses a `$_MUX16_` with all inputs set to ``1'x`` + + step through the original script, calling `stat` after each step to find + when the `$_MUX16_` is added + + find that the `$_MUX16_` is introduced by a call to `muxcover`, but all the + inputs are defined, so calling `techmap` now works as expected + + * and from running `bugpoint` with the failing techmap you know that the + cell with index ``2297`` will fail, so you can now call ``select + top/*$2297`` to limit to just that cell, and optionally call ``design + -save pre_bug`` or ``write_rtlil -selected pre_bug.il`` to save this state + + + next you step through the remaining commands and call `dump` after each to + find when the inputs are disconnected + + find that ``opt -full`` has optimized away portions of the circuit, leading + to `opt_expr` setting the undriven mux inputs to ``x``, but failing to + remove the now unnecessary `$_MUX16_` + +- in this example, you might've stopped with the minimal reproducer, fixed the + bug in ``+/xilinx/cells_map.v``, and carried on +- but by following the failure back you've managed to identify a problem with + `opt_expr` that could be causing other problems either now or in the future + + Creating an issue on GitHub --------------------------- From e776f1dca20d406097e47b230e247a23b4d59ddf Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:59 +1200 Subject: [PATCH 153/181] bugpoint.rst: More paragraphs What do I do with the minimized design and (the first half of) Minimizing scripts --- docs/source/using_yosys/bugpoint.rst | 107 ++++++++++++++++++--------- 1 file changed, 74 insertions(+), 33 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 54559de32..80694fd2d 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -204,9 +204,10 @@ saved. What do I do with the minimized design? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- check minimized design still fails, especially if not using `write_rtlil` -- e.g. if you ran :ref:`bugpoint_script` below, then calling ``yosys -s - min.v`` should still fail in the same way +First off, check the minimized design still fails. This is especially important +if you're not using `write_rtlil` to output the minimized design. For example, +if you ran :ref:`bugpoint_script` below, then calling ``yosys -s +min.v`` should still fail in the same way. .. code-block:: yoscrypt :caption: example `bugpoint` minimizer @@ -216,12 +217,21 @@ What do I do with the minimized design? bugpoint -script write_verilog min.v -- `write_rtlil` is more reliable since `bugpoint` will have run that exact - code through the failing script; other ``write_*`` commands convert from the - RTLIL and then back again during the ``read_*`` which can result in - differences which mean the design no longer fails -- check out :ref:`using_yosys/bugpoint:identifying issues` for more on what to - do next +The `write_rtlil` command is generally more reliable, since `bugpoint` will have +run that exact code through the failing script. Other ``write_*`` commands +convert from the RTLIL and then back again during the ``read_*`` which can +result in differences which mean the design no longer fails. + +.. note:: + + Simply calling Yosys with the output of ``write_*``, as in ``yosys -s + min.v``, does not guarantee that the corresponding ``read_*`` + will be used. For more about this, refer to + :doc:`/using_yosys/more_scripting/load_design`, or load the design explicitly + with ``yosys -p 'read_verilog min.v' -s ``. + +Once you've verified the failure still happens, check out +:ref:`using_yosys/bugpoint:identifying issues` for more on what to do next. .. _minimize your script: @@ -229,19 +239,43 @@ What do I do with the minimized design? Minimizing scripts ------------------ -- reminder to back up original code before modifying it +If you're using a command line prompt, such as ``yosys -p 'synth_xilinx' -o +design.json design.v``, consider converting it to a script. It's generally much +easier to iterate over changes to a script in a file rather than one on the +command line, as well as being better for sharing with others. - + sometimes over-minimizing scripts can hide underlying issues, so maintaining - the original context is important +.. code-block:: yoscrypt + :caption: example script, ``script.ys``, for prompt ``yosys -p 'synth_xilinx' -o design.json design.v`` -- if you're using command line, convert it to a script -- if you're using one of the :doc:`/using_yosys/synthesis/synth`, replace it - with its contents + read_verilog design.v + synth_xilinx + write_json design.json - + can also do this piece-wise with the ``-run`` option - + e.g. replacing ``synth -top -lut`` with :ref:`replace_synth` - + the options ``-top -lut`` can be provided to each `synth` step, or - to just the step(s) where it is relevant, as done here +Next up you want to remove everything *after* the error occurs. Using the +``-L`` flag can help here, allowing you to specify a file to log to, such as +``yosys -L out.log -s script.ys``. Most commands will print a header message +when they begin; something like ``2.48. Executing HIERARCHY pass (managing +design hierarchy).`` The last header message will usually be the failing +command. There are some commands which don't print a header message, so you may +want to add ``echo on`` to the start of your script. The `echo` command echoes +each command executed, along with any arguments given to it. For the +`hierarchy` example above this might be ``yosys> hierarchy -check``. + +.. note:: + + It may also be helpful to use the `log` command to add messages which you can + then search for either in the terminal or the logfile. This can be quite + useful if your script contains script-passes, like the + :doc:`/using_yosys/synthesis/synth`, which call many sub-commands and you're + not sure exactly which script-pass is calling the failing command. + +If your final command calls sub-commands, replace it with its contents and +repeat the previous step. In the case of the +:doc:`/using_yosys/synthesis/synth`, as well as certain other script-passes, you +can use the ``-run`` option to simplify this. For example we can replace +``synth -top -lut`` with the :ref:`replace_synth`. The options ``-top + -lut`` can be provided to each `synth` step, or to just the step(s) where +it is relevant, as done here. .. code-block:: yoscrypt :caption: example replacement script for `synth` command @@ -252,20 +286,12 @@ Minimizing scripts synth -lut -run fine:check synth -run check: -- remove everything *after* the error occurs -- can use `log` command to print messages to help locate the failure point -- `echo` can also help (``echo on``) - - + if you used a ``-run`` option like in :ref:`replace_synth` above, you can - now replace the failing step with its contents and repeat the above if - needed - + checking the log should tell you the last command that ran which can make - this easier - + say we ran :ref:`replace_synth` and were able to remove the ``synth -run - check:`` and still got our error, then we check the log and we see the last - thing before the error was ``7.2. Executing MEMORY_MAP pass (converting - memories to logic and flip-flops).`` - + we can then update our script to the following: +Say we ran :ref:`replace_synth` and were able to remove the ``synth -run +check:`` and still got our error, then we check the log and we see the last +thing before the error was ``7.2. Executing MEMORY_MAP pass (converting memories +to logic and flip-flops)``. By checking the output of ``yosys -h synth`` (or the +`synth` help page) we can see that the `memory_map` pass is called in the +``fine`` step. We can then update our script to the following: .. code-block:: yoscrypt :caption: example replacement script for `synth` when `memory_map` is failing @@ -274,6 +300,21 @@ Minimizing scripts opt -fast -full memory_map +By giving `synth` the option ``-run :fine``, we are telling it to run from the +beginning of the script until the ``fine`` step, where we then give it the exact +commands to run. There are some cases where the commands given in the help +output are not an exact match for what is being run, but are instead a +simplification. If you find that replacing the script-pass with its contents +causes the error to disappear, or change, try calling the script-pass with +``echo on`` to see exactly what commands are being called and what options are +used. + +.. warning:: + + Before continuing further, *back up your code*. The following steps can + remove context and lead to over-minimizing scripts, hiding underlying issues. + Check out :ref:`using_yosys/bugpoint:Why context matters` to learn more. + - try ``write_rtlil ; design -reset; read_rtlil ;`` before the failure point From c75b07820ffbd5f317a88c94e7bae77d2e4e63f4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:53:59 +1200 Subject: [PATCH 154/181] Docs: More bugpoint bullets More info for creating GitHub issues and the different sections. Discuss additional details that can be included as comments on the issue. Also mention Gists for large files (preferable to downloading a .txt). Add a warning about external plugins/tools. Also add a note to `load_design.rst` about `Frontend`s and `-f` command line option. --- docs/source/using_yosys/bugpoint.rst | 102 ++++++++++++++++-- .../more_scripting/load_design.rst | 4 + 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 80694fd2d..2316510cf 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -498,10 +498,27 @@ Why context matters Creating an issue on GitHub --------------------------- -- "Reproduction Steps" is ideally a code-block (starting and ending with triple - backquotes) containing the minimized design (Verilog or RTLIL), followed by a - code-block containing the minimized yosys script OR a command line call to - yosys with code-formatting (starting and ending with single backquotes) +- use the `bug report template`_ + +.. _bug report template: https://github.com/YosysHQ/yosys/issues/new?template=bug_report.yml + +- short title briefly describing the issue, e.g. + + techmap of wide mux with undefined inputs raises error during synth_xilinx + + + tells us what's happening ("raises error") + + gives the command affected (`techmap`) + + an overview of the input design ("wide mux with undefined inputs") + + and some context where it was found ("during `synth_xilinx`") + + +Reproduction Steps +~~~~~~~~~~~~~~~~~~ + +- ideally a code-block (starting and ending with triple backquotes) containing + the minimized design (Verilog or RTLIL), followed by a code-block containing + the minimized yosys script OR a command line call to yosys with + code-formatting (starting and ending with single backquotes) .. code-block:: markdown @@ -535,5 +552,78 @@ Creating an issue on GitHub # minimum sequence of commands ``` -- any environment variables or command line options should also be mentioned in - the "Reproduction Steps" +- any environment variables or command line options should also be mentioned +- if the problem occurs for a range of values/designs, what is that range +- if you're using an external tool, such as ``valgrind``, to detect the issue, + what version of that tool are you using and what options are you giving it + +.. warning:: + + Please try to avoid the use of any external plugins/tools in the reproduction + steps if they are not directly related to the issue being raised. This + includes frontend plugins such as GHDL or slang; use `write_rtlil` on the + minimized design instead. This also includes tools which provide a wrapper + around Yosys such as OpenLane; you should instead minimize your input and + reproduction steps to just the Yosys part. + +"Expected Behaviour" +~~~~~~~~~~~~~~~~~~~~ + +- if you have a similar design/script that doesn't give the error, include it + here as a reference +- if the bug is that an error *should* be raised but isn't, are there any other + commands with similar error messages + + +"Actual Behaviour" +~~~~~~~~~~~~~~~~~~ + +- any error messages go here +- any details relevant to the crash that were found with ``--trace`` or + ``--debug`` flags +- if you identified the point of failure in the source code, you could mention + it here, or as a comment below + + + if possible, use a permalink to the source on GitHub + + you can browse the source repository for a certain commit with the failure + and open the source file, select the relevant lines (click on the line + number for the first relevant line, then while holding shift click on the + line number for the last relevant line), click on the `...` that appears and + select "Copy permalink" + + should look something like + ``https://github.com/YosysHQ/yosys/blob//path/to/file#L139-L147`` + + clicking on "Preview" should reveal a code block containing the lines of + source specified, with a link to the source file at the given commit + + +Additional details +~~~~~~~~~~~~~~~~~~ + +- once you have created the issue, any additional details can be added as a + comment on that issue +- could include any additional context as to what you were doing when you first + encountered the bug +- was this issue discovered through the use of a fuzzer +- if you've minimized the script, consider including the `bugpoint` script you + used, or the original script, e.g. + +.. code-block:: markdown + + Minimized with + ``` + read_verilog design.v + # original sequence of commands prior to error + bugpoint -script -grep "" + write_rtlil min.il + ``` + + OR + + Minimized from + `yosys -p ': original sequence of commands to produce error;' design.v` + +- if you're able to, it may also help to share the original un-minimized design + + + if the design is too big for a comment, consider turning it into a `Gist`_ + +.. _Gist: https://gist.github.com/ diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst index 1c597bdfc..7e417eff9 100644 --- a/docs/source/using_yosys/more_scripting/load_design.rst +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -68,6 +68,10 @@ Yosys frontends + executed as multiple successive calls to the frontend +- compatible with ``-f`` command line option, e.g. ``yosys -f verilog + design.txt`` will use the `read_verilog` frontend with the input file + ``design.txt`` + - `verific` and `read` commands are technically not 'Frontends', but their behaviour is kept in sync From 113a6f6e525f3fc98bac64c8fd6b65ae08ff12dd Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:00 +1200 Subject: [PATCH 155/181] bugpoint.rst: yosys -h bugpoint does work I just missed that it only gets included in the makefile if `DISABLE_SPAWN` is set, because I was looking for the C define `YOSYS_DISABLE_SPAWN`. --- docs/source/using_yosys/bugpoint.rst | 35 ++++++++++++---------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 2316510cf..e7bbfa665 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -49,24 +49,10 @@ The first thing to be aware of is that `bugpoint` is not available in every build of Yosys. Because the command works by invoking external processes, it requires that Yosys can spawn executables. Notably this means `bugpoint` is not able to be used in WebAssembly builds such as that available via YoWASP. The -easiest way to check your build of Yosys is by running ``yosys -qq -p '!echo -test'``. If this echoes ``test`` in the console, then `bugpoint` will work as -expected. If instead if it displays the text ``ERROR: Shell is not available.`` -then `bugpoint` will not work either. - -.. note:: - - The console command ``yosys -qq -p '!echo test'`` uses the ``-qq`` flag to - prevent Yosys from outputting non-error messages to the console. The ``-p`` - option executes ``!echo test`` as a Yosys command, attempting to pass ``echo - test`` to the shell for execution. For more about the ``-p`` option and - common pitfalls, check out :ref:`getting_started/scripting_intro:script - parsing` in the :doc:`/getting_started/index` section. - -.. TODO:: Add ``YOSYS_DISABLE_SPAWN`` check in ``bugpoint.cc``. - - At least in the help text, so that ``yosys -h bugpoint`` will correctly - indicate if the command will work instead of this roundabout method. +easiest way to check your build of Yosys is by running ``yosys -h bugpoint``. If +Yosys displays the help text for `bugpoint` then it is available for use, but if +it instead prints "No such command or cell type: bugpoint", then `bugpoint` is +not available. Next you need to separate loading the design from the failure point; you should be aiming to reproduce the failure by running ``yosys -s -s @@ -315,6 +301,15 @@ used. remove context and lead to over-minimizing scripts, hiding underlying issues. Check out :ref:`using_yosys/bugpoint:Why context matters` to learn more. +When a problem is occurring many steps into a script, minimizing the design at +the start of the script isn't always enough to identify the cause of the issue. +Each extra step of the script can lead to larger sections of the input design +being needed for the specific problem to be preserved until it causes a crash. +So to find the smallest possible reproducer it can sometimes be helpful to +remove commands prior to the failure point. + + + - try ``write_rtlil ; design -reset; read_rtlil ;`` before the failure point @@ -588,8 +583,8 @@ Reproduction Steps + you can browse the source repository for a certain commit with the failure and open the source file, select the relevant lines (click on the line number for the first relevant line, then while holding shift click on the - line number for the last relevant line), click on the `...` that appears and - select "Copy permalink" + line number for the last relevant line), click on the ``...`` that appears + and select "Copy permalink" + should look something like ``https://github.com/YosysHQ/yosys/blob//path/to/file#L139-L147`` + clicking on "Preview" should reveal a code block containing the lines of From 20a573953cbb483aec74e4f66d0cabe3463679c3 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:00 +1200 Subject: [PATCH 156/181] bugpoint.rst: Minimizing scripts part 2: electric boogaloo --- docs/source/using_yosys/bugpoint.rst | 66 ++++++++++++++++++---------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index e7bbfa665..ca54bc8f0 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -308,38 +308,60 @@ being needed for the specific problem to be preserved until it causes a crash. So to find the smallest possible reproducer it can sometimes be helpful to remove commands prior to the failure point. +The simplest way to do this is by writing out the design, resetting the current +state, and reading back the design: +.. code-block:: yoscrypt -- try ``write_rtlil ; design -reset; read_rtlil ;`` before - the failure point + write_rtlil ; design -reset; read_rtlil ; - + ideally you now have a single command that is producing an error and can - `minimize your RTLIL`_ with the ```` output - + if not, try to move the write/reset/read earlier in the script until you can - reproduce the error - + if you have no idea where exactly you should put the reset, the best way is - to use a "binary search" type approach, reducing the possible options by - half after each attempt +In most cases, this can be inserted immediately before the failing command while +still producing the error, allowing you to `minimize your RTLIL`_ with the +```` output. For our previous example with `memory_map`, if +:ref:`map_reset` still gives the same error, then we should now be able to call +``yosys design.il -p 'memory_map'`` to reproduce it. - * for example, your script has 16 commands in it before failing on the 17th - * if resetting immediately before the 17th doesn't reproduce the error, try - between the 8th and 9th (8 is half of the total 16) - * if that produces the error then you can remove everything before the - `read_rtlil` and try reset again in the middle of what's left, making sure - to use a different name for the output file so that you don't overwrite - what you've already got - * if the error isn't produced then you need to go earlier still, so in this - case you would do between the 4th and 5th (4 is half of the previous 8) - * repeat this until you can't reduce the remaining commands any further +.. code-block:: yoscrypt + :caption: resetting the design immediately before failure + :name: map_reset + + synth -top -run :fine + opt -fast -full + write_rtlil design.il; design -reset; read_rtlil design.il; + memory_map + +If that doesn't give the error (or doesn't give the same error), then you should +try to move the write/reset/read earlier in the script until it does. If you +have no idea where exactly you should put the reset, the best way is to use a +"binary search" type approach, reducing the possible options by half after each +attempt. + + As an example, your script has 16 commands in it before failing on the 17th. + If resetting immediately before the 17th doesn't reproduce the error, try + between the 8th and 9th (8 is half of the total 16). If that produces the + error then you can remove everything before the `read_rtlil` and try reset + again in the middle of what's left, making sure to use a different name for + the output file so that you don't overwrite what you've already got. If the + error isn't produced then you need to go earlier still, so in this case you + would do between the 4th and 5th (4 is half of the previous 8). Repeat this + until you can't reduce the remaining commands any further. .. TODO:: is it possible to dump scratchpad? is there anything else in the yosys/design state that doesn't get included in `write_rtlil`? -- you can also try to remove or comment out commands prior to the failing - command; just because the first and last commands are needed doesn't mean that - every command between them is +A more conservative, but more involved, method is to remove or comment out +commands prior to the failing command. Each command, or group of commands, can +be disabled one at a time while checking if the error still occurs, eventually +giving the smallest subset of commands needed to take the original input through +to the error. The difficulty with this method is that depending on your design, +some commands may be necessary even if they aren't needed to reproduce the +error. For example, if your design includes ``process`` blocks, many commands +will fail unless you run the `proc` command. While this approach can do a +better job of maintaining context, it is often easier to *recover* the context +after the design has been minimized for producing the error. For more on +recovering context, checkout :ref:`using_yosys/bugpoint:Why context matters`. Minimizing Verilog designs From f0b4f7012e689fcf98b991075e3cf67365504d5b Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:00 +1200 Subject: [PATCH 157/181] bugpoint.rst: Extra notes Move `yosys -h bugpoint` failure into a code-block to break up text. Same for the `exec -expect-return` example. TODOs on #5068 being merged. --- docs/source/using_yosys/bugpoint.rst | 36 ++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index ca54bc8f0..6f58e3d5b 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -24,6 +24,13 @@ Minimizing failing (or bugged) designs .. _iverilog: https://steveicarus.github.io/iverilog/ .. _verilator: https://www.veripool.org/verilator/ +- are there any warnings before the error (either immediately before or in an + earlier command) that could be related? +- does calling `check` before the failure give any errors or warnings? +- did you call `hierarchy` before the failure? + + + can you call ``hierarchy -check``? + - make sure to back up your code (design source and yosys script(s)) before making any modifications @@ -50,9 +57,15 @@ build of Yosys. Because the command works by invoking external processes, it requires that Yosys can spawn executables. Notably this means `bugpoint` is not able to be used in WebAssembly builds such as that available via YoWASP. The easiest way to check your build of Yosys is by running ``yosys -h bugpoint``. If -Yosys displays the help text for `bugpoint` then it is available for use, but if -it instead prints "No such command or cell type: bugpoint", then `bugpoint` is -not available. +Yosys displays the help text for `bugpoint` then it is available for use. + +.. code-block:: console + :caption: `bugpoint` is unavailable + + $ yosys -h bugpoint + + -- Running command `help bugpoint' -- + No such command or cell type: bugpoint Next you need to separate loading the design from the failure point; you should be aiming to reproduce the failure by running ``yosys -s -s @@ -87,8 +100,11 @@ making use of the `exec` command in ````. This is especially useful when Yosys is outputting an invalid design, or when some other tool is incompatible with the design. Be sure to use the ``exec -expect-*`` options so that the pass/fail can be detected correctly. Multiple calls to `exec` can be -made, or even entire shell scripts (e.g. ``exec -expect-return 1 -- bash -``). +made, or even entire shell scripts: + +.. code-block:: yoscrypt + + exec -expect-return 1 --bash Our final failure we can use with `bugpoint` is one returned by a wrapper process, such as ``valgrind`` or ``timeout``. In this case you will be calling @@ -98,6 +114,8 @@ leak or excessive runtime. Note however that unlike the `exec` command, there is currently no way to check the return status or messages from the wrapper process; only a binary pass/fail. +.. TODO:: above note pending updated bugpoint #5068 + How do I use bugpoint? ~~~~~~~~~~~~~~~~~~~~~~ @@ -140,11 +158,7 @@ For more about the options available, check ``help bugpoint`` or work with runtime errors such as a ``SEGFAULT`` as it is only able to match strings from the log file. -.. TODO:: Consider checking ``run_command`` return value for runtime errors. - - Currently ``BugpointPass::run_yosys`` returns ``run_command(yosys_cmdline) == - 0``, so it shouldn't be too hard to add an option for it. Could also be - used with the ``-runner`` option, which might give it a bit more flexibility. +.. TODO:: above note pending updated bugpoint #5068 By default, `bugpoint` is able to remove any part of the design. In order to keep certain parts, for instance because you already know they are related to @@ -177,6 +191,8 @@ you are using the ``-runner`` option, try replacing the `bugpoint` command with test.il`` to check it works as expected and returns a non-zero status. +.. TODO:: note on ``!`` (link to :ref:`getting_started/scripting_intro:script parsing`) + Depending on the size of your design, and the length of your ````, `bugpoint` may take some time; remember, it will run ``yosys -s `` on each iteration of the design. The bigger the design, the more iterations. From 65b75049aa5da7683e576a2c7165f30c1560e885 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:00 +1200 Subject: [PATCH 158/181] docs: Shuffling bug reporting guidelines Move the "creating an issue" section from bugpoint.rst to "reporting bugs" in `contributing.rst`. Fix link to `CONTRIBUTING.md`. Update `CONTRIBUTING.md` to refer to the bugpoint guide instead of the stack overflow guide. --- CONTRIBUTING.md | 16 +-- docs/source/using_yosys/bugpoint.rst | 136 +----------------- .../extending_yosys/contributing.rst | 136 +++++++++++++++++- 3 files changed, 144 insertions(+), 144 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c4376cc4..74f9ab10d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,14 +19,14 @@ much easier for someone to respond and help. ### Bug reports -Before you submit an issue, please have a search of the existing issues in case -one already exists. Making sure that you have a minimal, complete and -verifiable example (MVCE) is a great way to quickly check an existing issue -against a new one. Stack overflow has a guide on [how to create an -MVCE](https://stackoverflow.com/help/minimal-reproducible-example). The -[`bugpoint` -command](https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/bugpoint.html) -in Yosys can be helpful for this process. +Before you submit an issue, please check out the [how-to guide for +`bugpoint`](https://yosys.readthedocs.io/en/latest/using_yosys/bugpoint.html). +This guide will take you through the process of using the [`bugpoint` +command](https://yosys.readthedocs.io/en/latest/cmd/bugpoint.html) in Yosys to +produce a [minimal, complete and verifiable +example](https://stackoverflow.com/help/minimal-reproducible-example) (MVCE). +Providing an MVCE with your bug report drastically increases the likelihood that +someone will be able to help resolve your issue. # Using pull requests diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 6f58e3d5b..89c58a8f4 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -491,7 +491,7 @@ Identifying issues .. _the existing issues: https://github.com/YosysHQ/yosys/issues - if there are no existing or related issues already, then check out the steps - for :ref:`using_yosys/bugpoint:creating an issue on github` + for :ref:`yosys_internals/extending_yosys/contributing:reporting bugs` Why context matters @@ -526,137 +526,3 @@ Why context matters bug in ``+/xilinx/cells_map.v``, and carried on - but by following the failure back you've managed to identify a problem with `opt_expr` that could be causing other problems either now or in the future - - -Creating an issue on GitHub ---------------------------- - -- use the `bug report template`_ - -.. _bug report template: https://github.com/YosysHQ/yosys/issues/new?template=bug_report.yml - -- short title briefly describing the issue, e.g. - - techmap of wide mux with undefined inputs raises error during synth_xilinx - - + tells us what's happening ("raises error") - + gives the command affected (`techmap`) - + an overview of the input design ("wide mux with undefined inputs") - + and some context where it was found ("during `synth_xilinx`") - - -Reproduction Steps -~~~~~~~~~~~~~~~~~~ - -- ideally a code-block (starting and ending with triple backquotes) containing - the minimized design (Verilog or RTLIL), followed by a code-block containing - the minimized yosys script OR a command line call to yosys with - code-formatting (starting and ending with single backquotes) - -.. code-block:: markdown - - min.v - ```verilog - // minimized Verilog design - ``` - - min.ys - ``` - read_verilog min.v - # minimum sequence of commands to reproduce error - ``` - - OR - - `yosys -p ': minimum sequence of commands;' min.v` - -- alternatively can provide a single code-block which includes the minimized - design as a "here document" followed by the sequence of commands which - reproduce the error - - + see :doc:`/using_yosys/more_scripting/load_design` for more on heredocs. - -.. code-block:: markdown - - ``` - read_rtlil </path/to/file#L139-L147`` - + clicking on "Preview" should reveal a code block containing the lines of - source specified, with a link to the source file at the given commit - - -Additional details -~~~~~~~~~~~~~~~~~~ - -- once you have created the issue, any additional details can be added as a - comment on that issue -- could include any additional context as to what you were doing when you first - encountered the bug -- was this issue discovered through the use of a fuzzer -- if you've minimized the script, consider including the `bugpoint` script you - used, or the original script, e.g. - -.. code-block:: markdown - - Minimized with - ``` - read_verilog design.v - # original sequence of commands prior to error - bugpoint -script -grep "" - write_rtlil min.il - ``` - - OR - - Minimized from - `yosys -p ': original sequence of commands to produce error;' design.v` - -- if you're able to, it may also help to share the original un-minimized design - - + if the design is too big for a comment, consider turning it into a `Gist`_ - -.. _Gist: https://gist.github.com/ diff --git a/docs/source/yosys_internals/extending_yosys/contributing.rst b/docs/source/yosys_internals/extending_yosys/contributing.rst index 69258aa5f..70170fc48 100644 --- a/docs/source/yosys_internals/extending_yosys/contributing.rst +++ b/docs/source/yosys_internals/extending_yosys/contributing.rst @@ -7,7 +7,7 @@ Contributing to Yosys |CONTRIBUTING|_ file. .. |CONTRIBUTING| replace:: :file:`CONTRIBUTING.md` -.. _CONTRIBUTING: https://github.com/YosysHQ/yosys/CONTRIBUTING.md +.. _CONTRIBUTING: https://github.com/YosysHQ/yosys/blob/main/CONTRIBUTING.md Coding Style ------------ @@ -42,3 +42,137 @@ for implicit type casts, always use ``GetSize(foobar)`` instead of ``foobar.size()``. (``GetSize()`` is defined in :file:`kernel/yosys.h`) Use range-based for loops whenever applicable. + + +Reporting bugs +-------------- + +- use the `bug report template`_ + +.. _bug report template: https://github.com/YosysHQ/yosys/issues/new?template=bug_report.yml + +- short title briefly describing the issue, e.g. + + techmap of wide mux with undefined inputs raises error during synth_xilinx + + + tells us what's happening ("raises error") + + gives the command affected (`techmap`) + + an overview of the input design ("wide mux with undefined inputs") + + and some context where it was found ("during `synth_xilinx`") + + +Reproduction Steps +~~~~~~~~~~~~~~~~~~ + +- ideally a code-block (starting and ending with triple backquotes) containing + the minimized design (Verilog or RTLIL), followed by a code-block containing + the minimized yosys script OR a command line call to yosys with + code-formatting (starting and ending with single backquotes) + +.. code-block:: markdown + + min.v + ```verilog + // minimized Verilog design + ``` + + min.ys + ``` + read_verilog min.v + # minimum sequence of commands to reproduce error + ``` + + OR + + `yosys -p ': minimum sequence of commands;' min.v` + +- alternatively can provide a single code-block which includes the minimized + design as a "here document" followed by the sequence of commands which + reproduce the error + + + see :doc:`/using_yosys/more_scripting/load_design` for more on heredocs. + +.. code-block:: markdown + + ``` + read_rtlil </path/to/file#L139-L147`` + + clicking on "Preview" should reveal a code block containing the lines of + source specified, with a link to the source file at the given commit + + +Additional details +~~~~~~~~~~~~~~~~~~ + +- once you have created the issue, any additional details can be added as a + comment on that issue +- could include any additional context as to what you were doing when you first + encountered the bug +- was this issue discovered through the use of a fuzzer +- if you've minimized the script, consider including the `bugpoint` script you + used, or the original script, e.g. + +.. code-block:: markdown + + Minimized with + ``` + read_verilog design.v + # original sequence of commands prior to error + bugpoint -script -grep "" + write_rtlil min.il + ``` + + OR + + Minimized from + `yosys -p ': original sequence of commands to produce error;' design.v` + +- if you're able to, it may also help to share the original un-minimized design + + + if the design is too big for a comment, consider turning it into a `Gist`_ + +.. _Gist: https://gist.github.com/ From c47b533a3da6a6938ea735a19f6c2ba578f80a30 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:01 +1200 Subject: [PATCH 159/181] docs: Split bugpoint.rst into user/developer Minimizing scripts (and more generally identifying root cause) isn't necessary for regular bug reports. Rather, it can be useful for developers working on *fixing* bugs, and also for fuzzers to avoid spam. Minor adjustments to `bugpoint.rst`. Add note to `advanced_bugpoint.rst` about primitives when minimizing scripts. --- docs/source/using_yosys/bugpoint.rst | 225 +----------------- .../extending_yosys/advanced_bugpoint.rst | 208 ++++++++++++++++ .../yosys_internals/extending_yosys/index.rst | 1 + 3 files changed, 217 insertions(+), 217 deletions(-) create mode 100644 docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 89c58a8f4..82eab3cfa 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -38,8 +38,6 @@ Minimizing failing (or bugged) designs error while trying to debug it -.. _minimize your RTLIL: - Minimizing RTLIL designs with bugpoint -------------------------------------- @@ -74,12 +72,6 @@ be aiming to reproduce the failure by running ``yosys -s -s Check out the instructions for :ref:`using_yosys/bugpoint:minimizing verilog designs` below. -The commands in ```` only need to be run once, while those in -```` will be run on each iteration of `bugpoint`. If you haven't -already, following the instructions for :ref:`using_yosys/bugpoint:minimizing -scripts` will also help with identifying exactly which commands are needed to -produce the failure and which can be safely moved to the loading script. - .. note:: You should also be able to run the two scripts separately, calling first @@ -236,150 +228,6 @@ Once you've verified the failure still happens, check out :ref:`using_yosys/bugpoint:identifying issues` for more on what to do next. -.. _minimize your script: - -Minimizing scripts ------------------- - -If you're using a command line prompt, such as ``yosys -p 'synth_xilinx' -o -design.json design.v``, consider converting it to a script. It's generally much -easier to iterate over changes to a script in a file rather than one on the -command line, as well as being better for sharing with others. - -.. code-block:: yoscrypt - :caption: example script, ``script.ys``, for prompt ``yosys -p 'synth_xilinx' -o design.json design.v`` - - read_verilog design.v - synth_xilinx - write_json design.json - -Next up you want to remove everything *after* the error occurs. Using the -``-L`` flag can help here, allowing you to specify a file to log to, such as -``yosys -L out.log -s script.ys``. Most commands will print a header message -when they begin; something like ``2.48. Executing HIERARCHY pass (managing -design hierarchy).`` The last header message will usually be the failing -command. There are some commands which don't print a header message, so you may -want to add ``echo on`` to the start of your script. The `echo` command echoes -each command executed, along with any arguments given to it. For the -`hierarchy` example above this might be ``yosys> hierarchy -check``. - -.. note:: - - It may also be helpful to use the `log` command to add messages which you can - then search for either in the terminal or the logfile. This can be quite - useful if your script contains script-passes, like the - :doc:`/using_yosys/synthesis/synth`, which call many sub-commands and you're - not sure exactly which script-pass is calling the failing command. - -If your final command calls sub-commands, replace it with its contents and -repeat the previous step. In the case of the -:doc:`/using_yosys/synthesis/synth`, as well as certain other script-passes, you -can use the ``-run`` option to simplify this. For example we can replace -``synth -top -lut`` with the :ref:`replace_synth`. The options ``-top - -lut`` can be provided to each `synth` step, or to just the step(s) where -it is relevant, as done here. - -.. code-block:: yoscrypt - :caption: example replacement script for `synth` command - :name: replace_synth - - synth -top -run :coarse - synth -lut -run coarse:fine - synth -lut -run fine:check - synth -run check: - -Say we ran :ref:`replace_synth` and were able to remove the ``synth -run -check:`` and still got our error, then we check the log and we see the last -thing before the error was ``7.2. Executing MEMORY_MAP pass (converting memories -to logic and flip-flops)``. By checking the output of ``yosys -h synth`` (or the -`synth` help page) we can see that the `memory_map` pass is called in the -``fine`` step. We can then update our script to the following: - -.. code-block:: yoscrypt - :caption: example replacement script for `synth` when `memory_map` is failing - - synth -top -run :fine - opt -fast -full - memory_map - -By giving `synth` the option ``-run :fine``, we are telling it to run from the -beginning of the script until the ``fine`` step, where we then give it the exact -commands to run. There are some cases where the commands given in the help -output are not an exact match for what is being run, but are instead a -simplification. If you find that replacing the script-pass with its contents -causes the error to disappear, or change, try calling the script-pass with -``echo on`` to see exactly what commands are being called and what options are -used. - -.. warning:: - - Before continuing further, *back up your code*. The following steps can - remove context and lead to over-minimizing scripts, hiding underlying issues. - Check out :ref:`using_yosys/bugpoint:Why context matters` to learn more. - -When a problem is occurring many steps into a script, minimizing the design at -the start of the script isn't always enough to identify the cause of the issue. -Each extra step of the script can lead to larger sections of the input design -being needed for the specific problem to be preserved until it causes a crash. -So to find the smallest possible reproducer it can sometimes be helpful to -remove commands prior to the failure point. - -The simplest way to do this is by writing out the design, resetting the current -state, and reading back the design: - -.. code-block:: yoscrypt - - write_rtlil ; design -reset; read_rtlil ; - -In most cases, this can be inserted immediately before the failing command while -still producing the error, allowing you to `minimize your RTLIL`_ with the -```` output. For our previous example with `memory_map`, if -:ref:`map_reset` still gives the same error, then we should now be able to call -``yosys design.il -p 'memory_map'`` to reproduce it. - -.. code-block:: yoscrypt - :caption: resetting the design immediately before failure - :name: map_reset - - synth -top -run :fine - opt -fast -full - write_rtlil design.il; design -reset; read_rtlil design.il; - memory_map - -If that doesn't give the error (or doesn't give the same error), then you should -try to move the write/reset/read earlier in the script until it does. If you -have no idea where exactly you should put the reset, the best way is to use a -"binary search" type approach, reducing the possible options by half after each -attempt. - - As an example, your script has 16 commands in it before failing on the 17th. - If resetting immediately before the 17th doesn't reproduce the error, try - between the 8th and 9th (8 is half of the total 16). If that produces the - error then you can remove everything before the `read_rtlil` and try reset - again in the middle of what's left, making sure to use a different name for - the output file so that you don't overwrite what you've already got. If the - error isn't produced then you need to go earlier still, so in this case you - would do between the 4th and 5th (4 is half of the previous 8). Repeat this - until you can't reduce the remaining commands any further. - -.. TODO:: is it possible to dump scratchpad? - - is there anything else in the yosys/design state that doesn't get included in - `write_rtlil`? - -A more conservative, but more involved, method is to remove or comment out -commands prior to the failing command. Each command, or group of commands, can -be disabled one at a time while checking if the error still occurs, eventually -giving the smallest subset of commands needed to take the original input through -to the error. The difficulty with this method is that depending on your design, -some commands may be necessary even if they aren't needed to reproduce the -error. For example, if your design includes ``process`` blocks, many commands -will fail unless you run the `proc` command. While this approach can do a -better job of maintaining context, it is often easier to *recover* the context -after the design has been minimized for producing the error. For more on -recovering context, checkout :ref:`using_yosys/bugpoint:Why context matters`. - - Minimizing Verilog designs -------------------------- @@ -393,9 +241,9 @@ Minimizing Verilog designs + if the problem is parameter specific you may be able to change the default parameters so that they match the problematic configuration -- as with `minimize your script`_, if you have no idea what is or is not - relevant, try to follow a "binary search" type approach where you remove (or - comment out) roughly half of what's left at a time +- if you have no idea what is or is not relevant, try to follow a "binary + search" type approach where you remove (or comment out) roughly half of what's + left at a time - focusing on one type of object at a time simplifies the process, removing as many as you can until the error disappears if any of the remaining objects are removed @@ -448,36 +296,13 @@ Identifying issues to a right shift (or `$shr`)? + is the issue tied to specific parameters, widths, or values? -- if the failing command was part of a larger script, such as one of the - :doc:`/using_yosys/synthesis/synth`, you could try to follow the design - through the script - - + sometimes when a command is raising an error, you're seeing a symptom rather - than the underlying issue - + an earlier command may be putting the design in an invalid state which isn't - picked up until the error is raised - + check out :ref:`using_yosys/bugpoint:Why context matters` - + if you're using a fuzzer to find issues in Yosys, you should be prepared to - do this step - -- if you're familiar with C/C++ you might try to have a look at the source - code of the command that's failing - - + even if you can't fix the problem yourself, it can be very helpful for - anyone else investigating if you're able to identify where exactly the - issue is - + if you're using a fuzzer to find issues in Yosys, you should be prepared to - do this step - .. warning:: - In the event that you are unable to identify the root cause of a fuzzer - generated issue, **do not** open more than one issue at a time. You have no - way of being able to tell if multiple fuzzer generated issues are simply - different cases of the same problem, and opening multiple issues for the same - problem means more time is spent on triaging and diagnosing bug reports and - less on fixing the problem. If you are found to be doing this, your issues - may be closed without further investigation. + If you are using a fuzzer to find bugs, follow the instructions for + :doc:`/yosys_internals/extending_yosys/advanced_bugpoint`. **Do not** open + more than one fuzzer generated issue at a time if you can not identify the + root cause. If you are found to be doing this, your issues may be closed + without further investigation. - search `the existing issues`_ and see if someone has already made a bug report @@ -492,37 +317,3 @@ Identifying issues - if there are no existing or related issues already, then check out the steps for :ref:`yosys_internals/extending_yosys/contributing:reporting bugs` - - -Why context matters -------------------- - -- if you did `minimize your script`_, and removed commands prior to the failure - to get a smaller design, try to work backwards and find which commands may - have contributed to the design failing -- especially important when the bug is happening inside of a ``synth_*`` script -- example (#4590) - - + say you did all the minimization and found that the error occurs when a call - to ``techmap -map +/xilinx/cells_map.v`` with ``MIN_MUX_INPUTS`` defined - parses a `$_MUX16_` with all inputs set to ``1'x`` - + step through the original script, calling `stat` after each step to find - when the `$_MUX16_` is added - + find that the `$_MUX16_` is introduced by a call to `muxcover`, but all the - inputs are defined, so calling `techmap` now works as expected - - * and from running `bugpoint` with the failing techmap you know that the - cell with index ``2297`` will fail, so you can now call ``select - top/*$2297`` to limit to just that cell, and optionally call ``design - -save pre_bug`` or ``write_rtlil -selected pre_bug.il`` to save this state - - + next you step through the remaining commands and call `dump` after each to - find when the inputs are disconnected - + find that ``opt -full`` has optimized away portions of the circuit, leading - to `opt_expr` setting the undriven mux inputs to ``x``, but failing to - remove the now unnecessary `$_MUX16_` - -- in this example, you might've stopped with the minimal reproducer, fixed the - bug in ``+/xilinx/cells_map.v``, and carried on -- but by following the failure back you've managed to identify a problem with - `opt_expr` that could be causing other problems either now or in the future diff --git a/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst b/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst new file mode 100644 index 000000000..32d334581 --- /dev/null +++ b/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst @@ -0,0 +1,208 @@ +Identifying the root cause of bugs +================================== + +This document references Yosys internals and is intended for people interested +in solving or investigating Yosys bugs. This also applies if you are using a +fuzzing tool; fuzzers have a tendency to find many variations of the same bug, +so identifying the root cause is important for avoiding issue spam. + +- if the failing command was part of a larger script, such as one of the + :doc:`/using_yosys/synthesis/synth`, you could try to follow the design + through the script + + + sometimes when a command is raising an error, you're seeing a symptom rather + than the underlying issue + + an earlier command may be putting the design in an invalid state which isn't + picked up until the error is raised + + check out :ref:`yosys_internals/extending_yosys/advanced_bugpoint:why context matters` + +- if you're familiar with C/C++ you might try to have a look at the source + code of the command that's failing + + + even if you can't fix the problem yourself, it can be very helpful for + anyone else investigating if you're able to identify where exactly the + issue is + +Minimizing scripts +------------------ + +.. TODO:: disclaimer this is intended as advanced usage, and generally not necessary for bug reports + +If you're using a command line prompt, such as ``yosys -p 'synth_xilinx' -o +design.json design.v``, consider converting it to a script. It's generally much +easier to iterate over changes to a script in a file rather than one on the +command line, as well as being better for sharing with others. + +.. code-block:: yoscrypt + :caption: example script, ``script.ys``, for prompt ``yosys -p 'synth_xilinx' -o design.json design.v`` + + read_verilog design.v + synth_xilinx + write_json design.json + +Next up you want to remove everything *after* the error occurs. Using the +``-L`` flag can help here, allowing you to specify a file to log to, such as +``yosys -L out.log -s script.ys``. Most commands will print a header message +when they begin; something like ``2.48. Executing HIERARCHY pass (managing +design hierarchy).`` The last header message will usually be the failing +command. There are some commands which don't print a header message, so you may +want to add ``echo on`` to the start of your script. The `echo` command echoes +each command executed, along with any arguments given to it. For the +`hierarchy` example above this might be ``yosys> hierarchy -check``. + +.. note:: + + It may also be helpful to use the `log` command to add messages which you can + then search for either in the terminal or the logfile. This can be quite + useful if your script contains script-passes, like the + :doc:`/using_yosys/synthesis/synth`, which call many sub-commands and you're + not sure exactly which script-pass is calling the failing command. + +If your final command calls sub-commands, replace it with its contents and +repeat the previous step. In the case of the +:doc:`/using_yosys/synthesis/synth`, as well as certain other script-passes, you +can use the ``-run`` option to simplify this. For example we can replace +``synth -top -lut`` with the :ref:`replace_synth`. The options ``-top + -lut`` can be provided to each `synth` step, or to just the step(s) where +it is relevant, as done here. + +.. code-block:: yoscrypt + :caption: example replacement script for `synth` command + :name: replace_synth + + synth -top -run :coarse + synth -lut -run coarse:fine + synth -lut -run fine:check + synth -run check: + +Say we ran :ref:`replace_synth` and were able to remove the ``synth -run +check:`` and still got our error, then we check the log and we see the last +thing before the error was ``7.2. Executing MEMORY_MAP pass (converting memories +to logic and flip-flops)``. By checking the output of ``yosys -h synth`` (or the +`synth` help page) we can see that the `memory_map` pass is called in the +``fine`` step. We can then update our script to the following: + +.. code-block:: yoscrypt + :caption: example replacement script for `synth` when `memory_map` is failing + + synth -top -run :fine + opt -fast -full + memory_map + +By giving `synth` the option ``-run :fine``, we are telling it to run from the +beginning of the script until the ``fine`` step, where we then give it the exact +commands to run. There are some cases where the commands given in the help +output are not an exact match for what is being run, but are instead a +simplification. If you find that replacing the script-pass with its contents +causes the error to disappear, or change, try calling the script-pass with +``echo on`` to see exactly what commands are being called and what options are +used. + +.. warning:: + + Before continuing further, *back up your code*. The following steps can + remove context and lead to over-minimizing scripts, hiding underlying issues. + Check out :ref:`yosys_internals/extending_yosys/advanced_bugpoint:Why + context matters` to learn more. + +When a problem is occurring many steps into a script, minimizing the design at +the start of the script isn't always enough to identify the cause of the issue. +Each extra step of the script can lead to larger sections of the input design +being needed for the specific problem to be preserved until it causes a crash. +So to find the smallest possible reproducer it can sometimes be helpful to +remove commands prior to the failure point. + +The simplest way to do this is by writing out the design, resetting the current +state, and reading back the design: + +.. code-block:: yoscrypt + + write_rtlil ; design -reset; read_rtlil ; + +In most cases, this can be inserted immediately before the failing command while +still producing the error, allowing you to :ref:`minimize your +RTLIL` with the +```` output. For our previous example with `memory_map`, if +:ref:`map_reset` still gives the same error, then we should now be able to call +``yosys design.il -p 'memory_map'`` to reproduce it. + +.. code-block:: yoscrypt + :caption: resetting the design immediately before failure + :name: map_reset + + synth -top -run :fine + opt -fast -full + write_rtlil design.il; design -reset; read_rtlil design.il; + memory_map + +If that doesn't give the error (or doesn't give the same error), then you should +try to move the write/reset/read earlier in the script until it does. If you +have no idea where exactly you should put the reset, the best way is to use a +"binary search" type approach, reducing the possible options by half after each +attempt. + +.. note:: + + By default, `write_rtlil` doesn't include platform specific IP blocks and + other primitive cell models which are typically loaded with a ``read_verilog + -lib`` command at the start of the synthesis script. You may have to + duplicate these commands *after* the call to ``design -reset``. It is also + possible to write out *everything* with ``select =*; write_rtlil -selected + ``. + +As an example, your script has 16 commands in it before failing on the 17th. If +resetting immediately before the 17th doesn't reproduce the error, try between +the 8th and 9th (8 is half of the total 16). If that produces the error then +you can remove everything before the `read_rtlil` and try reset again in the +middle of what's left, making sure to use a different name for the output file +so that you don't overwrite what you've already got. If the error isn't +produced then you need to go earlier still, so in this case you would do between +the 4th and 5th (4 is half of the previous 8). Repeat this until you can't +reduce the remaining commands any further. + +A more conservative, but more involved, method is to remove or comment out +commands prior to the failing command. Each command, or group of commands, can +be disabled one at a time while checking if the error still occurs, eventually +giving the smallest subset of commands needed to take the original input through +to the error. The difficulty with this method is that depending on your design, +some commands may be necessary even if they aren't needed to reproduce the +error. For example, if your design includes ``process`` blocks, many commands +will fail unless you run the `proc` command. While this approach can do a +better job of maintaining context, it is often easier to *recover* the context +after the design has been minimized for producing the error. For more on +recovering context, checkout +:ref:`yosys_internals/extending_yosys/advanced_bugpoint:Why context matters`. + + +Why context matters +------------------- + +- if you did minimized your script, and removed commands prior to the failure + to get a smaller design, try to work backwards and find which commands may + have contributed to the design failing +- especially important when the bug is happening inside of a ``synth_*`` script +- example (#4590) + + + say you did all the minimization and found that the error occurs when a call + to ``techmap -map +/xilinx/cells_map.v`` with ``MIN_MUX_INPUTS`` defined + parses a `$_MUX16_` with all inputs set to ``1'x`` + + step through the original script, calling `stat` after each step to find + when the `$_MUX16_` is added + + find that the `$_MUX16_` is introduced by a call to `muxcover`, but all the + inputs are defined, so calling `techmap` now works as expected + + * and from running `bugpoint` with the failing techmap you know that the + cell with index ``2297`` will fail, so you can now call ``select + top/*$2297`` to limit to just that cell, and optionally call ``design + -save pre_bug`` or ``write_rtlil -selected pre_bug.il`` to save this state + + + next you step through the remaining commands and call `dump` after each to + find when the inputs are disconnected + + find that ``opt -full`` has optimized away portions of the circuit, leading + to `opt_expr` setting the undriven mux inputs to ``x``, but failing to + remove the now unnecessary `$_MUX16_` + +- in this example, you might've stopped with the minimal reproducer, fixed the + bug in ``+/xilinx/cells_map.v``, and carried on +- but by following the failure back you've managed to identify a problem with + `opt_expr` that could be causing other problems either now or in the future diff --git a/docs/source/yosys_internals/extending_yosys/index.rst b/docs/source/yosys_internals/extending_yosys/index.rst index 4ee21517b..72843ecd6 100644 --- a/docs/source/yosys_internals/extending_yosys/index.rst +++ b/docs/source/yosys_internals/extending_yosys/index.rst @@ -11,6 +11,7 @@ of interest for developers looking to customise Yosys builds. extensions build_verific functional_ir + advanced_bugpoint contributing test_suites From aa6c6fd2832bfb59acf84e7822aea919b2ac2440 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:01 +1200 Subject: [PATCH 160/181] bugpoint.rst: Some paragraphs on verilog --- docs/source/using_yosys/bugpoint.rst | 57 ++++++++++++++++++---------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 82eab3cfa..a2d0b901e 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -231,29 +231,47 @@ Once you've verified the failure still happens, check out Minimizing Verilog designs -------------------------- -- manual process -- made easier if the error message is able to identify the source line or name - of the object -- reminder to back up original code before modifying it -- if a specific module is causing the problem, try to set that as the top - module, you can then remove +Unlike RTLIL designs where we can use `bugpoint`, minimizing Verilog designs is +a much more manual, iterative process. Be sure to check any errors or warnings +for messages that might identify source lines or object names that might be +causing the failure, and back up your source code before modifying it. At any +point in the process, you can check for anything that is unused or totally +disconnected (ports, wires, etc) and remove them too. - + if the problem is parameter specific you may be able to change the default - parameters so that they match the problematic configuration +.. note:: -- if you have no idea what is or is not relevant, try to follow a "binary - search" type approach where you remove (or comment out) roughly half of what's - left at a time -- focusing on one type of object at a time simplifies the process, removing as - many as you can until the error disappears if any of the remaining objects are - removed -- periodically check if anything is totally disconnected (ports, wires, etc), if - it is then it can be removed too -- start by removing cells (instances of modules) + If a specific module is causing the problem, try to set that as the top + module instead. Any parameters should have their default values changed to + match the failing usage. - + if a module has no more instances, remove it entirely +As a rule of thumb, try to split things roughly in half at each step; similar to +a "binary search". If you have 10 cells (instances of modules) in your top +module, and have no idea what is causing the issue, split them into two groups +of 5 cells. For each group of cells, try remove them and see if the failure +still happens. If the error still occurs with the first group removed, but +disappears when the second group is removed, then the first group can be safely +removed. If a module has no more instances, remove it entirely. Repeat this +for each remaining group of cells until each group only has 1 cell in it and no +more cells can be removed without making the error disappear. You can also +repeat this for each module still in your design. + +After minimizing the number of cells, do the same for the process blocks in your +top module. And again for any generate blocks and combinational blocks. +Remember to check for any ports or signals which are no longer used and remove +those too. Any signals which are written but never read can also be removed. + +.. note:: + + Depending on where the design is failing, there are some commands which may + help in identifying unused objects in the design. `hierarchy` will identify + which modules are used and which are not, but check for `$paramod` modules + before removing unused ones. ``debug clean`` will list all unused wires in + each module, as well as unused cells which were automatically generated + (giving the line number of the source that generated them). Adding the + ``-purge`` flag will also include named wires that would normally be ignored + by `clean`. Though when there are large numbers of unused wires it is often + easier to just delete sections of the code and see what happens. -- then processes - try to remove or reduce assignments and operations + are there any wires/registers which get read but never written? @@ -267,6 +285,7 @@ Minimizing Verilog designs ``'0`` + if you have enable or reset logic, does the error still happen without that? + can you reduce an ``if .. else`` to a single case? + + can you remove states from a ``case`` block? - if you're planning to share the minimized code: From 6b7756b67a3226cc84d7473add7167d771b3ad30 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:01 +1200 Subject: [PATCH 161/181] bugpoint.rst: Finish paragraphs Update text to assume bugpoint PR changes. --- docs/source/using_yosys/bugpoint.rst | 221 ++++++++++++++++----------- 1 file changed, 131 insertions(+), 90 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index a2d0b901e..38a978695 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -1,41 +1,62 @@ Minimizing failing (or bugged) designs ====================================== -- how to guide -- assumes knowledge and familiarity with Yosys -- something is wrong with your design OR something is wrong with Yosys +.. TODO:: pending merge of https://github.com/YosysHQ/yosys/pull/5068 - + how to work out which +This document is a how-to guide for reducing problematic designs to the bare +minimum needed for reproducing the issue. This is a Yosys specific alternative +to the Stack Overflow article: `How to create a Minimal, Reproducible Example`_, +and is intended to help when there's something wrong with your design, or with +Yosys itself. -- *read* the error message -- is it a Yosys error? (starts with ERROR:) +.. _How to create a Minimal, Reproducible Example: https://stackoverflow.com/help/minimal-reproducible-example - + does it give you a line number from your design +.. note:: -- is it a runtime error, e.g. SEGFAULT -- are you using the latest release of Yosys + This guide assumes a moderate degree of familiarity with Yosys and requires + some amount of problem solving ability. - + has your problem already been fixed -- is your input design valid? +Before you start +---------------- - + if you're using Verilog, try load it with `iverilog`_ or `verilator`_ +The first (and often overlooked) step, is to check for and *read* any error +messages or warnings. Passing the ``-q`` flag when running Yosys will make it +so that only warnings and error messages are written to the console. Don't just +read the last message either, there may be warnings that indicate a problem +before it happens. While some things may only be regarded as warnings, such as +multiple drivers for the same signal or logic loops, these can cause problems in +some synthesis flows but not others. + +A Yosys error (one that starts with ``ERROR:``) may give you a line number from +your design, or the name of the object causing issues. If so, you may already +have enough information to resolve the problem, or at least understand why it's +happening. + +.. note:: + + If you're not already, try using the latest version from the `Yosys GitHub`_. + You may find that your issue has already been fixed! And even if it isn't, + testing with two different versions is a good way to ensure reproducibility. + +.. _Yosys GitHub: https://github.com/YosysHQ/yosys + +Another thing to be aware of is that Yosys generally doesn't perform rigorous +checking of input designs to ensure they are valid. This is especially true for +the `read_verilog` frontend. It is instead recommended that you try load it +with `iverilog`_ or `verilator`_ first, as an invalid design can often lead to +unexpected issues. .. _iverilog: https://steveicarus.github.io/iverilog/ .. _verilator: https://www.veripool.org/verilator/ -- are there any warnings before the error (either immediately before or in an - earlier command) that could be related? -- does calling `check` before the failure give any errors or warnings? -- did you call `hierarchy` before the failure? - - + can you call ``hierarchy -check``? - -- make sure to back up your code (design source and yosys script(s)) before - making any modifications - - + even if the code itself isn't important, this can help avoid "losing" the - error while trying to debug it +If you're using a custom synthesis script, try take a bit of time to figure out +which command is failing. Calling ``echo on`` at the start of your script will +`echo` each command executed; the last echo before the error should then be +where the error has come from. Check the help message for the failing command; +does it indicate limited support, or mention some other command that needs to be +run first? You can also try to call `check` and/or ``hierarchy -check`` before +the failure to see if they report and errors or warnings. Minimizing RTLIL designs with bugpoint @@ -47,6 +68,12 @@ developed for Yosys crashes, `bugpoint` can also be used for designs that lead to non-fatal errors, or even failures in other tools that use the output of a Yosys script. +.. note:: + + Make sure to back up your code (design source and yosys script(s)) before + making any modifications. Even if the code itself isn't important, this can + help avoid "losing" the error while trying to debug it. + Can I use bugpoint? ~~~~~~~~~~~~~~~~~~~ @@ -102,11 +129,7 @@ Our final failure we can use with `bugpoint` is one returned by a wrapper process, such as ``valgrind`` or ``timeout``. In this case you will be calling something like `` yosys -s design.il``. Here, Yosys is run under a wrapper process which checks for some failure state, like a memory -leak or excessive runtime. Note however that unlike the `exec` command, there -is currently no way to check the return status or messages from the wrapper -process; only a binary pass/fail. - -.. TODO:: above note pending updated bugpoint #5068 +leak or excessive runtime. How do I use bugpoint? @@ -125,8 +148,6 @@ you can use. Make sure to configure it with the correct filenames and use only one of the methods to load the design. Fill in the ``-grep`` option with the error message printed just before. If you are using a wrapper process for your failure state, add the ``-runner ""`` option to the `bugpoint` call. -For more about the options available, check ``help bugpoint`` or -:doc:`/cmd/bugpoint`. .. code-block:: yoscrypt :caption: ```` template script @@ -142,15 +163,17 @@ For more about the options available, check ``help bugpoint`` or # Save minimized design write_rtlil min.il +The ``-grep`` option is used to search the log file generated by the Yosys under +test. If the error message is generated by something else, such as a wrapper +process or compiler sanitizer, then you should instead use ``-err_grep``. For +an OS error, like a SEGFAULT, you can also use ``-expect-return`` to check the +error code returned. + .. note:: - Using ``-grep ""`` with `bugpoint` is optional, but helps to ensure - that the minimized design is reproducing the right error, especially when - ```` contains more than one command. Unfortunately this does not - work with runtime errors such as a ``SEGFAULT`` as it is only able to match - strings from the log file. - -.. TODO:: above note pending updated bugpoint #5068 + Checking the error message or return status with is optional, but helps to + ensure that the minimized design is reproducing the right error, especially + when ```` contains more than one command. By default, `bugpoint` is able to remove any part of the design. In order to keep certain parts, for instance because you already know they are related to @@ -177,13 +200,16 @@ error on undefined behaviour but only want the child process to halt on error. Once you have finished configuration, you can now run ``yosys ``. The first thing `bugpoint` will do is test the input design fails. If it doesn't, make sure you are using the right ``yosys`` executable; unless the -``-yosys`` option is provided, it will use whatever the shell defaults to. If -you are using the ``-runner`` option, try replacing the `bugpoint` command with -``write_rtlil test.il`` and then on a new line, ``! yosys -s - test.il`` to check it works as expected and returns a non-zero -status. +``-yosys`` option is provided, it will use whatever the shell defaults to, *not* +the current ``yosys``. If you are using the ``-runner`` option, try replacing +the `bugpoint` command with ``write_rtlil test.il`` and then on a new line, +``! yosys -s test.il`` to check it works as expected and +returns a non-zero status. -.. TODO:: note on ``!`` (link to :ref:`getting_started/scripting_intro:script parsing`) +.. seealso:: + + For more on script parsing and the use of ``!``, check out + :ref:`getting_started/scripting_intro:script parsing`. Depending on the size of your design, and the length of your ````, `bugpoint` may take some time; remember, it will run ``yosys -s `` @@ -231,12 +257,19 @@ Once you've verified the failure still happens, check out Minimizing Verilog designs -------------------------- +.. seealso:: + + This section is not specific to Yosys, so feel free to use another guide such + as Stack Overflow's `How to create a Minimal, Reproducible Example`_. + Unlike RTLIL designs where we can use `bugpoint`, minimizing Verilog designs is a much more manual, iterative process. Be sure to check any errors or warnings for messages that might identify source lines or object names that might be causing the failure, and back up your source code before modifying it. At any point in the process, you can check for anything that is unused or totally -disconnected (ports, wires, etc) and remove them too. +disconnected (ports, wires, etc) and remove them too. If you have multiple +source files, try to reduce them down to a single file; either by removing files +or combining them. .. note:: @@ -264,7 +297,7 @@ those too. Any signals which are written but never read can also be removed. Depending on where the design is failing, there are some commands which may help in identifying unused objects in the design. `hierarchy` will identify - which modules are used and which are not, but check for `$paramod` modules + which modules are used and which are not, but check for ``$paramod`` modules before removing unused ones. ``debug clean`` will list all unused wires in each module, as well as unused cells which were automatically generated (giving the line number of the source that generated them). Adding the @@ -272,48 +305,70 @@ those too. Any signals which are written but never read can also be removed. by `clean`. Though when there are large numbers of unused wires it is often easier to just delete sections of the code and see what happens. -- try to remove or reduce assignments and operations +Next, try to remove or reduce assignments (``a = b``) and operations (``a + +b``). A good place to start is by checking for any wires/registers which are +read but never written. Try removing the signal declaration and replacing +references to it with ``'0`` or ``'x``. Do this with any constants too. Try to +replace strings with numeric values, and wide signals with smaller ones, then +see if the error persists. - + are there any wires/registers which get read but never written? +Check if there are any operations that you can simplify, like replacing ``a & +'0`` with ``'0``. If you have enable or reset logic, try removing it and see if +the error still occurs. Try reducing ``if .. else`` and ``case`` blocks to a +single case. Even if that doesn't work, you may still be able to remove some +paths; start with cases that appear to be unreachable and go from there. - * try removing the signal declaration and replacing references to it with - ``'0`` or ``'x`` - * try this with constants too +If you're planning to share the minimized code, remember to make sure there is +no sensitive or proprietary data in the design. Maybe rename that +``ibex_prefetch_buffer`` module to ``buf``, and ``very_important_signal_name`` +could just as easily be ``sig``. The point here isn't to make names as small as +possible, but rather to remove the context that is no longer necessary. Calling +something ``multiplier_output_value`` doesn't mean as much if you no longer have +the multiplier being referred to; but if the name does still make sense then +it's fine to leave it as-is. - + can you replace strings with numeric values? - + are you able to simplify any operations? like replacing ``a & '0`` with - ``'0`` - + if you have enable or reset logic, does the error still happen without that? - + can you reduce an ``if .. else`` to a single case? - + can you remove states from a ``case`` block? +.. note:: -- if you're planning to share the minimized code: - - + make sure there is no sensitive or proprietary data in the design - + instead of a long string of numbers and letters that had some meaning (or - were randomly or sequentially generated), can you give it a single character - name like ``a`` or ``x`` - + please try to keep things in English, using the letters a-z and numbers 0-9 - (unless the error is arising because of the names used) + When sharing code on the `Yosys GitHub`_, please try to keep things in + English. Declarations and strings should stick to the letters a-z and + numbers 0-9, unless the error is arising because of the names/characters + used. Identifying issues ------------------ -- does the failing command indicate limited support, or does it mention some - other command that needs to be run first? -- if you're able to, try to match the minimized design back to its original - context +When identifying issues, it is quite useful to understand the conditions under +which the issue is occurring. While there are occasionally bugs that affect a +significant number of designs, Yosys changes are tested on a variety of designs +and operating systems which typically catch any such issues before they make it +into the main branch. So what is is it about your situation that makes it +unusual? - + could you achieve the same thing a different way? - + and if so, does this other method have the same issue? +.. note:: -- try to change the design in small ways and see what happens + If you have access to a different platform you could also check if your issue + is reproducible there. Some issues may be specific to the platform or build + of Yosys. - + `bugpoint` can reduce and simplify a design, but it doesn't *change* much - + what happens if you change operators, for example a left shift (or `$shl`) - to a right shift (or `$shr`)? - + is the issue tied to specific parameters, widths, or values? +Try to match the minimized design back to its original context. Could you +achieve the same thing a different way, and if so, does this other method have +the same issue? Try to change the design in small ways and see what happens; +while `bugpoint` can reduce and simplify a design, it doesn't *change* much. +What happens if you change operators, for example a left shift (or `$shl`) to a +right shift (or `$shr`)? Try to see if the issue is tied to specific +parameters, widths, or values. + +Search `the existing issues`_ and see if someone has already made a bug report. +This is where changing the design and finding the limits of what causes the +failure really comes in handy. If you're more familiar with how the problem can +arise, you may be able to find a related issue more easily. If an issue already +exists for one case of the problem but you've found other cases, you can comment +on the issue and help get it solved. If there are no existing or related issues +already, then check out the steps for +:ref:`yosys_internals/extending_yosys/contributing:reporting bugs`. + +.. _the existing issues: https://github.com/YosysHQ/yosys/issues .. warning:: @@ -322,17 +377,3 @@ Identifying issues more than one fuzzer generated issue at a time if you can not identify the root cause. If you are found to be doing this, your issues may be closed without further investigation. - -- search `the existing issues`_ and see if someone has already made a bug report - - + this is where changing the design and finding the limits of what causes the - failure really comes in handy - + if you're more familiar with how the problem can arise, you may be able to - find a related issue more easily - + if an issue already exists for one case of the problem but you've found - other cases, you can comment on the issue and help get it solved - -.. _the existing issues: https://github.com/YosysHQ/yosys/issues - -- if there are no existing or related issues already, then check out the steps - for :ref:`yosys_internals/extending_yosys/contributing:reporting bugs` From c994b59ac6a0a87df33fe5ace0a162571329d2e2 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:01 +1200 Subject: [PATCH 162/181] advanced_bugpoint.rst: Paragraphing --- .../extending_yosys/advanced_bugpoint.rst | 170 +++++++++++------- 1 file changed, 109 insertions(+), 61 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst b/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst index 32d334581..570155c61 100644 --- a/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst +++ b/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst @@ -6,27 +6,40 @@ in solving or investigating Yosys bugs. This also applies if you are using a fuzzing tool; fuzzers have a tendency to find many variations of the same bug, so identifying the root cause is important for avoiding issue spam. -- if the failing command was part of a larger script, such as one of the - :doc:`/using_yosys/synthesis/synth`, you could try to follow the design - through the script +If you're familiar with C/C++, you might try to have a look at the source code +of the command that's failing. Even if you can't fix the problem yourself, it +can be very helpful for anyone else investigating if you're able to identify +where the issue is arising. - + sometimes when a command is raising an error, you're seeing a symptom rather - than the underlying issue - + an earlier command may be putting the design in an invalid state which isn't - picked up until the error is raised - + check out :ref:`yosys_internals/extending_yosys/advanced_bugpoint:why context matters` -- if you're familiar with C/C++ you might try to have a look at the source - code of the command that's failing +Finding the failing command +--------------------------- + +Using the ``-L`` flag can help here, allowing you to specify a file to log to, +such as ``yosys -L out.log -s script.ys``. Most commands will print a header +message when they begin; something like ``2.48. Executing HIERARCHY pass +(managing design hierarchy).`` The last header message will usually be the +failing command. There are some commands which don't print a header message, so +you may want to add ``echo on`` to the start of your script. The `echo` command +echoes each command executed, along with any arguments given to it. For the +`hierarchy` example above this might be ``yosys> hierarchy -check``. + +.. note:: + + It may also be helpful to use the `log` command to add messages which you can + then search for either in the terminal or the logfile. This can be quite + useful if your script contains script-passes, like the + :doc:`/using_yosys/synthesis/synth`, which call many sub-commands and you're + not sure exactly which script-pass is calling the failing command. - + even if you can't fix the problem yourself, it can be very helpful for - anyone else investigating if you're able to identify where exactly the - issue is Minimizing scripts ------------------ -.. TODO:: disclaimer this is intended as advanced usage, and generally not necessary for bug reports +.. warning:: + + This section is intended as **advanced usage**, and generally not necessary + for normal bug reports. If you're using a command line prompt, such as ``yosys -p 'synth_xilinx' -o design.json design.v``, consider converting it to a script. It's generally much @@ -40,28 +53,10 @@ command line, as well as being better for sharing with others. synth_xilinx write_json design.json -Next up you want to remove everything *after* the error occurs. Using the -``-L`` flag can help here, allowing you to specify a file to log to, such as -``yosys -L out.log -s script.ys``. Most commands will print a header message -when they begin; something like ``2.48. Executing HIERARCHY pass (managing -design hierarchy).`` The last header message will usually be the failing -command. There are some commands which don't print a header message, so you may -want to add ``echo on`` to the start of your script. The `echo` command echoes -each command executed, along with any arguments given to it. For the -`hierarchy` example above this might be ``yosys> hierarchy -check``. - -.. note:: - - It may also be helpful to use the `log` command to add messages which you can - then search for either in the terminal or the logfile. This can be quite - useful if your script contains script-passes, like the - :doc:`/using_yosys/synthesis/synth`, which call many sub-commands and you're - not sure exactly which script-pass is calling the failing command. - -If your final command calls sub-commands, replace it with its contents and -repeat the previous step. In the case of the -:doc:`/using_yosys/synthesis/synth`, as well as certain other script-passes, you -can use the ``-run`` option to simplify this. For example we can replace +Next up you want to remove everything *after* the error occurs. If your final +command calls sub-commands, replace it with its contents first. In the case of +the :doc:`/using_yosys/synthesis/synth`, as well as certain other script-passes, +you can use the ``-run`` option to simplify this. For example we can replace ``synth -top -lut`` with the :ref:`replace_synth`. The options ``-top -lut`` can be provided to each `synth` step, or to just the step(s) where it is relevant, as done here. @@ -177,32 +172,85 @@ recovering context, checkout Why context matters ------------------- -- if you did minimized your script, and removed commands prior to the failure - to get a smaller design, try to work backwards and find which commands may - have contributed to the design failing -- especially important when the bug is happening inside of a ``synth_*`` script -- example (#4590) +Sometimes when a command is raising an error, you're seeing a symptom rather +than the underlying issue. It's possible that an earlier command may be putting +the design in an invalid state, which isn't picked up until the error is raised. +This is particularly true for the pre-packaged +:doc:`/using_yosys/synthesis/synth`, which rely on a combination of generic and +architecture specific passes. As new features are added to Yosys and more +designs are supported, the types of cells output by a pass can grow and change; +and sometimes this leads to a mismatch in what a pass is intended to handle. + +If you minimized your script, and removed commands prior to the failure to get a +smaller reproducer, try to work backwards and find which commands may have +contributed to the design failing. From the minimized design you should have +some understanding of the cell or cells which are producing the error; but where +did those cells come from? The name and/or type of the cell can often point you +in the right direction: + +.. code-block:: + + # internal cell types start with a $ + # lowercase for word-level, uppercase for bit-level + $and + $_AND_ + + # cell types with $__ are typically intermediate cells used in techmapping + $__MUL16X16 + + # cell types without a $ are either user-defined or architecture specific + my_module + SB_MAC16 + + # object names might give you the name of the pass that created them + $procdff$1204 + $memory\rom$rdmux[0][0][0]$a$1550 + + # or even the line number in the Yosys source + $auto$muxcover.cc:557:implement_best_cover$2152 + $auto$alumacc.cc:495:replace_alu$1209 + +Try running the unminimized script and search the log for the names of the +objects in your minimized design. In the case of cells you can also search for +the type of the cell. Remember that calling `stat` will list all the types of +cells currently used in the design, and `select -list =*` will list the names of +of all the current objects. You can add these commands to your script, or use +an interactive terminal to run each command individually. Adding them to the +script can be more repeatable, but if it takes a long time to run to the point +you're interested in then an interactive shell session can give you more +flexibility once you reach that point. You can also add a call to the `shell` +command at any point in a script to start an interactive session at a given +point; allowing you to script any preparation steps, then come back once it's +done. + +A worked example +~~~~~~~~~~~~~~~~ - + say you did all the minimization and found that the error occurs when a call - to ``techmap -map +/xilinx/cells_map.v`` with ``MIN_MUX_INPUTS`` defined - parses a `$_MUX16_` with all inputs set to ``1'x`` - + step through the original script, calling `stat` after each step to find - when the `$_MUX16_` is added - + find that the `$_MUX16_` is introduced by a call to `muxcover`, but all the - inputs are defined, so calling `techmap` now works as expected +Say you did all the minimization and found that an error in `synth_xilinx` +occurs when a call to ``techmap -map +/xilinx/cells_map.v`` with +``MIN_MUX_INPUTS`` defined parses a `$_MUX16_` with all inputs set to ``1'x``. +You could fix the bug in ``+/xilinx/cells_map.v``, but that might only solve +this one case while leaving other problems that haven't been found yet. So you +step through the original script, calling `stat` after each step to find when +the `$_MUX16_` is added. - * and from running `bugpoint` with the failing techmap you know that the - cell with index ``2297`` will fail, so you can now call ``select - top/*$2297`` to limit to just that cell, and optionally call ``design - -save pre_bug`` or ``write_rtlil -selected pre_bug.il`` to save this state +You find that the `$_MUX16_` is introduced by a call to `muxcover`, but all the +inputs are defined, so calling `techmap` now works as expected. From running +`bugpoint` with the failing techmap you know that the cell with index ``2297`` +will fail, so you call ``select top/*$2297`` to limit to just that cell. This +can then be saved with ``design -save pre_bug`` or ``write_rtlil -selected +pre_bug.il``, so that you don't have to re-run all the earlier steps to get back +here. - + next you step through the remaining commands and call `dump` after each to - find when the inputs are disconnected - + find that ``opt -full`` has optimized away portions of the circuit, leading - to `opt_expr` setting the undriven mux inputs to ``x``, but failing to - remove the now unnecessary `$_MUX16_` +Next you step through the remaining commands and call `dump` after each to find +when the inputs are disconnected. You find that ``opt -full`` has optimized +away portions of the circuit, leading to `opt_expr` setting the undriven mux +inputs to ``x``, but failing to remove the now unnecessary `$_MUX16_`. Now +you've identified a problem in `opt_expr` that affects all of the wide muxes, +and could happen in any synthesis flow, not just `synth_xilinx`. -- in this example, you might've stopped with the minimal reproducer, fixed the - bug in ``+/xilinx/cells_map.v``, and carried on -- but by following the failure back you've managed to identify a problem with - `opt_expr` that could be causing other problems either now or in the future +.. seealso:: + + This example is taken from `YosysHQ/yosys#4590 + `_ and can be reproduced with a + version of Yosys between 0.45 and 0.51. From 29d334186c8acc0329eb9b6cf06ac686d71e514e Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:01 +1200 Subject: [PATCH 163/181] bugpoint.rst: How to creduce --- docs/source/using_yosys/bugpoint.rst | 87 +++++++++++++++++++++------- 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 38a978695..5c4bf3ae7 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -262,20 +262,74 @@ Minimizing Verilog designs This section is not specific to Yosys, so feel free to use another guide such as Stack Overflow's `How to create a Minimal, Reproducible Example`_. -Unlike RTLIL designs where we can use `bugpoint`, minimizing Verilog designs is -a much more manual, iterative process. Be sure to check any errors or warnings -for messages that might identify source lines or object names that might be -causing the failure, and back up your source code before modifying it. At any -point in the process, you can check for anything that is unused or totally -disconnected (ports, wires, etc) and remove them too. If you have multiple -source files, try to reduce them down to a single file; either by removing files -or combining them. +Be sure to check any errors or warnings for messages that might identify source +lines or object names that might be causing the failure, and back up your source +code before modifying it. If you have multiple source files, you should start +by reducing them down to a single file. If a specific file is failing to read, +try removing everything else and just focus on that one. If your source uses +the `include` directive, replace it with the contents of the file referenced. -.. note:: +Unlike RTLIL designs where we can use `bugpoint`, Yosys does not provide any +tools for minimizing Verilog designs. Instead, you should use an external tool +like `C-Reduce`_ (with the ``--not-c`` flag) or `sv-bugpoint`_. - If a specific module is causing the problem, try to set that as the top - module instead. Any parameters should have their default values changed to - match the failing usage. +.. _C-Reduce: https://github.com/csmith-project/creduce +.. _sv-bugpoint: https://github.com/antmicro/sv-bugpoint + +C-Reduce +~~~~~~~~ + +As a very brief overview for using C-Reduce, you want your failing source design +(``test.v``), and some shell script which checks for the error being +investigated (``test.sh``). Below is an :ref:`egtest` which uses `logger` and +the ``-expect error "" 1`` option to perform a similar role to +``bugpoint -grep``, along with ``verilator`` to lint the code and make sure it +is still valid. + +.. code-block:: bash + :caption: Example test.sh + :name: egtest + + #!/bin/bash + verilator --lint-only test.v &&/ + yosys -p 'logger -expect error "unsupported" 1; read_verilog test.v' + +.. code-block:: verilog + :caption: input test.v + + module top(input clk, a, b, c, output x, y, z); + always @(posedge clk) begin + if (a == 1'b1) + $stop; + end + assign x = a; + assign y = a ^ b; + assign z = c; + endmodule + +In this example ``read_verilog test.v`` is giving an error message that contains +the string "unsupported" because the ``$stop`` system task is only supported in +``initial`` blocks. By calling ``creduce ./test.sh test.v --not-c`` we can +minimize the design to just the failing code, while still being valid Verilog. + +.. code-block:: verilog + :caption: output test.v + + module a; + always begin $stop; + end endmodule + + +Doing it manually +~~~~~~~~~~~~~~~~~ + +If for some reason you are unable to use a tool to minimize your code, you can +still do it manually. But it can be a time consuming process and requires a lot +of iteration. At any point in the process, you can check for anything that is +unused or totally disconnected (ports, wires, etc) and remove them. If a +specific module is causing the problem, try to set that as the top module +instead. Any parameters should have their default values changed to match the +failing usage. As a rule of thumb, try to split things roughly in half at each step; similar to a "binary search". If you have 10 cells (instances of modules) in your top @@ -318,15 +372,6 @@ the error still occurs. Try reducing ``if .. else`` and ``case`` blocks to a single case. Even if that doesn't work, you may still be able to remove some paths; start with cases that appear to be unreachable and go from there. -If you're planning to share the minimized code, remember to make sure there is -no sensitive or proprietary data in the design. Maybe rename that -``ibex_prefetch_buffer`` module to ``buf``, and ``very_important_signal_name`` -could just as easily be ``sig``. The point here isn't to make names as small as -possible, but rather to remove the context that is no longer necessary. Calling -something ``multiplier_output_value`` doesn't mean as much if you no longer have -the multiplier being referred to; but if the name does still make sense then -it's fine to leave it as-is. - .. note:: When sharing code on the `Yosys GitHub`_, please try to keep things in From 4924670325f0768cb71f5072e300a874d1bef02e Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:02 +1200 Subject: [PATCH 164/181] bugpoint.rst: How to sv-bugpoint --- docs/source/using_yosys/bugpoint.rst | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 5c4bf3ae7..41fc60cdb 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -287,7 +287,7 @@ the ``-expect error "" 1`` option to perform a similar role to is still valid. .. code-block:: bash - :caption: Example test.sh + :caption: Example test.sh for C-Reduce :name: egtest #!/bin/bash @@ -320,6 +320,25 @@ minimize the design to just the failing code, while still being valid Verilog. end endmodule +sv-bugpoint +~~~~~~~~~~~ + +sv-bugpoint works quite similarly to C-Reduce, except it requires an output +directory to be provided and the check script needs to accept the target file as +an input argument: ``sv-bugpoint outDir/ test.sh test.v`` + +.. code-block:: bash + :caption: Example test.sh for sv-bugpoint + + #!/bin/bash + verilator --lint-only $1 &&/ + yosys -p "logger -expect error \"unsupported\" 1; read_verilog $1" + +Notice that the commands for ``yosys -p`` are now in double quotes (``"``), and +the quotes around the error string are escaped (``\"``). This is necessary for +the ``$1`` argument subsitution to work correctly. + + Doing it manually ~~~~~~~~~~~~~~~~~ From 96b072aeb31ae6e476d419b1eb814febf75b31c6 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:02 +1200 Subject: [PATCH 165/181] advanced_bugpoint.rst: --dump-design Also fix missing double backtick. --- .../extending_yosys/advanced_bugpoint.rst | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst b/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst index 570155c61..22e4b1b7a 100644 --- a/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst +++ b/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst @@ -213,16 +213,31 @@ in the right direction: Try running the unminimized script and search the log for the names of the objects in your minimized design. In the case of cells you can also search for the type of the cell. Remember that calling `stat` will list all the types of -cells currently used in the design, and `select -list =*` will list the names of -of all the current objects. You can add these commands to your script, or use -an interactive terminal to run each command individually. Adding them to the -script can be more repeatable, but if it takes a long time to run to the point -you're interested in then an interactive shell session can give you more +cells currently used in the design, and ``select -list =*`` will list the names +of of all the current objects. You can add these commands to your script, or +use an interactive terminal to run each command individually. Adding them to +the script can be more repeatable, but if it takes a long time to run to the +point you're interested in then an interactive shell session can give you more flexibility once you reach that point. You can also add a call to the `shell` command at any point in a script to start an interactive session at a given point; allowing you to script any preparation steps, then come back once it's done. +The ``--dump-design`` option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yosys provides the ``--dump-design`` option (or ``-P`` for short) for dumping +the design at specific steps of the script based on the log header. If the last +step before an error is ``7.2. Executing MEMORY_MAP pass (converting memories to +logic and flip-flops)``, then calling Yosys with ``--dump-design 7.2:bad.il`` +will save the design *before* this command runs, in the file ``bad.il``. + +It is also possible to use this option multiple times, e.g. ``-P2:hierarchy.il +-P7 -P7.2:bad.il``, to get multiple dumps in the same run. This can make it +easier to follow the design through each step to find where certain cells or +connections are coming from. ``--dump-design ALL`` is also allowed, writing out +the design at each log header. + A worked example ~~~~~~~~~~~~~~~~ From bfe2418a67ea6f586c4b99a094c871f19c2a9871 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:02 +1200 Subject: [PATCH 166/181] bugpoint.rst: Expand note on checking errors --- docs/source/using_yosys/bugpoint.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 41fc60cdb..ce84fd285 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -171,9 +171,12 @@ error code returned. .. note:: - Checking the error message or return status with is optional, but helps to - ensure that the minimized design is reproducing the right error, especially - when ```` contains more than one command. + Checking the error message or return status is optional, but highly + recommended. `bugpoint` can quite easily introduce bugs by creating + malformed designs that commands were not intended to handle. By having some + way to check the error, `bugpoint` can ensure that it is the *right* error + being reproduced. This is even more important when ```` contains + more than one command. By default, `bugpoint` is able to remove any part of the design. In order to keep certain parts, for instance because you already know they are related to From 8750ca42d38883030c703d31213cfcaeacf28b82 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:54:02 +1200 Subject: [PATCH 167/181] docs: Fix formatting --- docs/source/using_yosys/bugpoint.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index ce84fd285..60cabd879 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -270,7 +270,7 @@ lines or object names that might be causing the failure, and back up your source code before modifying it. If you have multiple source files, you should start by reducing them down to a single file. If a specific file is failing to read, try removing everything else and just focus on that one. If your source uses -the `include` directive, replace it with the contents of the file referenced. +the ``include`` directive, replace it with the contents of the file referenced. Unlike RTLIL designs where we can use `bugpoint`, Yosys does not provide any tools for minimizing Verilog designs. Instead, you should use an external tool From f22fe912379aee2a7263896d984a1cdc7fa0d5fa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 00:27:22 +0000 Subject: [PATCH 168/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c828aa2d5..e5f23a0fd 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+150 +YOSYS_VER := 0.55+158 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From f769e6e24524ebde9fec4cf0fddcf6910a51170f Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 5 Aug 2025 12:27:10 +0200 Subject: [PATCH 169/181] Update ABC --- abc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abc b/abc index fcd8ac34d..fa7fa163d 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit fcd8ac34d6105b48f32dfaf31983b071c46fb7b9 +Subproject commit fa7fa163dacdf81bdcb72b2d2713fbe9984b62bb From 2ad3532a557de0ee512b5a6ec6fe3b8fd8efca7d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 00:27:13 +0000 Subject: [PATCH 170/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e5f23a0fd..283040755 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+158 +YOSYS_VER := 0.55+194 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 3959d19291c68a521b8d332c253d4f20356e3879 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:52:12 +1200 Subject: [PATCH 171/181] Reapply "Add groups to command reference" This reverts commit 81f87ce6ede480de2c976938921eef8b9e79f9db. --- .github/workflows/prepare-docs.yml | 1 - Makefile | 36 +- backends/functional/test_generic.cc | 4 +- docs/.gitignore | 1 - docs/Makefile | 2 +- docs/source/_static/custom.css | 5 + docs/source/appendix/auxlibs.rst | 5 +- docs/source/cmd/index_backends.rst | 5 + docs/source/cmd/index_formal.rst | 5 + docs/source/cmd/index_frontends.rst | 5 + docs/source/cmd/index_internal.rst | 152 ++++++ docs/source/cmd/index_kernel.rst | 5 + docs/source/cmd/index_other.rst | 9 + docs/source/cmd/index_passes.rst | 14 + docs/source/cmd/index_passes_cmds.rst | 5 + docs/source/cmd/index_passes_equiv.rst | 5 + docs/source/cmd/index_passes_fsm.rst | 5 + docs/source/cmd/index_passes_hierarchy.rst | 5 + docs/source/cmd/index_passes_memory.rst | 5 + docs/source/cmd/index_passes_opt.rst | 5 + docs/source/cmd/index_passes_proc.rst | 5 + docs/source/cmd/index_passes_sat.rst | 5 + docs/source/cmd/index_passes_status.rst | 5 + docs/source/cmd/index_passes_techmap.rst | 7 + docs/source/cmd/index_techlibs.rst | 11 + docs/source/cmd/index_techlibs_achronix.rst | 5 + docs/source/cmd/index_techlibs_anlogic.rst | 5 + docs/source/cmd/index_techlibs_common.rst | 5 + .../source/cmd/index_techlibs_coolrunner2.rst | 5 + docs/source/cmd/index_techlibs_easic.rst | 5 + docs/source/cmd/index_techlibs_ecp5.rst | 5 + docs/source/cmd/index_techlibs_fabulous.rst | 5 + docs/source/cmd/index_techlibs_gatemate.rst | 5 + docs/source/cmd/index_techlibs_gowin.rst | 5 + docs/source/cmd/index_techlibs_greenpak4.rst | 5 + docs/source/cmd/index_techlibs_ice40.rst | 5 + docs/source/cmd/index_techlibs_intel.rst | 5 + docs/source/cmd/index_techlibs_intel_alm.rst | 5 + docs/source/cmd/index_techlibs_lattice.rst | 5 + .../cmd/index_techlibs_lattice_nexus.rst | 5 + docs/source/cmd/index_techlibs_microchip.rst | 5 + .../cmd/index_techlibs_microchip_sf2.rst | 5 + docs/source/cmd/index_techlibs_nanoxplore.rst | 5 + docs/source/cmd/index_techlibs_quicklogic.rst | 5 + docs/source/cmd/index_techlibs_xilinx.rst | 5 + docs/source/cmd_ref.rst | 33 +- .../code_examples/macro_commands/prep.ys | 23 + docs/source/conf.py | 12 +- docs/source/getting_started/example_synth.rst | 63 ++- .../getting_started/scripting_intro.rst | 4 +- docs/source/index.rst | 2 +- .../interactive_investigation.rst | 10 +- .../more_scripting/load_design.rst | 2 +- .../using_yosys/more_scripting/selections.rst | 6 +- docs/source/using_yosys/synthesis/fsm.rst | 2 +- docs/source/using_yosys/synthesis/memory.rst | 2 +- docs/source/using_yosys/synthesis/opt.rst | 8 +- docs/source/using_yosys/synthesis/proc.rst | 2 +- docs/source/using_yosys/synthesis/synth.rst | 33 +- .../using_yosys/synthesis/techmap_synth.rst | 6 +- docs/source/using_yosys/verilog.rst | 2 +- docs/source/yosys_internals/hashing.rst | 2 +- docs/util/{cellref.py => cell_documenter.py} | 0 docs/util/cmd_documenter.py | 443 ++++++++++++++++++ docs/util/{cmdref.py => custom_directives.py} | 152 +++++- kernel/json.h | 4 +- kernel/log_help.cc | 151 ++++++ kernel/log_help.h | 132 ++++++ kernel/register.cc | 438 +++++++++-------- kernel/register.h | 46 +- passes/cmds/check.cc | 6 + passes/cmds/chformal.cc | 109 ++--- passes/cmds/cover.cc | 14 +- passes/cmds/dft_tag.cc | 6 + passes/cmds/edgetypes.cc | 6 + passes/cmds/example_dt.cc | 8 +- passes/cmds/exec.cc | 9 +- passes/cmds/future.cc | 6 + passes/cmds/glift.cc | 11 +- passes/cmds/internal_stats.cc | 7 +- passes/cmds/logcmd.cc | 10 +- passes/cmds/logger.cc | 9 +- passes/cmds/ltp.cc | 6 + passes/cmds/plugin.cc | 6 + passes/cmds/portarcs.cc | 6 + passes/cmds/portlist.cc | 6 + passes/cmds/printattrs.cc | 6 + passes/cmds/scc.cc | 11 +- passes/cmds/scratchpad.cc | 10 +- passes/cmds/select.cc | 18 +- passes/cmds/setenv.cc | 11 +- passes/cmds/show.cc | 10 +- passes/cmds/sta.cc | 6 + passes/cmds/stat.cc | 6 + passes/cmds/tee.cc | 10 +- passes/cmds/torder.cc | 6 + passes/cmds/trace.cc | 11 + passes/cmds/viz.cc | 6 + passes/cmds/write_file.cc | 6 + passes/cmds/xprop.cc | 6 + passes/hierarchy/Makefile.inc | 1 + passes/{techmap => hierarchy}/flatten.cc | 0 passes/opt/rmports.cc | 11 +- passes/pmgen/test_pmgen.cc | 4 +- passes/sat/assertpmux.cc | 6 + passes/sat/async2sync.cc | 6 + passes/sat/clk2fflogic.cc | 6 + passes/sat/cutpoint.cc | 6 + passes/sat/expose.cc | 10 +- passes/sat/fmcombine.cc | 6 + passes/sat/fminit.cc | 6 + passes/sat/formalff.cc | 6 + passes/sat/freduce.cc | 14 +- passes/sat/miter.cc | 10 +- passes/sat/mutate.cc | 6 + passes/sat/qbfsat.cc | 6 + passes/sat/recover_names.cc | 6 + passes/sat/sat.cc | 14 +- passes/sat/supercover.cc | 6 + passes/sat/synthprop.cc | 12 +- passes/techmap/Makefile.inc | 1 - passes/tests/test_abcloop.cc | 4 +- passes/tests/test_autotb.cc | 4 +- passes/tests/test_cell.cc | 4 +- 124 files changed, 2030 insertions(+), 463 deletions(-) create mode 100644 docs/source/cmd/index_backends.rst create mode 100644 docs/source/cmd/index_formal.rst create mode 100644 docs/source/cmd/index_frontends.rst create mode 100644 docs/source/cmd/index_internal.rst create mode 100644 docs/source/cmd/index_kernel.rst create mode 100644 docs/source/cmd/index_other.rst create mode 100644 docs/source/cmd/index_passes.rst create mode 100644 docs/source/cmd/index_passes_cmds.rst create mode 100644 docs/source/cmd/index_passes_equiv.rst create mode 100644 docs/source/cmd/index_passes_fsm.rst create mode 100644 docs/source/cmd/index_passes_hierarchy.rst create mode 100644 docs/source/cmd/index_passes_memory.rst create mode 100644 docs/source/cmd/index_passes_opt.rst create mode 100644 docs/source/cmd/index_passes_proc.rst create mode 100644 docs/source/cmd/index_passes_sat.rst create mode 100644 docs/source/cmd/index_passes_status.rst create mode 100644 docs/source/cmd/index_passes_techmap.rst create mode 100644 docs/source/cmd/index_techlibs.rst create mode 100644 docs/source/cmd/index_techlibs_achronix.rst create mode 100644 docs/source/cmd/index_techlibs_anlogic.rst create mode 100644 docs/source/cmd/index_techlibs_common.rst create mode 100644 docs/source/cmd/index_techlibs_coolrunner2.rst create mode 100644 docs/source/cmd/index_techlibs_easic.rst create mode 100644 docs/source/cmd/index_techlibs_ecp5.rst create mode 100644 docs/source/cmd/index_techlibs_fabulous.rst create mode 100644 docs/source/cmd/index_techlibs_gatemate.rst create mode 100644 docs/source/cmd/index_techlibs_gowin.rst create mode 100644 docs/source/cmd/index_techlibs_greenpak4.rst create mode 100644 docs/source/cmd/index_techlibs_ice40.rst create mode 100644 docs/source/cmd/index_techlibs_intel.rst create mode 100644 docs/source/cmd/index_techlibs_intel_alm.rst create mode 100644 docs/source/cmd/index_techlibs_lattice.rst create mode 100644 docs/source/cmd/index_techlibs_lattice_nexus.rst create mode 100644 docs/source/cmd/index_techlibs_microchip.rst create mode 100644 docs/source/cmd/index_techlibs_microchip_sf2.rst create mode 100644 docs/source/cmd/index_techlibs_nanoxplore.rst create mode 100644 docs/source/cmd/index_techlibs_quicklogic.rst create mode 100644 docs/source/cmd/index_techlibs_xilinx.rst create mode 100644 docs/source/code_examples/macro_commands/prep.ys rename docs/util/{cellref.py => cell_documenter.py} (100%) create mode 100644 docs/util/cmd_documenter.py rename docs/util/{cmdref.py => custom_directives.py} (81%) create mode 100644 kernel/log_help.cc create mode 100644 kernel/log_help.h rename passes/{techmap => hierarchy}/flatten.cc (100%) diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml index fb1fab426..b47f5f3dd 100644 --- a/.github/workflows/prepare-docs.yml +++ b/.github/workflows/prepare-docs.yml @@ -59,7 +59,6 @@ jobs: with: name: cmd-ref-${{ github.sha }} path: | - docs/source/cmd docs/source/generated docs/source/_images docs/source/code_examples diff --git a/Makefile b/Makefile index 283040755..ce438f5db 100644 --- a/Makefile +++ b/Makefile @@ -115,12 +115,6 @@ BISON ?= bison STRIP ?= strip AWK ?= awk -ifneq ($(shell :; command -v rsync),) -RSYNC_CP ?= rsync -rc -else -RSYNC_CP ?= cp -ru -endif - ifeq ($(OS), Darwin) PLUGIN_LINKFLAGS += -undefined dynamic_lookup LINKFLAGS += -rdynamic @@ -532,7 +526,6 @@ LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VE endif endif - ifeq ($(ENABLE_COVER),1) CXXFLAGS += -DYOSYS_ENABLE_COVER endif @@ -634,6 +627,7 @@ $(eval $(call add_include_file,frontends/blif/blifparse.h)) $(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 kernel/io.o kernel/gzip.o +OBJS += kernel/log_help.o OBJS += kernel/binding.o kernel/tclapi.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 kernel/sexpr.o OBJS += kernel/drivertools.o kernel/functional.o @@ -1035,19 +1029,8 @@ ifeq ($(ENABLE_PYOSYS),1) endif endif -# also others, but so long as it doesn't fail this is enough to know we tried -docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) - $(Q) mkdir -p docs/source/cmd - $(Q) mkdir -p temp/docs/source/cmd - $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' - $(Q) $(RSYNC_CP) temp/docs/source/cmd docs/source - $(Q) rm -rf temp -docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) - $(Q) mkdir -p docs/source/cell - $(Q) mkdir -p temp/docs/source/cell - $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-cells-manual' - $(Q) $(RSYNC_CP) temp/docs/source/cell docs/source - $(Q) rm -rf temp +docs/source/generated/cmds.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) + $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cmds-json $@' docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' @@ -1064,6 +1047,15 @@ docs/source/generated/functional/rosette.diff: backends/functional/smtlib.cc bac PHONY: docs/gen/functional_ir docs/gen/functional_ir: docs/source/generated/functional/smtlib.cc docs/source/generated/functional/rosette.diff +docs/source/generated/%.log: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) + $(Q) ./$(PROGRAM_PREFIX)yosys -qQT -h '$*' -l $@ + +docs/source/generated/chformal.cc: passes/cmds/chformal.cc docs/source/generated + $(Q) cp $< $@ + +PHONY: docs/gen/chformal +docs/gen/chformal: docs/source/generated/chformal.log docs/source/generated/chformal.cc + PHONY: docs/gen docs/usage docs/reqs docs/gen: $(TARGETS) $(Q) $(MAKE) -C docs gen @@ -1099,7 +1091,7 @@ docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep -docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage docs/gen/functional_ir +docs/prep: docs/source/generated/cells.json docs/source/generated/cmds.json docs/gen docs/usage docs/gen/functional_ir docs/gen/chformal DOC_TARGET ?= html docs: docs/prep @@ -1123,7 +1115,7 @@ clean: rm -f tests/tools/cmp_tbdata rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS)) -$(MAKE) -C docs clean - rm -rf docs/source/cmd docs/util/__pycache__ + rm -rf docs/util/__pycache__ rm -f *.whl rm -f libyosys.so diff --git a/backends/functional/test_generic.cc b/backends/functional/test_generic.cc index a9dfd0c70..42d6c2b95 100644 --- a/backends/functional/test_generic.cc +++ b/backends/functional/test_generic.cc @@ -116,7 +116,9 @@ struct MemContentsTest { struct FunctionalTestGeneric : public Pass { - FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {} + FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") { + internal(); + } void help() override { diff --git a/docs/.gitignore b/docs/.gitignore index 65bbcdeae..09bb59048 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,5 +1,4 @@ /build/ -/source/cmd /source/generated /source/_images/**/*.log /source/_images/**/*.aux diff --git a/docs/Makefile b/docs/Makefile index a8874bb83..fb3e03b79 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -47,7 +47,7 @@ help: .PHONY: clean clean: clean-examples rm -rf $(BUILDDIR)/* - rm -rf source/cmd util/__pycache__ + rm -rf util/__pycache__ rm -rf source/generated $(MAKE) -C source/_images clean diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css index b08194c05..60faf6812 100644 --- a/docs/source/_static/custom.css +++ b/docs/source/_static/custom.css @@ -18,3 +18,8 @@ .literal-block-wrapper .code-block-caption .caption-number { padding-right: 0.5em } + +/* Don't double shrink text in a literal in an optionlist */ +kbd .option>.literal { + font-size: revert; +} diff --git a/docs/source/appendix/auxlibs.rst b/docs/source/appendix/auxlibs.rst index 8c78ed6b3..192ac0944 100644 --- a/docs/source/appendix/auxlibs.rst +++ b/docs/source/appendix/auxlibs.rst @@ -29,8 +29,7 @@ ezSAT The files in ``libs/ezsat`` provide a library for simplifying generating CNF formulas for SAT solvers. It also contains bindings of MiniSAT. The ezSAT -library is written by C. Wolf. It is used by the `sat` pass (see -:doc:`/cmd/sat`). +library is written by C. Wolf. It is used by the `sat` pass. fst --- @@ -78,4 +77,4 @@ SubCircuit The files in ``libs/subcircuit`` provide a library for solving the subcircuit isomorphism problem. It is written by C. Wolf and based on the Ullmann Subgraph Isomorphism Algorithm :cite:p:`UllmannSubgraphIsomorphism`. It is used by the -extract pass (see :doc:`../cmd/extract`). +`extract` pass. diff --git a/docs/source/cmd/index_backends.rst b/docs/source/cmd/index_backends.rst new file mode 100644 index 000000000..373c26def --- /dev/null +++ b/docs/source/cmd/index_backends.rst @@ -0,0 +1,5 @@ +Writing output files +-------------------- + +.. autocmdgroup:: backends + :members: diff --git a/docs/source/cmd/index_formal.rst b/docs/source/cmd/index_formal.rst new file mode 100644 index 000000000..b8b134c17 --- /dev/null +++ b/docs/source/cmd/index_formal.rst @@ -0,0 +1,5 @@ +Formal verification +------------------- + +.. autocmdgroup:: formal + :members: diff --git a/docs/source/cmd/index_frontends.rst b/docs/source/cmd/index_frontends.rst new file mode 100644 index 000000000..b64fdc9b9 --- /dev/null +++ b/docs/source/cmd/index_frontends.rst @@ -0,0 +1,5 @@ +Reading input files +------------------- + +.. autocmdgroup:: frontends + :members: diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst new file mode 100644 index 000000000..bfb369dde --- /dev/null +++ b/docs/source/cmd/index_internal.rst @@ -0,0 +1,152 @@ +Internal commands for developers +-------------------------------- + +.. autocmdgroup:: internal + :members: + +Writing command help +-------------------- + +- use `chformal` as an example +- generated help content below + +.. _chformal autocmd: + +.. autocmd:: chformal + :noindex: + +The ``formatted_help()`` method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``PrettyHelp::get_current()`` +- ``PrettyHelp::set_group()`` + + + used with ``.. autocmdgroup:: `` + + can assign group and return false + + if no group is set, will try to use ``source_location`` and assign group + from path to source file + +- return value + + + true means help content added to current ``PrettyHelp`` + + false to use ``Pass::help()`` + +- adding content + + + help content is a list of ``ContentListing`` nodes, each one having a type, + body, and its own list of children ``ContentListing``\ s + + ``PrettyHelp::get_root()`` returns the root ``ContentListing`` (``type="root"``) + + ``ContentListing::{usage, option, codeblock, paragraph}`` each add a + ``ContentListing`` to the current node, with type the same as the method + + * the first argument is the body of the new node + * ``usage`` shows how to call the command (i.e. its "signature") + * ``paragraph`` content is formatted as a paragraph of text with line breaks + added automatically + * ``codeblock`` content is displayed verbatim, use line breaks as desired; + takes an optional ``language`` argument for assigning the language in RST + output for code syntax highlighting (use ``yoscrypt`` for yosys script + syntax highlighting) + * ``option`` lists a single option for the command, usually starting with a + dash (``-``); takes an optional second argument which adds a paragraph + node as a means of description + + + ``ContentListing::open_usage`` creates and returns a new usage node, can be + used to e.g. add text/options specific to a given usage of the command + + ``ContentListing::open_option`` creates and returns a new option node, can + be used to e.g. add multiple paragraphs to an option's description + + paragraphs are treated as raw RST, allowing for inline formatting and + references as if it were written in the RST file itself + +.. literalinclude:: /generated/chformal.cc + :language: c++ + :start-at: bool formatted_help() + :end-before: void execute + :caption: ``ChformalPass::formatted_help()`` from :file:`passes/cmds/chformal.cc` + +Dumping command help to json +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `help -dump-cells-json cmds.json` + + + generates a ``ContentListing`` for each command registered in Yosys + + tries to parse unformatted ``Pass::help()`` output if + ``Pass::formatted_help()`` is unimplemented or returns false + + * if a line starts with four spaces followed by the name of the command then + a space, it is parsed as a signature (usage node) + * if a line is indented and starts with a dash (``-``), it is parsed as an + option + * anything else is parsed as a codeblock and added to either the root node + or the current option depending on the indentation + + + dictionary of command name to ``ContentListing`` + + * uses ``ContentListing::to_json()`` recursively for each node in root + * root node used for source location of class definition + * includes flags set during pass constructor (e.g. ``experimental_flag`` set + by ``Pass::experimental()``) + * also title (``short_help`` argument in ``Pass::Pass``), group, and class + name + + + dictionary of group name to list of commands in that group + +- used by sphinx autodoc to generate help content + +.. literalinclude:: /generated/cmds.json + :language: json + :start-at: "chformal": { + :end-before: "chparam": { + :caption: `chformal` in generated :file:`cmds.json` + +.. note:: Synthesis command scripts are special cased + + If the final block of help output starts with the string `"The following + commands are executed by this synthesis command:\n"`, then the rest of the + code block is formatted as ``yoscrypt`` (e.g. `synth_ice40`). The caveat + here is that if the ``script()`` calls ``run()`` on any commands *prior* to + the first ``check_label`` then the auto detection will break and revert to + unformatted code (e.g. `synth_fabulous`). + +Command line rendering +~~~~~~~~~~~~~~~~~~~~~~ + +- if ``Pass::formatted_help()`` returns true, will call + ``PrettyHelp::log_help()`` + + + traverse over the children of the root node and render as plain text + + effectively the reverse of converting unformatted ``Pass::help()`` text + + lines are broken at 80 characters while maintaining indentation (controlled + by ``MAX_LINE_LEN`` in :file:`kernel/log_help.cc`) + + each line is broken into words separated by spaces, if a given word starts + and ends with backticks they will be stripped + +- if it returns false it will call ``Pass::help()`` which should call ``log()`` + directly to print and format help text + + + if ``Pass::help()`` is not overridden then a default message about missing + help will be displayed + +.. literalinclude:: /generated/chformal.log + :lines: 2- + +RST generated from autocmd +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- below is the raw RST output from ``autocmd`` (``YosysCmdDocumenter`` class in + :file:`docs/util/cmd_documenter.py`) for `chformal` command +- heading will be rendered as a subheading of the most recent heading (see + `chformal autocmd`_ above rendered under `Writing command help`_) +- ``.. cmd:def:: `` line is indexed for cross references with ``:cmd:ref:`` + directive (`chformal autocmd`_ above uses ``:noindex:`` option so that + `chformal` still links to the correct location) + + + ``:title:`` option controls text that appears when hovering over the + `chformal` link + +- commands with warning flags (experimental or internal) add a ``.. warning`` + block before any of the help content +- if a command has no ``source_location`` the ``.. note`` at the bottom will + instead link to :doc:`/cmd/index_other` + +.. autocmd_rst:: chformal diff --git a/docs/source/cmd/index_kernel.rst b/docs/source/cmd/index_kernel.rst new file mode 100644 index 000000000..c6891b5e5 --- /dev/null +++ b/docs/source/cmd/index_kernel.rst @@ -0,0 +1,5 @@ +Yosys kernel commands +--------------------- + +.. autocmdgroup:: kernel + :members: diff --git a/docs/source/cmd/index_other.rst b/docs/source/cmd/index_other.rst new file mode 100644 index 000000000..540cf9e49 --- /dev/null +++ b/docs/source/cmd/index_other.rst @@ -0,0 +1,9 @@ +:orphan: + +Other commands +============== + +Unknown source location + +.. autocmdgroup:: unknown + :members: diff --git a/docs/source/cmd/index_passes.rst b/docs/source/cmd/index_passes.rst new file mode 100644 index 000000000..b652be004 --- /dev/null +++ b/docs/source/cmd/index_passes.rst @@ -0,0 +1,14 @@ +Passes +------ + +.. toctree:: + :maxdepth: 2 + :glob: + + /cmd/index_passes_hierarchy + /cmd/index_passes_proc + /cmd/index_passes_fsm + /cmd/index_passes_memory + /cmd/index_passes_opt + /cmd/index_passes_techmap + /cmd/index_passes_* diff --git a/docs/source/cmd/index_passes_cmds.rst b/docs/source/cmd/index_passes_cmds.rst new file mode 100644 index 000000000..d7b448f07 --- /dev/null +++ b/docs/source/cmd/index_passes_cmds.rst @@ -0,0 +1,5 @@ +Design modification +------------------- + +.. autocmdgroup:: passes/cmds + :members: diff --git a/docs/source/cmd/index_passes_equiv.rst b/docs/source/cmd/index_passes_equiv.rst new file mode 100644 index 000000000..6ed2c3c18 --- /dev/null +++ b/docs/source/cmd/index_passes_equiv.rst @@ -0,0 +1,5 @@ +Equivalence checking +-------------------- + +.. autocmdgroup:: passes/equiv + :members: diff --git a/docs/source/cmd/index_passes_fsm.rst b/docs/source/cmd/index_passes_fsm.rst new file mode 100644 index 000000000..43af5dce6 --- /dev/null +++ b/docs/source/cmd/index_passes_fsm.rst @@ -0,0 +1,5 @@ +FSM handling +------------ + +.. autocmdgroup:: passes/fsm + :members: diff --git a/docs/source/cmd/index_passes_hierarchy.rst b/docs/source/cmd/index_passes_hierarchy.rst new file mode 100644 index 000000000..27a0faeb7 --- /dev/null +++ b/docs/source/cmd/index_passes_hierarchy.rst @@ -0,0 +1,5 @@ +Working with hierarchy +---------------------- + +.. autocmdgroup:: passes/hierarchy + :members: diff --git a/docs/source/cmd/index_passes_memory.rst b/docs/source/cmd/index_passes_memory.rst new file mode 100644 index 000000000..b4edb88e7 --- /dev/null +++ b/docs/source/cmd/index_passes_memory.rst @@ -0,0 +1,5 @@ +Memory handling +--------------- + +.. autocmdgroup:: passes/memory + :members: diff --git a/docs/source/cmd/index_passes_opt.rst b/docs/source/cmd/index_passes_opt.rst new file mode 100644 index 000000000..ddeb5ce10 --- /dev/null +++ b/docs/source/cmd/index_passes_opt.rst @@ -0,0 +1,5 @@ +Optimization passes +------------------- + +.. autocmdgroup:: passes/opt + :members: diff --git a/docs/source/cmd/index_passes_proc.rst b/docs/source/cmd/index_passes_proc.rst new file mode 100644 index 000000000..1ad8d85b4 --- /dev/null +++ b/docs/source/cmd/index_passes_proc.rst @@ -0,0 +1,5 @@ +Converting process blocks +------------------------- + +.. autocmdgroup:: passes/proc + :members: diff --git a/docs/source/cmd/index_passes_sat.rst b/docs/source/cmd/index_passes_sat.rst new file mode 100644 index 000000000..a2571fedb --- /dev/null +++ b/docs/source/cmd/index_passes_sat.rst @@ -0,0 +1,5 @@ +Simulating circuits +------------------- + +.. autocmdgroup:: passes/sat + :members: diff --git a/docs/source/cmd/index_passes_status.rst b/docs/source/cmd/index_passes_status.rst new file mode 100644 index 000000000..a157ed840 --- /dev/null +++ b/docs/source/cmd/index_passes_status.rst @@ -0,0 +1,5 @@ +Design status +------------- + +.. autocmdgroup:: passes/status + :members: diff --git a/docs/source/cmd/index_passes_techmap.rst b/docs/source/cmd/index_passes_techmap.rst new file mode 100644 index 000000000..1682cd181 --- /dev/null +++ b/docs/source/cmd/index_passes_techmap.rst @@ -0,0 +1,7 @@ +Technology mapping +------------------ + +.. seealso:: :doc:`/cmd/index_techlibs` + +.. autocmdgroup:: passes/techmap + :members: diff --git a/docs/source/cmd/index_techlibs.rst b/docs/source/cmd/index_techlibs.rst new file mode 100644 index 000000000..043620a3b --- /dev/null +++ b/docs/source/cmd/index_techlibs.rst @@ -0,0 +1,11 @@ +Technology libraries +==================== + +Listed in alphabetical order. + +.. toctree:: + :maxdepth: 2 + :glob: + + /cmd/index_techlibs_common + /cmd/index_techlibs_* diff --git a/docs/source/cmd/index_techlibs_achronix.rst b/docs/source/cmd/index_techlibs_achronix.rst new file mode 100644 index 000000000..d4d96d24a --- /dev/null +++ b/docs/source/cmd/index_techlibs_achronix.rst @@ -0,0 +1,5 @@ +Achronix +------------------ + +.. autocmdgroup:: techlibs/achronix + :members: diff --git a/docs/source/cmd/index_techlibs_anlogic.rst b/docs/source/cmd/index_techlibs_anlogic.rst new file mode 100644 index 000000000..8a2e6b577 --- /dev/null +++ b/docs/source/cmd/index_techlibs_anlogic.rst @@ -0,0 +1,5 @@ +Anlogic +------------------ + +.. autocmdgroup:: techlibs/anlogic + :members: diff --git a/docs/source/cmd/index_techlibs_common.rst b/docs/source/cmd/index_techlibs_common.rst new file mode 100644 index 000000000..532f4291e --- /dev/null +++ b/docs/source/cmd/index_techlibs_common.rst @@ -0,0 +1,5 @@ +Generic +------------------ + +.. autocmdgroup:: techlibs/common + :members: diff --git a/docs/source/cmd/index_techlibs_coolrunner2.rst b/docs/source/cmd/index_techlibs_coolrunner2.rst new file mode 100644 index 000000000..23d91a500 --- /dev/null +++ b/docs/source/cmd/index_techlibs_coolrunner2.rst @@ -0,0 +1,5 @@ +CoolRunner-II +------------------ + +.. autocmdgroup:: techlibs/coolrunner2 + :members: diff --git a/docs/source/cmd/index_techlibs_easic.rst b/docs/source/cmd/index_techlibs_easic.rst new file mode 100644 index 000000000..c6398ddf3 --- /dev/null +++ b/docs/source/cmd/index_techlibs_easic.rst @@ -0,0 +1,5 @@ +eASIC +------------------ + +.. autocmdgroup:: techlibs/easic + :members: diff --git a/docs/source/cmd/index_techlibs_ecp5.rst b/docs/source/cmd/index_techlibs_ecp5.rst new file mode 100644 index 000000000..29fd309cf --- /dev/null +++ b/docs/source/cmd/index_techlibs_ecp5.rst @@ -0,0 +1,5 @@ +ECP5 +------------------ + +.. autocmdgroup:: techlibs/ecp5 + :members: diff --git a/docs/source/cmd/index_techlibs_fabulous.rst b/docs/source/cmd/index_techlibs_fabulous.rst new file mode 100644 index 000000000..96f04f40a --- /dev/null +++ b/docs/source/cmd/index_techlibs_fabulous.rst @@ -0,0 +1,5 @@ +FABulous +------------------ + +.. autocmdgroup:: techlibs/fabulous + :members: diff --git a/docs/source/cmd/index_techlibs_gatemate.rst b/docs/source/cmd/index_techlibs_gatemate.rst new file mode 100644 index 000000000..951d0000b --- /dev/null +++ b/docs/source/cmd/index_techlibs_gatemate.rst @@ -0,0 +1,5 @@ +Gatemate +------------------ + +.. autocmdgroup:: techlibs/gatemate + :members: diff --git a/docs/source/cmd/index_techlibs_gowin.rst b/docs/source/cmd/index_techlibs_gowin.rst new file mode 100644 index 000000000..cdcb1c2ee --- /dev/null +++ b/docs/source/cmd/index_techlibs_gowin.rst @@ -0,0 +1,5 @@ +Gowin +------------------ + +.. autocmdgroup:: techlibs/gowin + :members: diff --git a/docs/source/cmd/index_techlibs_greenpak4.rst b/docs/source/cmd/index_techlibs_greenpak4.rst new file mode 100644 index 000000000..add1ab102 --- /dev/null +++ b/docs/source/cmd/index_techlibs_greenpak4.rst @@ -0,0 +1,5 @@ +GreenPAK4 +------------------ + +.. autocmdgroup:: techlibs/greenpak4 + :members: diff --git a/docs/source/cmd/index_techlibs_ice40.rst b/docs/source/cmd/index_techlibs_ice40.rst new file mode 100644 index 000000000..1c4b2d07a --- /dev/null +++ b/docs/source/cmd/index_techlibs_ice40.rst @@ -0,0 +1,5 @@ +iCE40 +------------------ + +.. autocmdgroup:: techlibs/ice40 + :members: diff --git a/docs/source/cmd/index_techlibs_intel.rst b/docs/source/cmd/index_techlibs_intel.rst new file mode 100644 index 000000000..6b3a26222 --- /dev/null +++ b/docs/source/cmd/index_techlibs_intel.rst @@ -0,0 +1,5 @@ +Intel (MAX10, Cyclone IV) +------------------------- + +.. autocmdgroup:: techlibs/intel + :members: diff --git a/docs/source/cmd/index_techlibs_intel_alm.rst b/docs/source/cmd/index_techlibs_intel_alm.rst new file mode 100644 index 000000000..afadfc13e --- /dev/null +++ b/docs/source/cmd/index_techlibs_intel_alm.rst @@ -0,0 +1,5 @@ +Intel ALM (Cyclone V, Arria V, Cyclone 10 GX) +--------------------------------------------- + +.. autocmdgroup:: techlibs/intel_alm + :members: diff --git a/docs/source/cmd/index_techlibs_lattice.rst b/docs/source/cmd/index_techlibs_lattice.rst new file mode 100644 index 000000000..985bf0bbd --- /dev/null +++ b/docs/source/cmd/index_techlibs_lattice.rst @@ -0,0 +1,5 @@ +Lattice +------------------ + +.. autocmdgroup:: techlibs/lattice + :members: diff --git a/docs/source/cmd/index_techlibs_lattice_nexus.rst b/docs/source/cmd/index_techlibs_lattice_nexus.rst new file mode 100644 index 000000000..d5ac4184c --- /dev/null +++ b/docs/source/cmd/index_techlibs_lattice_nexus.rst @@ -0,0 +1,5 @@ +Lattice Nexus +------------------ + +.. autocmdgroup:: techlibs/nexus + :members: diff --git a/docs/source/cmd/index_techlibs_microchip.rst b/docs/source/cmd/index_techlibs_microchip.rst new file mode 100644 index 000000000..06613a59c --- /dev/null +++ b/docs/source/cmd/index_techlibs_microchip.rst @@ -0,0 +1,5 @@ +Microchip +------------------ + +.. autocmdgroup:: techlibs/microchip + :members: diff --git a/docs/source/cmd/index_techlibs_microchip_sf2.rst b/docs/source/cmd/index_techlibs_microchip_sf2.rst new file mode 100644 index 000000000..4ebe47f33 --- /dev/null +++ b/docs/source/cmd/index_techlibs_microchip_sf2.rst @@ -0,0 +1,5 @@ +Microchip - SmartFusion2/IGLOO2 +----------------------------------- + +.. autocmdgroup:: techlibs/sf2 + :members: diff --git a/docs/source/cmd/index_techlibs_nanoxplore.rst b/docs/source/cmd/index_techlibs_nanoxplore.rst new file mode 100644 index 000000000..9eff4681c --- /dev/null +++ b/docs/source/cmd/index_techlibs_nanoxplore.rst @@ -0,0 +1,5 @@ +NanoXplore +------------------ + +.. autocmdgroup:: techlibs/nanoxplore + :members: diff --git a/docs/source/cmd/index_techlibs_quicklogic.rst b/docs/source/cmd/index_techlibs_quicklogic.rst new file mode 100644 index 000000000..54d199eb0 --- /dev/null +++ b/docs/source/cmd/index_techlibs_quicklogic.rst @@ -0,0 +1,5 @@ +QuickLogic +------------------ + +.. autocmdgroup:: techlibs/quicklogic + :members: diff --git a/docs/source/cmd/index_techlibs_xilinx.rst b/docs/source/cmd/index_techlibs_xilinx.rst new file mode 100644 index 000000000..df5112b7e --- /dev/null +++ b/docs/source/cmd/index_techlibs_xilinx.rst @@ -0,0 +1,5 @@ +Xilinx +------------------ + +.. autocmdgroup:: techlibs/xilinx + :members: diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index d2d59ba54..668516a0b 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -5,10 +5,31 @@ Command line reference .. literalinclude:: /generated/yosys :start-at: Usage -.. toctree:: - :caption: Command reference - :maxdepth: 1 - :glob: +.. _cmd_ref: - /appendix/env_vars - /cmd/* +Command reference +----------------- + +.. todo:: Can we warn on command groups that aren't included anywhere? + +:ref:`List of all commands` + +.. toctree:: + :maxdepth: 2 + + /appendix/env_vars + /cmd/index_frontends + /cmd/index_backends + /cmd/index_kernel + /cmd/index_formal + +.. toctree:: + :maxdepth: 3 + + /cmd/index_passes + /cmd/index_techlibs + +.. toctree:: + :maxdepth: 2 + + /cmd/index_internal diff --git a/docs/source/code_examples/macro_commands/prep.ys b/docs/source/code_examples/macro_commands/prep.ys new file mode 100644 index 000000000..1bec907f6 --- /dev/null +++ b/docs/source/code_examples/macro_commands/prep.ys @@ -0,0 +1,23 @@ +#start:The following commands are executed by this synthesis command: +#end:$ +begin: + hierarchy -check [-top | -auto-top] + +coarse: + proc [-ifx] + flatten (if -flatten) + future + opt_expr -keepdc + opt_clean + check + opt -noff -keepdc + wreduce -keepdc [-memx] + memory_dff (if -rdff) + memory_memx (if -memx) + opt_clean + memory_collect + opt -noff -keepdc -fast + +check: + stat + check diff --git a/docs/source/conf.py b/docs/source/conf.py index e587b8d31..7cdea5df1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -43,8 +43,12 @@ html_static_path = ['_static', "_images"] # default to no highlight highlight_language = 'none' -# default single quotes to attempt auto reference, or fallback to code +# default single quotes to attempt auto reference, or fallback to yoscrypt default_role = 'autoref' +rst_prolog = """ +.. role:: yoscrypt(code) + :language: yoscrypt +""" extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex'] @@ -105,12 +109,14 @@ latex_elements = { # custom cmd-ref parsing/linking sys.path += [os.path.dirname(__file__) + "/../"] -extensions.append('util.cmdref') +extensions.append('util.custom_directives') # use autodocs extensions.append('sphinx.ext.autodoc') -extensions.append('util.cellref') +extensions.append('util.cell_documenter') cells_json = Path(__file__).parent / 'generated' / 'cells.json' +extensions.append('util.cmd_documenter') +cmds_json = Path(__file__).parent / 'generated' / 'cmds.json' from sphinx.application import Sphinx def setup(app: Sphinx) -> None: diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst index e215586cc..ccf0d252b 100644 --- a/docs/source/getting_started/example_synth.rst +++ b/docs/source/getting_started/example_synth.rst @@ -70,7 +70,7 @@ At the bottom of the `help` output for `synth_ice40` is the complete list of commands called by this script. Let's start with the section labeled ``begin``: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: begin: :end-before: flatten: @@ -143,8 +143,8 @@ line refers to the line numbers of the start/end of the corresponding ``always @`` block. In the case of an ``initial`` block, we instead see the ``PROC`` referring to line 0. -To handle these, let us now introduce the next command: :doc:`/cmd/proc`. `proc` -is a macro command like `synth_ice40`. Rather than modifying the design +To handle these, let us now introduce the next command: :cmd:title:`proc`. +`proc` is a macro command like `synth_ice40`. Rather than modifying the design directly, it instead calls a series of other commands. In the case of `proc`, these sub-commands work to convert the behavioral logic of processes into multiplexers and registers. Let's see what happens when we run it. For now, we @@ -188,7 +188,7 @@ opt_expr `. .. note:: - :doc:`/cmd/clean` can also be called with two semicolons after any command, + :cmd:title:`clean` can also be called with two semicolons after any command, for example we could have called :yoscrypt:`opt_expr;;` instead of :yoscrypt:`opt_expr; clean`. You may notice some scripts will end each line with ``;;``. It is beneficial to run `clean` before inspecting intermediate @@ -215,8 +215,8 @@ Note that if we tried to run this command now then we would get an error. This is because we already removed all of the modules other than ``addr_gen``. We could restart our shell session, but instead let's use two new commands: -- :doc:`/cmd/design`, and -- :doc:`/cmd/read_verilog`. +- :cmd:title:`design`, and +- :cmd:title:`read_verilog`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -251,7 +251,7 @@ our design won't run into this issue, we can skip the ``-defer``. We can also run `proc` now to finish off the full :ref:`synth_begin`. Because the design schematic is quite large, we will be showing just the data path for the ``rdata`` output. If you would like to see the entire design for yourself, -you can do so with :doc:`/cmd/show`. Note that the `show` command only works +you can do so with :cmd:title:`show`. Note that the `show` command only works with a single module, so you may need to call it with :yoscrypt:`show fifo`. :ref:`show_intro` section in :doc:`/getting_started/scripting_intro` has more on how to use `show`. @@ -283,7 +283,7 @@ Flattening At this stage of a synthesis flow there are a few other commands we could run. In `synth_ice40` we get these: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: flatten: :end-before: coarse: @@ -355,7 +355,7 @@ Part 1 In the iCE40 flow, we start with the following commands: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: coarse: :end-before: wreduce @@ -371,7 +371,7 @@ wasting time on something we know is impossible. Next up is :yoscrypt:`opt -nodffe -nosdff` performing a set of simple optimizations on the design. This command also ensures that only a specific subset of FF types are included, in preparation for the next command: -:doc:`/cmd/fsm`. Both `opt` and `fsm` are macro commands which are explored in +:cmd:title:`fsm`. Both `opt` and `fsm` are macro commands which are explored in more detail in :doc:`/using_yosys/synthesis/opt` and :doc:`/using_yosys/synthesis/fsm` respectively. @@ -403,7 +403,7 @@ Part 2 The next group of commands performs a series of optimizations: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: wreduce :end-before: t:$mul @@ -411,7 +411,7 @@ The next group of commands performs a series of optimizations: :caption: ``coarse`` section (part 2) :name: synth_coarse2 -First up is :doc:`/cmd/wreduce`. If we run this we get the following: +First up is :cmd:title:`wreduce`. If we run this we get the following: .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -432,7 +432,7 @@ the schematic and see the output of that cell has now changed. ``rdata`` output after `wreduce` -The next two (new) commands are :doc:`/cmd/peepopt` and :doc:`/cmd/share`. +The next two (new) commands are :cmd:title:`peepopt` and :cmd:title:`share`. Neither of these affect our design, and they're explored in more detail in :doc:`/using_yosys/synthesis/opt`, so let's skip over them. :yoscrypt:`techmap -map +/cmp2lut.v -D LUT_WIDTH=4` optimizes certain comparison operators by @@ -440,7 +440,7 @@ converting them to LUTs instead. The usage of `techmap` is explored more in :doc:`/using_yosys/synthesis/techmap_synth`. Our next command to run is -:doc:`/cmd/memory_dff`. +:cmd:title:`memory_dff`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -475,7 +475,7 @@ will only be performed if called with the ``-dsp`` flag: :yoscrypt:`synth_ice40 -dsp`. While our example has nothing that could be mapped to DSPs we can still take a quick look at the commands here and describe what they do. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: t:$mul :end-before: alumacc @@ -514,7 +514,7 @@ Part 4 That brings us to the fourth and final part for the iCE40 synthesis flow: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: alumacc :end-before: map_ram: @@ -543,7 +543,7 @@ Once these cells have been inserted, the call to `opt` can combine cells which are now identical but may have been missed due to e.g. the difference between `$add` and `$sub`. -The other new command in this part is :doc:`/cmd/memory`. `memory` is another +The other new command in this part is :cmd:title:`memory`. `memory` is another macro command which we examine in more detail in :doc:`/using_yosys/synthesis/memory`. For this document, let us focus just on the step most relevant to our example: `memory_collect`. Up until this point, @@ -594,7 +594,7 @@ Memory blocks Mapping to hard memory blocks uses a combination of `memory_libmap` and `techmap`. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ram: :end-before: map_ffram: @@ -636,7 +636,7 @@ into flip flops (the ``logic fallback``) with `memory_map`. .. |techlibs/ice40/brams_map.v| replace:: :file:`techlibs/ice40/brams_map.v` .. _techlibs/ice40/brams_map.v: https://github.com/YosysHQ/yosys/tree/main/techlibs/ice40/brams_map.v -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ffram: :end-before: map_gates: @@ -671,7 +671,7 @@ an explosion in cells as multi-bit `$mux` and `$adffe` are replaced with single-bit `$_MUX_` and `$_DFFE_PP0P_` cells, while the `$alu` is replaced with primitive `$_OR_` and `$_NOT_` gates and a `$lut` cell. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_gates: :end-before: map_ffs: @@ -700,7 +700,7 @@ mapped to hardware into gate-level primitives. This includes optimizing `$_MUX_` cells where one of the inputs is a constant ``1'0``, replacing it instead with an `$_AND_` cell. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ffs: :end-before: map_luts: @@ -725,7 +725,7 @@ LUTs `abc`. For more on what these do, and what the difference between these two commands are, refer to :doc:`/using_yosys/synthesis/abc`. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_luts: :end-before: map_cells: @@ -742,7 +742,7 @@ commands are, refer to :doc:`/using_yosys/synthesis/abc`. Finally we use `techmap` to map the generic `$lut` cells to iCE40 ``SB_LUT4`` cells. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_cells: :end-before: check: @@ -784,19 +784,18 @@ Final steps The next section of the iCE40 synth flow performs some sanity checking and final tidy up: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: check: - :end-before: blif: :dedent: :name: check :caption: ``check`` section The new commands here are: -- :doc:`/cmd/autoname`, -- :doc:`/cmd/stat`, and -- :doc:`/cmd/blackbox`. +- :cmd:title:`autoname`, +- :cmd:title:`stat`, and +- :cmd:title:`blackbox`. The output from `stat` is useful for checking resource utilization; providing a list of cells used in the design and the number of each, as well as the number @@ -835,9 +834,9 @@ Synthesis output The iCE40 synthesis flow has the following output modes available: -- :doc:`/cmd/write_blif`, -- :doc:`/cmd/write_edif`, and -- :doc:`/cmd/write_json`. +- `write_blif`, +- `write_edif`, and +- `write_json`. As an example, if we called :yoscrypt:`synth_ice40 -top fifo -json fifo.json`, our synthesized ``fifo`` design will be output as :file:`fifo.json`. We can @@ -848,4 +847,4 @@ is beyond the scope of this documentation. .. _nextpnr: https://github.com/YosysHQ/nextpnr -.. seealso:: :doc:`/cmd/synth_ice40` +.. seealso:: :cmd:title:`synth_ice40` diff --git a/docs/source/getting_started/scripting_intro.rst b/docs/source/getting_started/scripting_intro.rst index 6a6e4ff51..c44ce82a8 100644 --- a/docs/source/getting_started/scripting_intro.rst +++ b/docs/source/getting_started/scripting_intro.rst @@ -129,7 +129,7 @@ module. Detailed documentation of the select framework can be found under :doc:`/using_yosys/more_scripting/selections` or in the command reference at -:doc:`/cmd/select`. +:cmd:title:`select`. .. _show_intro: @@ -226,7 +226,7 @@ those used in options, must be a single expression instead. .. _GraphViz color docs: https://graphviz.org/doc/info/colors For all of the options available to `show`, check the command reference at -:doc:`/cmd/show`. +:cmd:title:`show`. .. seealso:: :ref:`interactive_show` on the :doc:`/using_yosys/more_scripting/interactive_investigation` page. diff --git a/docs/source/index.rst b/docs/source/index.rst index 61dc114ef..403100093 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -5,7 +5,7 @@ Yosys Open SYnthesis Suite Yosys is an open source framework for RTL synthesis. To learn more about Yosys, see :doc:`/introduction`. For a quick guide on how to get started using Yosys, check out :doc:`/getting_started/index`. For the complete list of commands -available, go to :ref:`commandindex`. +available, go to :ref:`cmd_ref`. .. todo:: look into command ref improvements diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst index ca4c76ee7..0d1a17503 100644 --- a/docs/source/using_yosys/more_scripting/interactive_investigation.rst +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -323,10 +323,10 @@ tools). design into an equivalent design that is easier to analyse. - Commands such as `eval` and `sat` can be used to investigate the behavior of the circuit. -- :doc:`/cmd/show`. -- :doc:`/cmd/dump`. -- :doc:`/cmd/add` and :doc:`/cmd/delete` can be used to modify and reorganize a - design dynamically. +- :cmd:title:`show`. +- :cmd:title:`dump`. +- :cmd:title:`add` and :cmd:title:`delete` can be used to modify and reorganize + a design dynamically. The code used is included in the Yosys code base under |code_examples/scrambler|_. @@ -358,7 +358,7 @@ reorganizing a module in Yosys and checking the resulting circuit. .. figure:: /_images/code_examples/scrambler/scrambler_p02.* :class: width-helper invert-helper -Analyzing the resulting circuit with :doc:`/cmd/eval`: +Analyzing the resulting circuit with :cmd:title:`eval`: .. todo:: replace inline code diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst index 7e417eff9..e5006c16e 100644 --- a/docs/source/using_yosys/more_scripting/load_design.rst +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -86,7 +86,7 @@ Yosys frontends The `read_verilog` command """""""""""""""""""""""""" -- :doc:`/cmd/read_verilog` +- :doc:`/cmd/index_frontends` - supports most of Verilog-2005 - limited support for SystemVerilog - some non-standard features/extensions for enabling formal verification diff --git a/docs/source/using_yosys/more_scripting/selections.rst b/docs/source/using_yosys/more_scripting/selections.rst index e82f23497..1f3912956 100644 --- a/docs/source/using_yosys/more_scripting/selections.rst +++ b/docs/source/using_yosys/more_scripting/selections.rst @@ -93,7 +93,7 @@ Special patterns can be used to select by object property or type. For example: A:blabla` - select all `$add` cells from the module foo: :yoscrypt:`select foo/t:$add` -A complete list of pattern expressions can be found in :doc:`/cmd/select`. +A complete list of pattern expressions can be found in :cmd:title:`select`. Operations on selections ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -141,7 +141,7 @@ Some of the special ``%``-codes: - ``%i``: intersection of top two elements on stack -- pop 2, push 1 - ``%n``: inverse of top element on stack -- pop 1, push 1 -See :doc:`/cmd/select` for the full list. +See :cmd:title:`select` for the full list. Expanding selections ^^^^^^^^^^^^^^^^^^^^ @@ -354,7 +354,7 @@ boolean operations such as intersection (``%i``) and difference (``%d``) are powerful tools for extracting the relevant portions of the circuit under investigation. -Again, see :doc:`/cmd/select` for full documentation of these expressions. +Again, see :cmd:title:`select` for full documentation of these expressions. Incremental selection ^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/using_yosys/synthesis/fsm.rst b/docs/source/using_yosys/synthesis/fsm.rst index 6fad81d54..07a3cc9cc 100644 --- a/docs/source/using_yosys/synthesis/fsm.rst +++ b/docs/source/using_yosys/synthesis/fsm.rst @@ -10,7 +10,7 @@ other commands: :start-after: #end: :caption: Passes called by `fsm` -See also :doc:`/cmd/fsm`. +See also :doc:`/cmd/index_passes_fsm`. The algorithms used for FSM detection and extraction are influenced by a more general reported technique :cite:p:`fsmextract`. diff --git a/docs/source/using_yosys/synthesis/memory.rst b/docs/source/using_yosys/synthesis/memory.rst index a8f2280f7..9b81fb6dc 100644 --- a/docs/source/using_yosys/synthesis/memory.rst +++ b/docs/source/using_yosys/synthesis/memory.rst @@ -26,7 +26,7 @@ Some quick notes: decoder logic and registers. For more information about `memory`, such as disabling certain sub commands, see -:doc:`/cmd/memory`. +:doc:`/cmd/index_passes_memory`. Example ------- diff --git a/docs/source/using_yosys/synthesis/opt.rst b/docs/source/using_yosys/synthesis/opt.rst index 43b558739..56330bc37 100644 --- a/docs/source/using_yosys/synthesis/opt.rst +++ b/docs/source/using_yosys/synthesis/opt.rst @@ -11,8 +11,8 @@ The `opt` macro command The Yosys pass `opt` runs a number of simple optimizations. This includes removing unused signals and cells and const folding. It is recommended to run -this pass after each major step in the synthesis script. As listed in -:doc:`/cmd/opt`, this macro command calls the following ``opt_*`` commands: +this pass after each major step in the synthesis script. This macro command +calls the following ``opt_*`` commands: .. literalinclude:: /code_examples/macro_commands/opt.ys :language: yoscrypt @@ -233,7 +233,5 @@ Other optimizations .. todo:: more on the other optimizations -- :doc:`/cmd/wreduce` -- :doc:`/cmd/peepopt` -- :doc:`/cmd/share` +- Check :doc:`/cmd/index_passes_opt` for more. - `abc` and `abc9`, see also: :doc:`abc`. diff --git a/docs/source/using_yosys/synthesis/proc.rst b/docs/source/using_yosys/synthesis/proc.rst index b7cd348d7..c4ab0f0ea 100644 --- a/docs/source/using_yosys/synthesis/proc.rst +++ b/docs/source/using_yosys/synthesis/proc.rst @@ -17,7 +17,7 @@ commands in a sensible order: After all the ``proc_*`` commands, `opt_expr` is called. This can be disabled by calling :yoscrypt:`proc -noopt`. For more information about `proc`, such as -disabling certain sub commands, see :doc:`/cmd/proc`. +disabling certain sub commands, see :doc:`/cmd/index_passes_proc`. Many commands can not operate on modules with "processess" in them. Usually a call to `proc` is the first command in the actual synthesis procedure after diff --git a/docs/source/using_yosys/synthesis/synth.rst b/docs/source/using_yosys/synthesis/synth.rst index e3d82931b..db81d0790 100644 --- a/docs/source/using_yosys/synthesis/synth.rst +++ b/docs/source/using_yosys/synthesis/synth.rst @@ -6,44 +6,23 @@ Synth commands Packaged ``synth_*`` commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following is a list of all synth commands included in Yosys for different -platforms. Each command runs a script of sub commands specific to the platform -being targeted. Note that not all of these scripts are actively maintained and -may not be up-to-date. - -- :doc:`/cmd/synth_achronix` -- :doc:`/cmd/synth_anlogic` -- :doc:`/cmd/synth_coolrunner2` -- :doc:`/cmd/synth_easic` -- :doc:`/cmd/synth_ecp5` -- :doc:`/cmd/synth_efinix` -- :doc:`/cmd/synth_fabulous` -- :doc:`/cmd/synth_gatemate` -- :doc:`/cmd/synth_gowin` -- :doc:`/cmd/synth_greenpak4` -- :doc:`/cmd/synth_ice40` -- :doc:`/cmd/synth_intel` (MAX10, Cyclone IV) -- :doc:`/cmd/synth_intel_alm` (Cyclone V, Arria V, Cyclone 10 GX) -- :doc:`/cmd/synth_lattice` -- :doc:`/cmd/synth_nexus` -- :doc:`/cmd/synth_quicklogic` -- :doc:`/cmd/synth_sf2` -- :doc:`/cmd/synth_xilinx` +A list of all synth commands included in Yosys for different platforms can be +found under :doc:`/cmd/index_techlibs`. Each command runs a script of sub +commands specific to the platform being targeted. Note that not all of these +scripts are actively maintained and may not be up-to-date. General synthesis ~~~~~~~~~~~~~~~~~ In addition to the above hardware-specific synth commands, there is also -:doc:`/cmd/prep`. This command is limited to coarse-grain synthesis, without +:cmd:title:`prep`. This command is limited to coarse-grain synthesis, without getting into any architecture-specific mappings or optimizations. Among other things, this is useful for design verification. The following commands are executed by the `prep` command: -.. literalinclude:: /cmd/prep.rst +.. literalinclude:: /code_examples/macro_commands/prep.ys :start-at: begin: - :end-before: .. only:: latex - :dedent: :doc:`/getting_started/example_synth` covers most of these commands and what they do. diff --git a/docs/source/using_yosys/synthesis/techmap_synth.rst b/docs/source/using_yosys/synthesis/techmap_synth.rst index 9e9d42df5..54a715342 100644 --- a/docs/source/using_yosys/synthesis/techmap_synth.rst +++ b/docs/source/using_yosys/synthesis/techmap_synth.rst @@ -33,9 +33,9 @@ reader may find this map file as :file:`techlibs/common/techmap.v` in the Yosys source tree. Additional features have been added to techmap to allow for conditional mapping -of cells (see :doc:`/cmd/techmap`). This can for example be useful if the target -architecture supports hardware multipliers for certain bit-widths but not for -others. +of cells (see :doc:`/cmd/index_passes_techmap`). This can for example be useful +if the target architecture supports hardware multipliers for certain bit-widths +but not for others. A usual synthesis flow would first use the techmap pass to directly map some RTL cells to coarse-grain cells provided by the target architecture (if any) and diff --git a/docs/source/using_yosys/verilog.rst b/docs/source/using_yosys/verilog.rst index 1701458f7..92f223e49 100644 --- a/docs/source/using_yosys/verilog.rst +++ b/docs/source/using_yosys/verilog.rst @@ -94,7 +94,7 @@ Verilog Attributes and non-standard features - The ``keep_hierarchy`` attribute on cells and modules keeps the `flatten` command from flattening the indicated cells and modules. -- The `gate_cost_equivalent` attribute on a module can be used to specify +- The ``gate_cost_equivalent`` attribute on a module can be used to specify the estimated cost of the module as a number of basic gate instances. See the help message of command `keep_hierarchy` which interprets this attribute. diff --git a/docs/source/yosys_internals/hashing.rst b/docs/source/yosys_internals/hashing.rst index c6e22c0cf..b9608d99e 100644 --- a/docs/source/yosys_internals/hashing.rst +++ b/docs/source/yosys_internals/hashing.rst @@ -138,7 +138,7 @@ Previously, the interface to implement hashing on custom types was just independently and then ad-hoc combined with the hash function with some xorshift operations thrown in to mix bits together somewhat. A plugin can stay compatible with both versions prior and after the break by implementing both interfaces -based on the existance and value of `YS_HASHING_VERSION`. +based on the existance and value of ``YS_HASHING_VERSION``. .. code-block:: cpp :caption: Example hash compatibility wrapper diff --git a/docs/util/cellref.py b/docs/util/cell_documenter.py similarity index 100% rename from docs/util/cellref.py rename to docs/util/cell_documenter.py diff --git a/docs/util/cmd_documenter.py b/docs/util/cmd_documenter.py new file mode 100644 index 000000000..9347d8ffd --- /dev/null +++ b/docs/util/cmd_documenter.py @@ -0,0 +1,443 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +from dataclasses import dataclass +import json +from pathlib import Path, PosixPath, WindowsPath +import re + +from typing import Any +from sphinx.application import Sphinx +from sphinx.ext import autodoc +from sphinx.ext.autodoc import Documenter +from sphinx.util import logging + +logger = logging.getLogger(__name__) + +# cmd signature +cmd_ext_sig_re = re.compile( + r'''^ ([\w/]+::)? # optional group + ([\w$._]+?) # module name + (?:\.([\w_]+))? # optional: thing name + (::[\w_]+)? # attribute + \s* $ # and nothing more + ''', re.VERBOSE) + +class YosysCmdContentListing: + type: str + body: str + source_file: str + source_line: int + options: dict[str, str] + content: list[YosysCmdContentListing] + + def __init__( + self, + type: str = "", + body: str = "", + source_file: str = "unknown", + source_line: int = 0, + options: dict[str, str] = {}, + content: list[dict[str]] = [], + ): + self.type = type + self.body = body + self.source_file = source_file + self.source_line = source_line + self.options = options + self.content = [YosysCmdContentListing(**c) for c in content] + +class YosysCmd: + name: str + title: str + content: list[YosysCmdContentListing] + group: str + source_file: str + source_line: int + source_func: str + experimental_flag: bool + internal_flag: bool + + def __init__( + self, + name:str = "", title:str = "", + content: list[dict[str]] = [], + group: str = 'unknown', + source_file: str = "", + source_line: int = 0, + source_func: str = "", + experimental_flag: bool = False, + internal_flag: bool = False, + ) -> None: + self.name = name + self.title = title + self.content = [YosysCmdContentListing(**c) for c in content] + self.group = group + self.source_file = source_file + self.source_line = source_line + self.source_func = source_func + self.experimental_flag = experimental_flag + self.internal_flag = internal_flag + +class YosysCmdGroupDocumenter(Documenter): + objtype = 'cmdgroup' + priority = 10 + object: tuple[str, list[str]] + lib_key = 'groups' + + option_spec = Documenter.option_spec.copy() + option_spec.update({ + 'caption': autodoc.annotation_option, + 'members': autodoc.members_option, + 'source': autodoc.bool_option, + 'linenos': autodoc.bool_option, + }) + + __cmd_lib: dict[str, list[str] | dict[str]] | None = None + @property + def cmd_lib(self) -> dict[str, list[str] | dict[str]]: + if not self.__cmd_lib: + self.__cmd_lib = {} + cmds_obj: dict[str, dict[str, dict[str]]] + try: + with open(self.config.cmds_json, "r") as f: + cmds_obj = json.loads(f.read()) + except FileNotFoundError: + logger.warning( + f"unable to find cmd lib at {self.config.cmds_json}", + type = 'cmdref', + subtype = 'cmd_lib' + ) + cmds_obj = {} + for (name, obj) in cmds_obj.get(self.lib_key, {}).items(): + self.__cmd_lib[name] = obj + return self.__cmd_lib + + @classmethod + def can_document_member( + cls, + member: Any, + membername: str, + isattr: bool, + parent: Any + ) -> bool: + return False + + def parse_name(self) -> bool: + if not self.options.caption: + self.content_indent = '' + self.fullname = self.modname = self.name + return True + + def import_object(self, raiseerror: bool = False) -> bool: + # get cmd + try: + self.object = (self.modname, self.cmd_lib[self.modname]) + except KeyError: + if raiseerror: + raise + return False + + self.real_modname = self.modname + return True + + def get_sourcename(self) -> str: + return self.env.doc2path(self.env.docname) + + def format_name(self) -> str: + return self.options.caption or '' + + def format_signature(self, **kwargs: Any) -> str: + return self.modname + + def add_directive_header(self, sig: str) -> None: + pass + + def add_content(self, more_content: Any | None) -> None: + pass + + def filter_members( + self, + members: list[tuple[str, Any]], + want_all: bool + ) -> list[tuple[str, Any, bool]]: + return [(x[0], x[1], False) for x in members] + + def get_object_members( + self, + want_all: bool + ) -> tuple[bool, list[tuple[str, Any]]]: + ret: list[tuple[str, str]] = [] + + if want_all: + for member in self.object[1]: + ret.append((member, self.modname)) + else: + memberlist = self.options.members or [] + for name in memberlist: + if name in self.object: + ret.append((name, self.modname)) + else: + logger.warning(('unknown module mentioned in :members: option: ' + f'group {self.modname}, module {name}'), + type='cmdref') + + return False, ret + + def document_members(self, all_members: bool = False) -> None: + want_all = (all_members or + self.options.inherited_members or + self.options.members is autodoc.ALL) + # find out which members are documentable + members_check_module, members = self.get_object_members(want_all) + + # document non-skipped members + memberdocumenters: list[tuple[Documenter, bool]] = [] + for (mname, member, isattr) in self.filter_members(members, want_all): + classes = [cls for cls in self.documenters.values() + if cls.can_document_member(member, mname, isattr, self)] + if not classes: + # don't know how to document this member + continue + # prefer the documenter with the highest priority + classes.sort(key=lambda cls: cls.priority) + # give explicitly separated module name, so that members + # of inner classes can be documented + full_mname = self.format_signature() + '::' + mname + documenter = classes[-1](self.directive, full_mname, self.indent) + memberdocumenters.append((documenter, isattr)) + + member_order = self.options.member_order or self.config.autodoc_member_order + memberdocumenters = self.sort_members(memberdocumenters, member_order) + + for documenter, isattr in memberdocumenters: + documenter.generate( + all_members=True, real_modname=self.real_modname, + check_module=members_check_module and not isattr) + + def generate( + self, + more_content: Any | None = None, + real_modname: str | None = None, + check_module: bool = False, + all_members: bool = False + ) -> None: + if not self.parse_name(): + # need a cmd lib to import from + logger.warning( + f"don't know which cmd lib to import for autodocumenting {self.name}", + type = 'cmdref' + ) + return + + sourcename = self.get_sourcename() + + imported_object = self.import_object(); + if self.lib_key == 'groups' and self.name == 'unknown': + if imported_object: + logger.warning(f"Found commands assigned to group {self.name}: {[x[0] for x in self.object]}", type='cmdref') + else: + return + elif not imported_object: + log_msg = f"unable to load {self.name} with {type(self)}" + if self.lib_key == 'groups': + logger.info(log_msg, type = 'cmdref') + self.add_line(f'.. warning:: No commands found for group {self.name!r}', sourcename) + self.add_line('', sourcename) + self.add_line(' Documentation may have been built without ``source_location`` support.', sourcename) + self.add_line(' Try check :doc:`/cmd/index_other`.', sourcename) + else: + logger.warning(log_msg, type = 'cmdref') + return + + # check __module__ of object (for members not given explicitly) + # if check_module: + # if not self.check_module(): + # return + + self.add_line('', sourcename) + + # format the object's signature, if any + try: + sig = self.format_signature() + except Exception as exc: + logger.warning(('error while formatting signature for %s: %s'), + self.fullname, exc, type='cmdref') + return + + # generate the directive header and options, if applicable + self.add_directive_header(sig) + self.add_line('', sourcename) + + # e.g. the module directive doesn't have content + self.indent += self.content_indent + + # add all content (from docstrings, attribute docs etc.) + self.add_content(more_content) + + # document members, if possible + self.document_members(all_members) + +class YosysCmdDocumenter(YosysCmdGroupDocumenter): + objtype = 'cmd' + priority = 15 + object: YosysCmd + lib_key = 'cmds' + + @classmethod + def can_document_member( + cls, + member: Any, + membername: str, + isattr: bool, + parent: Any + ) -> bool: + if membername.startswith('$'): + return False + return isinstance(parent, YosysCmdGroupDocumenter) + + def parse_name(self) -> bool: + try: + matched = cmd_ext_sig_re.match(self.name) + group, modname, thing, attribute = matched.groups() + except AttributeError: + logger.warning(('invalid signature for auto%s (%r)') % (self.objtype, self.name), + type='cmdref') + return False + + self.modname = modname + self.groupname = group or '' + self.attribute = attribute or '' + self.fullname = ((self.modname) + (thing or '')) + + return True + + def import_object(self, raiseerror: bool = False) -> bool: + if super().import_object(raiseerror): + self.object = YosysCmd(self.modname, **self.object[1]) + return True + return False + + def get_sourcename(self) -> str: + try: + return self.object.source_file + except AttributeError: + return super().get_sourcename() + + def format_name(self) -> str: + return self.object.name + + def format_signature(self, **kwargs: Any) -> str: + return self.fullname + self.attribute + + def add_directive_header(self, sig: str) -> None: + domain = getattr(self, 'domain', self.objtype) + directive = getattr(self, 'directivetype', 'def') + source_name = self.object.source_file + source_line = self.object.source_line + + title = f'{self.object.name} - {self.object.title}' + self.add_line(title, source_name, source_line) + self.add_line('#' * len(title), source_name, source_line) + + # cmd definition + self.add_line(f'.. {domain}:{directive}:: {sig}', source_name, source_line) + if self.object.title: + self.add_line(f' :title: {self.object.title}', source_name, source_line) + + if self.options.noindex: + self.add_line(' :noindex:', source_name) + + def add_content(self, more_content: Any | None) -> None: + # set sourcename and add content from attribute documentation + domain = getattr(self, 'domain', self.objtype) + source_name = self.object.source_file + source_line = self.object.source_line + + if self.object.experimental_flag: + self.add_line(f'.. warning:: This command is experimental', source_name, source_line) + self.add_line('\n', source_name) + + if self.object.internal_flag: + self.add_line(f'.. warning:: This command is intended for internal developer use only', source_name, source_line) + self.add_line('\n', source_name) + + def render(content_list: YosysCmdContentListing, indent: int=0): + content_source = content_list.source_file or source_name + indent_str = ' '*indent + if content_list.type == 'usage': + if content_list.body: + self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::{content_list.body}', content_source) + else: + self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::', content_source) + self.add_line(f'{indent_str} :noindex:', source_name) + self.add_line('', source_name) + elif content_list.type == 'option': + self.add_line(f'{indent_str}:{content_list.type} {content_list.body}:', content_source) + elif content_list.type == 'text': + self.add_line(f'{indent_str}{content_list.body}', content_source) + self.add_line('', source_name) + elif content_list.type == 'code': + language_str = content_list.options.get('language', '') + self.add_line(f'{indent_str}.. code-block:: {language_str}', source_name) + self.add_line('', source_name) + for body_line in content_list.body.splitlines(): + self.add_line(f'{indent_str} {body_line}', content_source) + self.add_line('', source_name) + else: + logger.warning(f"unknown content type '{content_list.type}'") + for content in content_list.content: + render(content, indent+1) + + for content in self.object.content: + render(content) + + if self.get_sourcename() != 'unknown': + self.add_line('\n', source_name) + self.add_line(f'.. note:: Help text automatically generated from :file:`{source_name}:{source_line}`', source_name) + + # add additional content (e.g. from document), if present + if more_content: + for line, src in zip(more_content.data, more_content.items): + self.add_line(line, src[0], src[1]) + + def get_object_members( + self, + want_all: bool + ) -> tuple[bool, list[tuple[str, Any]]]: + + return False, [] + +class YosysCmdRstDocumenter(YosysCmdDocumenter): + objtype = 'cmd_rst' + priority = 0 + + @classmethod + def can_document_member(cls, *args) -> bool: + return False + + def add_directive_header(self, sig): + source_name = self.object.source_file + cmd = self.object.name + self.add_line(f'.. code-block:: rst', source_name) + self.add_line(f' :caption: Generated rst for ``.. autocmd:: {cmd}``', source_name) + + def add_content(self, more_content): + source_name = self.object.source_file + cmd = self.object.name + self.domain = 'cmd' + super().add_directive_header(cmd) + self.add_line('', source_name) + self.indent += self.content_indent + super().add_content(more_content) + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_config_value('cmds_json', False, 'html', [Path, PosixPath, WindowsPath]) + app.setup_extension('sphinx.ext.autodoc') + app.add_autodocumenter(YosysCmdGroupDocumenter) + app.add_autodocumenter(YosysCmdDocumenter) + app.add_autodocumenter(YosysCmdRstDocumenter) + return { + 'version': '2', + 'parallel_read_safe': True, + } diff --git a/docs/util/cmdref.py b/docs/util/custom_directives.py similarity index 81% rename from docs/util/cmdref.py rename to docs/util/custom_directives.py index a31b08e0d..b90584aa7 100644 --- a/docs/util/cmdref.py +++ b/docs/util/custom_directives.py @@ -4,20 +4,21 @@ from __future__ import annotations import re from typing import cast +import warnings from docutils import nodes -from docutils.nodes import Node, Element, system_message +from docutils.nodes import Node, Element, Text from docutils.parsers.rst import directives from docutils.parsers.rst.states import Inliner from sphinx.application import Sphinx from sphinx.domains import Domain, Index from sphinx.domains.std import StandardDomain from sphinx.environment import BuildEnvironment -from sphinx.roles import XRefRole +from sphinx.roles import XRefRole, SphinxRole from sphinx.directives import ObjectDescription from sphinx.directives.code import container_wrapper from sphinx.util.nodes import make_refnode -from sphinx.util.docfields import Field +from sphinx.util.docfields import Field, GroupedField from sphinx import addnodes class TocNode(ObjectDescription): @@ -31,7 +32,7 @@ class TocNode(ObjectDescription): signode['ids'].append(idx) def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature) -> tuple[str, ...]: - if 'fullname' not in sig_node: + if 'tocname' not in sig_node: return () modname = sig_node.get('module') @@ -57,16 +58,56 @@ class TocNode(ObjectDescription): return '.'.join(parents + [name]) return '' -class CommandNode(TocNode): +class NodeWithOptions(TocNode): + """A custom node with options.""" + + doc_field_types = [ + GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')), + ] + + def transform_content(self, contentnode: addnodes.desc_content) -> None: + """hack `:option -thing: desc` into a proper option list with yoscrypt highlighting""" + newchildren = [] + for node in contentnode: + newnode = node + if isinstance(node, nodes.field_list): + newnode = nodes.option_list() + for field in node: + is_option = False + option_list_item = nodes.option_list_item() + for child in field: + if isinstance(child, nodes.field_name): + option_group = nodes.option_group() + option_list_item += option_group + option = nodes.option() + option_group += option + name, text = child.rawsource.split(' ', 1) + is_option = name == 'option' + literal = nodes.literal(text=text) + literal['classes'] += ['code', 'highlight', 'yoscrypt'] + literal['language'] = 'yoscrypt' + option += literal + if not is_option: warnings.warn(f'unexpected option \'{name}\' in {field.source}') + elif isinstance(child, nodes.field_body): + description = nodes.description() + description += child.children + option_list_item += description + if is_option: + newnode += option_list_item + newchildren.append(newnode) + contentnode.children = newchildren + +class CommandNode(NodeWithOptions): """A custom node that describes a command.""" name = 'cmd' required_arguments = 1 - option_spec = { + option_spec = NodeWithOptions.option_spec.copy() + option_spec.update({ 'title': directives.unchanged, 'tags': directives.unchanged - } + }) def handle_signature(self, sig, signode: addnodes.desc_signature): signode['fullname'] = sig @@ -93,6 +134,46 @@ class CommandNode(TocNode): idx, 0)) +class CommandUsageNode(NodeWithOptions): + """A custom node that describes command usages""" + + name = 'cmdusage' + + option_spec = NodeWithOptions.option_spec + option_spec.update({ + 'usage': directives.unchanged, + }) + + def handle_signature(self, sig: str, signode: addnodes.desc_signature): + parts = sig.split('::') + if len(parts) > 2: parts.pop(0) + use = parts[-1] + signode['fullname'] = '::'.join(parts) + usage = self.options.get('usage', use) + if usage: + signode['tocname'] = usage + signode += addnodes.desc_name(text=usage) + return signode['fullname'] + + def add_target_and_index( + self, + name: str, + sig: str, + signode: addnodes.desc_signature + ) -> None: + idx = ".".join(name.split("::")) + signode['ids'].append(idx) + if 'noindex' not in self.options: + tocname: str = signode.get('tocname', name) + objs = self.env.domaindata[self.domain]['objects'] + # (name, sig, typ, docname, anchor, prio) + objs.append((name, + tocname, + type(self).name, + self.env.docname, + idx, + 1)) + class PropNode(TocNode): name = 'prop' fieldname = 'props' @@ -393,7 +474,7 @@ class TagIndex(Index): lis.append(( dispname, 0, docname, anchor, - docname, '', typ + '', '', '' )) ret = [(k, v) for k, v in sorted(content.items())] @@ -432,18 +513,19 @@ class CommandIndex(Index): Qualifier and description are not rendered e.g. in LaTeX output. """ - content = {} + content: dict[str, list[tuple]] = {} items = ((name, dispname, typ, docname, anchor) for name, dispname, typ, docname, anchor, prio in self.domain.get_objects() if typ == self.name) items = sorted(items, key=lambda item: item[0]) for name, dispname, typ, docname, anchor in items: + title = self.domain.data['obj2title'].get(name) lis = content.setdefault(self.shortname, []) lis.append(( dispname, 0, docname, anchor, - '', '', typ + '', '', title )) ret = [(k, v) for k, v in sorted(content.items())] @@ -507,16 +589,27 @@ class PropIndex(TagIndex): return (ret, True) +class TitleRefRole(XRefRole): + """XRefRole used which has the cmd title as the displayed text.""" + pass + +class OptionRole(SphinxRole): + def run(self) -> tuple[list[Node], list]: + return self.inliner.interpreted(self.rawtext, self.text, 'yoscrypt', self.lineno) + class CommandDomain(Domain): name = 'cmd' label = 'Yosys commands' roles = { - 'ref': XRefRole() + 'ref': XRefRole(), + 'title': TitleRefRole(), + 'option': OptionRole(), } directives = { 'def': CommandNode, + 'usage': CommandUsageNode, } indices = { @@ -542,7 +635,7 @@ class CommandDomain(Domain): def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): - + match = [(docname, anchor, name) for name, sig, typ, docname, anchor, prio in self.get_objects() if sig == target] @@ -552,9 +645,17 @@ class CommandDomain(Domain): targ = match[0][1] qual_name = match[0][2] title = self.data['obj2title'].get(qual_name, targ) - - return make_refnode(builder,fromdocname,todocname, - targ, contnode, title) + + if typ == 'title': + # caller wants the title in the content of the node + cmd = contnode.astext() + contnode = Text(f'{cmd} - {title}') + return make_refnode(builder, fromdocname, todocname, + targ, contnode) + else: + # cmd title as hover text + return make_refnode(builder, fromdocname, todocname, + targ, contnode, title) else: print(f"Missing ref for {target} in {fromdocname} ") return None @@ -592,10 +693,18 @@ class CellDomain(CommandDomain): def autoref(name, rawtext: str, text: str, lineno, inliner: Inliner, options=None, content=None): - role = 'cell:ref' if text[0] == '$' else 'cmd:ref' - if text.startswith("help ") and text.count(' ') == 1: - _, cmd = text.split(' ', 1) - text = f'{text} <{cmd}>' + words = text.split(' ') + if len(words) == 2 and words[0] == "help": + IsLinkable = True + thing = words[1] + else: + IsLinkable = len(words) == 1 and words[0][0] != '-' + thing = words[0] + if IsLinkable: + role = 'cell:ref' if thing[0] == '$' else 'cmd:ref' + text = f'{text} <{thing}>' + else: + role = 'yoscrypt' return inliner.interpreted(rawtext, text, role, lineno) def setup(app: Sphinx): @@ -622,4 +731,7 @@ def setup(app: Sphinx): app.add_role('autoref', autoref) - return {'version': '0.2'} + return { + 'version': '0.3', + 'parallel_read_safe': False, + } diff --git a/kernel/json.h b/kernel/json.h index c9aa0e045..e8905c8e1 100644 --- a/kernel/json.h +++ b/kernel/json.h @@ -90,10 +90,10 @@ public: template void array(const T &&values) { - begin_object(); + begin_array(); for (auto &item : values) value(item); - end_object(); + end_array(); } }; diff --git a/kernel/log_help.cc b/kernel/log_help.cc new file mode 100644 index 000000000..45228b024 --- /dev/null +++ b/kernel/log_help.cc @@ -0,0 +1,151 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2025 Krystine Dawn + * + * 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/log_help.h" + +USING_YOSYS_NAMESPACE + +Json ContentListing::to_json() { + Json::object object; + object["type"] = type; + if (body.length()) object["body"] = body; + if (strcmp(source_file, "unknown") != 0) object["source_file"] = source_file; + if (source_line != 0) object["source_line"] = source_line; + object["options"] = Json(options); + Json::array content_array; + for (auto child : _content) content_array.push_back(child->to_json()); + object["content"] = content_array; + return object; +} + +void ContentListing::usage(const string &text, + const source_location location) +{ + log_assert(type.compare("root") == 0); + add_content("usage", text, location); +} + +void ContentListing::option(const string &text, const string &description, + const source_location location) +{ + auto option = open_option(text); + if (description.length()) + option->add_content("text", description, location); +} + +void ContentListing::codeblock(const string &code, const string &language, + const source_location location) +{ + add_content("code", code, location); + back()->set_option("language", language); +} + +void ContentListing::paragraph(const string &text, + const source_location location) +{ + add_content("text", text, location); +} + +ContentListing* ContentListing::open_usage(const string &text, + const source_location location) +{ + usage(text, location); + return back(); +} + +ContentListing* ContentListing::open_option(const string &text, + const source_location location) +{ + log_assert(type.compare("root") == 0 || type.compare("usage") == 0); + auto option = new ContentListing("option", text, location); + add_content(option); + return option; +} + +#define MAX_LINE_LEN 80 +void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) { + if (pass_str.empty()) + return; + std::istringstream iss(pass_str); + if (leading_newline) + log("\n"); + for (std::string line; std::getline(iss, line);) { + log("%s", indent_str.c_str()); + auto curr_len = indent_str.length(); + std::istringstream lss(line); + for (std::string word; std::getline(lss, word, ' ');) { + while (word[0] == '`' && word.back() == '`') + word = word.substr(1, word.length()-2); + if (curr_len + word.length() >= MAX_LINE_LEN-1) { + curr_len = 0; + log("\n%s", indent_str.c_str()); + } + if (word.length()) { + log("%s ", word.c_str()); + curr_len += word.length() + 1; + } + } + log("\n"); + } +} +void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) { + std::string indent_str(indent*4, ' '); + log_pass_str(pass_str, indent_str, leading_newline); +} + +PrettyHelp *current_help = nullptr; + +PrettyHelp::PrettyHelp() +{ + _prior = current_help; + _root_listing = ContentListing("root", ""); + + current_help = this; +} + +PrettyHelp::~PrettyHelp() +{ + current_help = _prior; +} + +PrettyHelp *PrettyHelp::get_current() +{ + if (current_help == nullptr) + new PrettyHelp(); + return current_help; +} + +void PrettyHelp::log_help() +{ + for (auto content : _root_listing.get_content()) { + if (content->type.compare("usage") == 0) { + log_pass_str(content->body, 1, true); + log("\n"); + } else if (content->type.compare("option") == 0) { + log_pass_str(content->body, 1); + for (auto text : content->get_content()) { + log_pass_str(text->body, 2); + log("\n"); + } + } else { + log_pass_str(content->body, 0); + log("\n"); + } + } +} diff --git a/kernel/log_help.h b/kernel/log_help.h new file mode 100644 index 000000000..0a40fc531 --- /dev/null +++ b/kernel/log_help.h @@ -0,0 +1,132 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2025 Krystine Dawn + * + * 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 LOG_HELP_H +#define LOG_HELP_H + +#include "kernel/yosys_common.h" +#include "kernel/json.h" + +YOSYS_NAMESPACE_BEGIN + +class ContentListing { + vector _content; +public: + string type; + string body; + const char* source_file; + int source_line; + std::map options; + + ContentListing( + string type = "root", string body = "", + const char* source_file = "unknown", int source_line = 0 + ) : type(type), body(body), source_file(source_file), source_line(source_line) { + _content = {}; + options = {}; + } + + ContentListing(string type, string body, source_location location) : + ContentListing(type, body, location.file_name(), location.line()) { } + + void add_content(ContentListing *new_content) { + _content.push_back(new_content); + } + + void add_content(string type, string body, source_location location) { + auto new_content = new ContentListing(type, body, location); + add_content(new_content); + } + + bool has_content() { return _content.size() != 0; } + const vector get_content() { + const vector content = _content; + return content; + } + ContentListing* back() { + return _content.back(); + } + + void set_option(string key, string val = "") { + options[key] = val; + } + + void usage( + const string &text, + const source_location location = source_location::current() + ); + void option( + const string &text, + const string &description = "", + const source_location location = source_location::current() + ); + void codeblock( + const string &code, + const string &language = "none", + const source_location location = source_location::current() + ); + void paragraph( + const string &text, + const source_location location = source_location::current() + ); + + ContentListing* open_usage( + const string &text, + const source_location location = source_location::current() + ); + ContentListing* open_option( + const string &text, + const source_location location = source_location::current() + ); + + Json to_json(); +}; + +class PrettyHelp +{ +public: + string group = "unknown"; + +private: + PrettyHelp *_prior; + ContentListing _root_listing; + +public: + PrettyHelp(); + ~PrettyHelp(); + + static PrettyHelp *get_current(); + + bool has_content() { return _root_listing.has_content(); } + const vector get_content() { + return _root_listing.get_content(); + } + ContentListing* get_root() { + return &_root_listing; + } + + void set_group(const string g) { group = g; } + bool has_group() { return group.compare("unknown") != 0; } + + void log_help(); +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/register.cc b/kernel/register.cc index af1823b5b..ea2a2624f 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -21,6 +21,7 @@ #include "kernel/satgen.h" #include "kernel/json.h" #include "kernel/gzip.h" +#include "kernel/log_help.h" #include #include @@ -41,7 +42,8 @@ std::map backend_register; std::vector Frontend::next_args; -Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help) +Pass::Pass(std::string name, std::string short_help, source_location location) : + pass_name(name), short_help(short_help), location(location) { next_queued_pass = first_queued_pass; first_queued_pass = this; @@ -116,9 +118,19 @@ void Pass::post_execute(Pass::pre_post_exec_state_t state) void Pass::help() { - log("\n"); - log("No help message for command `%s'.\n", pass_name.c_str()); - log("\n"); + auto prettyHelp = PrettyHelp(); + if (formatted_help()) { + prettyHelp.log_help(); + } else { + log("\n"); + log("No help message for command `%s'.\n", pass_name.c_str()); + log("\n"); + } +} + +bool Pass::formatted_help() +{ + return false; } void Pass::clear_flags() @@ -381,8 +393,8 @@ void ScriptPass::help_script() script(); } -Frontend::Frontend(std::string name, std::string short_help) : - Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help), +Frontend::Frontend(std::string name, std::string short_help, source_location location) : + Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help, location), frontend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name) { } @@ -527,8 +539,8 @@ void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string } } -Backend::Backend(std::string name, std::string short_help) : - Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help), +Backend::Backend(std::string name, std::string short_help, source_location location) : + Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help, location), backend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name) { } @@ -681,6 +693,23 @@ static string get_cell_name(string name) { return is_code_getter(name) ? name.substr(0, name.length()-1) : name; } +static void log_warning_flags(Pass *pass) { + bool has_warnings = false; + const string name = pass->pass_name; + if (pass->experimental_flag) { + if (!has_warnings) log("\n"); + has_warnings = true; + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", name.c_str()); + } + if (pass->internal_flag) { + if (!has_warnings) log("\n"); + has_warnings = true; + log("WARNING: THE '%s' COMMAND IS INTENDED FOR INTERNAL DEVELOPER USE ONLY.\n", name.c_str()); + } + if (has_warnings) + log("\n"); +} + static struct CellHelpMessages { dict cell_help; CellHelpMessages() { @@ -706,155 +735,211 @@ struct HelpPass : public Pass { log(" help + .... print verilog code for given cell type\n"); log("\n"); } - void write_cmd_rst(std::string cmd, std::string title, std::string text) - { - FILE *f = fopen(stringf("docs/source/cmd/%s.rst", cmd.c_str()).c_str(), "wt"); - // make header - size_t char_len = cmd.length() + 3 + title.length(); - std::string title_line = "\n"; - title_line.insert(0, char_len, '='); - fprintf(f, "%s", title_line.c_str()); - fprintf(f, "%s - %s\n", cmd.c_str(), title.c_str()); - fprintf(f, "%s\n", title_line.c_str()); + bool dump_cmds_json(PrettyJson &json) { + // init json + json.begin_object(); + json.entry("version", "Yosys command reference"); + json.entry("generator", yosys_version_str); - // render html - fprintf(f, ".. cmd:def:: %s\n", cmd.c_str()); - fprintf(f, " :title: %s\n\n", title.c_str()); - fprintf(f, " .. only:: html\n\n"); - std::stringstream ss; - std::string textcp = text; - ss << text; - bool IsUsage = true; - int blank_count = 0; - size_t def_strip_count = 0; - bool WasDefinition = false; - for (std::string line; std::getline(ss, line, '\n');) { - // find position of first non space character - std::size_t first_pos = line.find_first_not_of(" \t"); - std::size_t last_pos = line.find_last_not_of(" \t"); - if (first_pos == std::string::npos) { - // skip formatting empty lines - if (!WasDefinition) - fputc('\n', f); - blank_count += 1; - continue; + bool raise_error = false; + std::map> groups; + + json.name("cmds"); json.begin_object(); + // iterate over commands + for (auto &it : pass_register) { + auto name = it.first; + auto pass = it.second; + auto title = pass->short_help; + + auto cmd_help = PrettyHelp(); + auto has_pretty_help = pass->formatted_help(); + + if (!has_pretty_help) { + enum PassUsageState { + PUState_none, + PUState_signature, + PUState_options, + PUState_optionbody, + }; + + source_location null_source; + string current_buffer = ""; + auto root_listing = cmd_help.get_root(); + auto current_listing = root_listing; + + // dump command help + std::ostringstream buf; + log_streams.push_back(&buf); + pass->help(); + log_streams.pop_back(); + std::stringstream ss; + ss << buf.str(); + + // parse command help + size_t def_strip_count = 0; + auto current_state = PUState_none; + auto catch_verific = false; + auto blank_lines = 2; + for (string line; std::getline(ss, line, '\n');) { + // find position of first non space character + std::size_t first_pos = line.find_first_not_of(" \t"); + std::size_t last_pos = line.find_last_not_of(" \t"); + if (first_pos == std::string::npos) { + switch (current_state) + { + case PUState_signature: + root_listing->usage(current_buffer, null_source); + current_listing = root_listing; + current_state = PUState_none; + current_buffer = ""; + break; + case PUState_none: + case PUState_optionbody: + blank_lines += 1; + break; + default: + break; + } + // skip empty lines + continue; + } + + // strip leading and trailing whitespace + std::string stripped_line = line.substr(first_pos, last_pos - first_pos +1); + bool IsDefinition = stripped_line[0] == '-'; + IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; + bool IsDedent = def_strip_count && first_pos < def_strip_count; + bool IsIndent = def_strip_count < first_pos; + + // line looks like a signature + bool IsSignature = stripped_line.find(name) == 0 && (stripped_line.length() == name.length() || stripped_line.at(name.size()) == ' '); + + if (IsSignature && first_pos <= 4 && (blank_lines >= 2 || current_state == PUState_signature)) { + if (current_state == PUState_options || current_state == PUState_optionbody) { + current_listing->codeblock(current_buffer, "none", null_source); + current_buffer = ""; + } else if (current_state == PUState_signature) { + root_listing->usage(current_buffer, null_source); + current_buffer = ""; + } else if (current_state == PUState_none && !current_buffer.empty()) { + current_listing->codeblock(current_buffer, "none", null_source); + current_buffer = ""; + } + current_listing = root_listing; + current_state = PUState_signature; + def_strip_count = first_pos; + catch_verific = false; + } else if (IsDedent) { + def_strip_count = first_pos; + if (current_state == PUState_optionbody) { + if (!current_buffer.empty()) { + current_listing->codeblock(current_buffer, "none", null_source); + current_buffer = ""; + } + if (IsIndent) { + current_state = PUState_options; + current_listing = root_listing->back(); + } else { + current_state = PUState_none; + current_listing = root_listing; + } + } else { + current_state = PUState_none; + } + } + + if (IsDefinition && !catch_verific && current_state != PUState_signature) { + if (!current_buffer.empty()) { + current_listing->codeblock(current_buffer, "none", null_source); + current_buffer = ""; + } + current_state = PUState_options; + current_listing = root_listing->open_option(stripped_line, null_source); + def_strip_count = first_pos; + } else { + if (current_state == PUState_options) { + current_state = PUState_optionbody; + } + if (current_buffer.empty()) + current_buffer = stripped_line; + else if (current_state == PUState_signature && IsIndent) + current_buffer += stripped_line; + else if (current_state == PUState_none) { + current_buffer += (blank_lines > 0 ? "\n\n" : "\n") + line; + } else + current_buffer += (blank_lines > 0 ? "\n\n" : "\n") + stripped_line; + if (stripped_line.compare("Command file parser supports following commands in file:") == 0) + catch_verific = true; + } + blank_lines = 0; + } + + if (!current_buffer.empty()) { + if (current_buffer.size() > 64 && current_buffer.substr(0, 64).compare("The following commands are executed by this synthesis command:\n\n") == 0) { + current_listing->paragraph(current_buffer.substr(0, 62), null_source); + current_listing->codeblock(current_buffer.substr(64), "yoscrypt", null_source); + } else + current_listing->codeblock(current_buffer, "none", null_source); + current_buffer = ""; + } } - // strip leading and trailing whitespace - std::string stripped_line = line.substr(first_pos, last_pos - first_pos +1); - bool IsDefinition = stripped_line[0] == '-'; - IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; - bool IsDedent = def_strip_count && first_pos <= def_strip_count; - bool IsIndent = first_pos == 2 || first_pos == 4; - if (cmd.compare(0, 7, "verific") == 0) - // verific.cc has strange and different formatting from the rest - IsIndent = false; - - // another usage block - bool NewUsage = stripped_line.find(cmd) == 0; - - if (IsUsage) { - if (stripped_line.compare(0, 4, "See ") == 0) { - // description refers to another function - fprintf(f, "\n %s\n", stripped_line.c_str()); - } else { - // usage should be the first line of help output - fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); - WasDefinition = true; + // attempt auto group + if (!cmd_help.has_group()) { + string source_file = pass->location.file_name(); + bool has_source = source_file.compare("unknown") != 0; + if (pass->internal_flag) + cmd_help.group = "internal"; + else if (source_file.find("backends/") == 0 || (!has_source && name.find("read_") == 0)) + cmd_help.group = "backends"; + else if (source_file.find("frontends/") == 0 || (!has_source && name.find("write_") == 0)) + cmd_help.group = "frontends"; + else if (has_source) { + auto last_slash = source_file.find_last_of('/'); + if (last_slash != string::npos) { + auto parent_path = source_file.substr(0, last_slash); + cmd_help.group = parent_path; + } } - IsUsage = false; - } else if (IsIndent && NewUsage && (blank_count >= 2 || WasDefinition)) { - // another usage block - fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); - WasDefinition = true; - def_strip_count = 0; - } else if (IsIndent && IsDefinition && (blank_count || WasDefinition)) { - // format definition term - fprintf(f, "\n\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); - WasDefinition = true; - def_strip_count = first_pos; - } else { - if (IsDedent) { - fprintf(f, "\n\n ::\n"); - def_strip_count = first_pos; - } else if (WasDefinition) { - fprintf(f, "::\n"); - WasDefinition = false; - } - fprintf(f, "\n %s", line.substr(def_strip_count, std::string::npos).c_str()); + // implicit !has_source + else if (name.find("equiv") == 0) + cmd_help.group = "passes/equiv"; + else if (name.find("fsm") == 0) + cmd_help.group = "passes/fsm"; + else if (name.find("memory") == 0) + cmd_help.group = "passes/memory"; + else if (name.find("opt") == 0) + cmd_help.group = "passes/opt"; + else if (name.find("proc") == 0) + cmd_help.group = "passes/proc"; } - blank_count = 0; + if (groups.count(cmd_help.group) == 0) { + groups[cmd_help.group] = vector(); + } + groups[cmd_help.group].push_back(name); + + // write to json + json.name(name.c_str()); json.begin_object(); + json.entry("title", title); + json.name("content"); json.begin_array(); + for (auto content : cmd_help.get_content()) + json.value(content->to_json()); + json.end_array(); + json.entry("group", cmd_help.group); + json.entry("source_file", pass->location.file_name()); + json.entry("source_line", pass->location.line()); + json.entry("source_func", pass->location.function_name()); + json.entry("experimental_flag", pass->experimental_flag); + json.entry("internal_flag", pass->internal_flag); + json.end_object(); } - fputc('\n', f); + json.end_object(); - // render latex - fprintf(f, ".. only:: latex\n\n"); - fprintf(f, " ::\n\n"); - std::stringstream ss2; - ss2 << textcp; - for (std::string line; std::getline(ss2, line, '\n');) { - fprintf(f, " %s\n", line.c_str()); - } - fclose(f); - } - void write_cell_rst(Yosys::SimHelper cell, Yosys::CellType ct) - { - // open - FILE *f = fopen(stringf("docs/source/cell/%s.rst", cell.filesafe_name().c_str()).c_str(), "wt"); + json.entry("groups", groups); - // make header - string title_line; - if (cell.title.length()) - title_line = stringf("%s - %s", cell.name.c_str(), cell.title.c_str()); - else title_line = cell.name; - string underline = "\n"; - underline.insert(0, title_line.length(), '='); - fprintf(f, "%s\n", title_line.c_str()); - fprintf(f, "%s\n", underline.c_str()); - - // help text, with cell def for links - fprintf(f, ".. cell:def:: %s\n", cell.name.c_str()); - if (cell.title.length()) - fprintf(f, " :title: %s\n\n", cell.title.c_str()); - else - fprintf(f, " :title: %s\n\n", cell.name.c_str()); - std::stringstream ss; - ss << cell.desc; - for (std::string line; std::getline(ss, line, '\n');) { - fprintf(f, " %s\n", line.c_str()); - } - - // properties - fprintf(f, "\nProperties"); - fprintf(f, "\n----------\n\n"); - dict prop_dict = { - {"is_evaluable", ct.is_evaluable}, - {"is_combinatorial", ct.is_combinatorial}, - {"is_synthesizable", ct.is_synthesizable}, - }; - for (auto &it : prop_dict) { - fprintf(f, "- %s: %s\n", it.first.c_str(), it.second ? "true" : "false"); - } - - // source code - fprintf(f, "\nSimulation model (Verilog)"); - fprintf(f, "\n--------------------------\n\n"); - fprintf(f, ".. code-block:: verilog\n"); - fprintf(f, " :caption: %s\n\n", cell.source.c_str()); - std::stringstream ss2; - ss2 << cell.code; - for (std::string line; std::getline(ss2, line, '\n');) { - fprintf(f, " %s\n", line.c_str()); - } - - // footer - fprintf(f, "\n.. note::\n\n"); - fprintf(f, " This page was auto-generated from the output of\n"); - fprintf(f, " ``help %s``.\n", cell.name.c_str()); - - // close - fclose(f); + json.end_object(); + return raise_error; } bool dump_cells_json(PrettyJson &json) { // init json @@ -960,11 +1045,7 @@ struct HelpPass : public Pass { log("="); log("\n"); it.second->help(); - if (it.second->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); - log("\n"); - } + log_warning_flags(it.second); } } else if (args[1] == "-cells") { @@ -978,44 +1059,9 @@ struct HelpPass : public Pass { log("\n"); return; } - // this option is undocumented as it is for internal use only - else if (args[1] == "-write-rst-command-reference-manual") { - for (auto &it : pass_register) { - std::ostringstream buf; - log_streams.push_back(&buf); - it.second->help(); - if (it.second->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); - log("\n"); - } - log_streams.pop_back(); - write_cmd_rst(it.first, it.second->short_help, buf.str()); - } - } - // this option is also undocumented as it is for internal use only - else if (args[1] == "-write-rst-cells-manual") { - bool raise_error = false; - for (auto &it : yosys_celltypes.cell_types) { - auto name = it.first.str(); - if (cell_help_messages.contains(name)) { - write_cell_rst(cell_help_messages.get(name), it.second); - } else { - log("ERROR: Missing cell help for cell '%s'.\n", name.c_str()); - raise_error |= true; - } - } - if (raise_error) { - log_error("One or more cells defined in celltypes.h are missing help documentation.\n"); - } - } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); - if (pass_register.at(args[1])->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", args[1].c_str()); - log("\n"); - } + log_warning_flags(pass_register.at(args[1])); } else if (cell_help_messages.contains(args[1])) { auto help_cell = cell_help_messages.get(args[1]); @@ -1044,7 +1090,17 @@ struct HelpPass : public Pass { log("No such command or cell type: %s\n", args[1].c_str()); return; } else if (args.size() == 3) { - if (args[1] == "-dump-cells-json") { + // this option is undocumented as it is for internal use only + if (args[1] == "-dump-cmds-json") { + PrettyJson json; + if (!json.write_to_file(args[2])) + log_error("Can't open file `%s' for writing: %s\n", args[2].c_str(), strerror(errno)); + if (dump_cmds_json(json)) { + log_abort(); + } + } + // this option is undocumented as it is for internal use only + else if (args[1] == "-dump-cells-json") { PrettyJson json; if (!json.write_to_file(args[2])) log_error("Can't open file `%s' for writing: %s\n", args[2].c_str(), strerror(errno)); @@ -1052,6 +1108,8 @@ struct HelpPass : public Pass { log_error("One or more cells defined in celltypes.h are missing help documentation.\n"); } } + else + log("Unknown help command: `%s %s'\n", args[1].c_str(), args[2].c_str()); return; } diff --git a/kernel/register.h b/kernel/register.h index f4e2127e1..e8c017c1d 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -23,27 +23,62 @@ #include "kernel/yosys_common.h" #include "kernel/yosys.h" +#include +#if __cpp_lib_source_location == 201907L + #include + using std::source_location; +#elif defined(__has_include) +# if __has_include() + #include + using std::experimental::source_location; +# else + #define SOURCE_FALLBACK +# endif +#else + #define SOURCE_FALLBACK +#endif + +#ifdef SOURCE_FALLBACK +struct source_location { // dummy placeholder + int line() const { return 0; } + int column() const { return 0; } + const char* file_name() const { return "unknown"; } + const char* function_name() const { return "unknown"; } + static const source_location current(...) { return source_location(); } +}; +#endif + YOSYS_NAMESPACE_BEGIN struct Pass { std::string pass_name, short_help; - Pass(std::string name, std::string short_help = "** document me **"); + source_location location; + Pass(std::string name, std::string short_help = "** document me **", + source_location location = source_location::current()); // Prefer overriding 'Pass::on_shutdown()' if possible virtual ~Pass(); + // Makes calls to log() to generate help message virtual void help(); + // Uses PrettyHelp::get_current() to produce a more portable formatted help message + virtual bool formatted_help(); virtual void clear_flags(); virtual void execute(std::vector args, RTLIL::Design *design) = 0; int call_counter; int64_t runtime_ns; bool experimental_flag = false; + bool internal_flag = false; void experimental() { experimental_flag = true; } + void internal() { + internal_flag = true; + } + struct pre_post_exec_state_t { Pass *parent_pass; int64_t begin_ns; @@ -81,7 +116,8 @@ struct ScriptPass : Pass RTLIL::Design *active_design; std::string active_run_from, active_run_to; - ScriptPass(std::string name, std::string short_help = "** document me **") : Pass(name, short_help) { } + ScriptPass(std::string name, std::string short_help = "** document me **", source_location location = source_location::current()) : + Pass(name, short_help, location) { } virtual void script() = 0; @@ -99,7 +135,8 @@ struct Frontend : Pass static std::string last_here_document; std::string frontend_name; - Frontend(std::string name, std::string short_help = "** document me **"); + Frontend(std::string name, std::string short_help = "** document me **", + source_location location = source_location::current()); void run_register() override; ~Frontend() override; void execute(std::vector args, RTLIL::Design *design) override final; @@ -115,7 +152,8 @@ struct Frontend : Pass struct Backend : Pass { std::string backend_name; - Backend(std::string name, std::string short_help = "** document me **"); + Backend(std::string name, std::string short_help = "** document me **", + source_location location = source_location::current()); void run_register() override; ~Backend() override; void execute(std::vector args, RTLIL::Design *design) override final; diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 83fe781a0..8bbcb8da0 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -22,12 +22,18 @@ #include "kernel/celledges.h" #include "kernel/celltypes.h" #include "kernel/utils.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct CheckPass : public Pass { CheckPass() : Pass("check", "check for obvious problems in the design") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index 572ed2153..ccda023c0 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -70,62 +71,62 @@ static bool is_triggered_check_cell(RTLIL::Cell * cell) } struct ChformalPass : public Pass { - ChformalPass() : Pass("chformal", "change formal constraints of the design") { } - void help() override - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" chformal [types] [mode] [options] [selection]\n"); - log("\n"); - log("Make changes to the formal constraints of the design. The [types] options\n"); - log("the type of constraint to operate on. If none of the following options are\n"); - log("given, the command will operate on all constraint types:\n"); - log("\n"); - log(" -assert $assert cells, representing assert(...) constraints\n"); - log(" -assume $assume cells, representing assume(...) constraints\n"); - log(" -live $live cells, representing assert(s_eventually ...)\n"); - log(" -fair $fair cells, representing assume(s_eventually ...)\n"); - log(" -cover $cover cells, representing cover() statements\n"); - log("\n"); - log(" Additionally chformal will operate on $check cells corresponding to the\n"); - log(" selected constraint types.\n"); - log("\n"); - log("Exactly one of the following modes must be specified:\n"); - log("\n"); - log(" -remove\n"); - log(" remove the cells and thus constraints from the design\n"); - log("\n"); - log(" -early\n"); - log(" bypass FFs that only delay the activation of a constraint. When inputs\n"); - log(" of the bypassed FFs do not remain stable between clock edges, this may\n"); - log(" result in unexpected behavior.\n"); - log("\n"); - log(" -delay \n"); - log(" delay activation of the constraint by clock cycles\n"); - log("\n"); - log(" -skip \n"); - log(" ignore activation of the constraint in the first clock cycles\n"); - log("\n"); - log(" -coverenable\n"); - log(" add cover statements for the enable signals of the constraints\n"); - log("\n"); + ChformalPass() : Pass("chformal", "change formal constraints of the design") {} + + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + + auto content_root = help->get_root(); + + content_root->usage("chformal [types] [mode] [options] [selection]"); + content_root->paragraph( + "Make changes to the formal constraints of the design. The [types] options " + "the type of constraint to operate on. If none of the following options are " + "given, the command will operate on all constraint types:" + ); + + content_root->option("-assert", "`$assert` cells, representing ``assert(...)`` constraints"); + content_root->option("-assume", "`$assume` cells, representing ``assume(...)`` constraints"); + content_root->option("-live", "`$live` cells, representing ``assert(s_eventually ...)``"); + content_root->option("-fair", "`$fair` cells, representing ``assume(s_eventually ...)``"); + content_root->option("-cover", "`$cover` cells, representing ``cover()`` statements"); + content_root->paragraph( + "Additionally chformal will operate on `$check` cells corresponding to the " + "selected constraint types." + ); + + content_root->paragraph("Exactly one of the following modes must be specified:"); + + content_root->option("-remove", "remove the cells and thus constraints from the design"); + content_root->option("-early", + "bypass FFs that only delay the activation of a constraint. When inputs " + "of the bypassed FFs do not remain stable between clock edges, this may " + "result in unexpected behavior." + ); + content_root->option("-delay ", "delay activation of the constraint by clock cycles"); + content_root->option("-skip ", "ignore activation of the constraint in the first clock cycles"); + auto cover_option = content_root->open_option("-coverenable"); + cover_option->paragraph( + "add cover statements for the enable signals of the constraints" + ); #ifdef YOSYS_ENABLE_VERIFIC - log(" Note: For the Verific frontend it is currently not guaranteed that a\n"); - log(" reachable SVA statement corresponds to an active enable signal.\n"); - log("\n"); + cover_option->paragraph( + "Note: For the Verific frontend it is currently not guaranteed that a " + "reachable SVA statement corresponds to an active enable signal." + ); #endif - log(" -assert2assume\n"); - log(" -assert2cover\n"); - log(" -assume2assert\n"); - log(" -live2fair\n"); - log(" -fair2live\n"); - log(" change the roles of cells as indicated. these options can be combined\n"); - log("\n"); - log(" -lower\n"); - log(" convert each $check cell into an $assert, $assume, $live, $fair or\n"); - log(" $cover cell. If the $check cell contains a message, also produce a\n"); - log(" $print cell.\n"); - log("\n"); + content_root->option("-assert2assume"); + content_root->option("-assert2cover"); + content_root->option("-assume2assert"); + content_root->option("-live2fair"); + content_root->option("-fair2live", "change the roles of cells as indicated. these options can be combined"); + content_root->option("-lower", + "convert each $check cell into an $assert, $assume, $live, $fair or " + "$cover cell. If the $check cell contains a message, also produce a " + "$print cell." + ); + return true; } void execute(std::vector args, RTLIL::Design *design) override { diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 1db3e2ca0..47354f1d5 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include #ifndef _WIN32 @@ -26,15 +27,18 @@ # include #endif -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" - USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct CoverPass : public Pass { - CoverPass() : Pass("cover", "print code coverage counters") { } + CoverPass() : Pass("cover", "print code coverage counters") { + internal(); + } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/dft_tag.cc b/passes/cmds/dft_tag.cc index b6afe6f89..347c8efa4 100644 --- a/passes/cmds/dft_tag.cc +++ b/passes/cmds/dft_tag.cc @@ -22,6 +22,7 @@ #include "kernel/modtools.h" #include "kernel/sigtools.h" #include "kernel/yosys.h" +#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -952,6 +953,11 @@ struct DftTagWorker { struct DftTagPass : public Pass { DftTagPass() : Pass("dft_tag", "create tagging logic for data flow tracking") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc index 5b53f50cc..933bd457f 100644 --- a/passes/cmds/edgetypes.cc +++ b/passes/cmds/edgetypes.cc @@ -19,12 +19,18 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct EdgetypePass : public Pass { EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/example_dt.cc b/passes/cmds/example_dt.cc index 2870e062b..b10f50502 100644 --- a/passes/cmds/example_dt.cc +++ b/passes/cmds/example_dt.cc @@ -21,15 +21,17 @@ struct ExampleWorker struct ExampleDtPass : public Pass { - ExampleDtPass() : Pass("example_dt", "drivertools example") {} + ExampleDtPass() : Pass("example_dt", "drivertools example") { + internal(); + } - void help() override + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log("TODO: add help message\n"); log("\n"); - } + } void execute(std::vector args, RTLIL::Design *design) override diff --git a/passes/cmds/exec.cc b/passes/cmds/exec.cc index e9cc34b30..486fa1c2b 100644 --- a/passes/cmds/exec.cc +++ b/passes/cmds/exec.cc @@ -17,8 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" #include #if defined(_WIN32) @@ -38,6 +38,11 @@ PRIVATE_NAMESPACE_BEGIN struct ExecPass : public Pass { ExecPass() : Pass("exec", "execute commands in the operating system shell") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/future.cc b/passes/cmds/future.cc index b03613c9b..5dcf46bcf 100644 --- a/passes/cmds/future.cc +++ b/passes/cmds/future.cc @@ -24,6 +24,7 @@ #include "kernel/sigtools.h" #include "kernel/utils.h" #include "kernel/yosys.h" +#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -110,6 +111,11 @@ struct FutureWorker { struct FuturePass : public Pass { FuturePass() : Pass("future", "resolve future sampled value functions") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/glift.cc b/passes/cmds/glift.cc index 6a7d070d7..0c321eba6 100644 --- a/passes/cmds/glift.cc +++ b/passes/cmds/glift.cc @@ -17,10 +17,9 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" #include "kernel/utils.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -425,6 +424,12 @@ public: struct GliftPass : public Pass { GliftPass() : Pass("glift", "create GLIFT models and optimization problems") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/internal_stats.cc b/passes/cmds/internal_stats.cc index 28822f237..00456b8f9 100644 --- a/passes/cmds/internal_stats.cc +++ b/passes/cmds/internal_stats.cc @@ -18,8 +18,6 @@ */ #include -#include -#include #include "kernel/yosys.h" #include "kernel/celltypes.h" @@ -71,7 +69,10 @@ std::optional current_mem_bytes() { } struct InternalStatsPass : public Pass { - InternalStatsPass() : Pass("internal_stats", "print internal statistics") { } + InternalStatsPass() : Pass("internal_stats", "print internal statistics") { + experimental(); + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/logcmd.cc b/passes/cmds/logcmd.cc index 3b82ac48c..0238627d1 100644 --- a/passes/cmds/logcmd.cc +++ b/passes/cmds/logcmd.cc @@ -18,15 +18,19 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct LogPass : public Pass { LogPass() : Pass("log", "print text and log files") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/logger.cc b/passes/cmds/logger.cc index 241a8799f..276810201 100644 --- a/passes/cmds/logger.cc +++ b/passes/cmds/logger.cc @@ -17,14 +17,19 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct LoggerPass : public Pass { LoggerPass() : Pass("logger", "set logger properties") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/ltp.cc b/passes/cmds/ltp.cc index 22bdaab44..b3134b110 100644 --- a/passes/cmds/ltp.cc +++ b/passes/cmds/ltp.cc @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -141,6 +142,11 @@ struct LtpWorker struct LtpPass : public Pass { LtpPass() : Pass("ltp", "print longest topological path") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index 4ad7c165b..a653844b7 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #ifdef YOSYS_ENABLE_PLUGINS # include @@ -122,6 +123,11 @@ void load_plugin(std::string, std::vector) struct PluginPass : public Pass { PluginPass() : Pass("plugin", "load and list loaded plugins") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/portarcs.cc b/passes/cmds/portarcs.cc index a6ed75de3..97682efbb 100644 --- a/passes/cmds/portarcs.cc +++ b/passes/cmds/portarcs.cc @@ -22,6 +22,7 @@ #include "kernel/rtlil.h" #include "kernel/utils.h" #include "kernel/celltypes.h" +#include "kernel/log_help.h" PRIVATE_NAMESPACE_BEGIN USING_YOSYS_NAMESPACE @@ -38,6 +39,11 @@ static RTLIL::SigBit canonical_bit(RTLIL::SigBit bit) struct PortarcsPass : Pass { PortarcsPass() : Pass("portarcs", "derive port arcs for propagation delay") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { diff --git a/passes/cmds/portlist.cc b/passes/cmds/portlist.cc index 03048422d..f78d9d3b6 100644 --- a/passes/cmds/portlist.cc +++ b/passes/cmds/portlist.cc @@ -19,12 +19,18 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct PortlistPass : public Pass { PortlistPass() : Pass("portlist", "list (top-level) ports") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/printattrs.cc b/passes/cmds/printattrs.cc index 2a5034c13..c8b0a1d1f 100644 --- a/passes/cmds/printattrs.cc +++ b/passes/cmds/printattrs.cc @@ -18,12 +18,18 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct PrintAttrsPass : public Pass { PrintAttrsPass() : Pass("printattrs", "print attributes of selected objects") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 0f988e57a..e55e63828 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -21,12 +21,10 @@ // Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010 // http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm -#include "kernel/register.h" +#include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/log.h" -#include -#include +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -252,6 +250,11 @@ struct SccWorker struct SccPass : public Pass { SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/scratchpad.cc b/passes/cmds/scratchpad.cc index aecc4c17d..4a63f2f60 100644 --- a/passes/cmds/scratchpad.cc +++ b/passes/cmds/scratchpad.cc @@ -18,15 +18,19 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct ScratchpadPass : public Pass { ScratchpadPass() : Pass("scratchpad", "get/set values in the scratchpad") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 1d75091af..901f923f8 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -20,8 +20,7 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include -#include +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -1085,6 +1084,11 @@ PRIVATE_NAMESPACE_BEGIN struct SelectPass : public Pass { SelectPass() : Pass("select", "modify and view the list of selected objects") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -1664,6 +1668,11 @@ struct SelectPass : public Pass { struct CdPass : public Pass { CdPass() : Pass("cd", "a shortcut for 'select -module '") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -1776,6 +1785,11 @@ static void log_matches(const char *title, Module *module, const T &list) struct LsPass : public Pass { LsPass() : Pass("ls", "list modules or objects in modules") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/setenv.cc b/passes/cmds/setenv.cc index 27f2eea28..850d7c961 100644 --- a/passes/cmds/setenv.cc +++ b/passes/cmds/setenv.cc @@ -17,15 +17,18 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" -#include +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SetenvPass : public Pass { SetenvPass() : Pass("setenv", "set an environment variable") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 8a1bd58c4..4eb6569e6 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -17,10 +17,9 @@ * */ -#include "kernel/register.h" +#include "kernel/yosys.h" #include "kernel/celltypes.h" -#include "kernel/log.h" -#include +#include "kernel/log_help.h" #ifndef _WIN32 # include @@ -658,6 +657,11 @@ struct ShowWorker struct ShowPass : public Pass { ShowPass() : Pass("show", "generate schematics using graphviz") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/sta.cc b/passes/cmds/sta.cc index 4ad0e96be..5dfac1575 100644 --- a/passes/cmds/sta.cc +++ b/passes/cmds/sta.cc @@ -21,6 +21,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/timinginfo.h" +#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -275,6 +276,11 @@ struct StaWorker struct StaPass : public Pass { StaPass() : Pass("sta", "perform static timing analysis") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 6b93621f1..af7023bdd 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -25,6 +25,7 @@ #include "kernel/cost.h" #include "kernel/gzip.h" #include "libs/json11/json11.hpp" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -367,6 +368,11 @@ void read_liberty_cellarea(dict &cell_area, string libert struct StatPass : public Pass { StatPass() : Pass("stat", "print some statistics") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index 853f1bad3..fbd42e311 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -18,15 +18,19 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct TeePass : public Pass { TeePass() : Pass("tee", "redirect command output to file") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc index 1620c0bca..537b6793d 100644 --- a/passes/cmds/torder.cc +++ b/passes/cmds/torder.cc @@ -21,12 +21,18 @@ #include "kernel/celltypes.h" #include "kernel/sigtools.h" #include "kernel/utils.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct TorderPass : public Pass { TorderPass() : Pass("torder", "print cells in topological order") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc index 400542776..39ed8e60e 100644 --- a/passes/cmds/trace.cc +++ b/passes/cmds/trace.cc @@ -19,6 +19,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -60,6 +61,11 @@ struct TraceMonitor : public RTLIL::Monitor struct TracePass : public Pass { TracePass() : Pass("trace", "redirect command output to file") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -96,6 +102,11 @@ struct TracePass : public Pass { struct DebugPass : public Pass { DebugPass() : Pass("debug", "run command with debug log messages enabled") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/viz.cc b/passes/cmds/viz.cc index 131e799ab..4c73b4d71 100644 --- a/passes/cmds/viz.cc +++ b/passes/cmds/viz.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/log_help.h" #ifndef _WIN32 # include @@ -817,6 +818,11 @@ struct VizWorker struct VizPass : public Pass { VizPass() : Pass("viz", "visualize data flow graph") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index ea9b3f556..a22fdaf2a 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -19,12 +19,18 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct WriteFileFrontend : public Frontend { WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/status"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/xprop.cc b/passes/cmds/xprop.cc index dc5befc27..d2d0c4d8e 100644 --- a/passes/cmds/xprop.cc +++ b/passes/cmds/xprop.cc @@ -24,6 +24,7 @@ #include "kernel/sigtools.h" #include "kernel/utils.h" #include "kernel/yosys.h" +#include "kernel/log_help.h" #include USING_YOSYS_NAMESPACE @@ -1100,6 +1101,11 @@ struct XpropWorker struct XpropPass : public Pass { XpropPass() : Pass("xprop", "formal x propagation") {} + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index 3cd1b6180..ff1a2fcc5 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -1,4 +1,5 @@ +OBJS += passes/hierarchy/flatten.o OBJS += passes/hierarchy/hierarchy.o OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o diff --git a/passes/techmap/flatten.cc b/passes/hierarchy/flatten.cc similarity index 100% rename from passes/techmap/flatten.cc rename to passes/hierarchy/flatten.cc diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc index 9fa9f5c2d..0ac391790 100644 --- a/passes/opt/rmports.cc +++ b/passes/opt/rmports.cc @@ -17,17 +17,20 @@ * */ -#include "kernel/register.h" #include "kernel/sigtools.h" -#include "kernel/log.h" -#include -#include +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct RmportsPassPass : public Pass { RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("techlibs/greenpak4"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index fc41848f7..892500850 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -117,7 +117,9 @@ void opt_eqpmux(test_pmgen_pm &pm) } struct TestPmgenPass : public Pass { - TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } + TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc index 991977ab9..7b3357f82 100644 --- a/passes/sat/assertpmux.cc +++ b/passes/sat/assertpmux.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/utils.h" @@ -182,6 +183,11 @@ struct AssertpmuxWorker struct AssertpmuxPass : public Pass { AssertpmuxPass() : Pass("assertpmux", "adds asserts for parallel muxes") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index 93c7e96c8..e86a78d81 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -27,6 +28,11 @@ PRIVATE_NAMESPACE_BEGIN struct Async2syncPass : public Pass { Async2syncPass() : Pass("async2sync", "convert async FF inputs to sync circuits") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index 348bab727..6b94cbe19 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -33,6 +34,11 @@ struct SampledSig { struct Clk2fflogicPass : public Pass { Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index dcd399a57..485e44fd6 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -25,6 +26,11 @@ PRIVATE_NAMESPACE_BEGIN struct CutpointPass : public Pass { CutpointPass() : Pass("cutpoint", "adds formal cut points to the design") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 9afe00d5c..1e975db0f 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -17,11 +17,10 @@ * */ -#include "kernel/register.h" +#include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -217,6 +216,11 @@ RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width struct ExposePass : public Pass { ExposePass() : Pass("expose", "convert internal signals to module ports") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/cmds"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc index 220cf5c52..575b2f40d 100644 --- a/passes/sat/fmcombine.cc +++ b/passes/sat/fmcombine.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" @@ -237,6 +238,11 @@ struct FmcombineWorker struct FmcombinePass : public Pass { FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/fminit.cc b/passes/sat/fminit.cc index 5f4ec0068..547082164 100644 --- a/passes/sat/fminit.cc +++ b/passes/sat/fminit.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -25,6 +26,11 @@ PRIVATE_NAMESPACE_BEGIN struct FminitPass : public Pass { FminitPass() : Pass("fminit", "set init values/sequences for formal") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/formalff.cc b/passes/sat/formalff.cc index 1d87fcc3b..286bf2976 100644 --- a/passes/sat/formalff.cc +++ b/passes/sat/formalff.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -486,6 +487,11 @@ void HierarchyWorker::propagate() struct FormalFfPass : public Pass { FormalFfPass() : Pass("formalff", "prepare FFs for formal") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index 52e80f667..6063c5ab9 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -17,17 +17,12 @@ * */ -#include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/consteval.h" #include "kernel/sigtools.h" -#include "kernel/log.h" #include "kernel/satgen.h" -#include -#include -#include -#include -#include +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -761,6 +756,11 @@ struct FreduceWorker struct FreducePass : public Pass { FreducePass() : Pass("freduce", "perform functional reduction") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 8f27c4c6f..9bcf25547 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -17,9 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -395,6 +394,11 @@ void create_miter_assert(struct Pass *that, std::vector args, RTLIL struct MiterPass : public Pass { MiterPass() : Pass("miter", "automatically create a miter circuit") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc index 3075ef3f0..43373ce0e 100644 --- a/passes/sat/mutate.cc +++ b/passes/sat/mutate.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -728,6 +729,11 @@ void mutate_cnot(Design *design, const mutate_opts_t &opts, bool one) struct MutatePass : public Pass { MutatePass() : Pass("mutate", "generate or apply design mutations") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc index db6836ea1..7a7a31806 100644 --- a/passes/sat/qbfsat.cc +++ b/passes/sat/qbfsat.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/consteval.h" #include "qbfsat.h" @@ -504,6 +505,11 @@ QbfSolveOptions parse_args(const std::vector &args) { struct QbfSatPass : public Pass { QbfSatPass() : Pass("qbfsat", "solve a 2QBF-SAT problem in the circuit") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/recover_names.cc b/passes/sat/recover_names.cc index cddd8771c..7ed8b1304 100644 --- a/passes/sat/recover_names.cc +++ b/passes/sat/recover_names.cc @@ -23,6 +23,7 @@ #include "kernel/celltypes.h" #include "kernel/utils.h" #include "kernel/satgen.h" +#include "kernel/log_help.h" #include #include @@ -690,6 +691,11 @@ struct RecoverNamesWorker { struct RecoverNamesPass : public Pass { RecoverNamesPass() : Pass("recover_names", "Execute a lossy mapping command and recover original netnames") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("passes/opt"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 0c2143fc9..2f20880cb 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -21,17 +21,12 @@ // Niklas Een and Niklas Sörensson (2003) // http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.8161 -#include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/consteval.h" #include "kernel/sigtools.h" -#include "kernel/log.h" #include "kernel/satgen.h" -#include -#include -#include -#include -#include +#include "kernel/yosys.h" +#include "kernel/log_help.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -902,6 +897,11 @@ void print_qed() struct SatPass : public Pass { SatPass() : Pass("sat", "solve a SAT problem in the circuit") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/supercover.cc b/passes/sat/supercover.cc index 38dbd3cf9..f1b3ad09c 100644 --- a/passes/sat/supercover.cc +++ b/passes/sat/supercover.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/log_help.h" #include "kernel/sigtools.h" USING_YOSYS_NAMESPACE @@ -25,6 +26,11 @@ PRIVATE_NAMESPACE_BEGIN struct SupercoverPass : public Pass { SupercoverPass() : Pass("supercover", "add hi/lo cover cells for each wire bit") { } + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/synthprop.cc b/passes/sat/synthprop.cc index 5553abec2..4703e4ad7 100644 --- a/passes/sat/synthprop.cc +++ b/passes/sat/synthprop.cc @@ -18,7 +18,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ + #include "kernel/yosys.h" +#include "kernel/log_help.h" YOSYS_NAMESPACE_BEGIN @@ -179,7 +181,13 @@ void SynthPropWorker::run() struct SyntProperties : public Pass { SyntProperties() : Pass("synthprop", "synthesize SVA properties") { } - virtual void help() + bool formatted_help() override { + auto *help = PrettyHelp::get_current(); + help->set_group("formal"); + return false; + } + + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -208,7 +216,7 @@ struct SyntProperties : public Pass { log("\n"); } - virtual void execute(std::vector args, RTLIL::Design* design) + void execute(std::vector args, RTLIL::Design* design) override { log_header(design, "Executing SYNTHPROP pass.\n"); SynthPropWorker worker(design); diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index c9fe98a74..91b3b563a 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -1,5 +1,4 @@ -OBJS += passes/techmap/flatten.o OBJS += passes/techmap/techmap.o OBJS += passes/techmap/simplemap.o OBJS += passes/techmap/dfflibmap.o diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc index 9e7adaab1..ed54ce164 100644 --- a/passes/tests/test_abcloop.cc +++ b/passes/tests/test_abcloop.cc @@ -243,7 +243,9 @@ static void test_abcloop() } struct TestAbcloopPass : public Pass { - TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { } + TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index 404d1e48d..306e760ee 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -326,7 +326,9 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int s } struct TestAutotbBackend : public Backend { - TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { } + TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index a34eafc2f..b6385766c 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -705,7 +705,9 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: } struct TestCellPass : public Pass { - TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { } + TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { + internal(); + } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From 7fe4ae45fd8f60dddb6c4a48eb8844c44ced9351 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:52:12 +1200 Subject: [PATCH 172/181] log_help: Fix mem leaks `_content` vector owns elements so that when the `ContentListing` is deleted so is the content. Remove `get_content()` method in favour of `begin()` and `end()` const iterators. More `const` in general, and iterations over `ContentListing` use `&content` to avoid copying data. --- kernel/log_help.cc | 27 +++++++++++++-------------- kernel/log_help.h | 36 ++++++++++++++++-------------------- kernel/register.cc | 4 ++-- 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 45228b024..30c06a7c3 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -21,7 +21,7 @@ USING_YOSYS_NAMESPACE -Json ContentListing::to_json() { +Json ContentListing::to_json() const { Json::object object; object["type"] = type; if (body.length()) object["body"] = body; @@ -29,7 +29,7 @@ Json ContentListing::to_json() { if (source_line != 0) object["source_line"] = source_line; object["options"] = Json(options); Json::array content_array; - for (auto child : _content) content_array.push_back(child->to_json()); + for (auto child : _content) content_array.push_back(child.to_json()); object["content"] = content_array; return object; } @@ -73,9 +73,8 @@ ContentListing* ContentListing::open_option(const string &text, const source_location location) { log_assert(type.compare("root") == 0 || type.compare("usage") == 0); - auto option = new ContentListing("option", text, location); - add_content(option); - return option; + add_content("option", text, location); + return back(); } #define MAX_LINE_LEN 80 @@ -131,20 +130,20 @@ PrettyHelp *PrettyHelp::get_current() return current_help; } -void PrettyHelp::log_help() +void PrettyHelp::log_help() const { - for (auto content : _root_listing.get_content()) { - if (content->type.compare("usage") == 0) { - log_pass_str(content->body, 1, true); + for (auto &content : _root_listing) { + if (content.type.compare("usage") == 0) { + log_pass_str(content.body, 1, true); log("\n"); - } else if (content->type.compare("option") == 0) { - log_pass_str(content->body, 1); - for (auto text : content->get_content()) { - log_pass_str(text->body, 2); + } else if (content.type.compare("option") == 0) { + log_pass_str(content.body, 1); + for (auto text : content) { + log_pass_str(text.body, 2); log("\n"); } } else { - log_pass_str(content->body, 0); + log_pass_str(content.body, 0); log("\n"); } } diff --git a/kernel/log_help.h b/kernel/log_help.h index 0a40fc531..3ce0ac7aa 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN class ContentListing { - vector _content; + vector _content; public: string type; string body; @@ -45,22 +45,17 @@ public: ContentListing(string type, string body, source_location location) : ContentListing(type, body, location.file_name(), location.line()) { } - void add_content(ContentListing *new_content) { - _content.push_back(new_content); - } - void add_content(string type, string body, source_location location) { - auto new_content = new ContentListing(type, body, location); - add_content(new_content); + _content.push_back({type, body, location}); } - bool has_content() { return _content.size() != 0; } - const vector get_content() { - const vector content = _content; - return content; - } + bool has_content() const { return _content.size() != 0; } + + vector::const_iterator begin() const { return _content.cbegin(); } + vector::const_iterator end() const { return _content.cend(); } + ContentListing* back() { - return _content.back(); + return &_content.back(); } void set_option(string key, string val = "") { @@ -95,7 +90,7 @@ public: const source_location location = source_location::current() ); - Json to_json(); + Json to_json() const; }; class PrettyHelp @@ -113,18 +108,19 @@ public: static PrettyHelp *get_current(); - bool has_content() { return _root_listing.has_content(); } - const vector get_content() { - return _root_listing.get_content(); - } + bool has_content() const { return _root_listing.has_content(); } + + vector::const_iterator begin() const { return _root_listing.begin(); } + vector::const_iterator end() const { return _root_listing.end(); } + ContentListing* get_root() { return &_root_listing; } void set_group(const string g) { group = g; } - bool has_group() { return group.compare("unknown") != 0; } + bool has_group() const { return group.compare("unknown") != 0; } - void log_help(); + void log_help() const; }; YOSYS_NAMESPACE_END diff --git a/kernel/register.cc b/kernel/register.cc index ea2a2624f..0b02a6aa5 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -923,8 +923,8 @@ struct HelpPass : public Pass { json.name(name.c_str()); json.begin_object(); json.entry("title", title); json.name("content"); json.begin_array(); - for (auto content : cmd_help.get_content()) - json.value(content->to_json()); + for (auto &content : cmd_help) + json.value(content.to_json()); json.end_array(); json.entry("group", cmd_help.group); json.entry("source_file", pass->location.file_name()); From 891a907a3005fdb0f154f93d79943e563d8454c6 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:52:13 +1200 Subject: [PATCH 173/181] Add and use ENABLE_HELP_SOURCE Conditionally include help source tracking to preserve ABI. Docs builds can (and should) use `ENABLE_HELP_SOURCE` so that the generated sphinx docs can perform default grouping and link to source files. Regular user-builds don't need the source tracking. --- .github/workflows/prepare-docs.yml | 1 + Makefile | 5 +++++ README.md | 6 ++++++ kernel/register.h | 28 ++++++++++++++-------------- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml index b47f5f3dd..a02febb3b 100644 --- a/.github/workflows/prepare-docs.yml +++ b/.github/workflows/prepare-docs.yml @@ -47,6 +47,7 @@ jobs: echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf + echo "ENABLE_HELP_SOURCE := 1" >> Makefile.conf make -j$procs ENABLE_LTO=1 - name: Prepare docs diff --git a/Makefile b/Makefile index ce438f5db..1463055bd 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ ENABLE_VERIFIC_LIBERTY := 0 ENABLE_COVER := 1 ENABLE_LIBYOSYS := 0 ENABLE_ZLIB := 1 +ENABLE_HELP_SOURCE := 0 # python wrappers ENABLE_PYOSYS := 0 @@ -109,6 +110,10 @@ PLUGIN_LINKFLAGS += -L"$(LIBDIR)" PLUGIN_LIBS := -lyosys_exe endif +ifeq ($(ENABLE_HELP_SOURCE),1) +CXXFLAGS += -DYOSYS_ENABLE_HELP_SOURCE +endif + PKG_CONFIG ?= pkg-config SED ?= sed BISON ?= bison diff --git a/README.md b/README.md index 6c576f682..78b8bfc51 100644 --- a/README.md +++ b/README.md @@ -276,3 +276,9 @@ From the root of the repository, run `make docs`. This will build/rebuild yosys as necessary before generating the website documentation from the yosys help commands. To build for pdf instead of html, call `make docs DOC_TARGET=latexpdf`. + +It is recommended to use the `ENABLE_HELP_SOURCE` make option for Yosys builds +that will be used to build the documentation. This option enables source +location tracking for passes and improves the command reference through grouping +related commands and allowing for the documentation to link to the corresponding +source files. diff --git a/kernel/register.h b/kernel/register.h index e8c017c1d..fba72538f 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -23,22 +23,22 @@ #include "kernel/yosys_common.h" #include "kernel/yosys.h" -#include -#if __cpp_lib_source_location == 201907L - #include - using std::source_location; -#elif defined(__has_include) -# if __has_include() - #include - using std::experimental::source_location; -# else - #define SOURCE_FALLBACK -# endif -#else - #define SOURCE_FALLBACK +#ifdef YOSYS_ENABLE_HELP_SOURCE + #include +# if __cpp_lib_source_location == 201907L + #include + using std::source_location; + #define HAS_SOURCE_LOCATION +# elif defined(__has_include) +# if __has_include() + #include + using std::experimental::source_location; + #define HAS_SOURCE_LOCATION +# endif +# endif #endif -#ifdef SOURCE_FALLBACK +#ifndef HAS_SOURCE_LOCATION struct source_location { // dummy placeholder int line() const { return 0; } int column() const { return 0; } From ab403635e317619c23606b0aa31befe819c19fdb Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:52:13 +1200 Subject: [PATCH 174/181] CI: Enable source tracking for reusable build The `test-docs-build` jobs require source tracking enabled to prevent warnings-as-errors. Also add an extra note to the readme in case users run into the same. --- .github/workflows/test-build.yml | 1 + README.md | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 22d3a94f8..bdd290189 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -225,6 +225,7 @@ jobs: run: | make config-clang echo "ENABLE_CCACHE := 1" >> Makefile.conf + echo "ENABLE_HELP_SOURCE := 1" >> Makefile.conf make -j$procs - name: Install doc prereqs diff --git a/README.md b/README.md index 78b8bfc51..e5b8d1072 100644 --- a/README.md +++ b/README.md @@ -281,4 +281,6 @@ It is recommended to use the `ENABLE_HELP_SOURCE` make option for Yosys builds that will be used to build the documentation. This option enables source location tracking for passes and improves the command reference through grouping related commands and allowing for the documentation to link to the corresponding -source files. +source files. Without this, a warning will be raised during the Sphinx build +about `Found commands assigned to group unknown` and `make docs` is configured +to fail on warnings by default. From 20a51742f4bdbf4f6c70950f41e91b2e1fd95710 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:52:13 +1200 Subject: [PATCH 175/181] Docs: Fix cmd links from bugpoint docs --- docs/source/using_yosys/bugpoint.rst | 2 +- .../more_scripting/load_design.rst | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst index 60cabd879..c524470af 100644 --- a/docs/source/using_yosys/bugpoint.rst +++ b/docs/source/using_yosys/bugpoint.rst @@ -185,7 +185,7 @@ the failure, you can use the ``bugpoint_keep`` attribute. This can be done with ``setattr -set bugpoint_keep 1 [selection]`` from a Yosys script. It is also possible to limit `bugpoint` to only removing certain *kinds* of objects, such as only removing entire modules or cells (instances of modules). For more about -the options available, check ``help bugpoint`` or :doc:`/cmd/bugpoint`. +the options available, check ``help bugpoint`` or :cmd:title:`bugpoint`. In some situations, it may also be helpful to use `setenv` before `bugpoint` to set environment variables for the spawned processes. An example of this is diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst index e5006c16e..9aa028418 100644 --- a/docs/source/using_yosys/more_scripting/load_design.rst +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -57,6 +57,7 @@ The `read` command Yosys frontends ~~~~~~~~~~~~~~~ +- :doc:`/cmd/index_frontends` - typically start with ``read_`` - built-in support for heredocs @@ -86,7 +87,12 @@ Yosys frontends The `read_verilog` command """""""""""""""""""""""""" -- :doc:`/cmd/index_frontends` +- :cmd:title:`read_verilog`; also + + + :cmd:title:`verilog_defaults`, + + :cmd:title:`verilog_defines`, and + + :cmd:title:`read_verilog_file_list` + - supports most of Verilog-2005 - limited support for SystemVerilog - some non-standard features/extensions for enabling formal verification @@ -117,12 +123,12 @@ The `read_verilog` command Other built-in ``read_*`` commands """""""""""""""""""""""""""""""""" -- :doc:`/cmd/read_rtlil` -- :doc:`/cmd/read_aiger` -- :doc:`/cmd/read_blif` -- :doc:`/cmd/read_json` -- :doc:`/cmd/read_liberty` -- :doc:`/cmd/read_xaiger2` +- :cmd:title:`read_rtlil` +- :cmd:title:`read_aiger` +- :cmd:title:`read_blif` +- :cmd:title:`read_json` +- :cmd:title:`read_liberty` +- :cmd:title:`read_xaiger2` .. TODO:: does `write_file` count? From 96108ad8b42ddd002fa43ecf25673998fc95eaf9 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 6 Aug 2025 21:34:37 +0100 Subject: [PATCH 176/181] kernel/register.h: whitespace --- kernel/register.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/register.h b/kernel/register.h index fba72538f..534cfbc28 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -116,7 +116,7 @@ struct ScriptPass : Pass RTLIL::Design *active_design; std::string active_run_from, active_run_to; - ScriptPass(std::string name, std::string short_help = "** document me **", source_location location = source_location::current()) : + ScriptPass(std::string name, std::string short_help = "** document me **", source_location location = source_location::current()) : Pass(name, short_help, location) { } virtual void script() = 0; From b610afbc1bbbd73d4d4cdd7eb2d873c58b97445f Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 6 Aug 2025 21:37:28 +0100 Subject: [PATCH 177/181] py_wrap_generator.py: whitespace --- misc/py_wrap_generator.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index 4857a9dc3..b9af5fe70 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -200,7 +200,7 @@ class WType: t.cont = candidate if(t.name not in known_containers): - return None + return None return t prefix = "" @@ -447,7 +447,7 @@ class PythonDictTranslator(Translator): if types[0].attr_type != attr_types.star: text += "*" text += key_tmp_name + "->get_cpp_obj()" - + text += ", " if types[1].name not in classnames: text += val_tmp_name @@ -457,7 +457,7 @@ class PythonDictTranslator(Translator): text += val_tmp_name + "->get_cpp_obj()" text += "));\n" + prefix + "}" return text - + #Generate c++ code to translate to a boost::python::dict @classmethod def translate_cpp(c, varname, types, prefix, ref): @@ -498,7 +498,7 @@ class DictTranslator(PythonDictTranslator): #Sub_type for std::map class MapTranslator(PythonDictTranslator): insert_name = "insert" - orig_name = "std::map" + orig_name = "std::map" #Translator for std::pair. Derived from PythonDictTranslator because the #gen_type function is the same (because both have two template parameters) @@ -684,7 +684,7 @@ class Attribute: if self.wtype.name in known_containers: return known_containers[self.wtype.name].typename return prefix + self.wtype.name - + #Generate Translation code for the attribute def gen_translation(self): if self.wtype.name in known_containers: @@ -948,7 +948,7 @@ class WClass: text = "\n\t\tclass_<" + self.name + base_info + ">(\"" + self.name + "\"" text += body return text - + def contains_default_constr(self): for c in self.found_constrs: @@ -1773,7 +1773,7 @@ class WMember: if self.wtype.name in classnames: text += ")" text += ";" - + if self.wtype.name in classnames: text += "\n\t\treturn *ret_;" elif self.wtype.name in known_containers: @@ -1795,12 +1795,12 @@ class WMember: text += "\n\t{" text += ret.gen_translation() text += "\n\t\tthis->get_cpp_obj()->" + self.name + " = " + ret.gen_call() + ";" - text += "\n\t}\n" + text += "\n\t}\n" return text; def gen_boost_py(self): - text = "\n\t\t\t.add_property(\"" + self.name + "\", &" + self.member_of.name + "::get_var_py_" + self.name + text = "\n\t\t\t.add_property(\"" + self.name + "\", &" + self.member_of.name + "::get_var_py_" + self.name if not self.is_const: text += ", &" + self.member_of.name + "::set_var_py_" + self.name text += ")" @@ -1926,7 +1926,7 @@ class WGlobal: if self.wtype.name in classnames: text += ")" text += ";" - + if self.wtype.name in classnames: text += "\n\t\treturn *ret_;" elif self.wtype.name in known_containers: @@ -1948,12 +1948,12 @@ class WGlobal: text += "\n\t{" text += ret.gen_translation() text += "\n\t\t" + self.namespace + "::" + self.name + " = " + ret.gen_call() + ";" - text += "\n\t}\n" + text += "\n\t}\n" return text; def gen_boost_py(self): - text = "\n\t\t\t.add_static_property(\"" + self.name + "\", &" + "YOSYS_PYTHON::get_var_py_" + self.name + text = "\n\t\t\t.add_static_property(\"" + self.name + "\", &" + "YOSYS_PYTHON::get_var_py_" + self.name if not self.is_const: text += ", &YOSYS_PYTHON::set_var_py_" + self.name text += ")" From 46a711d56695d7468e209f4bd104d0c1c271312a Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 6 Aug 2025 21:38:03 +0100 Subject: [PATCH 178/181] py_wrap_generator.py: support srd::source_location as trailing default argument --- misc/py_wrap_generator.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index b9af5fe70..9de34e74e 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -71,7 +71,7 @@ keyword_aliases = { #These can be used without any explicit conversion primitive_types = ["void", "bool", "int", "double", "size_t", "std::string", - "string", "State", "char_p"] + "string", "State", "char_p", "std::source_location", "source_location"] from enum import Enum @@ -1137,10 +1137,18 @@ class WConstructor: str_def = str_def[0:found].strip() if len(str_def) == 0: return con - for arg in split_list(str_def, ","): + args = split_list(str_def, ",") + for i, arg in enumerate(args): parsed = Attribute.from_string(arg.strip(), containing_file, line_number) if parsed == None: return None + # Only allow std::source_location as defaulted last argument, and + # don't append so it takes default value + if parsed.wtype.name in ["std::source_location", "source_location"]: + if parsed.default_value is None or i != len(args) - 1: + debug("std::source_location not defaulted last arg of " + class_.name + " is unsupported", 2) + return None + continue con.args.append(parsed) return con @@ -1379,12 +1387,20 @@ class WFunction: str_def = str_def[:found].strip() if(len(str_def) == 0): return func - for arg in split_list(str_def, ","): + args = split_list(str_def, ",") + for i, arg in enumerate(args): if arg.strip() == "...": continue parsed = Attribute.from_string(arg.strip(), containing_file, line_number) if parsed == None: return None + # Only allow std::source_location as defaulted last argument, and + # don't append so it takes default value + if parsed.wtype.name in ["std::source_location", "source_location"]: + if parsed.default_value is None or i != len(args) - 1: + debug("std::source_location not defaulted last arg of " + func.name + " is unsupported", 2) + return None + continue func.args.append(parsed) return func From ab66d8b814edd60dc3f4fa51819889d84023411d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 00:27:08 +0000 Subject: [PATCH 179/181] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1463055bd..506f6a99c 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+194 +YOSYS_VER := 0.55+209 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 9c447ad9d4b1ea589369364eea38b4d70da2c599 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Thu, 7 Aug 2025 07:59:29 +0200 Subject: [PATCH 180/181] Release version 0.56 --- CHANGELOG | 16 +++++++++++++++- Makefile | 4 ++-- docs/source/conf.py | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d8f2f8804..4678f78e4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,8 +2,22 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.55 .. Yosys 0.56-dev +Yosys 0.55 .. Yosys 0.56 -------------------------- + * New commands and options + - Added "-unescape" option to "rename" pass. + - Added "-assert2cover" option to "chformal" pass. + - Added "linecoverage" pass to generate lcov report from selection. + - Added "opt_hier" pass to enable hierarchical optimization. + - Added "-hieropt" option to "synth" pass. + - Added "-expect-return", "-err-grep" and "-suffix" options + to "bugpoint" pass. + - Added "raise_error" dev pass. + + * Various + - Added groups to command reference documentation. + - Added bugpoint guide to documentation. + - verific: correctly reset Verific flags after import. Yosys 0.54 .. Yosys 0.55 -------------------------- diff --git a/Makefile b/Makefile index 506f6a99c..8de1e54eb 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+209 +YOSYS_VER := 0.56 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) @@ -182,7 +182,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 60f126c.. | wc -l`/;" Makefile +# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 60f126c.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) diff --git a/docs/source/conf.py b/docs/source/conf.py index 7cdea5df1..ebec6915e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,7 +6,7 @@ import os project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' copyright ='2025 YosysHQ GmbH' -yosys_ver = "0.55" +yosys_ver = "0.56" # select HTML theme html_theme = 'furo-ys' From e6059d042df45f6eb28b858e9a27d277438b0eae Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Thu, 7 Aug 2025 09:20:45 +0200 Subject: [PATCH 181/181] Next dev cycle --- CHANGELOG | 3 +++ Makefile | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4678f78e4..6365a24e9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,9 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.56 .. Yosys 0.57-dev +-------------------------- + Yosys 0.55 .. Yosys 0.56 -------------------------- * New commands and options diff --git a/Makefile b/Makefile index 8de1e54eb..b27a45424 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.56 +YOSYS_VER := 0.56+0 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) @@ -182,7 +182,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: -# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 60f126c.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 9c447ad.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)