diff --git a/README.md b/README.md index 32ecd8205..22f44c601 100644 --- a/README.md +++ b/README.md @@ -370,6 +370,11 @@ 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 estimated cost of the module as a number of basic gate instances. See + the help message of command `keep_hierarchy` which interprets this + attribute. + - The ``init`` attribute on wires is set by the frontend when a register is initialized "FPGA-style" with ``reg foo = val``. It can be used during synthesis to add the necessary reset logic. @@ -591,7 +596,6 @@ Non-standard or SystemVerilog features for formal verification ``@(posedge )`` or ``@(negedge )`` when ```` is marked with the ``(* gclk *)`` Verilog attribute. - Supported features from SystemVerilog ===================================== diff --git a/passes/hierarchy/keep_hierarchy.cc b/passes/hierarchy/keep_hierarchy.cc index 6b947e673..bfae9fa38 100644 --- a/passes/hierarchy/keep_hierarchy.cc +++ b/passes/hierarchy/keep_hierarchy.cc @@ -23,20 +23,80 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +struct ThresholdHierarchyKeeping { + Design *design; + CellCosts costs; + dict done; + pool in_progress; + uint64_t threshold; + + ThresholdHierarchyKeeping(Design *design, uint64_t threshold) + : design(design), costs(design), threshold(threshold) {} + + uint64_t visit(RTLIL::Module *module) { + if (module->has_attribute(ID(gate_cost_equivalent))) + return module->attributes[ID(gate_cost_equivalent)].as_int(); + + if (module->has_attribute(ID(keep_hierarchy))) + return 0; + + if (module->get_blackbox_attribute()) + log_error("Missing cost information on instanced blackbox %s\n", log_id(module)); + + if (done.count(module)) + return done.at(module); + + if (in_progress.count(module)) + log_error("Circular hierarchy\n"); + in_progress.insert(module); + + uint64_t size = 0; + module->has_processes_warn(); + + for (auto cell : module->cells()) { + if (!cell->type.isPublic()) { + size += costs.get(cell); + } else { + RTLIL::Module *submodule = design->module(cell->type); + if (!submodule) + log_error("Hierarchy contains unknown module '%s' (instanced as %s in %s)\n", + log_id(cell->type), log_id(cell), log_id(module)); + size += visit(submodule); + } + } + + if (size > threshold) { + log("Keeping %s (estimated size above threshold: %llu > %llu).\n", log_id(module), size, threshold); + module->set_bool_attribute(ID::keep_hierarchy); + size = 0; + } + + in_progress.erase(module); + done[module] = size; + return size; + } +}; + struct KeepHierarchyPass : public Pass { - KeepHierarchyPass() : Pass("keep_hierarchy", "add the keep_hierarchy attribute") {} + KeepHierarchyPass() : Pass("keep_hierarchy", "selectively add the keep_hierarchy attribute") {} void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" keep_hierarchy [options]\n"); + log(" keep_hierarchy [options] [selection]\n"); log("\n"); log("Add the keep_hierarchy attribute.\n"); log("\n"); log(" -min_cost \n"); - log(" only add the attribute to modules estimated to have more\n"); - log(" than gates after simple techmapping. Intended\n"); - log(" for tuning trade-offs between quality and yosys runtime.\n"); + log(" only add the attribute to modules estimated to have more than \n"); + log(" gates after simple techmapping. Intended for tuning trade-offs between\n"); + log(" quality and yosys runtime.\n"); + log("\n"); + log(" When evaluating a module's cost, gates which are within a submodule\n"); + log(" which is marked with the keep_hierarchy attribute are not counted\n"); + log(" towards the upper module's cost. This applies to both when the attribute\n"); + log(" was added by this command or was pre-existing.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { @@ -54,16 +114,15 @@ struct KeepHierarchyPass : public Pass { } extra_args(args, argidx, design); - CellCosts costs(design); + if (min_cost) { + RTLIL::Module *top = design->top_module(); + if (!top) + log_cmd_error("'-min_cost' mode requires a single top module in the design\n"); - for (auto module : design->selected_modules()) { - if (min_cost) { - unsigned int cost = costs.get(module); - if (cost > min_cost) { - log("Marking %s (module too big: %d > %d).\n", log_id(module), cost, min_cost); - module->set_bool_attribute(ID::keep_hierarchy); - } - } else { + ThresholdHierarchyKeeping worker(design, min_cost); + worker.visit(top); + } else { + for (auto module : design->selected_modules()) { log("Marking %s.\n", log_id(module)); module->set_bool_attribute(ID::keep_hierarchy); } diff --git a/tests/various/keep_hierarchy.ys b/tests/various/keep_hierarchy.ys new file mode 100644 index 000000000..3f509dc64 --- /dev/null +++ b/tests/various/keep_hierarchy.ys @@ -0,0 +1,53 @@ +read_verilog <