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 01/59] 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 02/59] 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 03/59] 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 04/59] 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 05/59] 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 06/59] 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 07/59] 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 08/59] 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 09/59] 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 10/59] 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 11/59] 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 12/59] 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 13/59] 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 14/59] 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 15/59] 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 16/59] 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 17/59] 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 18/59] 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 19/59] 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 20/59] 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 21/59] 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 22/59] 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 23/59] 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 24/59] 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 25/59] 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 26/59] 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 27/59] 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 28/59] 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 29/59] 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 30/59] 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 31/59] 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 32/59] 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 33/59] 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 34/59] 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 35/59] 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 36/59] 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 37/59] 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 38/59] 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 39/59] 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 40/59] 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 41/59] 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 42/59] 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 43/59] 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 44/59] 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 45/59] 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 46/59] 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 47/59] 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 48/59] 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 49/59] 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 50/59] 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 51/59] 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 52/59] 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 53/59] 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 54/59] 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 55/59] 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 56/59] 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 57/59] 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 58/59] 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 59/59] 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())]