From 6edf9c86cb26c6422d6c62ee61ebfbb522a42595 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 3 Dec 2024 17:36:00 +0100 Subject: [PATCH] libparse: add LibertyMergedCells, enable multiple -liberty args for dfflibmap and clockgate --- passes/techmap/clockgate.cc | 41 +++++++------- passes/techmap/dfflibmap.cc | 89 ++++++++++++++----------------- passes/techmap/libparse.cc | 8 +-- passes/techmap/libparse.h | 37 +++++++++++-- tests/techmap/clockgate.ys | 43 +++++++++++++++ tests/techmap/clockgate_neg.lib | 55 +++++++++++++++++++ tests/techmap/clockgate_pos.lib | 55 +++++++++++++++++++ tests/techmap/dfflibmap.ys | 10 ++++ tests/techmap/dfflibmap_dffn.lib | 23 ++++++++ tests/techmap/dfflibmap_dffsr.lib | 33 ++++++++++++ 10 files changed, 317 insertions(+), 77 deletions(-) create mode 100644 tests/techmap/clockgate_neg.lib create mode 100644 tests/techmap/clockgate_pos.lib create mode 100644 tests/techmap/dfflibmap_dffn.lib create mode 100644 tests/techmap/dfflibmap_dffsr.lib diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index 1def5235b..44fdc7e47 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -40,29 +40,15 @@ ClockGateCell icg_from_arg(std::string& name, std::string& str) { } static std::pair, std::optional> - find_icgs(std::string filename, std::vector const& dont_use_cells) { - std::ifstream f; - f.open(filename.c_str()); - if (f.fail()) - log_cmd_error("Can't open liberty file `%s': %s\n", filename.c_str(), strerror(errno)); - LibertyParser libparser(f); - f.close(); - auto ast = libparser.ast; - + find_icgs(std::vector cells, std::vector const& dont_use_cells) { // We will pick the most suitable ICG absed on tie_lo count and area struct ICGRankable : public ClockGateCell { double area; }; std::optional best_pos; std::optional best_neg; - if (ast->id != "library") - log_error("Format error in liberty file.\n"); - // This is a lot of boilerplate, isn't it? - for (auto cell : ast->children) + for (auto cell : cells) { - if (cell->id != "cell" || cell->args.size() != 1) - continue; - const LibertyAst *dn = cell->find("dont_use"); if (dn != nullptr && dn->value == "true") continue; @@ -223,7 +209,7 @@ struct ClockgatePass : public Pass { log(" cell with ports named , , .\n"); log(" The ICG's clock enable pin must be active high.\n"); log(" -liberty \n"); - log(" If specified, ICGs will be selected from the liberty file\n"); + log(" If specified, ICGs will be selected from the liberty files\n"); log(" if available. Priority is given to cells with fewer tie_lo\n"); log(" inputs and smaller size. This removes the need to manually\n"); log(" specify -pos or -neg and -tie_lo.\n"); @@ -281,7 +267,7 @@ struct ClockgatePass : public Pass { std::optional pos_icg_desc; std::optional neg_icg_desc; std::vector tie_lo_pins; - std::string liberty_file; + std::vector liberty_files; std::vector dont_use_cells; int min_net_size = 0; @@ -304,8 +290,9 @@ struct ClockgatePass : public Pass { continue; } if (args[argidx] == "-liberty" && argidx+1 < args.size()) { - liberty_file = args[++argidx]; + std::string liberty_file = args[++argidx]; rewrite_filename(liberty_file); + liberty_files.push_back(liberty_file); continue; } if (args[argidx] == "-dont_use" && argidx+1 < args.size()) { @@ -319,10 +306,20 @@ struct ClockgatePass : public Pass { break; } - if (!liberty_file.empty()) + if (!liberty_files.empty()) { + LibertyMergedCells merged; + for (auto path : liberty_files) { + std::ifstream f; + f.open(path.c_str()); + if (f.fail()) + log_cmd_error("Can't open liberty file `%s': %s\n", path.c_str(), strerror(errno)); + LibertyParser p(f); + merged.merge(p); + f.close(); + } std::tie(pos_icg_desc, neg_icg_desc) = - find_icgs(liberty_file, dont_use_cells); - else { + find_icgs(merged.cells, dont_use_cells); + } else { for (auto pin : tie_lo_pins) { if (pos_icg_desc) pos_icg_desc->tie_lo_pins.push_back(pin); diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index b6c340c00..b475cdd2d 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -229,7 +229,7 @@ static bool parse_pin(const LibertyAst *cell, const LibertyAst *attr, std::strin return false; } -static void find_cell(const LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool has_enable, bool enapol, std::vector &dont_use_cells) +static void find_cell(std::vector cells, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool has_enable, bool enapol, std::vector &dont_use_cells) { const LibertyAst *best_cell = nullptr; std::map best_cell_ports; @@ -237,14 +237,8 @@ static void find_cell(const LibertyAst *ast, IdString cell_type, bool clkpol, bo bool best_cell_noninv = false; double best_cell_area = 0; - if (ast->id != "library") - log_error("Format error in liberty file.\n"); - - for (auto cell : ast->children) + for (auto cell : cells) { - if (cell->id != "cell" || cell->args.size() != 1) - continue; - const LibertyAst *dn = cell->find("dont_use"); if (dn != nullptr && dn->value == "true") continue; @@ -355,7 +349,7 @@ static void find_cell(const LibertyAst *ast, IdString cell_type, bool clkpol, bo } } -static void find_cell_sr(const LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, bool has_enable, bool enapol, std::vector &dont_use_cells) +static void find_cell_sr(std::vector cells, IdString cell_type, bool clkpol, bool setpol, bool clrpol, bool has_enable, bool enapol, std::vector &dont_use_cells) { const LibertyAst *best_cell = nullptr; std::map best_cell_ports; @@ -365,14 +359,8 @@ static void find_cell_sr(const LibertyAst *ast, IdString cell_type, bool clkpol, log_assert(!enapol && "set/reset cell with enable is unimplemented due to lack of cells for testing"); - if (ast->id != "library") - log_error("Format error in liberty file.\n"); - - for (auto cell : ast->children) + for (auto cell : cells) { - if (cell->id != "cell" || cell->args.size() != 1) - continue; - const LibertyAst *dn = cell->find("dont_use"); if (dn != nullptr && dn->value == "true") continue; @@ -561,7 +549,7 @@ struct DfflibmapPass : public Pass { log(" dfflibmap [-prepare] [-map-only] [-info] [-dont_use ] -liberty [selection]\n"); log("\n"); log("Map internal flip-flop cells to the flip-flop cells in the technology\n"); - log("library specified in the given liberty file.\n"); + log("library specified in the given liberty files.\n"); log("\n"); log("This pass may add inverters as needed. Therefore it is recommended to\n"); log("first run this pass and then map the logic paths to the target technology.\n"); @@ -590,11 +578,11 @@ struct DfflibmapPass : public Pass { log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); log_push(); - std::string liberty_file; bool prepare_mode = false; bool map_only_mode = false; bool info_mode = false; + std::vector liberty_files; std::vector dont_use_cells; size_t argidx; @@ -602,8 +590,9 @@ struct DfflibmapPass : public Pass { { std::string arg = args[argidx]; if (arg == "-liberty" && argidx+1 < args.size()) { - liberty_file = args[++argidx]; + std::string liberty_file = args[++argidx]; rewrite_filename(liberty_file); + liberty_files.push_back(liberty_file); continue; } if (arg == "-prepare") { @@ -636,41 +625,45 @@ struct DfflibmapPass : public Pass { if (modes > 1) log_cmd_error("Only one of -prepare, -map-only, or -info options should be given!\n"); - if (liberty_file.empty()) + if (liberty_files.empty()) log_cmd_error("Missing `-liberty liberty_file' option!\n"); - std::ifstream f; - f.open(liberty_file.c_str()); - if (f.fail()) - log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); - LibertyParser libparser(f); - f.close(); + LibertyMergedCells merged; + for (auto path : liberty_files) { + std::ifstream f; + f.open(path.c_str()); + if (f.fail()) + log_cmd_error("Can't open liberty file `%s': %s\n", path.c_str(), strerror(errno)); + LibertyParser p(f); + merged.merge(p); + f.close(); + } - find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_N_), false, false, false, false, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_P_), true, false, false, false, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_NN0_), false, true, false, false, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_NN1_), false, true, false, true, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_NP0_), false, true, true, false, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_NP1_), false, true, true, true, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_PN0_), true, true, false, false, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_PN1_), true, true, false, true, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_PP0_), true, true, true, false, false, false, dont_use_cells); + find_cell(merged.cells, ID($_DFF_PP1_), true, true, true, true, false, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFFE_NN_), false, false, false, false, true, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFFE_NP_), false, false, false, false, true, true, dont_use_cells); - find_cell(libparser.ast, ID($_DFFE_PN_), true, false, false, false, true, false, dont_use_cells); - find_cell(libparser.ast, ID($_DFFE_PP_), true, false, false, false, true, true, dont_use_cells); + find_cell(merged.cells, ID($_DFFE_NN_), false, false, false, false, true, false, dont_use_cells); + find_cell(merged.cells, ID($_DFFE_NP_), false, false, false, false, true, true, dont_use_cells); + find_cell(merged.cells, ID($_DFFE_PN_), true, false, false, false, true, false, dont_use_cells); + find_cell(merged.cells, ID($_DFFE_PP_), true, false, false, false, true, true, dont_use_cells); - find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, false, false, dont_use_cells); - find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, false, false, dont_use_cells); - find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, false, false, dont_use_cells); - find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, false, false, dont_use_cells); - find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, false, false, dont_use_cells); - find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, false, false, dont_use_cells); - find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, false, false, dont_use_cells); - find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, false, false, dont_use_cells); + find_cell_sr(merged.cells, ID($_DFFSR_NNN_), false, false, false, false, false, dont_use_cells); + find_cell_sr(merged.cells, ID($_DFFSR_NNP_), false, false, true, false, false, dont_use_cells); + find_cell_sr(merged.cells, ID($_DFFSR_NPN_), false, true, false, false, false, dont_use_cells); + find_cell_sr(merged.cells, ID($_DFFSR_NPP_), false, true, true, false, false, dont_use_cells); + find_cell_sr(merged.cells, ID($_DFFSR_PNN_), true, false, false, false, false, dont_use_cells); + find_cell_sr(merged.cells, ID($_DFFSR_PNP_), true, false, true, false, false, dont_use_cells); + find_cell_sr(merged.cells, ID($_DFFSR_PPN_), true, true, false, false, false, dont_use_cells); + find_cell_sr(merged.cells, ID($_DFFSR_PPP_), true, true, true, false, false, dont_use_cells); log(" final dff cell mappings:\n"); logmap_all(); diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 29174c10f..06dd6288e 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -503,12 +503,12 @@ LibertyAst *LibertyParser::parse() #ifndef FILTERLIB -void LibertyParser::error() +void LibertyParser::error() const { log_error("Syntax error in liberty file on line %d.\n", line); } -void LibertyParser::error(const std::string &str) +void LibertyParser::error(const std::string &str) const { std::stringstream ss; ss << "Syntax error in liberty file on line " << line << ".\n"; @@ -518,13 +518,13 @@ void LibertyParser::error(const std::string &str) #else -void LibertyParser::error() +void LibertyParser::error() const { fprintf(stderr, "Syntax error in liberty file on line %d.\n", line); exit(1); } -void LibertyParser::error(const std::string &str) +void LibertyParser::error(const std::string &str) const { std::stringstream ss; ss << "Syntax error in liberty file on line " << line << ".\n"; diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 3c56dee60..fef569927 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -86,8 +86,10 @@ namespace Yosys bool eval(dict& values); }; + class LibertyMergedCells; class LibertyParser { + friend class LibertyMergedCells; private: std::istream &f; int line; @@ -98,10 +100,10 @@ namespace Yosys anything else is a single character. */ int lexer(std::string &str); - + LibertyAst *parse(); - void error(); - void error(const std::string &str); + void error() const; + void error(const std::string &str) const; public: const LibertyAst *ast; @@ -109,6 +111,35 @@ namespace Yosys LibertyParser(std::istream &f) : f(f), line(1), ast(parse()) {} ~LibertyParser() { if (ast) delete ast; } }; + + class LibertyMergedCells + { + std::vector asts; + + public: + std::vector cells; + void merge(LibertyParser &parser) + { + if (parser.ast) { + const LibertyAst *ast = parser.ast; + asts.push_back(ast); + // The parser no longer owns its top level ast, but we do. + // sketchy zone + parser.ast = nullptr; + if (ast->id != "library") + parser.error("Top level entity isn't \"library\".\n"); + for (const LibertyAst *cell : ast->children) + if (cell->id == "cell" && cell->args.size() == 1) + cells.push_back(cell); + } + } + ~LibertyMergedCells() + { + for (auto ast : asts) + delete ast; + } + }; + } #endif diff --git a/tests/techmap/clockgate.ys b/tests/techmap/clockgate.ys index 5bcabff00..da0f9dc42 100644 --- a/tests/techmap/clockgate.ys +++ b/tests/techmap/clockgate.ys @@ -235,6 +235,49 @@ select -module dffe_11 -assert-count 0 t:\$_NOT_ #------------------------------------------------------------------------------ +# test multiple liberty files to behave the same way +design -load before +clockgate -liberty clockgate_pos.lib -liberty clockgate_neg.lib + +# rising edge ICGs +select -module dffe_00 -assert-count 0 t:\\pos_small +select -module dffe_01 -assert-count 0 t:\\pos_small + +select -module dffe_10 -assert-count 1 t:\\pos_small +select -module dffe_11 -assert-count 1 t:\\pos_small + +# falling edge ICGs +select -module dffe_00 -assert-count 1 t:\\neg_small +select -module dffe_01 -assert-count 1 t:\\neg_small + +select -module dffe_10 -assert-count 0 t:\\neg_small +select -module dffe_11 -assert-count 0 t:\\neg_small + +# and nothing else +select -module dffe_00 -assert-count 0 t:\\pos_big +select -module dffe_01 -assert-count 0 t:\\pos_big +select -module dffe_10 -assert-count 0 t:\\pos_big +select -module dffe_11 -assert-count 0 t:\\pos_big +select -module dffe_00 -assert-count 0 t:\\pos_small_tielo +select -module dffe_01 -assert-count 0 t:\\pos_small_tielo +select -module dffe_10 -assert-count 0 t:\\pos_small_tielo +select -module dffe_11 -assert-count 0 t:\\pos_small_tielo +select -module dffe_00 -assert-count 0 t:\\neg_big +select -module dffe_01 -assert-count 0 t:\\neg_big +select -module dffe_10 -assert-count 0 t:\\neg_big +select -module dffe_11 -assert-count 0 t:\\neg_big +select -module dffe_00 -assert-count 0 t:\\neg_small_tielo +select -module dffe_01 -assert-count 0 t:\\neg_small_tielo +select -module dffe_10 -assert-count 0 t:\\neg_small_tielo +select -module dffe_11 -assert-count 0 t:\\neg_small_tielo + +# if necessary, EN is inverted, since the given ICG +# is assumed to have an active-high EN +select -module dffe_10 -assert-count 1 t:\$_NOT_ +select -module dffe_11 -assert-count 0 t:\$_NOT_ + +#------------------------------------------------------------------------------ + design -load before clockgate -liberty clockgate.lib -dont_use pos_small -dont_use neg_small diff --git a/tests/techmap/clockgate_neg.lib b/tests/techmap/clockgate_neg.lib new file mode 100644 index 000000000..5068f9c9a --- /dev/null +++ b/tests/techmap/clockgate_neg.lib @@ -0,0 +1,55 @@ +library(test) { + /* Integrated clock gating cells */ + cell (neg_big) { + area : 10; + clock_gating_integrated_cell : latch_negedge; + pin (GCLK) { + clock_gate_out_pin : true; + direction : output; + } + pin (CLK) { + clock_gate_clock_pin : true; + direction : input; + } + pin (CE) { + clock_gate_enable_pin : true; + direction : input; + } + } + cell (neg_small_tielo) { + area : 1; + clock_gating_integrated_cell : latch_negedge_precontrol; + pin (GCLK) { + clock_gate_out_pin : true; + direction : output; + } + pin (CLK) { + clock_gate_clock_pin : true; + direction : input; + } + pin (CE) { + clock_gate_enable_pin : true; + direction : input; + } + pin (SE) { + clock_gate_test_pin : true; + direction : input; + } + } + cell (neg_small) { + area : 1; + clock_gating_integrated_cell : latch_negedge; + pin (GCLK) { + clock_gate_out_pin : true; + direction : output; + } + pin (CLK) { + clock_gate_clock_pin : true; + direction : input; + } + pin (CE) { + clock_gate_enable_pin : true; + direction : input; + } + } +} \ No newline at end of file diff --git a/tests/techmap/clockgate_pos.lib b/tests/techmap/clockgate_pos.lib new file mode 100644 index 000000000..bab9e7cc7 --- /dev/null +++ b/tests/techmap/clockgate_pos.lib @@ -0,0 +1,55 @@ +library(test) { + /* Integrated clock gating cells */ + cell (pos_small_tielo) { + area : 1; + clock_gating_integrated_cell : latch_posedge_precontrol; + pin (GCLK) { + clock_gate_out_pin : true; + direction : output; + } + pin (CLK) { + clock_gate_clock_pin : true; + direction : input; + } + pin (CE) { + clock_gate_enable_pin : true; + direction : input; + } + pin (SE) { + clock_gate_test_pin : true; + direction : input; + } + } + cell (pos_big) { + area : 10; + clock_gating_integrated_cell : latch_posedge; + pin (GCLK) { + clock_gate_out_pin : true; + direction : output; + } + pin (CLK) { + clock_gate_clock_pin : true; + direction : input; + } + pin (CE) { + clock_gate_enable_pin : true; + direction : input; + } + } + cell (pos_small) { + area : 1; + clock_gating_integrated_cell : latch_posedge; + pin (GCLK) { + clock_gate_out_pin : true; + direction : output; + } + pin (CLK) { + clock_gate_clock_pin : true; + direction : input; + } + pin (CE) { + clock_gate_enable_pin : true; + direction : input; + } + } +} \ No newline at end of file diff --git a/tests/techmap/dfflibmap.ys b/tests/techmap/dfflibmap.ys index e8b125456..e9ba31969 100644 --- a/tests/techmap/dfflibmap.ys +++ b/tests/techmap/dfflibmap.ys @@ -59,6 +59,16 @@ select -assert-count 1 t:dffn select -assert-count 4 t:dffsr select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i +design -load orig +dfflibmap -prepare -liberty dfflibmap_dffn.lib -liberty dfflibmap_dffsr.lib +dfflibmap -map-only -liberty dfflibmap_dffn.lib -liberty dfflibmap_dffsr.lib +clean + +select -assert-count 4 t:$_NOT_ +select -assert-count 1 t:dffn +select -assert-count 4 t:dffsr +select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i + design -load orig dfflibmap -liberty dfflibmap.lib -dont_use *ffn clean diff --git a/tests/techmap/dfflibmap_dffn.lib b/tests/techmap/dfflibmap_dffn.lib new file mode 100644 index 000000000..fc38fc8bf --- /dev/null +++ b/tests/techmap/dfflibmap_dffn.lib @@ -0,0 +1,23 @@ +library(test) { + cell (dffn) { + area : 6; + ff("IQ", "IQN") { + next_state : "D"; + clocked_on : "!CLK"; + } + pin(D) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(Q) { + direction: output; + function : "IQ"; + } + pin(QN) { + direction: output; + function : "IQN"; + } + } +} diff --git a/tests/techmap/dfflibmap_dffsr.lib b/tests/techmap/dfflibmap_dffsr.lib new file mode 100644 index 000000000..e735dae1a --- /dev/null +++ b/tests/techmap/dfflibmap_dffsr.lib @@ -0,0 +1,33 @@ +library(test) { + cell (dffsr) { + area : 6; + ff("IQ", "IQN") { + next_state : "D"; + clocked_on : "CLK"; + clear : "CLEAR"; + preset : "PRESET"; + clear_preset_var1 : L; + clear_preset_var2 : L; + } + pin(D) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(CLEAR) { + direction : input; + } + pin(PRESET) { + direction : input; + } + pin(Q) { + direction: output; + function : "IQ"; + } + pin(QN) { + direction: output; + function : "IQN"; + } + } +}