From 1f1eb1ab576a7c7ab603d11aaa39cf1f8f38fd86 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:24:27 +1300 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 1ab3b242b..cc1694c7b 100644 --- a/Makefile +++ b/Makefile @@ -1009,12 +1009,6 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' $(Q) rsync -rc 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 -rc 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 fcb6ca769..a55be28b2 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -800,64 +800,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(); @@ -995,22 +937,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 31dba4752143586e1be88a0014cfa154e1becca0 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:24:27 +1300 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 41a489f085e5d661000f33683e0b6eff0cbb03f9 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:24:27 +1300 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 a55be28b2..2cacb9406 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 25ea9f232..31167d532 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 = {}); virtual ~Pass(); virtual void help(); @@ -43,6 +57,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 63be4d3dd3921ff4a6922d18707e01ad8a79422f Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:24:28 +1300 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 cc1694c7b..543ff23db 100644 --- a/Makefile +++ b/Makefile @@ -1010,6 +1010,9 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) rsync -rc 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 2cacb9406..642a63ae2 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -837,6 +837,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(); @@ -1009,7 +1148,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)); @@ -1017,6 +1166,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 ac80755521815a5e2dc140f9d5494c82f277b50a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:24:28 +1300 Subject: [PATCH 05/59] Docs: Test new pass help with chformal --- passes/cmds/chformal.cc | 103 +++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index e027103bb..03d97a8da 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -70,62 +70,59 @@ 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(" -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-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 5d239204f21322153cc16e32e420c62393986e9e Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:24:28 +1300 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 6a0e92167..405ed1fbc 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -110,6 +110,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 642a63ae2..1dcd477b4 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()); @@ -854,7 +883,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 { @@ -951,23 +980,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 31167d532..64b2ce42f 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 = {}); virtual ~Pass(); @@ -61,6 +63,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 e289e32e61b0058458fef861ec37cdec694e5ee0 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:24:28 +1300 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 405ed1fbc..5733a80d6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -41,8 +41,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 8607e3c537cca41b964848b3874277ec1dc58a79 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:02 +1300 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 543ff23db..e388b386f 100644 --- a/Makefile +++ b/Makefile @@ -1063,7 +1063,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 af9e0dd0f447cdb9dadaa8816f98e74f3d7cf19f Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:45 +1300 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 | 103 ++++++++++++++++++----------------- 6 files changed, 241 insertions(+), 175 deletions(-) create mode 100644 kernel/log_help.cc create mode 100644 kernel/log_help.h diff --git a/Makefile b/Makefile index e388b386f..2dbf698e9 100644 --- a/Makefile +++ b/Makefile @@ -620,6 +620,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 1dcd477b4..1a1a868b9 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() { } @@ -880,13 +822,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, @@ -934,7 +875,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) { @@ -947,19 +888,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(); } @@ -980,28 +921,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 64b2ce42f..6e7b74d5c 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -25,29 +25,14 @@ 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 **"); virtual ~Pass(); virtual void help(); + virtual bool help_v2(); virtual void clear_flags(); virtual void execute(std::vector args, RTLIL::Design *design) = 0; @@ -59,14 +44,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 03d97a8da..e2f1e0406 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,59 +71,59 @@ 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-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("-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 40e6fe82631841d653f68a85e4a46d601e1d2e40 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:45 +1300 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 e2f1e0406..1f3f8fe11 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("-assume2assert"); help->option("-live2fair"); @@ -122,6 +125,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 a7da7d3a56205136911dad6e8161b74e2392d5f5 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:45 +1300 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 2dbf698e9..29216df83 100644 --- a/Makefile +++ b/Makefile @@ -519,6 +519,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 09dfbea6dff0a7ebbcab5fda24b4897eac29165f Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:45 +1300 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 1a1a868b9..c1c2352db 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -824,7 +824,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) { @@ -921,7 +921,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 fd5e420edd7c43a3ad36137a2f8e3250b5d0325f Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:45 +1300 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 0e1434ac0c4d1f5d5a6c24051ee9cc8a9ddbcf8e Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:45 +1300 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 c1c2352db..dc0a08e28 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -829,12 +829,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); @@ -844,17 +848,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; } @@ -863,58 +881,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 69fa0f9fef160bf925fbc4f3735b6d832759f3c6 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:46 +1300 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 59bcd7ce144497598dd3d812213fea15f5092ac9 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:25:46 +1300 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 dc0a08e28..2e7e6d8ec 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -831,7 +831,6 @@ struct HelpPass : public Pass { enum PassUsageState { PUState_none, PUState_signature, - PUState_description, PUState_options, PUState_optionbody, }; @@ -851,6 +850,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"); @@ -861,18 +861,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; } @@ -888,11 +886,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; @@ -903,6 +905,7 @@ struct HelpPass : public Pass { current_state = PUState_options; if (!current_buffer.empty()) { cmd_help.codeblock(current_buffer, "none", null_source); + current_buffer = ""; } } else @@ -910,6 +913,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 { @@ -927,12 +934,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 4e88f864140333571545b38576cd22760bcb2f50 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:10 +1300 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 29216df83..28ea38f31 100644 --- a/Makefile +++ b/Makefile @@ -1067,7 +1067,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 a0da79981b51457f6962f05564c1a75fed3f64a8 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:10 +1300 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 35498ae3f65315ee07741c23c3c45c632f27570a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:10 +1300 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 2e7e6d8ec..2c85ea631 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -951,6 +951,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 620dcd8a382efed55b65af26844edfa788c9f8ca Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:10 +1300 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 2c85ea631..662e20144 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -850,7 +850,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"); @@ -882,9 +882,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 = ""; @@ -902,14 +902,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) { @@ -942,6 +948,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 431cb2d1b938b699c0ffcc40354634c05a6ceac6 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:10 +1300 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 1efd3aeb7d584a85d6608cb4191d87f2b3a099a4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:10 +1300 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 662e20144..03ba9d8ec 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) { } @@ -537,8 +538,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 6e7b74d5c..9830b1d13 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()); virtual ~Pass(); virtual void help(); @@ -81,7 +96,8 @@ struct ScriptPass : Pass RTLIL::Design *active_design; std::string active_run_from, active_run_to; - ScriptPass(std::string name, std::string short_help = "** document me **") : Pass(name, short_help) { } + ScriptPass(std::string name, std::string short_help = "** document me **", source_location location = source_location::current()) : + Pass(name, short_help, location) { } virtual void script() = 0; @@ -99,7 +115,8 @@ struct Frontend : Pass static std::string last_here_document; std::string frontend_name; - Frontend(std::string name, std::string short_help = "** document me **"); + Frontend(std::string name, std::string short_help = "** document me **", + source_location location = source_location::current()); void run_register() override; ~Frontend() override; void execute(std::vector args, RTLIL::Design *design) override final; @@ -115,7 +132,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 aedc13ab7fa2ac87e4fd31a5948e2f024c235cfd Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:10 +1300 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 1f3f8fe11..53bf203f3 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 07c2666447e082f535364d5d240c4e5e8a926db4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:10 +1300 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 03ba9d8ec..ea9376b36 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -27,6 +27,7 @@ #include #include #include +#include YOSYS_NAMESPACE_BEGIN @@ -816,6 +817,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 @@ -956,6 +958,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); @@ -963,12 +1001,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 11af1a275bd646af40c5b009bb6c6c228f18e644 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:11 +1300 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 49907914c41a476cd6b79d0bd10f00500e4114bd Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:11 +1300 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 c6180306d..bb2ef6bd2 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 343758371dbbe4cf5ca4a23de3bc2be256a25ff6 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:11 +1300 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 79dfb7912..db2162e55 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 28ea38f31..8a3f19fa8 100644 --- a/Makefile +++ b/Makefile @@ -1090,7 +1090,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__ clean-abc: $(MAKE) -C abc DEP= clean From 2575f87381ee58d14bfcca47f4ce9bdd8234159a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:11 +1300 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 8a3f19fa8..bbcadf712 100644 --- a/Makefile +++ b/Makefile @@ -519,10 +519,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 9830b1d13..5f5d33cff 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 d244b4a101503722fa13771da9b723c110a7c2fa Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:11 +1300 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 ea9376b36..3bdfc1740 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -27,7 +27,6 @@ #include #include #include -#include YOSYS_NAMESPACE_BEGIN @@ -969,9 +968,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 34c514bf96396d3ff87a12269a2066309daade64 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:11 +1300 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 5f5d33cff..8363e686c 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 68cb02d9f3ab615bbf5b82442b9d32c9a7d97e45 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:11 +1300 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 3bdfc1740..ef05b4512 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; } @@ -827,7 +827,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 8363e686c..9eb1c220d 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -58,8 +58,10 @@ struct Pass source_location location = source_location::current()); 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 53bf203f3..6ebb8cca4 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 a3125ce89285b79406b72c64a148a752bf313a95 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:11 +1300 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 bce73af3248354d98f275c450b2d207352c8bf3a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:12 +1300 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 f4ad93454257f2902704b87daaa53a216bb153ac Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:12 +1300 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 | 52 +++++------ 4 files changed, 166 insertions(+), 227 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 ef05b4512..c4e830fd2 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"); @@ -826,7 +829,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) { @@ -839,6 +842,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; @@ -861,7 +866,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; @@ -888,16 +894,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; @@ -905,15 +911,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; @@ -922,16 +928,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) { @@ -952,7 +958,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 6ebb8cca4..b82b7d89b 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -76,57 +76,57 @@ 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("-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("-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 b9485ce0947de7ba410dc64a6f89a698f724ecf0 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:12 +1300 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 c4e830fd2..3fe5e3fd7 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -695,6 +695,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() { @@ -1125,11 +1142,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") { @@ -1149,22 +1162,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 9eb1c220d..967003000 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -68,11 +68,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 39b7ccd3a..5a773159f 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 a898ade47369b5928febc7a857b46475cfb9c71d Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:12 +1300 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 3fe5e3fd7..4df3474be 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -844,7 +844,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(); @@ -984,7 +983,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"; @@ -1008,8 +1009,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) { @@ -1028,7 +1027,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 024bfcdc53d70651019bfd479a8b544fcd9e964a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:12 +1300 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 5733a80d6..3e6b957f3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -108,13 +108,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 bb2ef6bd2..f38c08b99 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 598b41406c8250a0adbc5d53525c0d8dec4d84ec Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:12 +1300 Subject: [PATCH 38/59] Docs: Don't force READTHEDOCS on local readthedocs detection should only trigger on actual readthedocs builds. --- docs/source/conf.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3e6b957f3..ffa6bc9b0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -18,13 +18,14 @@ html_theme_options: dict[str] = { } # try to fix the readthedocs detection -html_context: dict[str] = { - "READTHEDOCS": True, - "display_github": True, - "github_user": "YosysHQ", - "github_repo": "yosys", - "slug": "yosys", -} +if os.getenv("READTHEDOCS"): + html_context: dict[str] = { + "READTHEDOCS": True, + "display_github": True, + "github_user": "YosysHQ", + "github_repo": "yosys", + "slug": "yosys", + } # override source_branch if not main git_slug = os.getenv("READTHEDOCS_VERSION_NAME") From a74aeb13010c5b0c072a28ad55b02ac40567d152 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:12 +1300 Subject: [PATCH 39/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 4df3474be..a853576f2 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -989,8 +989,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 d1d419cb51564824770f81c1c417e0a74c51df04 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:12 +1300 Subject: [PATCH 40/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 bca6a5ec6..8948175d0 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 f81d492c8..9bbe0da47 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" @@ -485,6 +486,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 53f009e40..3d3234395 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 07b6a355539226671c85cf3758729b9f91c494d6 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:13 +1300 Subject: [PATCH 41/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 97b9a4c0954e9f1f2ca30803379686f81d0e07bf Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:13 +1300 Subject: [PATCH 42/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 b4ccaf8c089692dc009f3fbe10fe82031718d6b0 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:13 +1300 Subject: [PATCH 43/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 753b418a8b55cad5458494aa53b321815f8d4fd5 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:47 +1300 Subject: [PATCH 44/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 bbcadf712..177cd46d9 100644 --- a/Makefile +++ b/Makefile @@ -1028,6 +1028,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 @@ -1063,7 +1072,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 6f96f9c37338e72744cc1776ce0b7bedebb7732a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:47 +1300 Subject: [PATCH 45/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 40b207d62556df93565ff6404d4601789e2c4721 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:47 +1300 Subject: [PATCH 46/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 435e3d5fc1066581b273c7e2c2013829d65d829c Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:47 +1300 Subject: [PATCH 47/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 cc35dcc5f..3bdf5da98 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 3277e1608..11319024f 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 197bd9319..95933bf4c 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 aec4c964b..73b56cc59 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 @@ -1034,6 +1033,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---| @@ -1631,6 +1635,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---| @@ -1739,6 +1748,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 82b5c6bcf..15abafb2a 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 7e51b6cb1..c46aa8a0a 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 @@ -369,6 +370,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 39ed4a7a8..f7d8cefbe 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 9dd68bd00..2a851c1a2 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 fb944ca0fb3d3daeb9ede5267aff024745ff1784 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:47 +1300 Subject: [PATCH 48/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 b1c9097a121e160af0e4b12a3777182e658745dc Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 49/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 a2433ba34bb1ec235ccfe12610b93bc5da22a9d3 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 50/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 a853576f2..df3e1d453 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -974,7 +974,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 ebd6d5f85bcc2a4e2df826dbb3faf96dfbd983c7 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 51/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 | 34 +++--- 7 files changed, 117 insertions(+), 148 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 df3e1d453..411280058 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -947,13 +947,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 b82b7d89b..1903dceda 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,11 +116,11 @@ struct ChformalPass : public Pass { "reachable SVA statement corresponds to an active enable signal." ); #endif - modes_group->option("-assert2assume"); - 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("-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 b9f0fabff0343f9c0267e056f2cf20f02f1ed6e9 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 52/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 45225d13914d2e7bb89d1d6c3fbcc0ce90884e36 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 53/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 1161dfd274943ac70f9cc5f0c895dddd90553562 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 54/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 a1941963d..e3fd58a01 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 925b00dd94e3c48a4d35e3d23b5f7e5e41307c56 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 55/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 9966c5cd2d9c3529c65e1054681bacbd4d6c8394 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 56/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 84dbdb4b6bac9c297c57a6b10b737c6aee1ea051 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:48 +1300 Subject: [PATCH 57/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 4e1d16744..13d4ad1b7 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 032ab8e4442871cb062149fdc2edd1820dcdac01 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:34:40 +1300 Subject: [PATCH 58/59] Drop .rst dump --- Makefile | 8 ---- kernel/register.cc | 103 --------------------------------------------- 2 files changed, 111 deletions(-) diff --git a/Makefile b/Makefile index 177cd46d9..a07311ecd 100644 --- a/Makefile +++ b/Makefile @@ -1002,14 +1002,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 -rc 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 411280058..8ae8b9faf 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -737,98 +737,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(); @@ -1153,17 +1061,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 7d7c32d0563cf6c69d052020dc72868e09c96874 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:41:29 +1300 Subject: [PATCH 59/59] Makefile: Fix chformal.cc copy --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a07311ecd..faff73c1f 100644 --- a/Makefile +++ b/Makefile @@ -1024,7 +1024,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