From 7854d5ba217788bb66881237feac8ba2748758b9 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 18 Dec 2018 14:38:44 +0800 Subject: [PATCH 01/71] anlogic: fix dbits of Anlogic Eagle DRAM16X4 The dbits of DRAM16X4 is wrong set to 2, which leads to waste of DRAM bits. Fix the dbits number in the RAM configuration. Signed-off-by: Icenowy Zheng --- techlibs/anlogic/drams.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/anlogic/drams.txt b/techlibs/anlogic/drams.txt index 2bff14a03..eb94775ae 100644 --- a/techlibs/anlogic/drams.txt +++ b/techlibs/anlogic/drams.txt @@ -1,7 +1,7 @@ bram $__ANLOGIC_DRAM16X4 init 0 abits 4 - dbits 2 + dbits 4 groups 2 ports 1 1 wrmode 0 1 From fec8b3c81ffcf1f8a27e79250c05b6ea91571bbe Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 18 Dec 2018 15:37:43 +0800 Subject: [PATCH 02/71] Add "dffinit -strinit high low" On some platforms the string to initialize DFF might not be "high" and "low", e.g. with Anlogic TD it's "SET" and "RESET". Add a "-strinit" parameter for dffinit to allow specify the strings used for high and low. Signed-off-by: Icenowy Zheng --- passes/techmap/dffinit.cc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index a8eecc970..4fa15a35b 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -43,6 +43,11 @@ struct DffinitPass : public Pass { log(" initial value of 1 or 0. (multi-bit values are not supported in this\n"); log(" mode.)\n"); log("\n"); + log(" -strinit \n"); + log(" use string values in the command line to represent a single-bit\n"); + log(" initial value of 1 or 0. (multi-bit values are not supported in this\n"); + log(" mode.)\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { @@ -50,11 +55,20 @@ struct DffinitPass : public Pass { dict> ff_types; bool highlow_mode = false; + std::string high_string, low_string; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-highlow") { highlow_mode = true; + high_string = "high"; + low_string = "low"; + continue; + } + if (args[argidx] == "-strinit" && argidx+2 < args.size()) { + highlow_mode = true; + high_string = args[++argidx]; + low_string = args[++argidx]; continue; } if (args[argidx] == "-ff" && argidx+3 < args.size()) { @@ -121,9 +135,9 @@ struct DffinitPass : public Pass { log_error("Multi-bit init value for %s.%s.%s is incompatible with -highlow mode.\n", log_id(module), log_id(cell), log_id(it.second)); if (value[0] == State::S1) - value = Const("high"); + value = Const(high_string); else - value = Const("low"); + value = Const(low_string); } log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second), From 256fb8c95c3b974fb636f515297191a6fc70b356 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 18 Dec 2018 23:10:40 +0800 Subject: [PATCH 03/71] Add "dffinit -noreinit" parameter Sometimes the FF cell might be initialized during the map process, e.g. some FPGA platforms (Anlogic Eagle and Lattice ECP5 for example) has only a "SR" pin for a FF for async reset, that resets the FF to the initial value, which means the async reset value should be set as the initial value. In this case the DFFINIT pass shouldn't reinitialize it to a different value, which will lead to error. Add a "-noreinit" parameter for the safeguard. If a FF is not technically initialized before DFFINIT pass, the default value should be set to x. Signed-off-by: Icenowy Zheng --- passes/techmap/dffinit.cc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index 4fa15a35b..48390488e 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -48,13 +48,18 @@ struct DffinitPass : public Pass { log(" initial value of 1 or 0. (multi-bit values are not supported in this\n"); log(" mode.)\n"); log("\n"); + log(" -noreinit\n"); + log(" fail if the FF cell has already a defined initial value set in other\n"); + log(" passes and the initial value of the net it drives is not equal to\n"); + log(" the already defined initial value.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n"); dict> ff_types; - bool highlow_mode = false; + bool highlow_mode = false, noreinit = false; std::string high_string, low_string; size_t argidx; @@ -78,6 +83,10 @@ struct DffinitPass : public Pass { ff_types[cell_name][output_port] = init_param; continue; } + if (args[argidx] == "-noreinit") { + noreinit = true; + continue; + } break; } extra_args(args, argidx, design); @@ -126,6 +135,10 @@ struct DffinitPass : public Pass { continue; while (GetSize(value.bits) <= i) value.bits.push_back(State::S0); + if (noreinit && value.bits[i] != State::Sx && value.bits[i] != init_bits.at(sig[i])) + log_error("Trying to assign a different init value for %s.%s.%s which technically " + "have a conflicted init value.\n", + log_id(module), log_id(cell), log_id(it.second)); value.bits[i] = init_bits.at(sig[i]); cleanup_bits.insert(sig[i]); } From 4bf8ac728cbebabf2333f2a15b9e52c6af8b806a Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 18 Dec 2018 15:39:02 +0800 Subject: [PATCH 04/71] anlogic: set the init value of DFFs As dffinit has already supported for different initialization strings for DFFs and check for re-initialization, initialization of Anlogic DFFs are now ready to go. Support for set the init values of Anlogic DFFs. Signed-off-by: Icenowy Zheng --- techlibs/anlogic/cells_map.v | 28 ++++++++++++++-------------- techlibs/anlogic/synth_anlogic.cc | 1 + 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/techlibs/anlogic/cells_map.v b/techlibs/anlogic/cells_map.v index 36b920ef0..98b8d46ef 100644 --- a/techlibs/anlogic/cells_map.v +++ b/techlibs/anlogic/cells_map.v @@ -1,19 +1,19 @@ -module \$_DFF_N_ (input D, C, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(1'b0)); endmodule -module \$_DFF_P_ (input D, C, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(1'b0)); endmodule +module \$_DFF_N_ (input D, C, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(1'b0)); endmodule +module \$_DFF_P_ (input D, C, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(1'b0)); endmodule -module \$_DFFE_NN_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(E), .sr(1'b0)); endmodule -module \$_DFFE_NP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(E), .sr(1'b0)); endmodule -module \$_DFFE_PN_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(E), .sr(1'b0)); endmodule -module \$_DFFE_PP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(E), .sr(1'b0)); endmodule +module \$_DFFE_NN_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(E), .sr(1'b0)); endmodule +module \$_DFFE_NP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(E), .sr(1'b0)); endmodule +module \$_DFFE_PN_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(E), .sr(1'b0)); endmodule +module \$_DFFE_PP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(E), .sr(1'b0)); endmodule -module \$_DFF_NN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule -module \$_DFF_NN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("SET"), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule -module \$_DFF_NP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule -module \$_DFF_NP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("SET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule -module \$_DFF_PN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C) , .ce(1'b1), .sr(R)); endmodule -module \$_DFF_PN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("SET"), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule -module \$_DFF_PP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule -module \$_DFF_PP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("SET"), .SRMUX("SR"), . SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_NN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_NN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_NP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_NP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_PN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C) , .ce(1'b1), .sr(R)); endmodule +module \$_DFF_PN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_PP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_PP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), . SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule `ifndef NO_LUT module \$lut (A, Y); diff --git a/techlibs/anlogic/synth_anlogic.cc b/techlibs/anlogic/synth_anlogic.cc index a4eafeddf..492a830b9 100644 --- a/techlibs/anlogic/synth_anlogic.cc +++ b/techlibs/anlogic/synth_anlogic.cc @@ -170,6 +170,7 @@ struct SynthAnlogicPass : public ScriptPass { run("dffsr2dff"); run("techmap -D NO_LUT -map +/anlogic/cells_map.v"); + run("dffinit -strinit SET RESET -ff AL_MAP_SEQ q REGSET -noreinit"); run("opt_expr -mux_undef"); run("simplemap"); } From 23bb77867f56e966195d99d1d89b45d510d0b92d Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 18 Dec 2018 20:02:39 +0100 Subject: [PATCH 05/71] Minor style fixes Signed-off-by: Clifford Wolf --- backends/btor/btor.cc | 2 +- backends/btor/test_cells.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 backends/btor/test_cells.sh diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index d3fb9b858..53359bd7b 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -134,7 +134,7 @@ struct BtorWorker btorf_push(log_id(cell)); if (cell->type.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor", "$shl", "$sshl", "$shr", "$sshr", "$shift", "$shiftx", - "$concat", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) + "$concat", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) { string btor_op; if (cell->type == "$add") btor_op = "add"; diff --git a/backends/btor/test_cells.sh b/backends/btor/test_cells.sh old mode 100755 new mode 100644 From c9513c695a76124ef1155343e3c058767c418051 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Wed, 19 Dec 2018 09:36:53 +0800 Subject: [PATCH 06/71] Anlogic: let LUT5/6 have more cost than LUT4- According to the datasheet of Anlogic Eagle FPGAs, The LUTs natively in an Anlogic FPGA is LUT4 (in MSLICEs) and "Enhanced LUT5" (in LSLICEs). An "Enhanced LUT5" can be divided into two LUT4s. So a LUT5 will cost around 2x resource of a LUT4, and a LUT6 will cost 2x resource of a LUT5. Change the -lut parameter passed to the abc command to pass this cost info to the ABC process. Signed-off-by: Icenowy Zheng --- techlibs/anlogic/synth_anlogic.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/anlogic/synth_anlogic.cc b/techlibs/anlogic/synth_anlogic.cc index a4eafeddf..9c44599ea 100644 --- a/techlibs/anlogic/synth_anlogic.cc +++ b/techlibs/anlogic/synth_anlogic.cc @@ -176,7 +176,7 @@ struct SynthAnlogicPass : public ScriptPass if (check_label("map_luts")) { - run("abc -lut 6"); + run("abc -lut 4:6"); run("clean"); } From 3993ba71f7eb663fba73c0da4e35183b076168dd Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Wed, 19 Dec 2018 10:23:58 +0800 Subject: [PATCH 07/71] anlogic: fix Makefile.inc During the addition of DRAM inferring support, the installation of eagle_bb.v is accidentally removed. Fix this issue. Signed-off-by: Icenowy Zheng --- techlibs/anlogic/Makefile.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/techlibs/anlogic/Makefile.inc b/techlibs/anlogic/Makefile.inc index 59be83fd0..f37b5e7e9 100644 --- a/techlibs/anlogic/Makefile.inc +++ b/techlibs/anlogic/Makefile.inc @@ -5,5 +5,6 @@ OBJS += techlibs/anlogic/anlogic_eqn.o $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_map.v)) $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/arith_map.v)) $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_sim.v)) +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/eagle_bb.v)) $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams.txt)) $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams_map.v)) From 90d00182cfe358438d777f2ca7abacb4c6a2733c Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Wed, 19 Dec 2018 10:18:47 +0800 Subject: [PATCH 08/71] anlogic: implement DRAM initialization As the TD tool doesn't accept the DRAM cell to contain unknown values in the initial value, the initialzation support of DRAM is previously skipped. Now add the support by add a new pass to determine unknown values in the initial value. Signed-off-by: Icenowy Zheng --- techlibs/anlogic/Makefile.inc | 2 + techlibs/anlogic/anlogic_determine_init.cc | 72 ++++++++++++++++++++++ techlibs/anlogic/dram_init_16x4.vh | 16 +++++ techlibs/anlogic/drams.txt | 2 +- techlibs/anlogic/drams_map.v | 5 +- techlibs/anlogic/synth_anlogic.cc | 1 + 6 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 techlibs/anlogic/anlogic_determine_init.cc create mode 100644 techlibs/anlogic/dram_init_16x4.vh diff --git a/techlibs/anlogic/Makefile.inc b/techlibs/anlogic/Makefile.inc index f37b5e7e9..67cf9cf10 100644 --- a/techlibs/anlogic/Makefile.inc +++ b/techlibs/anlogic/Makefile.inc @@ -1,6 +1,7 @@ OBJS += techlibs/anlogic/synth_anlogic.o OBJS += techlibs/anlogic/anlogic_eqn.o +OBJS += techlibs/anlogic/anlogic_determine_init.o $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_map.v)) $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/arith_map.v)) @@ -8,3 +9,4 @@ $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_sim.v)) $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/eagle_bb.v)) $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams.txt)) $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams_map.v)) +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/dram_init_16x4.vh)) diff --git a/techlibs/anlogic/anlogic_determine_init.cc b/techlibs/anlogic/anlogic_determine_init.cc new file mode 100644 index 000000000..34b1d4f8a --- /dev/null +++ b/techlibs/anlogic/anlogic_determine_init.cc @@ -0,0 +1,72 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 Icenowy Zheng + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct AnlogicDetermineInitPass : public Pass { + AnlogicDetermineInitPass() : Pass("anlogic_determine_init", "Anlogic: Determine the init value of cells") { } + void help() YS_OVERRIDE + { + log("\n"); + log(" anlogic_determine_init [selection]\n"); + log("\n"); + log("Determine the init value of cells that doesn't allow unknown init value.\n"); + log("\n"); + } + + Const determine_init(Const init) + { + for (int i = 0; i < GetSize(init); i++) { + if (init[i] != State::S0 && init[i] != State::S1) + init[i] = State::S0; + } + + return init; + } + + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ANLOGIC_DETERMINE_INIT pass (determine init value for cells).\n"); + + extra_args(args, args.size(), design); + + size_t cnt = 0; + for (auto module : design->selected_modules()) + { + for (auto cell : module->selected_cells()) + { + if (cell->type == "\\EG_LOGIC_DRAM16X4") + { + cell->setParam("\\INIT_D0", determine_init(cell->getParam("\\INIT_D0"))); + cell->setParam("\\INIT_D1", determine_init(cell->getParam("\\INIT_D1"))); + cell->setParam("\\INIT_D2", determine_init(cell->getParam("\\INIT_D2"))); + cell->setParam("\\INIT_D3", determine_init(cell->getParam("\\INIT_D3"))); + cnt++; + } + } + } + log_header(design, "Updated %lu cells with determined init value.\n", cnt); + } +} AnlogicDetermineInitPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/anlogic/dram_init_16x4.vh b/techlibs/anlogic/dram_init_16x4.vh new file mode 100644 index 000000000..32fb1578c --- /dev/null +++ b/techlibs/anlogic/dram_init_16x4.vh @@ -0,0 +1,16 @@ +.INIT_D0({INIT[15*4+0], INIT[14*4+0], INIT[13*4+0], INIT[12*4+0], + INIT[11*4+0], INIT[10*4+0], INIT[9*4+0], INIT[8*4+0], + INIT[7*4+0], INIT[6*4+0], INIT[5*4+0], INIT[4*4+0], + INIT[3*4+0], INIT[2*4+0], INIT[1*4+0], INIT[0*4+0]}), +.INIT_D1({INIT[15*4+1], INIT[14*4+1], INIT[13*4+1], INIT[12*4+1], + INIT[11*4+1], INIT[10*4+1], INIT[9*4+1], INIT[8*4+1], + INIT[7*4+1], INIT[6*4+1], INIT[5*4+1], INIT[4*4+1], + INIT[3*4+1], INIT[2*4+1], INIT[1*4+1], INIT[0*4+1]}), +.INIT_D2({INIT[15*4+2], INIT[14*4+2], INIT[13*4+2], INIT[12*4+2], + INIT[11*4+2], INIT[10*4+2], INIT[9*4+2], INIT[8*4+2], + INIT[7*4+2], INIT[6*4+2], INIT[5*4+2], INIT[4*4+2], + INIT[3*4+2], INIT[2*4+2], INIT[1*4+2], INIT[0*4+2]}), +.INIT_D3({INIT[15*4+3], INIT[14*4+3], INIT[13*4+3], INIT[12*4+3], + INIT[11*4+3], INIT[10*4+3], INIT[9*4+3], INIT[8*4+3], + INIT[7*4+3], INIT[6*4+3], INIT[5*4+3], INIT[4*4+3], + INIT[3*4+3], INIT[2*4+3], INIT[1*4+3], INIT[0*4+3]}) diff --git a/techlibs/anlogic/drams.txt b/techlibs/anlogic/drams.txt index eb94775ae..4e903c0a2 100644 --- a/techlibs/anlogic/drams.txt +++ b/techlibs/anlogic/drams.txt @@ -1,5 +1,5 @@ bram $__ANLOGIC_DRAM16X4 - init 0 + init 1 abits 4 dbits 4 groups 2 diff --git a/techlibs/anlogic/drams_map.v b/techlibs/anlogic/drams_map.v index 87cbb6a45..084e2a25f 100644 --- a/techlibs/anlogic/drams_map.v +++ b/techlibs/anlogic/drams_map.v @@ -1,4 +1,5 @@ module \$__ANLOGIC_DRAM16X4 (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); + parameter [63:0]INIT = 64'bx; input CLK1; input [3:0] A1ADDR; @@ -8,7 +9,9 @@ module \$__ANLOGIC_DRAM16X4 (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); input [3:0] B1DATA; input B1EN; - EG_LOGIC_DRAM16X4 _TECHMAP_REPLACE_ ( + EG_LOGIC_DRAM16X4 #( + `include "dram_init_16x4.vh" + ) _TECHMAP_REPLACE_ ( .di(B1DATA), .waddr(B1ADDR), .wclk(CLK1), diff --git a/techlibs/anlogic/synth_anlogic.cc b/techlibs/anlogic/synth_anlogic.cc index 9c44599ea..b34f4788f 100644 --- a/techlibs/anlogic/synth_anlogic.cc +++ b/techlibs/anlogic/synth_anlogic.cc @@ -154,6 +154,7 @@ struct SynthAnlogicPass : public ScriptPass { run("memory_bram -rules +/anlogic/drams.txt"); run("techmap -map +/anlogic/drams_map.v"); + run("anlogic_determine_init"); } if (check_label("fine")) From a9ff81dd82d347b5ed867f142e61271aa40d85ee Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 20 Dec 2018 04:37:28 +0000 Subject: [PATCH 09/71] manual: document $meminit cell and memory_* passes. --- manual/CHAPTER_CellLib.tex | 27 +++++++++++++++++++++------ manual/CHAPTER_Overview.tex | 4 ++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 277e89328..6589bb2e7 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -211,14 +211,15 @@ Add information about {\tt \$sr} cells (set-reset flip-flops) and d-type latches \subsection{Memories} \label{sec:memcells} -Memories are either represented using RTLIL::Memory objects and {\tt \$memrd} and {\tt \$memwr} cells -or simply by using {\tt \$mem} cells. +Memories are either represented using RTLIL::Memory objects, {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit} +cells, or by {\tt \$mem} cells alone. In the first alternative the RTLIL::Memory objects hold the general metadata for the memory (bit width, size in number of words, etc.) and for each port a {\tt \$memrd} (read port) or {\tt \$memwr} (write port) cell is created. Having individual cells for read and write ports has the advantage that they can be consolidated using resource sharing passes. In some cases this drastically reduces the number of required -ports on the memory cell. +ports on the memory cell. In this alternative, memory initialization data is represented by {\tt \$meminit} cells, +which allow delaying constant folding for initialization addresses and data until after the frontend finishes. The {\tt \$memrd} cells have a clock input \B{CLK}, an enable input \B{EN}, an address input \B{ADDR}, and a data output \B{DATA}. They also have the @@ -273,6 +274,15 @@ edge if this parameter is {\tt 1'b0}. The cell with the higher integer value in this parameter wins a write conflict. \end{itemize} +The {\tt \$meminit} cells have an address input \B{ADDR} and a data input \B{DATA}. Both of the inputs +must resolve to a constant for synthesis to succeed. If two {\tt \$meminit} cells have the same +\B{MEMID} parameter and \B{ADDR} input, the contents of that memory at that address is undefined. + +\begin{itemize} +\item \B{MEMID} \\ +The name of the RTLIL::Memory object that is associated with this initialization cell. +\end{itemize} + The HDL frontend models a memory using RTLIL::Memory objects and asynchronous {\tt \$memrd} and {\tt \$memwr} cells. The {\tt memory} pass (i.e.~its various sub-passes) migrates {\tt \$dff} cells into the {\tt \$memrd} and {\tt \$memwr} cells making them synchronous, then @@ -295,6 +305,9 @@ The number of address bits. \item \B{WIDTH} \\ The number of data bits per word. +\item \B{INIT} \\ +The initial memory contents. + \item \B{RD\_PORTS} \\ The number of read ports on this memory cell. @@ -345,9 +358,11 @@ This input is \B{WR\_PORTS}*\B{ABITS} bits wide, containing all address signals This input is \B{WR\_PORTS}*\B{WIDTH} bits wide, containing all data signals for the write ports. \end{itemize} -The {\tt techmap} pass can be used to manually map {\tt \$mem} cells to -specialized memory cells on the target architecture, such as block ram resources -on an FPGA. +The {\tt memory\_collect} pass can be used to convert discrete {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit} cells +belonging to the same memory to a single {\tt \$mem} cell, whereas the {\tt memory\_unpack} pass performs the inverse operation. +The {\tt memory\_dff} pass can combine asynchronous memory ports that are fed by or feeding registers into synchronous memory ports. +The {\tt memory\_bram} pass can be used to recognize {\tt \$mem} cells that can be implemented with a block RAM resource on an FPGA. +The {\tt memory\_map} pass can be used to implement {\tt \$mem} cells as basic logic: word-wide DFFs and address decoders. \subsection{Finite State Machines} diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex index 964875d57..2feb0f1cb 100644 --- a/manual/CHAPTER_Overview.tex +++ b/manual/CHAPTER_Overview.tex @@ -428,8 +428,8 @@ memory object has the following properties: All read accesses to the memory are transformed to {\tt \$memrd} cells and all write accesses to {\tt \$memwr} cells by the language frontend. These cells consist of independent read- and write-ports -to the memory. The \B{MEMID} parameter on these cells is used to link them together and to the -RTLIL::Memory object they belong to. +to the memory. Memory initialization is transformed to {\tt \$meminit} cells by the language frontend. +The \B{MEMID} parameter on these cells is used to link them together and to the RTLIL::Memory object they belong to. The rationale behind using separate cells for the individual ports versus creating a large multiport memory cell right in the language frontend is that From 2ca237e08665e4307da5bf40b7f3e17ca3e640db Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 20 Dec 2018 07:29:46 +0000 Subject: [PATCH 10/71] tcl: add support for passing arguments to scripts. --- kernel/yosys.cc | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/kernel/yosys.cc b/kernel/yosys.cc index f002955ad..6884305d9 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -166,7 +166,7 @@ std::string vstringf(const char *fmt, va_list ap) std::string string; char *str = NULL; -#if defined(_WIN32 )|| defined(__CYGWIN__) +#if defined(_WIN32 )|| defined(__CYGWIN__) int sz = 64, rc; while (1) { va_list apc; @@ -637,8 +637,9 @@ extern Tcl_Interp *yosys_get_tcl_interp() struct TclPass : public Pass { TclPass() : Pass("tcl", "execute a TCL script file") { } void help() YS_OVERRIDE { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" tcl \n"); + log(" tcl [args]\n"); log("\n"); log("This command executes the tcl commands in the specified file.\n"); log("Use 'yosys cmd' to run the yosys command 'cmd' from tcl.\n"); @@ -648,14 +649,24 @@ struct TclPass : public Pass { log("'proc' and 'rename' are wrapped to tcl commands 'procs' and 'renames'\n"); log("in order to avoid a name collision with the built in commands.\n"); log("\n"); + log("If any arguments are specified, these arguments are provided to the script via\n"); + log("the standard $argc and $argv variables.\n"); + log("\n"); } - void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { + void execute(std::vector args, RTLIL::Design *) YS_OVERRIDE { if (args.size() < 2) log_cmd_error("Missing script file.\n"); - if (args.size() > 2) - extra_args(args, 1, design, false); - if (Tcl_EvalFile(yosys_get_tcl_interp(), args[1].c_str()) != TCL_OK) - log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp())); + + std::vector script_args; + for (auto it = args.begin() + 2; it != args.end(); ++it) + script_args.push_back(Tcl_NewStringObj((*it).c_str(), (*it).size())); + + Tcl_Interp *interp = yosys_get_tcl_interp(); + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(script_args.size()), 0); + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(script_args.size(), script_args.data()), 0); + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(args[1].c_str(), args[1].size()), 0); + if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK) + log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(interp)); } } TclPass; #endif From c04908c99740e616cae454aa6a5d7a3dd13af2fa Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 20 Dec 2018 07:59:40 +0000 Subject: [PATCH 11/71] manual: fix typos. --- manual/CHAPTER_CellLib.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 6589bb2e7..65a79020f 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -254,7 +254,7 @@ enable bit for each data bit), an address input \B{ADDR} and a data input \begin{itemize} \item \B{MEMID} \\ -The name of the RTLIL::Memory object that is associated with this read port. +The name of the RTLIL::Memory object that is associated with this write port. \item \B{ABITS} \\ The number of address bits (width of the \B{ADDR} input port). @@ -263,7 +263,7 @@ The number of address bits (width of the \B{ADDR} input port). The number of data bits (width of the \B{DATA} output port). \item \B{CLK\_ENABLE} \\ -When this parameter is non-zero, the clock is used. Otherwise this read port is asynchronous and +When this parameter is non-zero, the clock is used. Otherwise this write port is asynchronous and the \B{CLK} input is not used. \item \B{CLK\_POLARITY} \\ From 0c318e7db513bed1844f594ae780f854ed08e18f Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 21 Dec 2018 02:01:27 +0000 Subject: [PATCH 12/71] memory_collect: do not truncate 'x from \INIT. The semantics of an RTLIL constant that has less bits than its declared bit width is zero padding. Therefore, if the output of memory_collect will be used for simulation, truncating 'x from the end of \INIT will produce incorrect simulation results. --- passes/memory/memory_collect.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index 70d98713c..369fcc84e 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -184,9 +184,6 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory) mem->parameters["\\OFFSET"] = Const(memory->start_offset); mem->parameters["\\SIZE"] = Const(memory->size); mem->parameters["\\ABITS"] = Const(addr_bits); - - while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx) - init_data.bits.pop_back(); mem->parameters["\\INIT"] = init_data; log_assert(sig_wr_clk.size() == wr_ports); From 182d84ad54cff315e7afb07f5353a477e4c410f4 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 21 Dec 2018 01:26:08 +0000 Subject: [PATCH 13/71] manual: make description of $meminit ports match reality. --- manual/CHAPTER_CellLib.tex | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 65a79020f..d40a600ed 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -274,13 +274,25 @@ edge if this parameter is {\tt 1'b0}. The cell with the higher integer value in this parameter wins a write conflict. \end{itemize} -The {\tt \$meminit} cells have an address input \B{ADDR} and a data input \B{DATA}. Both of the inputs -must resolve to a constant for synthesis to succeed. If two {\tt \$meminit} cells have the same -\B{MEMID} parameter and \B{ADDR} input, the contents of that memory at that address is undefined. +The {\tt \$meminit} cells have an address input \B{ADDR} and a data input \B{DATA}, with the width +of the \B{DATA} port equal to \B{WIDTH} parameter times \B{WORDS} parameter. Both of the inputs +must resolve to a constant for synthesis to succeed. \begin{itemize} \item \B{MEMID} \\ The name of the RTLIL::Memory object that is associated with this initialization cell. + +\item \B{ABITS} \\ +The number of address bits (width of the \B{ADDR} input port). + +\item \B{WIDTH} \\ +The number of data bits per memory location. + +\item \B{WORDS} \\ +The number of consecutive memory locations initialized by this cell. + +\item \B{PRIORITY} \\ +The cell with the higher integer value in this parameter wins an initialization conflict. \end{itemize} The HDL frontend models a memory using RTLIL::Memory objects and asynchronous From b784440857d6b71f46ae8784cc3767962e4edcaa Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 22 Dec 2018 08:58:37 +0000 Subject: [PATCH 14/71] proc_clean: remove any empty cases at the end of the switch. Previously, only completely empty switches were removed. --- passes/proc/proc_clean.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index b9e43d1db..477d1ac60 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -77,18 +77,14 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did } else { - bool all_cases_are_empty = true; for (auto cs : sw->cases) { - if (cs->actions.size() != 0 || cs->switches.size() != 0) - all_cases_are_empty = false; if (max_depth != 0) proc_clean_case(cs, did_something, count, max_depth-1); } - if (all_cases_are_empty) { + while (!sw->cases.empty() && (sw->cases.back()->actions.empty() && sw->cases.back()->switches.empty())) { did_something = true; - for (auto cs : sw->cases) - delete cs; - sw->cases.clear(); + delete sw->cases.back(); + sw->cases.pop_back(); } } } From 18291c20d2b17689729d9c36b08967e928e4e32a Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 23 Dec 2018 09:04:23 +0000 Subject: [PATCH 15/71] proc_clean: remove any empty cases if all cases use all-def compare. --- kernel/rtlil.cc | 10 ++++++++++ kernel/rtlil.h | 4 ++++ passes/proc/proc_clean.cc | 34 ++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 14259f8ed..8404db5e9 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -3793,6 +3793,11 @@ RTLIL::CaseRule::~CaseRule() delete *it; } +bool RTLIL::CaseRule::empty() const +{ + return actions.empty() && switches.empty(); +} + RTLIL::CaseRule *RTLIL::CaseRule::clone() const { RTLIL::CaseRule *new_caserule = new RTLIL::CaseRule; @@ -3809,6 +3814,11 @@ RTLIL::SwitchRule::~SwitchRule() delete *it; } +bool RTLIL::SwitchRule::empty() const +{ + return cases.empty(); +} + RTLIL::SwitchRule *RTLIL::SwitchRule::clone() const { RTLIL::SwitchRule *new_switchrule = new RTLIL::SwitchRule; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 276540aa1..f877622aa 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1227,6 +1227,8 @@ struct RTLIL::CaseRule ~CaseRule(); void optimize(); + bool empty() const; + template void rewrite_sigspecs(T &functor); RTLIL::CaseRule *clone() const; }; @@ -1238,6 +1240,8 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject ~SwitchRule(); + bool empty() const; + template void rewrite_sigspecs(T &functor); RTLIL::SwitchRule *clone() const; }; diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 477d1ac60..3919e4b9c 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -77,14 +77,36 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did } else { - for (auto cs : sw->cases) { + bool all_fully_def = true; + for (auto cs : sw->cases) + { if (max_depth != 0) proc_clean_case(cs, did_something, count, max_depth-1); + for (auto cmp : cs->compare) + if (!cmp.is_fully_def()) + all_fully_def = false; } - while (!sw->cases.empty() && (sw->cases.back()->actions.empty() && sw->cases.back()->switches.empty())) { - did_something = true; - delete sw->cases.back(); - sw->cases.pop_back(); + if (all_fully_def) + { + for (auto cs = sw->cases.begin(); cs != sw->cases.end();) + { + if ((*cs)->empty()) + { + did_something = true; + delete *cs; + cs = sw->cases.erase(cs); + } + else ++cs; + } + } + else + { + while (!sw->cases.empty() && sw->cases.back()->empty()) + { + did_something = true; + delete sw->cases.back(); + sw->cases.pop_back(); + } } } } @@ -102,7 +124,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m } for (size_t i = 0; i < cs->switches.size(); i++) { RTLIL::SwitchRule *sw = cs->switches[i]; - if (sw->cases.size() == 0) { + if (sw->empty()) { cs->switches.erase(cs->switches.begin() + (i--)); did_something = true; delete sw; From 6dad1913779f729222f65e1098a4facb36c5837a Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 23 Dec 2018 15:45:09 +0100 Subject: [PATCH 16/71] Add "read_ilang -[no]overwrite" Signed-off-by: Clifford Wolf --- frontends/ilang/ilang_frontend.cc | 30 +++++++++++++++++++++++++++++- frontends/ilang/ilang_frontend.h | 2 ++ frontends/ilang/ilang_parser.y | 26 +++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc index d8783ac1d..6b302a796 100644 --- a/frontends/ilang/ilang_frontend.cc +++ b/frontends/ilang/ilang_frontend.cc @@ -44,11 +44,39 @@ struct IlangFrontend : public Frontend { log("Load modules from an ilang file to the current design. (ilang is a text\n"); log("representation of a design in yosys's internal format.)\n"); log("\n"); + log(" -nooverwrite\n"); + log(" ignore re-definitions of modules. (the default behavior is to\n"); + log(" create an error message if the existing module is not a blackbox\n"); + log(" module, and overwrite the existing module if it is a blackbox module.)\n"); + log("\n"); + log(" -overwrite\n"); + log(" overwrite existing modules with the same name\n"); + log("\n"); } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { + ILANG_FRONTEND::flag_nooverwrite = false; + ILANG_FRONTEND::flag_overwrite = false; + log_header(design, "Executing ILANG frontend.\n"); - extra_args(f, filename, args, 1); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-nooverwrite") { + ILANG_FRONTEND::flag_nooverwrite = true; + ILANG_FRONTEND::flag_overwrite = false; + continue; + } + if (arg == "-overwrite") { + ILANG_FRONTEND::flag_nooverwrite = false; + ILANG_FRONTEND::flag_overwrite = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + log("Input filename: %s\n", filename.c_str()); ILANG_FRONTEND::lexin = f; diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h index ad3ffec90..052dd4cb2 100644 --- a/frontends/ilang/ilang_frontend.h +++ b/frontends/ilang/ilang_frontend.h @@ -32,6 +32,8 @@ YOSYS_NAMESPACE_BEGIN namespace ILANG_FRONTEND { extern std::istream *lexin; extern RTLIL::Design *current_design; + extern bool flag_nooverwrite; + extern bool flag_overwrite; } YOSYS_NAMESPACE_END diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index b957ecd96..5bcc01f42 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -37,6 +37,8 @@ namespace ILANG_FRONTEND { std::vector*> switch_stack; std::vector case_stack; dict attrbuf; + bool flag_nooverwrite, flag_overwrite; + bool delete_current_module; } using namespace ILANG_FRONTEND; YOSYS_NAMESPACE_END @@ -93,18 +95,36 @@ design: module: TOK_MODULE TOK_ID EOL { - if (current_design->has($2)) - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); + delete_current_module = false; + if (current_design->has($2)) { + RTLIL::Module *existing_mod = current_design->module($2); + if (!flag_overwrite && attrbuf.count("\\blackbox") && attrbuf.at("\\blackbox").as_bool()) { + log("Ignoring blackbox re-definition of module %s.\n", $2); + delete_current_module = true; + } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); + } else if (flag_nooverwrite) { + log("Ignoring re-definition of module %s.\n", $2); + delete_current_module = true; + } else { + log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", $2); + current_design->remove(existing_mod); + } + } current_module = new RTLIL::Module; current_module->name = $2; current_module->attributes = attrbuf; - current_design->add(current_module); + if (!delete_current_module) + current_design->add(current_module); attrbuf.clear(); free($2); } module_body TOK_END { if (attrbuf.size() != 0) rtlil_frontend_ilang_yyerror("dangling attribute"); current_module->fixup_ports(); + if (delete_current_module) + delete current_module; + current_module = nullptr; } EOL; module_body: From 1b369442995ea3f83d0d0445bb34c8f8115c10a3 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 25 Dec 2018 22:47:46 +0800 Subject: [PATCH 17/71] anlogic: add latch cells Add latch cells to Anlogic cells replacement library by copying other FPGAs' latch code to it. Signed-off-by: Icenowy Zheng --- techlibs/anlogic/cells_map.v | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/techlibs/anlogic/cells_map.v b/techlibs/anlogic/cells_map.v index 36b920ef0..f54a81dcc 100644 --- a/techlibs/anlogic/cells_map.v +++ b/techlibs/anlogic/cells_map.v @@ -15,6 +15,18 @@ module \$_DFF_PN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REG module \$_DFF_PP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("RESET"), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule module \$_DFF_PP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET("SET"), .SRMUX("SR"), . SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule +module \$_DLATCH_N_ (E, D, Q); + wire [1023:0] _TECHMAP_DO_ = "simplemap; opt"; + input E, D; + output Q = !E ? D : Q; +endmodule + +module \$_DLATCH_P_ (E, D, Q); + wire [1023:0] _TECHMAP_DO_ = "simplemap; opt"; + input E, D; + output Q = E ? D : Q; +endmodule + `ifndef NO_LUT module \$lut (A, Y); parameter WIDTH = 0; From 99706b3bf48de121e58b76d4341d601f0bcc26cd Mon Sep 17 00:00:00 2001 From: Larry Doolittle Date: Fri, 28 Dec 2018 08:24:31 -0800 Subject: [PATCH 18/71] Squelch a little more trailing whitespace --- backends/simplec/test00_uut.v | 6 +++--- tests/sva/basic01.sv | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backends/simplec/test00_uut.v b/backends/simplec/test00_uut.v index 744dbe9e3..92329a6f9 100644 --- a/backends/simplec/test00_uut.v +++ b/backends/simplec/test00_uut.v @@ -3,12 +3,12 @@ module test(input [31:0] a, b, c, output [31:0] x, y, z, w); unit_y unit_y_inst (.a(a), .b(b), .c(c), .y(y)); assign z = a ^ b ^ c, w = z; endmodule - + module unit_x(input [31:0] a, b, c, output [31:0] x); assign x = (a & b) | c; endmodule - + module unit_y(input [31:0] a, b, c, output [31:0] y); assign y = a & (b | c); endmodule - + diff --git a/tests/sva/basic01.sv b/tests/sva/basic01.sv index 74ab93430..d5ad497dd 100644 --- a/tests/sva/basic01.sv +++ b/tests/sva/basic01.sv @@ -6,7 +6,7 @@ module top (input logic clock, ctrl); write <= ctrl; ready <= write; end - + a_rw: assert property ( @(posedge clock) !(read && write) ); `ifdef FAIL a_wr: assert property ( @(posedge clock) write |-> ready ); From ebe9351f82279f83ef16a24faac9853d40b3ece1 Mon Sep 17 00:00:00 2001 From: Larry Doolittle Date: Fri, 28 Dec 2018 08:21:53 -0800 Subject: [PATCH 19/71] Fix 7 instances of add_share_file to add_gen_share_file in techlibs/ecp5/Makefile.inc to permit out-of-tree builds --- techlibs/ecp5/Makefile.inc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/techlibs/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc index b23d5c70e..8df02be5f 100644 --- a/techlibs/ecp5/Makefile.inc +++ b/techlibs/ecp5/Makefile.inc @@ -34,11 +34,11 @@ techlibs/ecp5/bram_conn_4.vh: techlibs/ecp5/brams_connect.mk techlibs/ecp5/bram_conn_9.vh: techlibs/ecp5/brams_connect.mk techlibs/ecp5/bram_conn_18.vh: techlibs/ecp5/brams_connect.mk -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram_init_1_2_4.vh)) -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram_init_9_18_36.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_1_2_4.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_9_18_36.vh)) -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram_conn_1.vh)) -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram_conn_2.vh)) -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram_conn_4.vh)) -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram_conn_9.vh)) -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram_conn_18.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_1.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_2.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_4.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_9.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_18.vh)) From 0a840dd88334c382314db3aafc2f1da2a1969df2 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Mon, 31 Dec 2018 16:34:27 +0100 Subject: [PATCH 20/71] Fix handling of (* keep *) wires in wreduce Signed-off-by: Clifford Wolf --- passes/opt/wreduce.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 0164f58d6..8063b86a6 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -235,8 +235,11 @@ struct WreduceWorker } else { while (GetSize(sig) > 0) { - auto info = mi.query(sig[GetSize(sig)-1]); + auto bit = sig[GetSize(sig)-1]; + if (keep_bits.count(bit)) + break; + auto info = mi.query(bit); if (info->is_output || GetSize(info->ports) > 1) break; From 42c356c49c04a735fe27e672ff31452170f84b40 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 31 Dec 2018 23:53:23 +0000 Subject: [PATCH 21/71] opt_lut: eliminate LUTs evaluating to constants or inputs. --- passes/opt/opt_lut.cc | 81 +++++++++++++++++++++++++++++++++++++++ tests/opt/opt_lut_elim.il | 19 +++++++++ tests/opt/opt_lut_elim.ys | 3 ++ tests/opt/opt_lut_port.ys | 1 + 4 files changed, 104 insertions(+) create mode 100644 tests/opt/opt_lut_elim.il create mode 100644 tests/opt/opt_lut_elim.ys diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index be050c713..261af538f 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -187,6 +187,87 @@ struct OptLutWorker } show_stats_by_arity(); + log("\n"); + log("Eliminating LUTs.\n"); + for (auto lut : luts) + { + SigSpec lut_input = sigmap(lut->getPort("\\A")); + pool &lut_dlogic_inputs = luts_dlogic_inputs[lut]; + + vector lut_inputs; + for (auto &bit : lut_input) + { + if (bit.wire) + lut_inputs.push_back(sigmap(bit)); + } + + bool const0_match = true; + bool const1_match = true; + vector input_matches; + for (size_t i = 0; i < lut_inputs.size(); i++) + input_matches.push_back(true); + + for (int eval = 0; eval < 1 << lut_inputs.size(); eval++) + { + dict eval_inputs; + for (size_t i = 0; i < lut_inputs.size(); i++) + eval_inputs[lut_inputs[i]] = (eval >> i) & 1; + bool value = evaluate_lut(lut, eval_inputs); + if (value != 0) + const0_match = false; + if (value != 1) + const1_match = false; + for (size_t i = 0; i < lut_inputs.size(); i++) + { + if (value != eval_inputs[lut_inputs[i]]) + input_matches[i] = false; + } + } + + int input_match = -1; + for (size_t i = 0; i < lut_inputs.size(); i++) + if (input_matches[i]) + input_match = i; + + if (const0_match || const1_match || input_match != -1) + { + log("Found redundant cell %s.%s.\n", log_id(module), log_id(lut)); + + SigBit value; + if (const0_match) + { + log(" Cell evaluates constant 0.\n"); + value = State::S0; + } + if (const1_match) + { + log(" Cell evaluates constant 1.\n"); + value = State::S1; + } + if (input_match != -1) { + log(" Cell evaluates signal %s.\n", log_signal(lut_inputs[input_match])); + value = lut_inputs[input_match]; + } + + if (lut_dlogic_inputs.size()) + { + log(" Not eliminating cell (connected to dedicated logic).\n"); + } + else + { + SigSpec lut_output = lut->getPort("\\Y"); + module->connect(lut_output, value); + + module->remove(lut); + luts.erase(lut); + luts_arity.erase(lut); + luts_dlogics.erase(lut); + luts_dlogic_inputs.erase(lut); + } + } + } + show_stats_by_arity(); + log("\n"); log("Combining LUTs.\n"); pool worklist = luts; diff --git a/tests/opt/opt_lut_elim.il b/tests/opt/opt_lut_elim.il new file mode 100644 index 000000000..75675d983 --- /dev/null +++ b/tests/opt/opt_lut_elim.il @@ -0,0 +1,19 @@ +module \test + wire input 1 \i + + wire output 2 \o1 + cell $lut $1 + parameter \LUT 16'0110100110010110 + parameter \WIDTH 4 + connect \A { \i 3'000 } + connect \Y \o1 + end + + wire output 2 \o2 + cell $lut $2 + parameter \LUT 16'0110100010010110 + parameter \WIDTH 4 + connect \A { \i 3'000 } + connect \Y \o2 + end +end diff --git a/tests/opt/opt_lut_elim.ys b/tests/opt/opt_lut_elim.ys new file mode 100644 index 000000000..8e5e23aea --- /dev/null +++ b/tests/opt/opt_lut_elim.ys @@ -0,0 +1,3 @@ +read_ilang opt_lut_elim.il +opt_lut +select -assert-count 0 t:$lut diff --git a/tests/opt/opt_lut_port.ys b/tests/opt/opt_lut_port.ys index 51dfd988b..3cb4ecb23 100644 --- a/tests/opt/opt_lut_port.ys +++ b/tests/opt/opt_lut_port.ys @@ -1,2 +1,3 @@ read_ilang opt_lut_port.il +opt_lut select -assert-count 2 t:$lut From 8e53d2e0bf3584709cd320447ad5a89a80dcfd94 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 02:45:49 +0000 Subject: [PATCH 22/71] opt_expr: simplify any unsigned comparisons with all-0 and all-1. Before this commit, only unsigned comparisons with all-0 would be simplified. This commit also makes the code handling such comparisons to be more rigorous and not abort on unexpected input. --- passes/opt/opt_expr.cc | 86 +++++++++++++++++++++++++++++++-------- tests/opt/opt_expr_cmp.v | 11 +++++ tests/opt/opt_expr_cmp.ys | 4 ++ 3 files changed, 84 insertions(+), 17 deletions(-) create mode 100644 tests/opt/opt_expr_cmp.v create mode 100644 tests/opt/opt_expr_cmp.ys diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 610edc5e9..87597ce57 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1344,6 +1344,75 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + // simplify comparisons + // currently, only replaces comparisons with an extreme of the signal range with a constant + // TODO: since, unlike the following optimization, this one does not assume that both inputs to the cell are constant, + // it is more robust; the following optimization should be folded into this one at some point + if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le")) + { + IdString cmp_type = cell->type; + SigSpec var_sig = cell->getPort("\\A"); + SigSpec const_sig = cell->getPort("\\B"); + int var_width = cell->parameters["\\A_WIDTH"].as_int(); + int const_width = cell->parameters["\\B_WIDTH"].as_int(); + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + + if (!const_sig.is_fully_const()) + { + std::swap(var_sig, const_sig); + std::swap(var_width, const_width); + if (cmp_type == "$gt") + cmp_type = "$lt"; + else if (cmp_type == "$lt") + cmp_type = "$gt"; + else if (cmp_type == "$ge") + cmp_type = "$le"; + else if (cmp_type == "$le") + cmp_type = "$ge"; + } + + if (const_sig.is_fully_def() && const_sig.is_fully_const()) + { + const char *condition, *replacement; + SigSpec result_sig(State::S0, GetSize(cell->getPort("\\Y"))); + result_sig[0] = State::Sx; + + if (!is_signed) + { + if (const_sig.is_fully_zero() && cmp_type == "$lt") { + condition = "unsigned X<0"; + replacement = "constant 0"; + result_sig[0] = State::S0; + } + if (const_sig.is_fully_zero() && cmp_type == "$ge") { + condition = "unsigned X>=0"; + replacement = "constant 1"; + result_sig[0] = State::S1; + } + if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$gt") { + condition = "unsigned X>~0"; + replacement = "constant 0"; + result_sig[0] = State::S0; + } + if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$le") { + condition = "unsigned X<=~0"; + replacement = "constant 1"; + result_sig[0] = State::S1; + } + } + + if (result_sig.is_fully_def()) + { + log("Replacing %s cell `%s' (implementing %s) with %s.\n", + log_id(cell->type), log_id(cell), condition, replacement); + module->connect(cell->getPort("\\Y"), result_sig); + module->remove(cell); + did_something = true; + goto next_cell; + } + } + } + // replace a<0 or a>=0 with the top bit of a if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le")) { @@ -1405,23 +1474,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } else if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false) { - if (sigConst.is_fully_zero()) { - RTLIL::SigSpec a_prime(RTLIL::State::S0, GetSize(cell->getPort("\\Y"))); - if (is_lt) { - log("Replacing %s cell `%s' (implementing unsigned X<0) with constant false.\n", - log_id(cell->type), log_id(cell)); - a_prime[0] = RTLIL::State::S0; - } else { - log("Replacing %s cell `%s' (implementing unsigned X>=0) with constant true.\n", - log_id(cell->type), log_id(cell)); - a_prime[0] = RTLIL::State::S1; - } - module->connect(cell->getPort("\\Y"), a_prime); - module->remove(cell); - did_something = true; - goto next_cell; - } - int const_bit_set = get_onehot_bit_index(sigConst); if (const_bit_set >= 0 && const_bit_set < width) { int bit_set = const_bit_set; diff --git a/tests/opt/opt_expr_cmp.v b/tests/opt/opt_expr_cmp.v new file mode 100644 index 000000000..7eb65d531 --- /dev/null +++ b/tests/opt/opt_expr_cmp.v @@ -0,0 +1,11 @@ +module top(...); + input [3:0] a; + output o1 = 4'b0000 > a; + output o2 = 4'b0000 <= a; + output o3 = 4'b1111 < a; + output o4 = 4'b1111 >= a; + output o5 = a < 4'b0000; + output o6 = a >= 4'b0000; + output o7 = a > 4'b1111; + output o8 = a <= 4'b1111; +endmodule diff --git a/tests/opt/opt_expr_cmp.ys b/tests/opt/opt_expr_cmp.ys new file mode 100644 index 000000000..214ce8b11 --- /dev/null +++ b/tests/opt/opt_expr_cmp.ys @@ -0,0 +1,4 @@ +read_verilog opt_expr_cmp.v +equiv_opt -assert opt_expr -fine +design -load postopt +select -assert-count 0 t:$gt t:$ge t:$lt t:$le From 9e9846a6ead700756fbd7a5e6c72ccb424006934 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 03:01:25 +0000 Subject: [PATCH 23/71] opt_expr: refactor simplification of signed X>=0 and X<0. NFCI. --- passes/opt/opt_expr.cc | 50 +++++++++++++++++++++------------------- tests/opt/opt_expr_cmp.v | 22 +++++++++++------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 87597ce57..989c949ce 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1373,38 +1373,59 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (const_sig.is_fully_def() && const_sig.is_fully_const()) { - const char *condition, *replacement; + std::string condition, replacement; SigSpec result_sig(State::S0, GetSize(cell->getPort("\\Y"))); - result_sig[0] = State::Sx; + bool replace = false; if (!is_signed) - { + { /* unsigned */ if (const_sig.is_fully_zero() && cmp_type == "$lt") { condition = "unsigned X<0"; replacement = "constant 0"; result_sig[0] = State::S0; + replace = true; } if (const_sig.is_fully_zero() && cmp_type == "$ge") { condition = "unsigned X>=0"; replacement = "constant 1"; result_sig[0] = State::S1; + replace = true; } if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$gt") { condition = "unsigned X>~0"; replacement = "constant 0"; result_sig[0] = State::S0; + replace = true; } if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$le") { condition = "unsigned X<=~0"; replacement = "constant 1"; result_sig[0] = State::S1; + replace = true; + } + } + else + { /* signed */ + if (const_sig.is_fully_zero() && cmp_type == "$lt") + { + condition = "signed X<0"; + replacement = stringf("X[%d]", var_width - 1); + result_sig[0] = var_sig[var_width - 1]; + replace = true; + } + if (const_sig.is_fully_zero() && cmp_type == "$ge") + { + condition = "signed X>=0"; + replacement = stringf("X[%d]", var_width - 1); + module->addNot(NEW_ID, var_sig[var_width - 1], result_sig); + replace = true; } } - if (result_sig.is_fully_def()) + if (replace) { log("Replacing %s cell `%s' (implementing %s) with %s.\n", - log_id(cell->type), log_id(cell), condition, replacement); + log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str()); module->connect(cell->getPort("\\Y"), result_sig); module->remove(cell); did_something = true; @@ -1453,25 +1474,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } else log_abort(); - // replace a(signed) < 0 with the high bit of a - if (sigConst.is_fully_const() && sigConst.is_fully_zero() && var_signed == true) - { - RTLIL::SigSpec a_prime(RTLIL::State::S0, cell->parameters["\\Y_WIDTH"].as_int()); - a_prime[0] = sigVar[width - 1]; - if (is_lt) { - log("Replacing %s cell `%s' (implementing X<0) with X[%d]: %s\n", - log_id(cell->type), log_id(cell), width-1, log_signal(a_prime)); - module->connect(cell->getPort("\\Y"), a_prime); - module->remove(cell); - } else { - log("Replacing %s cell `%s' (implementing X>=0) with ~X[%d]: %s\n", - log_id(cell->type), log_id(cell), width-1, log_signal(a_prime)); - module->addNot(NEW_ID, a_prime, cell->getPort("\\Y")); - module->remove(cell); - } - did_something = true; - goto next_cell; - } else if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false) { int const_bit_set = get_onehot_bit_index(sigConst); diff --git a/tests/opt/opt_expr_cmp.v b/tests/opt/opt_expr_cmp.v index 7eb65d531..72372bdf0 100644 --- a/tests/opt/opt_expr_cmp.v +++ b/tests/opt/opt_expr_cmp.v @@ -1,11 +1,17 @@ module top(...); input [3:0] a; - output o1 = 4'b0000 > a; - output o2 = 4'b0000 <= a; - output o3 = 4'b1111 < a; - output o4 = 4'b1111 >= a; - output o5 = a < 4'b0000; - output o6 = a >= 4'b0000; - output o7 = a > 4'b1111; - output o8 = a <= 4'b1111; + + output o1_1 = 4'b0000 > a; + output o1_2 = 4'b0000 <= a; + output o1_3 = 4'b1111 < a; + output o1_4 = 4'b1111 >= a; + output o1_5 = a < 4'b0000; + output o1_6 = a >= 4'b0000; + output o1_7 = a > 4'b1111; + output o1_8 = a <= 4'b1111; + + output o2_1 = 4'sb0000 > $signed(a); + output o2_2 = 4'sb0000 <= $signed(a); + output o2_3 = $signed(a) < 4'sb0000; + output o2_4 = $signed(a) >= 4'sb0000; endmodule From 4fd458290c3da4c3f372f2e1fdd99829a9462a38 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 04:31:20 +0000 Subject: [PATCH 24/71] opt_expr: refactor simplification of unsigned X=onehot. NFCI. --- passes/opt/opt_expr.cc | 68 ++++++++++++++++++++++------------------ tests/opt/opt_expr_cmp.v | 5 +++ 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 989c949ce..9abbdc6ac 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1374,35 +1374,60 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (const_sig.is_fully_def() && const_sig.is_fully_const()) { std::string condition, replacement; - SigSpec result_sig(State::S0, GetSize(cell->getPort("\\Y"))); + SigSpec replace_sig(State::S0, GetSize(cell->getPort("\\Y"))); bool replace = false; + bool remove = false; if (!is_signed) { /* unsigned */ if (const_sig.is_fully_zero() && cmp_type == "$lt") { condition = "unsigned X<0"; replacement = "constant 0"; - result_sig[0] = State::S0; + replace_sig[0] = State::S0; replace = true; } if (const_sig.is_fully_zero() && cmp_type == "$ge") { condition = "unsigned X>=0"; replacement = "constant 1"; - result_sig[0] = State::S1; + replace_sig[0] = State::S1; replace = true; } if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$gt") { condition = "unsigned X>~0"; replacement = "constant 0"; - result_sig[0] = State::S0; + replace_sig[0] = State::S0; replace = true; } if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$le") { condition = "unsigned X<=~0"; replacement = "constant 1"; - result_sig[0] = State::S1; + replace_sig[0] = State::S1; replace = true; } + + int const_bit_set = get_onehot_bit_index(const_sig); + if (const_bit_set >= 0 && const_bit_set < var_width) + { + RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_set); + for (int i = const_bit_set; i < var_width; i++) { + var_high_sig[i - const_bit_set] = var_sig[i]; + } + + if (cmp_type == "$lt") + { + condition = stringf("unsigned X<%s", log_signal(const_sig)); + replacement = stringf("!X[%d:%d]", var_width - 1, const_bit_set); + module->addLogicNot(NEW_ID, var_high_sig, cell->getPort("\\Y")); + remove = true; + } + if (cmp_type == "$ge") + { + condition = stringf("unsigned X>=%s", log_signal(const_sig)); + replacement = stringf("|X[%d:%d]", var_width - 1, const_bit_set); + module->addReduceOr(NEW_ID, var_high_sig, cell->getPort("\\Y")); + remove = true; + } + } } else { /* signed */ @@ -1410,23 +1435,24 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons { condition = "signed X<0"; replacement = stringf("X[%d]", var_width - 1); - result_sig[0] = var_sig[var_width - 1]; + replace_sig[0] = var_sig[var_width - 1]; replace = true; } if (const_sig.is_fully_zero() && cmp_type == "$ge") { condition = "signed X>=0"; replacement = stringf("X[%d]", var_width - 1); - module->addNot(NEW_ID, var_sig[var_width - 1], result_sig); - replace = true; + module->addNot(NEW_ID, var_sig[var_width - 1], cell->getPort("\\Y")); + remove = true; } } - if (replace) + if (replace || remove) { log("Replacing %s cell `%s' (implementing %s) with %s.\n", log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str()); - module->connect(cell->getPort("\\Y"), result_sig); + if (replace) + module->connect(cell->getPort("\\Y"), replace_sig); module->remove(cell); did_something = true; goto next_cell; @@ -1477,26 +1503,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false) { int const_bit_set = get_onehot_bit_index(sigConst); - if (const_bit_set >= 0 && const_bit_set < width) { - int bit_set = const_bit_set; - RTLIL::SigSpec a_prime(RTLIL::State::S0, width - bit_set); - for (int i = bit_set; i < width; i++) { - a_prime[i - bit_set] = sigVar[i]; - } - if (is_lt) { - log("Replacing %s cell `%s' (implementing unsigned X<%s) with !X[%d:%d]: %s.\n", - log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime)); - module->addLogicNot(NEW_ID, a_prime, cell->getPort("\\Y")); - } else { - log("Replacing %s cell `%s' (implementing unsigned X>=%s) with |X[%d:%d]: %s.\n", - log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime)); - module->addReduceOr(NEW_ID, a_prime, cell->getPort("\\Y")); - } - module->remove(cell); - did_something = true; - goto next_cell; - } - else if(const_bit_set >= width && const_bit_set >= 0){ + if(const_bit_set >= width && const_bit_set >= 0){ RTLIL::SigSpec a_prime(RTLIL::State::S0, 1); if(is_lt){ a_prime[0] = RTLIL::State::S1; @@ -1509,7 +1516,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons module->remove(cell); did_something = true; goto next_cell; - } } } diff --git a/tests/opt/opt_expr_cmp.v b/tests/opt/opt_expr_cmp.v index 72372bdf0..500b15f1b 100644 --- a/tests/opt/opt_expr_cmp.v +++ b/tests/opt/opt_expr_cmp.v @@ -14,4 +14,9 @@ module top(...); output o2_2 = 4'sb0000 <= $signed(a); output o2_3 = $signed(a) < 4'sb0000; output o2_4 = $signed(a) >= 4'sb0000; + + output o3_1 = 4'b0100 > a; + output o3_2 = 4'b0100 <= a; + output o3_3 = a < 4'b0100; + output o3_4 = a >= 4'b0100; endmodule From a91892bba422bbe8de080a105a6fb4b44f96df1a Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 07:53:31 +0000 Subject: [PATCH 25/71] cmp2lut: new techmap pass. --- techlibs/common/Makefile.inc | 2 +- techlibs/common/cmp2lut.v | 105 +++++++++++++++++++++++++++++++++++ tests/lut/check_map.ys | 6 +- tests/lut/map_cmp.v | 29 ++++++++++ tests/lut/run-test.sh | 0 5 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 techlibs/common/cmp2lut.v create mode 100644 tests/lut/map_cmp.v mode change 100644 => 100755 tests/lut/run-test.sh diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index 70074f653..0e05620bc 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -26,5 +26,5 @@ $(eval $(call add_share_file,share,techlibs/common/pmux2mux.v)) $(eval $(call add_share_file,share,techlibs/common/adff2dff.v)) $(eval $(call add_share_file,share,techlibs/common/dff2ff.v)) $(eval $(call add_share_file,share,techlibs/common/gate2lut.v)) +$(eval $(call add_share_file,share,techlibs/common/cmp2lut.v)) $(eval $(call add_share_file,share,techlibs/common/cells.lib)) - diff --git a/techlibs/common/cmp2lut.v b/techlibs/common/cmp2lut.v new file mode 100644 index 000000000..8aa1eb957 --- /dev/null +++ b/techlibs/common/cmp2lut.v @@ -0,0 +1,105 @@ +// Certain arithmetic operations between a signal of width n and a constant can be directly mapped +// to a single k-LUT (where n <= k). This is preferable to normal alumacc techmapping process +// because for many targets, arithmetic techmapping creates hard logic (such as carry cells) which often +// cannot be optimized further. +// +// TODO: Currently, only comparisons with 1-bit output are mapped. Potentially, all arithmetic cells +// with n <= k inputs should be techmapped in this way, because this shortens the critical path +// from n to 1 by avoiding carry chains. + +(* techmap_celltype = "$eq $ne $lt $le $gt $ge" *) +module _90_lut_cmp_ (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +parameter _TECHMAP_CELLTYPE_ = ""; + +parameter _TECHMAP_CONSTMSK_A_ = 0; +parameter _TECHMAP_CONSTVAL_A_ = 0; +parameter _TECHMAP_CONSTMSK_B_ = 0; +parameter _TECHMAP_CONSTVAL_B_ = 0; + +function automatic integer gen_lut; + input integer width; + input integer operation; + input integer swap; + input integer sign; + input integer operand; + integer n, i_var, i_cst, lhs, rhs, o_bit; + begin + gen_lut = width'b0; + for (n = 0; n < (1 << width); n++) begin + if (sign) + i_var = n[width-1:0]; + else + i_var = n; + i_cst = operand; + if (swap) begin + lhs = i_cst; + rhs = i_var; + end else begin + lhs = i_var; + rhs = i_cst; + end + if (operation == 0) + o_bit = (lhs < rhs); + if (operation == 1) + o_bit = (lhs <= rhs); + if (operation == 2) + o_bit = (lhs > rhs); + if (operation == 3) + o_bit = (lhs >= rhs); + if (operation == 4) + o_bit = (lhs == rhs); + if (operation == 5) + o_bit = (lhs != rhs); + gen_lut = gen_lut | (o_bit << n); + end + end +endfunction + +generate + if (_TECHMAP_CELLTYPE_ == "$lt") + localparam operation = 0; + if (_TECHMAP_CELLTYPE_ == "$le") + localparam operation = 1; + if (_TECHMAP_CELLTYPE_ == "$gt") + localparam operation = 2; + if (_TECHMAP_CELLTYPE_ == "$ge") + localparam operation = 3; + if (_TECHMAP_CELLTYPE_ == "$eq") + localparam operation = 4; + if (_TECHMAP_CELLTYPE_ == "$ne") + localparam operation = 5; + + if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1) + wire _TECHMAP_FAIL_ = 1; + else if (&_TECHMAP_CONSTMSK_B_) + \$lut #( + .WIDTH(A_WIDTH), + .LUT({ gen_lut(A_WIDTH, operation, 0, A_SIGNED && B_SIGNED, _TECHMAP_CONSTVAL_B_) }) + ) _TECHMAP_REPLACE_ ( + .A(A), + .Y(Y) + ); + else if (&_TECHMAP_CONSTMSK_A_) + \$lut #( + .WIDTH(B_WIDTH), + .LUT({ gen_lut(B_WIDTH, operation, 1, A_SIGNED && B_SIGNED, _TECHMAP_CONSTVAL_A_) }) + ) _TECHMAP_REPLACE_ ( + .A(B), + .Y(Y) + ); + else + wire _TECHMAP_FAIL_ = 1; +endgenerate + +endmodule diff --git a/tests/lut/check_map.ys b/tests/lut/check_map.ys index dc0aaffc2..46854e82e 100644 --- a/tests/lut/check_map.ys +++ b/tests/lut/check_map.ys @@ -1,4 +1,6 @@ simplemap -equiv_opt -assert techmap -map +/gate2lut.v -D LUT_WIDTH=4 +equiv_opt -assert techmap -D LUT_WIDTH=4 -map +/cmp2lut.v design -load postopt -select -assert-count 1 t:$lut +equiv_opt -assert techmap -D LUT_WIDTH=4 -map +/gate2lut.v +design -load postopt +select -assert-count 0 t:* t:$lut %d diff --git a/tests/lut/map_cmp.v b/tests/lut/map_cmp.v new file mode 100644 index 000000000..5e413f894 --- /dev/null +++ b/tests/lut/map_cmp.v @@ -0,0 +1,29 @@ +module top(...); + input [3:0] a; + + output o1_1 = 4'b1010 <= a; + output o1_2 = 4'b1010 < a; + output o1_3 = 4'b1010 >= a; + output o1_4 = 4'b1010 > a; + output o1_5 = 4'b1010 == a; + output o1_6 = 4'b1010 != a; + + output o2_1 = a <= 4'b1010; + output o2_2 = a < 4'b1010; + output o2_3 = a >= 4'b1010; + output o2_4 = a > 4'b1010; + output o2_5 = a == 4'b1010; + output o2_6 = a != 4'b1010; + + output o3_1 = 4'sb0101 <= $signed(a); + output o3_2 = 4'sb0101 < $signed(a); + output o3_3 = 4'sb0101 >= $signed(a); + output o3_4 = 4'sb0101 > $signed(a); + output o3_5 = 4'sb0101 == $signed(a); + output o3_6 = 4'sb0101 != $signed(a); + + output o4_1 = $signed(a) <= 4'sb0000; + output o4_2 = $signed(a) < 4'sb0000; + output o4_3 = $signed(a) >= 4'sb0000; + output o4_4 = $signed(a) > 4'sb0000; +endmodule diff --git a/tests/lut/run-test.sh b/tests/lut/run-test.sh old mode 100644 new mode 100755 From fdff32dd731d92c854aef73ea9fad89635e82c0f Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 08:05:44 +0000 Subject: [PATCH 26/71] synth: improve script documentation. NFC. --- techlibs/common/synth.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index efb214759..5cadbba7a 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -186,19 +186,19 @@ struct SynthPass : public ScriptPass { run("proc"); if (help_mode || flatten) - run("flatten", "(if -flatten)"); + run("flatten", " (if -flatten)"); run("opt_expr"); run("opt_clean"); run("check"); run("opt"); run("wreduce"); if (!noalumacc) - run("alumacc"); + run("alumacc", " (unless -noalumacc)"); if (!noshare) - run("share"); + run("share", " (unless -noshare)"); run("opt"); if (!nofsm) - run("fsm" + fsm_opts); + run("fsm" + fsm_opts, " (unless -nofsm)"); run("opt -fast"); run("memory -nomap" + memory_opts); run("opt_clean"); @@ -214,8 +214,8 @@ struct SynthPass : public ScriptPass if (!noabc) { #ifdef YOSYS_ENABLE_ABC - run("abc -fast"); - run("opt -fast"); + run("abc -fast", "(unless -noabc)"); + run("opt -fast", "(unless -noabc)"); #endif } } From 18174202a957cb20137e13893ad002ac1b52b61b Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 08:25:03 +0000 Subject: [PATCH 27/71] synth: add k-LUT mode. --- techlibs/common/synth.cc | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 5cadbba7a..5ff813d18 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -51,6 +51,9 @@ struct SynthPass : public ScriptPass log(" -encfile \n"); log(" passed to 'fsm_recode' via 'fsm'\n"); log("\n"); + log(" -lut \n"); + log(" perform synthesis for a k-LUT architecture.\n"); + log("\n"); log(" -nofsm\n"); log(" do not run FSM optimization\n"); log("\n"); @@ -80,6 +83,7 @@ struct SynthPass : public ScriptPass string top_module, fsm_opts, memory_opts; bool autotop, flatten, noalumacc, nofsm, noabc, noshare; + int lut; void clear_flags() YS_OVERRIDE { @@ -89,6 +93,7 @@ struct SynthPass : public ScriptPass autotop = false; flatten = false; + lut = 0; noalumacc = false; nofsm = false; noabc = false; @@ -130,6 +135,10 @@ struct SynthPass : public ScriptPass flatten = true; continue; } + if (args[argidx] == "-lut") { + lut = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-nofsm") { nofsm = true; continue; @@ -192,6 +201,10 @@ struct SynthPass : public ScriptPass run("check"); run("opt"); run("wreduce"); + if (help_mode) + run("techmap -map +/cmp2lut.v", " (if -lut)"); + else + run(stringf("techmap -map +/cmp2lut.v -D LUT_WIDTH=%d", lut)); if (!noalumacc) run("alumacc", " (unless -noalumacc)"); if (!noshare) @@ -210,12 +223,33 @@ struct SynthPass : public ScriptPass run("memory_map"); run("opt -full"); run("techmap"); + if (help_mode) + { + run("techmap -map +/gate2lut.v", "(if -noabc and -lut)"); + run("clean; opt_lut", " (if -noabc and -lut)"); + } + else if (noabc && lut) + { + run(stringf("techmap -map +/gate2lut.v -D LUT_WIDTH=%d", lut)); + run("clean; opt_lut"); + } run("opt -fast"); if (!noabc) { #ifdef YOSYS_ENABLE_ABC - run("abc -fast", "(unless -noabc)"); - run("opt -fast", "(unless -noabc)"); + if (help_mode) + { + run("abc -fast", " (unless -noabc, unless -lut)"); + run("abc -fast -lut k", "(unless -noabc, if -lut)"); + } + else + { + if (lut) + run(stringf("abc -fast -lut %d", lut)); + else + run("abc -fast"); + } + run("opt -fast", " (unless -noabc)"); #endif } } From 17b283135658b84a0dfeec8b35f7d2bfe782be19 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 08:25:55 +0000 Subject: [PATCH 28/71] synth_ice40: use 4-LUT coarse synthesis mode. --- techlibs/ice40/synth_ice40.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index c2aed873b..4c4fdf7be 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -226,7 +226,7 @@ struct SynthIce40Pass : public ScriptPass if (check_label("coarse")) { - run("synth -run coarse"); + run("synth -lut 4 -run coarse"); } if (!nobram && check_label("bram", "(skip if -nobram)")) From f7363ac5086ccb8bdb97dcdbfed890c54e1ed153 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 08:40:01 +0000 Subject: [PATCH 29/71] opt_lut: count eliminated cells, and set opt.did_something for them. --- passes/opt/opt_lut.cc | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index 261af538f..8c564b0ed 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -36,7 +36,7 @@ struct OptLutWorker dict> luts_dlogics; dict> luts_dlogic_inputs; - int combined_count = 0; + int eliminated_count = 0, combined_count = 0; bool evaluate_lut(RTLIL::Cell *lut, dict inputs) { @@ -191,6 +191,12 @@ struct OptLutWorker log("Eliminating LUTs.\n"); for (auto lut : luts) { + if (limit == 0) + { + log("Limit reached.\n"); + break; + } + SigSpec lut_input = sigmap(lut->getPort("\\A")); pool &lut_dlogic_inputs = luts_dlogic_inputs[lut]; @@ -263,6 +269,10 @@ struct OptLutWorker luts_arity.erase(lut); luts_dlogics.erase(lut); luts_dlogic_inputs.erase(lut); + + eliminated_count++; + if (limit > 0) + limit--; } } } @@ -568,16 +578,20 @@ struct OptLutPass : public Pass { } extra_args(args, argidx, design); - int total_count = 0; + int eliminated_count = 0, combined_count = 0; for (auto module : design->selected_modules()) { - OptLutWorker worker(dlogic, module, limit - total_count); - total_count += worker.combined_count; + OptLutWorker worker(dlogic, module, limit - eliminated_count - combined_count); + eliminated_count += worker.eliminated_count; + combined_count += worker.combined_count; } - if (total_count) + if (eliminated_count) + design->scratchpad_set_bool("opt.did_something", true); + if (combined_count) design->scratchpad_set_bool("opt.did_something", true); log("\n"); - log("Combined %d LUTs.\n", total_count); + log("Eliminated %d LUTs.\n", eliminated_count); + log("Combined %d LUTs.\n", combined_count); } } OptLutPass; From 06143ab33f4064677586be2a2b0d763f0818e856 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 09:36:32 +0000 Subject: [PATCH 30/71] opt_lut: use a worklist, and revisit cells affected by elimination. --- passes/opt/opt_lut.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index 8c564b0ed..a79a9a2da 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -189,7 +189,8 @@ struct OptLutWorker log("\n"); log("Eliminating LUTs.\n"); - for (auto lut : luts) + pool worklist = luts; + while (worklist.size()) { if (limit == 0) { @@ -197,6 +198,7 @@ struct OptLutWorker break; } + auto lut = worklist.pop(); SigSpec lut_input = sigmap(lut->getPort("\\A")); pool &lut_dlogic_inputs = luts_dlogic_inputs[lut]; @@ -262,8 +264,13 @@ struct OptLutWorker else { SigSpec lut_output = lut->getPort("\\Y"); - module->connect(lut_output, value); + for (auto &port : index.query_ports(lut_output)) + { + if (port.cell != lut && luts.count(port.cell)) + worklist.insert(port.cell); + } + module->connect(lut_output, value); module->remove(lut); luts.erase(lut); luts_arity.erase(lut); @@ -280,7 +287,7 @@ struct OptLutWorker log("\n"); log("Combining LUTs.\n"); - pool worklist = luts; + worklist = luts; while (worklist.size()) { if (limit == 0) From c55dfb83693c6707ecb1d7dce7824a61e87eb44d Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 10:21:58 +0000 Subject: [PATCH 31/71] opt_lut: reflect changes in sigmap. Otherwise, some LUTs will be missed during elimination. --- passes/opt/opt_lut.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index a79a9a2da..b9a1ce7a7 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -271,6 +271,8 @@ struct OptLutWorker } module->connect(lut_output, value); + sigmap.add(lut_output, value); + module->remove(lut); luts.erase(lut); luts_arity.erase(lut); From efa278e232d20ea080743801bd91d55ec62955cf Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 7 Dec 2018 19:14:07 +0000 Subject: [PATCH 32/71] Fix typographical and grammatical errors and inconsistencies. The initial list of hits was generated with the codespell command below, and each hit was evaluated and fixed manually while taking context into consideration. DIRS="kernel/ frontends/ backends/ passes/ techlibs/" DIRS="${DIRS} libs/ezsat/ libs/subcircuit" codespell $DIRS -S *.o -L upto,iff,thru,synopsys,uint More hits were found by looking through comments and strings manually. --- README.md | 18 +++++++++--------- backends/simplec/simplec.cc | 2 +- backends/smt2/smtbmc.py | 8 ++++---- backends/table/table.cc | 2 +- backends/verilog/verilog_backend.cc | 2 +- frontends/ast/ast.cc | 4 ++-- frontends/blif/blifparse.cc | 2 +- frontends/liberty/liberty.cc | 2 +- frontends/verific/verificsva.cc | 8 ++++---- frontends/verilog/verilog_parser.y | 6 +++--- kernel/log.h | 2 +- kernel/yosys.cc | 2 +- libs/ezsat/ezminisat.h | 2 +- libs/subcircuit/README | 6 +++--- passes/cmds/chformal.cc | 4 ++-- passes/cmds/connect.cc | 6 +++--- passes/cmds/select.cc | 2 +- passes/cmds/setundef.cc | 2 +- passes/cmds/show.cc | 2 +- passes/cmds/tee.cc | 4 ++-- passes/fsm/fsm_detect.cc | 8 ++++---- passes/fsm/fsm_extract.cc | 4 ++-- passes/hierarchy/hierarchy.cc | 2 +- passes/opt/opt_expr.cc | 2 +- passes/opt/opt_lut.cc | 2 +- techlibs/achronix/speedster22i/cells_map.v | 16 ++++++++-------- techlibs/achronix/synth_achronix.cc | 2 +- techlibs/anlogic/synth_anlogic.cc | 2 +- techlibs/common/prep.cc | 2 +- techlibs/common/synth.cc | 2 +- techlibs/coolrunner2/synth_coolrunner2.cc | 2 +- techlibs/easic/synth_easic.cc | 2 +- techlibs/ecp5/cells_bb.v | 2 +- techlibs/ecp5/synth_ecp5.cc | 2 +- techlibs/gowin/synth_gowin.cc | 2 +- techlibs/greenpak4/synth_greenpak4.cc | 2 +- techlibs/ice40/synth_ice40.cc | 2 +- techlibs/intel/cyclonev/cells_sim.v | 2 +- techlibs/sf2/synth_sf2.cc | 2 +- techlibs/xilinx/synth_xilinx.cc | 2 +- 40 files changed, 74 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 840f2c8b2..883f2b0a5 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ reading the design using the Verilog frontend: yosys> read_verilog tests/simple/fiedler-cooley.v -writing the design to the console in yosys's internal format: +writing the design to the console in Yosys's internal format: yosys> write_ilang @@ -234,7 +234,7 @@ Unsupported Verilog-2005 Features ================================= The following Verilog-2005 features are not supported by -yosys and there are currently no plans to add support +Yosys and there are currently no plans to add support for them: - Non-synthesizable language features as defined in @@ -285,9 +285,9 @@ Verilog Attributes and non-standard features storage element. The register itself will always have all bits set to 'x' (undefined). The variable may only be used as blocking assigned temporary variable within an always block. This is mostly used internally - by yosys to synthesize Verilog functions and access arrays. + by Yosys to synthesize Verilog functions and access arrays. -- The ``onehot`` attribute on wires mark them as onehot state register. This +- The ``onehot`` attribute on wires mark them as one-hot state register. This is used for example for memory port sharing and set by the fsm_map pass. - The ``blackbox`` attribute on modules is used to mark empty stub modules @@ -319,13 +319,13 @@ Verilog Attributes and non-standard features through the synthesis. When entities are combined, a new |-separated string is created that contains all the string from the original entities. -- In addition to the ``(* ... *)`` attribute syntax, yosys supports +- In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset by adding an empty ``{* *}`` statement.) - In module parameter and port declarations, and cell port and parameter - lists, a trailing comma is ignored. This simplifies writing verilog code + lists, a trailing comma is ignored. This simplifies writing Verilog code generators a bit in some cases. - Modules can be declared with ``module mod_name(...);`` (with three dots @@ -410,11 +410,11 @@ Non-standard or SystemVerilog features for formal verification - The system functions ``$allconst`` and ``$allseq`` can be used to construct formal exist-forall problems. Assumptions only hold if the trace satisfies - the assumtion for all ``$allconst/$allseq`` values. For assertions and cover + the assumption for all ``$allconst/$allseq`` values. For assertions and cover statements it is sufficient if just one ``$allconst/$allseq`` value triggers the property (similar to ``$anyconst/$anyseq``). -- Wires/registers decalred using the ``anyconst/anyseq/allconst/allseq`` attribute +- Wires/registers declared using the ``anyconst/anyseq/allconst/allseq`` attribute (for example ``(* anyconst *) reg [7:0] foobar;``) will behave as if driven by a ``$anyconst/$anyseq/$allconst/$allseq`` function. @@ -485,6 +485,6 @@ Then execute, from the root of the repository: Notes: -- To run `make manual` you need to have installed yosys with `make install`, +- To run `make manual` you need to have installed Yosys with `make install`, otherwise it will fail on finding `kernel/yosys.h` while building `PRESENTATION_Prog`. diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc index 349bc5a6d..6f2ccbe20 100644 --- a/backends/simplec/simplec.cc +++ b/backends/simplec/simplec.cc @@ -748,7 +748,7 @@ struct SimplecBackend : public Backend { log("\n"); log(" write_simplec [options] [filename]\n"); log("\n"); - log("Write simple C code for simulating the design. The C code writen can be used to\n"); + log("Write simple C code for simulating the design. The C code written can be used to\n"); log("simulate the design in a C environment, but the purpose of this command is to\n"); log("generate code that works well with C-based formal verification.\n"); log("\n"); diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index 721a395e3..94a5e2da0 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -87,7 +87,7 @@ yosys-smtbmc [options] --aig : like above, but for map files and witness files that do not - share a filename prefix (or use differen file extensions). + share a filename prefix (or use different file extensions). --aig-noheader the AIGER witness file does not include the status and @@ -103,8 +103,8 @@ yosys-smtbmc [options] --presat check if the design with assumptions but without assertions is SAT before checking if assertions are UNSAT. This will - detect if there are contradicting assumtions. In some cases - this will also help to "warmup" the solver, potentially + detect if there are contradicting assumptions. In some cases + this will also help to "warm up" the solver, potentially yielding a speedup. --final-only @@ -149,7 +149,7 @@ yosys-smtbmc [options] --append add time steps at the end of the trace when creating a counter example (this additional time - steps will still be constrained by assumtions) + steps will still be constrained by assumptions) """ + so.helpmsg()) sys.exit(1) diff --git a/backends/table/table.cc b/backends/table/table.cc index 979273dd3..b75169ea4 100644 --- a/backends/table/table.cc +++ b/backends/table/table.cc @@ -109,7 +109,7 @@ struct TableBackend : public Backend { else if (cell->output(conn.first)) *f << "out" << "\t"; else - *f << "unkown" << "\t"; + *f << "unknown" << "\t"; *f << log_signal(sigmap(conn.second)) << "\n"; } diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 71db25f98..2537e18e5 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1447,7 +1447,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } if (!module->processes.empty()) - log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n" + log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n" "can't always be mapped directly to Verilog always blocks. Unintended\n" "changes in simulation behavior are possible! Use \"proc\" to convert\n" "processes to logic networks and registers.\n", log_id(module)); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 2c1561552..5a1bae7a7 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -36,14 +36,14 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; -// instanciate global variables (public API) +// instantiate global variables (public API) namespace AST { std::string current_filename; void (*set_line_num)(int) = NULL; int (*get_line_num)() = NULL; } -// instanciate global variables (private API) +// instantiate global variables (private API) namespace AST_INTERNAL { bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit; bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 034b3e70c..9116b257f 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -276,7 +276,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo if(lastcell == nullptr || module == nullptr) { - err_reason = stringf("No primative object to attach .cname %s.", p); + err_reason = stringf("No primitive object to attach .cname %s.", p); goto error_with_reason; } diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 4acfbf1cb..6e3cffaca 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -616,7 +616,7 @@ struct LibertyFrontend : public Frontend { LibertyAst *bus_type_node = node->find("bus_type"); if (!bus_type_node || !type_map.count(bus_type_node->value)) - log_error("Unkown or unsupported type for bus interface %s on cell %s.\n", + log_error("Unknown or unsupported type for bus interface %s on cell %s.\n", node->args.at(0).c_str(), log_id(cell_name)); int bus_type_width = std::get<0>(type_map.at(bus_type_node->value)); diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index cdc9ece8c..6681115df 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -827,9 +827,9 @@ struct SvaFsm for (auto &it : nodes[i].edges) { if (it.second != State::S1) - log(" egde %s -> %d\n", log_signal(it.second), it.first); + log(" edge %s -> %d\n", log_signal(it.second), it.first); else - log(" egde -> %d\n", it.first); + log(" edge -> %d\n", it.first); } for (auto &it : nodes[i].links) { @@ -856,9 +856,9 @@ struct SvaFsm for (auto &it : unodes[i].edges) { if (!it.second.empty()) - log(" egde %s -> %d\n", log_signal(it.second), it.first); + log(" edge %s -> %d\n", log_signal(it.second), it.first); else - log(" egde -> %d\n", it.first); + log(" edge -> %d\n", it.first); } for (auto &ctrl : unodes[i].accept) { diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 51e112ed3..a6718b020 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -794,7 +794,7 @@ more_path_inputs : list_of_path_outputs : specify_output_terminal_descriptor | list_of_path_outputs ',' specify_output_terminal_descriptor ; - + opt_polarity_operator : '+' | '-' @@ -819,7 +819,7 @@ system_timing_arg : system_timing_args : system_timing_arg | system_timing_args ',' system_timing_arg ; - + /* t_path_delay_expression : path_delay_expression; @@ -881,7 +881,7 @@ constant_mintypmax_expression : // for the time being this is OK, but we may write our own expr here. // as I'm not sure it is legal to use a full expr here (probably not) // On the other hand, other rules requiring constant expressions also use 'expr' -// (such as param assignment), so we may leave this as-is, perhaps assing runtime checks for constant-ness +// (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness constant_expression: expr ; diff --git a/kernel/log.h b/kernel/log.h index 0b4905c3a..e1f54a197 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -195,7 +195,7 @@ struct PerformanceTimer t += 1000000000ULL * (int64_t) rusage.ru_stime.tv_sec + (int64_t) rusage.ru_stime.tv_usec * 1000ULL; return t; # else -# error Dont know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?). +# error "Don't know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?)." # endif } diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 6884305d9..2ed0f4db4 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -744,7 +744,7 @@ std::string proc_self_dirname() return "/"; } #else - #error Dont know how to determine process executable base path! + #error "Don't know how to determine process executable base path!" #endif #ifdef EMSCRIPTEN diff --git a/libs/ezsat/ezminisat.h b/libs/ezsat/ezminisat.h index 983e6fd0e..3a34c13c8 100644 --- a/libs/ezsat/ezminisat.h +++ b/libs/ezsat/ezminisat.h @@ -28,7 +28,7 @@ #include // minisat is using limit macros and format macros in their headers that -// can be the source of some troubles when used from c++11. thefore we +// can be the source of some troubles when used from c++11. therefore we // don't force ezSAT users to use minisat headers.. namespace Minisat { class Solver; diff --git a/libs/subcircuit/README b/libs/subcircuit/README index b1335681e..ecaa987db 100644 --- a/libs/subcircuit/README +++ b/libs/subcircuit/README @@ -109,7 +109,7 @@ look at the demo.cc example program in this directory. Setting up graphs ----------------- -Instanciate the SubCircuit::Graph class and use the methods of this class to +Instantiate the SubCircuit::Graph class and use the methods of this class to set up the circuit. SubCircuit::Graph myGraph; @@ -152,7 +152,7 @@ rotate shift, The method createConstant() can be used to add a constant driver to a signal. The signal value is encoded as one char by bit, allowing for multi-valued -logic matching. The follwoing command sets the lowest bit of cell6.A to a +logic matching. The following command sets the lowest bit of cell6.A to a logic 1: myGraph.createConnection("cell6", "A", 0, '1'); @@ -314,7 +314,7 @@ bool userCompareEdge(needleGraphId, needleFromNodeId, needleFromUserData, needle Perform additional checks on a pair of a pair of adjacent nodes (one adjacent pair from the needle and one adjacent pair from the haystack) - to determine wheter this edge from the needle is compatible with + to determine whether this edge from the needle is compatible with that edge from the haystack. The default implementation always returns true. diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index 522758eae..7e32da65f 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -32,7 +32,7 @@ struct ChformalPass : public Pass { 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 folling options is given,\n"); + log("the type of constraint to operate on. If none of the following options are given,\n"); log("the command will operate on all constraint types:\n"); log("\n"); log(" -assert $assert cells, representing assert(...) constraints\n"); @@ -59,7 +59,7 @@ struct ChformalPass : public Pass { log(" -assume2assert\n"); log(" -live2fair\n"); log(" -fair2live\n"); - log(" change the roles of cells as indicated. this options can be combined\n"); + log(" change the roles of cells as indicated. these options can be combined\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index d480b79ac..f93bada27 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -137,7 +137,7 @@ struct ConnectPass : public Pass { if (!set_lhs.empty()) { if (!unset_expr.empty() || !port_cell.empty()) - log_cmd_error("Cant use -set together with -unset and/or -port.\n"); + log_cmd_error("Can't use -set together with -unset and/or -port.\n"); RTLIL::SigSpec sig_lhs, sig_rhs; if (!RTLIL::SigSpec::parse_sel(sig_lhs, design, module, set_lhs)) @@ -157,7 +157,7 @@ struct ConnectPass : public Pass { if (!unset_expr.empty()) { if (!port_cell.empty() || flag_nounset) - log_cmd_error("Cant use -unset together with -port and/or -nounset.\n"); + log_cmd_error("Can't use -unset together with -port and/or -nounset.\n"); RTLIL::SigSpec sig; if (!RTLIL::SigSpec::parse_sel(sig, design, module, unset_expr)) @@ -170,7 +170,7 @@ struct ConnectPass : public Pass { if (!port_cell.empty()) { if (flag_nounset) - log_cmd_error("Cant use -port together with -nounset.\n"); + log_cmd_error("Can't use -port together with -nounset.\n"); if (module->cells_.count(RTLIL::escape_id(port_cell)) == 0) log_cmd_error("Can't find cell %s.\n", port_cell.c_str()); diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index ba407ea8c..b5e8ef1af 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -987,7 +987,7 @@ struct SelectPass : public Pass { log("list of selected objects.\n"); log("\n"); log("Note that many commands support an optional [selection] argument that can be\n"); - log("used to YS_OVERRIDE the global selection for the command. The syntax of this\n"); + log("used to override the global selection for the command. The syntax of this\n"); log("optional argument is identical to the syntax of the argument\n"); log("described here.\n"); log("\n"); diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index a1dfa9b5c..56ef2d125 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -137,7 +137,7 @@ struct SetundefPass : public Pass { log(" replace with $anyconst drivers (for formal)\n"); log("\n"); log(" -random \n"); - log(" replace with random bits using the specified integer als seed\n"); + log(" replace with random bits using the specified integer as seed\n"); log(" value for the random number generator.\n"); log("\n"); log(" -init\n"); diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index a48873244..58acd302d 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -623,7 +623,7 @@ struct ShowPass : public Pass { log(" assigned to each unique value of this attribute.\n"); log("\n"); log(" -width\n"); - log(" annotate busses with a label indicating the width of the bus.\n"); + log(" annotate buses with a label indicating the width of the bus.\n"); log("\n"); log(" -signed\n"); log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n"); diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index ff80f3859..ee96ace86 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -37,7 +37,7 @@ struct TeePass : public Pass { log("specified logfile(s).\n"); log("\n"); log(" -q\n"); - log(" Do not print output to the normal destination (console and/or log file)\n"); + log(" Do not print output to the normal destination (console and/or log file).\n"); log("\n"); log(" -o logfile\n"); log(" Write output to this file, truncate if exists.\n"); @@ -46,7 +46,7 @@ struct TeePass : public Pass { log(" Write output to this file, append if exists.\n"); log("\n"); log(" +INT, -INT\n"); - log(" Add/subract INT from the -v setting for this command.\n"); + log(" Add/subtract INT from the -v setting for this command.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index fc504e98c..5ae991b28 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -196,13 +196,13 @@ static void detect_fsm(RTLIL::Wire *wire) vector warnings; if (is_module_port) - warnings.push_back("Forcing fsm recoding on module port might result in larger circuit.\n"); + warnings.push_back("Forcing FSM recoding on module port might result in larger circuit.\n"); if (!looks_like_good_state_reg) - warnings.push_back("Users of state reg look like fsm recoding might result in larger circuit.\n"); + warnings.push_back("Users of state reg look like FSM recoding might result in larger circuit.\n"); if (has_init_attr) - warnings.push_back("Init value on fsm state registers are ignored. Possible simulation-synthesis mismatch!"); + warnings.push_back("Initialization value on FSM state register is ignored. Possible simulation-synthesis mismatch!\n"); if (!looks_like_state_reg) warnings.push_back("Doesn't look like a proper FSM. Possible simulation-synthesis mismatch!\n"); @@ -236,7 +236,7 @@ static void detect_fsm(RTLIL::Wire *wire) log(" Users of register don't seem to benefit from recoding.\n"); if (has_init_attr) - log(" Register has an initialization value."); + log(" Register has an initialization value.\n"); if (is_self_resetting) log(" Circuit seems to be self-resetting.\n"); diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index 67551f673..6095eaf30 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -178,7 +178,7 @@ undef_bit_in_next_state: log_state_in = fsm_data.state_table.at(state_in); if (states.count(ce.values_map(ce.assign_map(dff_in)).as_const()) == 0) { - log(" transition: %10s %s -> INVALID_STATE(%s) %s %s\n", + log(" transition: %10s %s -> INVALID_STATE(%s) %s %s\n", log_signal(log_state_in), log_signal(tr.ctrl_in), log_signal(ce.values_map(ce.assign_map(dff_in))), log_signal(tr.ctrl_out), undef_bit_in_next_state_mode ? " SHORTENED" : ""); @@ -194,7 +194,7 @@ undef_bit_in_next_state: log_signal(log_state_in), log_signal(tr.ctrl_in), log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out)); } else { - log(" transition: %10s %s -> %10s %s \n", + log(" transition: %10s %s -> %10s %s \n", log_signal(log_state_in), log_signal(tr.ctrl_in), log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out)); } diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 0c782b8ab..0e28dbca2 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -543,7 +543,7 @@ struct HierarchyPass : public Pass { log(" an unknown module is used as cell type.\n"); log("\n"); log(" -simcheck\n"); - log(" like -check, but also thow an error if blackbox modules are\n"); + log(" like -check, but also throw an error if blackbox modules are\n"); log(" instantiated, and throw an error if the design has no top module\n"); log("\n"); log(" -purge_lib\n"); diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 610edc5e9..998c6507c 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1477,7 +1477,7 @@ struct OptExprPass : public Pass { log(" opt_expr [options] [selection]\n"); log("\n"); log("This pass performs const folding on internal cell types with constant inputs.\n"); - log("It also performs some simple expression rewritring.\n"); + log("It also performs some simple expression rewriting.\n"); log("\n"); log(" -mux_undef\n"); log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n"); diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index 261af538f..d1b8ab358 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -133,7 +133,7 @@ struct OptLutWorker // Second, make sure that the connection to dedicated logic is legal. If it is not legal, // it means one of the two things: // * The connection is spurious. I.e. this is dedicated logic that will be packed - // with some other LUT, and it just happens to be conected to this LUT as well. + // with some other LUT, and it just happens to be connected to this LUT as well. // * The connection is illegal. // In either of these cases, we don't need to concern ourselves with preserving the connection // between this LUT and this dedicated logic cell. diff --git a/techlibs/achronix/speedster22i/cells_map.v b/techlibs/achronix/speedster22i/cells_map.v index 95f5d59c5..9f647cbef 100755 --- a/techlibs/achronix/speedster22i/cells_map.v +++ b/techlibs/achronix/speedster22i/cells_map.v @@ -32,7 +32,7 @@ endmodule // > end buffers < // > Look-Up table < -// > VT: I still think Achronix folks would have choosen a better \ +// > VT: I still think Achronix folks would have chosen a better \ // > logic architecture. // LUT Map module \$lut (A, Y); @@ -43,30 +43,30 @@ module \$lut (A, Y); generate if (WIDTH == 1) begin // VT: This is not consistent and ACE will complain: assign Y = ~A[0]; - LUT4 #(.lut_function({4{LUT}})) _TECHMAP_REPLACE_ + LUT4 #(.lut_function({4{LUT}})) _TECHMAP_REPLACE_ (.dout(Y), .din0(A[0]), .din1(1'b0), .din2(1'b0), .din3(1'b0)); end else if (WIDTH == 2) begin - LUT4 #(.lut_function({4{LUT}})) _TECHMAP_REPLACE_ + LUT4 #(.lut_function({4{LUT}})) _TECHMAP_REPLACE_ (.dout(Y), .din0(A[0]), .din1(A[1]), .din2(1'b0), .din3(1'b0)); end else if(WIDTH == 3) begin - LUT4 #(.lut_function({2{LUT}})) _TECHMAP_REPLACE_ + LUT4 #(.lut_function({2{LUT}})) _TECHMAP_REPLACE_ (.dout(Y), .din0(A[0]), .din1(A[1]), .din2(A[2]), .din3(1'b0)); end else if(WIDTH == 4) begin - LUT4 #(.lut_function(LUT)) _TECHMAP_REPLACE_ + LUT4 #(.lut_function(LUT)) _TECHMAP_REPLACE_ (.dout(Y), .din0(A[0]), .din1(A[1]), .din2(A[2]), .din3(A[3])); end else wire _TECHMAP_FAIL_ = 1; endgenerate -endmodule +endmodule // > end LUT < // > Flops < // DFF flop module \$_DFF_P_ (input D, C, output Q); - DFF _TECHMAP_REPLACE_ + DFF _TECHMAP_REPLACE_ (.q(Q), .d(D), .ck(C)); -endmodule +endmodule diff --git a/techlibs/achronix/synth_achronix.cc b/techlibs/achronix/synth_achronix.cc index 92b10781d..3642e3bd3 100755 --- a/techlibs/achronix/synth_achronix.cc +++ b/techlibs/achronix/synth_achronix.cc @@ -108,7 +108,7 @@ struct SynthAchronixPass : public ScriptPass { extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_ACHRONIX pass.\n"); log_push(); diff --git a/techlibs/anlogic/synth_anlogic.cc b/techlibs/anlogic/synth_anlogic.cc index 9c44599ea..68f4399d4 100644 --- a/techlibs/anlogic/synth_anlogic.cc +++ b/techlibs/anlogic/synth_anlogic.cc @@ -119,7 +119,7 @@ struct SynthAnlogicPass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_ANLOGIC pass.\n"); log_push(); diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc index 897f37dbb..86fb4d6c6 100644 --- a/techlibs/common/prep.cc +++ b/techlibs/common/prep.cc @@ -153,7 +153,7 @@ struct PrepPass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing PREP pass.\n"); log_push(); diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index efb214759..d9565131e 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -155,7 +155,7 @@ struct SynthPass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH pass.\n"); log_push(); diff --git a/techlibs/coolrunner2/synth_coolrunner2.cc b/techlibs/coolrunner2/synth_coolrunner2.cc index a5dac3566..810380d4a 100644 --- a/techlibs/coolrunner2/synth_coolrunner2.cc +++ b/techlibs/coolrunner2/synth_coolrunner2.cc @@ -111,7 +111,7 @@ struct SynthCoolrunner2Pass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_COOLRUNNER2 pass.\n"); log_push(); diff --git a/techlibs/easic/synth_easic.cc b/techlibs/easic/synth_easic.cc index b5ed93be4..dd9e3dab7 100644 --- a/techlibs/easic/synth_easic.cc +++ b/techlibs/easic/synth_easic.cc @@ -117,7 +117,7 @@ struct SynthEasicPass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_EASIC pass.\n"); log_push(); diff --git a/techlibs/ecp5/cells_bb.v b/techlibs/ecp5/cells_bb.v index 057f9d737..425d62d24 100644 --- a/techlibs/ecp5/cells_bb.v +++ b/techlibs/ecp5/cells_bb.v @@ -484,7 +484,7 @@ module DCUA( parameter D_XGE_MODE = "0b0"; // These parameters don't do anything but are -// needed for compatability with Diamond +// needed for compatibility with Diamond parameter D_TX_MAX_RATE = "2.5"; parameter D_RX_MAX_RATE = "2.5"; parameter CH0_TXAMPLITUDE = "0d1300"; diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index 825e131c4..2e9176a84 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -189,7 +189,7 @@ struct SynthEcp5Pass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_ECP5 pass.\n"); log_push(); diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc index e3d924e26..9700b3898 100644 --- a/techlibs/gowin/synth_gowin.cc +++ b/techlibs/gowin/synth_gowin.cc @@ -109,7 +109,7 @@ struct SynthGowinPass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_GOWIN pass.\n"); log_push(); diff --git a/techlibs/greenpak4/synth_greenpak4.cc b/techlibs/greenpak4/synth_greenpak4.cc index b91d5273a..eeb001b46 100644 --- a/techlibs/greenpak4/synth_greenpak4.cc +++ b/techlibs/greenpak4/synth_greenpak4.cc @@ -120,7 +120,7 @@ struct SynthGreenPAK4Pass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); if (part != "SLG46140V" && part != "SLG46620V" && part != "SLG46621V") log_cmd_error("Invalid part name: '%s'\n", part.c_str()); diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index c2aed873b..626f6d381 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -198,7 +198,7 @@ struct SynthIce40Pass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_ICE40 pass.\n"); log_push(); diff --git a/techlibs/intel/cyclonev/cells_sim.v b/techlibs/intel/cyclonev/cells_sim.v index 5ecdabcfc..fa27c2c8e 100644 --- a/techlibs/intel/cyclonev/cells_sim.v +++ b/techlibs/intel/cyclonev/cells_sim.v @@ -54,7 +54,7 @@ module cyclonev_lcell_comb // Internal variables // Sub mask for fragmented LUTs wire [15:0] mask_a, mask_b, mask_c, mask_d; - // Independant output for fragmented LUTs + // Independent output for fragmented LUTs wire output_0, output_1, output_2, output_3; // Extended mode uses mux to define the output wire mux_0, mux_1; diff --git a/techlibs/sf2/synth_sf2.cc b/techlibs/sf2/synth_sf2.cc index 2676ea657..62b3cd0e2 100644 --- a/techlibs/sf2/synth_sf2.cc +++ b/techlibs/sf2/synth_sf2.cc @@ -118,7 +118,7 @@ struct SynthSf2Pass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_SF2 pass.\n"); log_push(); diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index b27c08529..6c11d885d 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -178,7 +178,7 @@ struct SynthXilinxPass : public Pass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); bool active = run_from.empty(); From 50b09de03320843660636c663629c649ab242321 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 2 Jan 2019 15:05:23 +0100 Subject: [PATCH 33/71] Fix VerificImporter asymmetric memories error message Signed-off-by: Clifford Wolf --- frontends/verific/verific.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 61d9d593c..5280a2b9c 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1201,7 +1201,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se { RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name())); if (memory->width != int(inst->Input2Size())) - log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name()); + log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetOutput()->Name()); RTLIL::SigSpec addr = operatorInput1(inst); RTLIL::SigSpec data = operatorInput2(inst); From 1eb101a38a0e6ca99cb1dfbcc77f16a6bff79465 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 2 Jan 2019 15:33:43 +0100 Subject: [PATCH 34/71] Improve VerificImporter support for writes to asymmetric memories Signed-off-by: Clifford Wolf --- frontends/verific/verific.cc | 53 ++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 5280a2b9c..94138cdd6 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1200,27 +1200,34 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (inst->Type() == OPER_WRITE_PORT || inst->Type() == OPER_CLOCKED_WRITE_PORT) { RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name())); - if (memory->width != int(inst->Input2Size())) + int numchunks = int(inst->Input2Size()) / memory->width; + int chunksbits = ceil_log2(numchunks); + + if ((numchunks * memory->width) != int(inst->Input2Size()) || (numchunks & (numchunks - 1)) != 0) log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetOutput()->Name()); - RTLIL::SigSpec addr = operatorInput1(inst); - RTLIL::SigSpec data = operatorInput2(inst); + for (int i = 0; i < numchunks; i++) + { + RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)}; + RTLIL::SigSpec data = operatorInput2(inst).extract(i * memory->width, memory->width); - RTLIL::Cell *cell = module->addCell(inst_name, "$memwr"); - cell->parameters["\\MEMID"] = memory->name.str(); - cell->parameters["\\CLK_ENABLE"] = false; - cell->parameters["\\CLK_POLARITY"] = true; - cell->parameters["\\PRIORITY"] = 0; - cell->parameters["\\ABITS"] = GetSize(addr); - cell->parameters["\\WIDTH"] = GetSize(data); - cell->setPort("\\EN", RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data))); - cell->setPort("\\CLK", RTLIL::State::S0); - cell->setPort("\\ADDR", addr); - cell->setPort("\\DATA", data); + RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name : + RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), "$memwr"); + cell->parameters["\\MEMID"] = memory->name.str(); + cell->parameters["\\CLK_ENABLE"] = false; + cell->parameters["\\CLK_POLARITY"] = true; + cell->parameters["\\PRIORITY"] = 0; + cell->parameters["\\ABITS"] = GetSize(addr); + cell->parameters["\\WIDTH"] = GetSize(data); + cell->setPort("\\EN", RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data))); + cell->setPort("\\CLK", RTLIL::State::S0); + cell->setPort("\\ADDR", addr); + cell->setPort("\\DATA", data); - if (inst->Type() == OPER_CLOCKED_WRITE_PORT) { - cell->parameters["\\CLK_ENABLE"] = true; - cell->setPort("\\CLK", net_map_at(inst->GetClock())); + if (inst->Type() == OPER_CLOCKED_WRITE_PORT) { + cell->parameters["\\CLK_ENABLE"] = true; + cell->setPort("\\CLK", net_map_at(inst->GetClock())); + } } continue; } @@ -1893,13 +1900,19 @@ struct VerificPass : public Pass { { Message::SetConsoleOutput(0); Message::RegisterCallBackMsg(msg_func); + RuntimeFlags::SetVar("db_preserve_user_nets", 1); RuntimeFlags::SetVar("db_allow_external_nets", 1); - RuntimeFlags::SetVar("vhdl_support_variable_slice", 1); - RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); + RuntimeFlags::SetVar("db_infer_wide_operators", 1); + RuntimeFlags::SetVar("veri_extract_dualport_rams", 0); RuntimeFlags::SetVar("veri_extract_multiport_rams", 1); - RuntimeFlags::SetVar("db_infer_wide_operators", 1); + + RuntimeFlags::SetVar("vhdl_extract_dualport_rams", 0); + RuntimeFlags::SetVar("vhdl_extract_multiport_rams", 1); + + RuntimeFlags::SetVar("vhdl_support_variable_slice", 1); + RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); // Workaround for VIPER #13851 RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1); From bf8db55ef36f6839827cd8bc69f673fd3fd43cca Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 05:04:28 +0000 Subject: [PATCH 35/71] opt_expr: improve simplification of comparisons with large constants. The idea behind this simplification is that a N-bit signal X being compared with an M-bit constant where M>N and the constant has Nth or higher bit set, it either always succeeds or always fails. However, the existing implementation only worked with one-hot signals for some reason. It also printed incorrect messages. This commit adjusts the simplification to have as much power as possible, and fixes other bugs. --- passes/opt/opt_expr.cc | 117 ++++++++++++++++----------------------- tests/opt/opt_expr_cmp.v | 18 ++++++ 2 files changed, 65 insertions(+), 70 deletions(-) diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 9abbdc6ac..76bcfd44f 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -259,6 +259,22 @@ bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative) return last_bit_one; } +int get_highest_hot_index(RTLIL::SigSpec signal) +{ + for (int i = GetSize(signal) - 1; i >= 0; i--) + { + if (signal[i] == RTLIL::State::S0) + continue; + + if (signal[i] == RTLIL::State::S1) + return i; + + break; + } + + return -1; +} + // if the signal has only one bit set, return the index of that bit. // otherwise return -1 int get_onehot_bit_index(RTLIL::SigSpec signal) @@ -1345,9 +1361,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } // simplify comparisons - // currently, only replaces comparisons with an extreme of the signal range with a constant - // TODO: since, unlike the following optimization, this one does not assume that both inputs to the cell are constant, - // it is more robust; the following optimization should be folded into this one at some point if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le")) { IdString cmp_type = cell->type; @@ -1405,29 +1418,53 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons replace = true; } - int const_bit_set = get_onehot_bit_index(const_sig); - if (const_bit_set >= 0 && const_bit_set < var_width) + int const_bit_hot = get_onehot_bit_index(const_sig); + if (const_bit_hot >= 0 && const_bit_hot < var_width) { - RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_set); - for (int i = const_bit_set; i < var_width; i++) { - var_high_sig[i - const_bit_set] = var_sig[i]; + RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot); + for (int i = const_bit_hot; i < var_width; i++) { + var_high_sig[i - const_bit_hot] = var_sig[i]; } if (cmp_type == "$lt") { condition = stringf("unsigned X<%s", log_signal(const_sig)); - replacement = stringf("!X[%d:%d]", var_width - 1, const_bit_set); + replacement = stringf("!X[%d:%d]", var_width - 1, const_bit_hot); module->addLogicNot(NEW_ID, var_high_sig, cell->getPort("\\Y")); remove = true; } if (cmp_type == "$ge") { condition = stringf("unsigned X>=%s", log_signal(const_sig)); - replacement = stringf("|X[%d:%d]", var_width - 1, const_bit_set); + replacement = stringf("|X[%d:%d]", var_width - 1, const_bit_hot); module->addReduceOr(NEW_ID, var_high_sig, cell->getPort("\\Y")); remove = true; } } + + int const_bit_set = get_highest_hot_index(const_sig); + if(const_bit_set >= var_width) + { + string cmp_name; + if (cmp_type == "$lt" || cmp_type == "$le") + { + if (cmp_type == "$lt") cmp_name = "<"; + if (cmp_type == "$le") cmp_name = "<="; + condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig)); + replacement = "constant 1"; + replace_sig[0] = State::S1; + replace = true; + } + if (cmp_type == "$gt" || cmp_type == "$ge") + { + if (cmp_type == "$gt") cmp_name = ">"; + if (cmp_type == "$ge") cmp_name = ">="; + condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig)); + replacement = "constant 0"; + replace_sig[0] = State::S0; + replace = true; + } + } } else { /* signed */ @@ -1460,66 +1497,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } - // replace a<0 or a>=0 with the top bit of a - if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le")) - { - //used to decide whether the signal needs to be negated - bool is_lt = false; - - //references the variable signal in the comparison - RTLIL::SigSpec sigVar; - - //references the constant signal in the comparison - RTLIL::SigSpec sigConst; - - // note that this signal must be constant for the optimization - // to take place, but it is not checked beforehand. - // If new passes are added, this signal must be checked for const-ness - - //width of the variable port - int width; - int const_width; - - bool var_signed; - - if (cell->type == "$lt" || cell->type == "$ge") { - is_lt = cell->type == "$lt" ? 1 : 0; - sigVar = cell->getPort("\\A"); - sigConst = cell->getPort("\\B"); - width = cell->parameters["\\A_WIDTH"].as_int(); - const_width = cell->parameters["\\B_WIDTH"].as_int(); - var_signed = cell->parameters["\\A_SIGNED"].as_bool(); - } else - if (cell->type == "$gt" || cell->type == "$le") { - is_lt = cell->type == "$gt" ? 1 : 0; - sigVar = cell->getPort("\\B"); - sigConst = cell->getPort("\\A"); - width = cell->parameters["\\B_WIDTH"].as_int(); - const_width = cell->parameters["\\A_WIDTH"].as_int(); - var_signed = cell->parameters["\\B_SIGNED"].as_bool(); - } else - log_abort(); - - if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false) - { - int const_bit_set = get_onehot_bit_index(sigConst); - if(const_bit_set >= width && const_bit_set >= 0){ - RTLIL::SigSpec a_prime(RTLIL::State::S0, 1); - if(is_lt){ - a_prime[0] = RTLIL::State::S1; - log("Replacing %s cell `%s' (implementing unsigned X[%d:0] < %s[%d:0]) with constant 0.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1); - } - else{ - log("Replacing %s cell `%s' (implementing unsigned X[%d:0]>= %s[%d:0]) with constant 1.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1); - } - module->connect(cell->getPort("\\Y"), a_prime); - module->remove(cell); - did_something = true; - goto next_cell; - } - } - } - next_cell:; #undef ACTION_DO #undef ACTION_DO_Y diff --git a/tests/opt/opt_expr_cmp.v b/tests/opt/opt_expr_cmp.v index 500b15f1b..5aff4b80f 100644 --- a/tests/opt/opt_expr_cmp.v +++ b/tests/opt/opt_expr_cmp.v @@ -19,4 +19,22 @@ module top(...); output o3_2 = 4'b0100 <= a; output o3_3 = a < 4'b0100; output o3_4 = a >= 4'b0100; + + output o4_1 = 5'b10000 > a; + output o4_2 = 5'b10000 >= a; + output o4_3 = 5'b10000 < a; + output o4_4 = 5'b10000 <= a; + output o4_5 = a < 5'b10000; + output o4_6 = a <= 5'b10000; + output o4_7 = a > 5'b10000; + output o4_8 = a >= 5'b10000; + + output o5_1 = 5'b10100 > a; + output o5_2 = 5'b10100 >= a; + output o5_3 = 5'b10100 < a; + output o5_4 = 5'b10100 <= a; + output o5_5 = a < 5'b10100; + output o5_6 = a <= 5'b10100; + output o5_7 = a > 5'b10100; + output o5_8 = a >= 5'b10100; endmodule From 07af772a7279d656564fb23eee457e71cc0d657f Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Jan 2019 14:09:53 +0000 Subject: [PATCH 36/71] flowmap: new techmap pass. --- passes/opt/Makefile.inc | 2 +- passes/techmap/Makefile.inc | 1 + passes/techmap/flowmap.cc | 873 ++++++++++++++++++++++++++++++++++++ 3 files changed, 875 insertions(+), 1 deletion(-) create mode 100644 passes/techmap/flowmap.cc diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 0f596b1f4..c3e0a2a40 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -6,12 +6,12 @@ OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_expr.o -OBJS += passes/opt/opt_lut.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o OBJS += passes/opt/wreduce.o OBJS += passes/opt/opt_demorgan.o OBJS += passes/opt/rmports.o +OBJS += passes/opt/opt_lut.o endif diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 4faa0ab00..cf9e198ad 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -36,6 +36,7 @@ OBJS += passes/techmap/attrmvcp.o OBJS += passes/techmap/attrmap.o OBJS += passes/techmap/zinit.o OBJS += passes/techmap/dff2dffs.o +OBJS += passes/techmap/flowmap.o endif GENFILES += passes/techmap/techmap.inc diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc new file mode 100644 index 000000000..cdca78db8 --- /dev/null +++ b/passes/techmap/flowmap.cc @@ -0,0 +1,873 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 whitequark + * + * 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. + * + */ + +// [[CITE]] +// Jason Cong; Yuzheng Ding, "An Optimal Technology Mapping Algorithm for Delay Optimization in Lookup-Table Based FPGA Designs," +// Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on, vol. 13, no. 1, Jan 1994 +// doi: 10.1109/43.273754 + +// Required reading material: +// +// Min-cut max-flow theorem: +// https://www.coursera.org/lecture/algorithms-part2/maxflow-mincut-theorem-beb9G +// FlowMap paper: +// http://cadlab.cs.ucla.edu/~cong/papers/iccad92.pdf + +// Notes on implementation: +// +// 1. In the paper, the nodes are logic elements (analogous to Yosys cells) and edges are wires. However, in our implementation, we use +// an inverted approach: the nodes are Yosys wire bits, and the edges are derived from (but aren't represented by) Yosys cells. +// This may seem counterintuitive. Three observations may help understanding this. First, for a cell with a 1-bit Y output that is +// the sole driver of its output net (which is the typical case), these representations are equivalent, because there is an exact +// correspondence between cells and output wires. Second, in the paper, primary inputs (analogous to Yosys cell or module ports) are +// nodes, and in Yosys, inputs are wires; our approach allows a direct mapping from both primary inputs and 1-output logic elements to +// flow graph nodes. Third, Yosys cells may have multiple outputs or multi-bit outputs, and by using Yosys wire bits as flow graph nodes, +// such cells are supported without any additional effort; any Yosys cell with n output wire bits ends up being split into n flow graph +// nodes. +// +// 2. The paper introduces three networks: Nt, Nt', and Nt''. The network Nt is directly represented by a subgraph of RTLIL graph, +// which is parsed into an equivalent but easier to traverse representation in FlowmapWorker. The network Nt' is built explicitly +// from a subgraph of Nt, and uses a similar representation in FlowGraph. The network Nt'' is implicit in FlowGraph, which is possible +// because of the following observation: each Nt' node corresponds to an Nt'' edge of capacity 1, and each Nt' edge corresponds to +// an Nt'' edge of capacity ∞. Therefore, we only need to explicitly record flow for Nt' edges and through Nt' nodes. +// +// 3. The paper ambiguously states: "Moreover, we can find such a cut (X′′, X̅′′) by performing a depth first search starting at the source s, +// and including in X′′ all the nodes which are reachable from s." This actually refers to a specific kind of search, mincut computation. +// Mincut computation involves computing the set of nodes reachable from s by an undirected path with no full (i.e. zero capacity) forward +// edges or empty (i.e. no flow) backward edges. + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" +#include "kernel/consteval.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct GraphStyle +{ + string label; + string color; + + GraphStyle(string label = "", string color = "black") : + label(label), color(color) {} +}; + +static string dot_escape(string value) +{ + std::string escaped; + for (char c : value) { + if (c == '\n') + { + escaped += "\\n"; + continue; + } + if (c == '\\' || c == '"') + escaped += "\\"; + escaped += c; + } + return escaped; +} + +static void dump_dot_graph(string filename, + pool nodes, dict> edges, + pool inputs, pool outputs, + std::function node_style = + [](RTLIL::SigBit) { return GraphStyle{}; }, + std::function edge_style = + [](RTLIL::SigBit, RTLIL::SigBit) { return GraphStyle{}; }, + string name = "") +{ + FILE *f = fopen(filename.c_str(), "w"); + fprintf(f, "digraph \"%s\" {\n", name.c_str()); + fprintf(f, " rankdir=\"TB\";\n"); + + dict ids; + for (auto node : nodes) + { + ids[node] = ids.size(); + + string shape = "ellipse"; + if (inputs[node]) + shape = "box"; + if (outputs[node]) + shape = "octagon"; + auto prop = node_style(node); + string id; + if (node == SigBit()) + id = "(source)"; + else + id = log_signal(node); + fprintf(f, " n%d [ shape=%s, fontname=\"Monospace\", label=\"%s%s\", color=\"%s\" ];\n", + ids[node], shape.c_str(), dot_escape(id).c_str(), dot_escape(prop.label.c_str()).c_str(), prop.color.c_str()); + } + + fprintf(f, " { rank=\"source\"; "); + for (auto input : inputs) + if (nodes[input]) + fprintf(f, "n%d; ", ids[input]); + fprintf(f, "}\n"); + + fprintf(f, " { rank=\"sink\"; "); + for (auto output : outputs) + if (nodes[output]) + fprintf(f, "n%d; ", ids[output]); + fprintf(f, "}\n"); + + for (auto edge : edges) + { + auto source = edge.first; + for (auto sink : edge.second) { + if (nodes[source] && nodes[sink]) + { + auto prop = edge_style(source, sink); + fprintf(f, " n%d -> n%d [ label=\"%s\", color=\"%s\" ];\n", + ids[source], ids[sink], dot_escape(prop.label.c_str()).c_str(), prop.color.c_str()); + } + } + } + + fprintf(f, "}\n"); + fclose(f); +} + +struct FlowGraph +{ + const RTLIL::SigBit source; + RTLIL::SigBit sink; + pool nodes = {source}; + dict> edges_fw, edges_bw; + + const int MAX_NODE_FLOW = 1; + dict node_flow; + dict, int> edge_flow; + + dict> collapsed; + + void dump_dot_graph(string filename) + { + auto node_style = [&](RTLIL::SigBit node) { + string label; + for (auto collapsed_node : collapsed[node]) + label += stringf(" %s", log_signal(collapsed_node)); + int flow = node_flow[node]; + if (node != source && node != sink) + label += stringf("\n%d/%d", flow, MAX_NODE_FLOW); + else + label += stringf("\n%d/∞", flow); + return GraphStyle{label, flow < MAX_NODE_FLOW ? "green" : "black"}; + }; + auto edge_style = [&](RTLIL::SigBit source, RTLIL::SigBit sink) { + int flow = edge_flow[{source, sink}]; + return GraphStyle{stringf("%d/∞", flow), flow > 0 ? "blue" : "black"}; + }; + ::dump_dot_graph(filename, nodes, edges_fw, {source}, {sink}, node_style, edge_style); + } + + // Here, we are working on the Nt'' network, but our representation is the Nt' network. + // The difference between these is that where in Nt' we have a subgraph: + // + // v1 -> v2 -> v3 + // + // in Nt'' we have a corresponding subgraph: + // + // v'1b -∞-> v'2t -f-> v'2b -∞-> v'3t + // + // To address this, we split each node v into two nodes, v't and v'b. This representation is virtual, + // in the sense that nodes v't and v'b are overlaid on top of the original node v, and only exist + // in paths and worklists. + + struct NodePrime + { + RTLIL::SigBit node; + bool is_bottom; + + NodePrime(RTLIL::SigBit node, bool is_bottom) : + node(node), is_bottom(is_bottom) {} + + bool operator==(const NodePrime &other) const + { + return node == other.node && is_bottom == other.is_bottom; + } + bool operator!=(const NodePrime &other) const + { + return !(*this == other); + } + unsigned int hash() const + { + return hash_ops>::hash({node, is_bottom}); + } + + static NodePrime top(RTLIL::SigBit node) + { + return NodePrime(node, /*is_bottom=*/false); + } + + static NodePrime bottom(RTLIL::SigBit node) + { + return NodePrime(node, /*is_bottom=*/true); + } + + NodePrime as_top() const + { + log_assert(is_bottom); + return top(node); + } + + NodePrime as_bottom() const + { + log_assert(!is_bottom); + return bottom(node); + } + }; + + bool find_augmenting_path(bool commit) + { + NodePrime source_prime = {source, true}; + NodePrime sink_prime = {sink, false}; + vector path = {source_prime}; + pool visited = {}; + bool found; + do { + found = false; + + auto node_prime = path.back(); + visited.insert(node_prime); + + if (!node_prime.is_bottom) // vt + { + if (!visited[node_prime.as_bottom()] && node_flow[node_prime.node] < MAX_NODE_FLOW) + { + path.push_back(node_prime.as_bottom()); + found = true; + } + else + { + for (auto node_pred : edges_bw[node_prime.node]) + { + if (!visited[NodePrime::bottom(node_pred)] && edge_flow[{node_pred, node_prime.node}] > 0) + { + path.push_back(NodePrime::bottom(node_pred)); + found = true; + break; + } + } + } + } + else // vb + { + if (!visited[node_prime.as_top()] && node_flow[node_prime.node] > 0) + { + path.push_back(node_prime.as_top()); + found = true; + } + else + { + for (auto node_succ : edges_fw[node_prime.node]) + { + if (!visited[NodePrime::top(node_succ)] /* && edge_flow[...] < ∞ */) + { + path.push_back(NodePrime::top(node_succ)); + found = true; + break; + } + } + } + } + + if (!found && path.size() > 1) + { + path.pop_back(); + found = true; + } + } while(path.back() != sink_prime && found); + + if (commit && path.back() == sink_prime) + { + auto prev_prime = path.front(); + for (auto node_prime : path) + { + if (node_prime == source_prime) + continue; + + log_assert(prev_prime.is_bottom ^ node_prime.is_bottom); + if (prev_prime.node == node_prime.node) + { + auto node = node_prime.node; + if (!prev_prime.is_bottom && node_prime.is_bottom) + { + log_assert(node_flow[node] == 0); + node_flow[node]++; + } + else + { + log_assert(node_flow[node] != 0); + node_flow[node]--; + } + } + else + { + if (prev_prime.is_bottom && !node_prime.is_bottom) + { + log_assert(true /* edge_flow[...] < ∞ */); + edge_flow[{prev_prime.node, node_prime.node}]++; + } + else + { + log_assert((edge_flow[{node_prime.node, prev_prime.node}] > 0)); + edge_flow[{node_prime.node, prev_prime.node}]--; + } + } + prev_prime = node_prime; + } + + node_flow[source]++; + node_flow[sink]++; + } + return path.back() == sink_prime; + } + + int maximum_flow(int order) + { + int flow = 0; + while (flow < order && find_augmenting_path(/*commit=*/true)) + flow++; + return flow + find_augmenting_path(/*commit=*/false); + } + + pair, pool> edge_cut() + { + pool x, xi; + + NodePrime source_prime = {source, true}; + NodePrime sink_prime = {sink, false}; + pool worklist = {source_prime}, visited; + while (!worklist.empty()) + { + auto node_prime = worklist.pop(); + if (visited[node_prime]) + continue; + visited.insert(node_prime); + + if (!node_prime.is_bottom) + x.insert(node_prime.node); + + // Mincut is constructed by traversing a graph in an undirected way along forward edges that aren't full, or backward edges + // that aren't empty. + if (!node_prime.is_bottom) // top + { + if (node_flow[node_prime.node] < MAX_NODE_FLOW) + worklist.insert(node_prime.as_bottom()); + for (auto node_pred : edges_bw[node_prime.node]) + if (edge_flow[{node_pred, node_prime.node}] > 0) + worklist.insert(NodePrime::bottom(node_pred)); + } + else // bottom + { + if (node_flow[node_prime.node] > 0) + worklist.insert(node_prime.as_top()); + for (auto node_succ : edges_fw[node_prime.node]) + if (true /* edge_flow[...] < ∞ */) + worklist.insert(NodePrime::top(node_succ)); + } + } + + for (auto node : nodes) + if (!x[node]) + xi.insert(node); + + for (auto collapsed_node : collapsed[sink]) + xi.insert(collapsed_node); + + log_assert(!x[sink] && xi[sink]); + return {x, xi}; + } +}; + +struct FlowmapWorker +{ + int order; + pool cell_types; + bool debug; + + RTLIL::Module *module; + SigMap sigmap; + ModIndex index; + pool cells; + + pool nodes, inputs, outputs; + dict> edges_fw, edges_bw; + dict labels; + + dict> lut_gates, lut_inputs; + + dict node_origins; + dict> cell_fanout; + + int mapped_count = 0, packed_count = 0, unique_packed_count = 0; + + void dump_dot_graph(string filename, pool subgraph = {}, pair, pool> cut = {}) + { + if (subgraph.empty()) + subgraph = nodes; + + auto node_style = [&](RTLIL::SigBit node) { + string label, color; + if (labels[node] == -1) + label = string("\n"); + else + label = stringf("\nl=%d", labels[node]); + color = "black"; + if (cut.first[node]) + color = "blue"; + if (cut.second[node]) + color = "red"; + return GraphStyle{label, color}; + }; + auto edge_style = [&](RTLIL::SigBit, RTLIL::SigBit) { + return GraphStyle{}; + }; + ::dump_dot_graph(filename, subgraph, edges_fw, inputs, outputs, node_style, edge_style, module->name.str()); + } + + pool find_subgraph(RTLIL::SigBit sink) + { + pool subgraph; + pool worklist = {sink}; + while (!worklist.empty()) + { + auto node = worklist.pop(); + subgraph.insert(node); + for (auto source : edges_bw[node]) + { + if (!subgraph[source]) + worklist.insert(source); + } + } + return subgraph; + } + + FlowGraph build_flow_graph(RTLIL::SigBit sink, int p) + { + FlowGraph flow_graph; + flow_graph.sink = sink; + + pool worklist = {sink}, visited; + while (!worklist.empty()) + { + auto node = worklist.pop(); + visited.insert(node); + + auto collapsed_node = labels[node] == p ? sink : node; + if (node != collapsed_node) + flow_graph.collapsed[collapsed_node].insert(node); + flow_graph.nodes.insert(collapsed_node); + + for (auto node_pred : edges_bw[node]) + { + auto collapsed_node_pred = labels[node_pred] == p ? sink : node_pred; + if (node_pred != collapsed_node_pred) + flow_graph.collapsed[collapsed_node_pred].insert(node_pred); + if (collapsed_node != collapsed_node_pred) + { + flow_graph.edges_bw[collapsed_node].insert(collapsed_node_pred); + flow_graph.edges_fw[collapsed_node_pred].insert(collapsed_node); + } + if (inputs[node_pred]) + { + flow_graph.edges_bw[collapsed_node_pred].insert(flow_graph.source); + flow_graph.edges_fw[flow_graph.source].insert(collapsed_node_pred); + } + + if (!visited[node_pred]) + worklist.insert(node_pred); + } + } + return flow_graph; + } + + FlowmapWorker(int order, pool cell_types, bool debug, RTLIL::Module *module) : + order(order), cell_types(cell_types), debug(debug), module(module), sigmap(module), index(module) + { + log("Labeling cells.\n"); + for (auto cell : module->selected_cells()) + { + if (cell_types[cell->type]) + { + if (!cell->known()) + { + log_error("Cell %s (%s.%s) is unknown.\n", cell->type.c_str(), log_id(module), log_id(cell)); + } + cells.insert(cell); + + for (auto conn : cell->connections()) + { + if (!cell->output(conn.first)) continue; + int offset = -1; + for (auto bit : conn.second) + { + offset++; + if (!bit.wire) continue; + auto mapped_bit = sigmap(bit); + if (nodes[mapped_bit]) + log_error("Multiple drivers found for wire %s.\n", log_signal(mapped_bit)); + nodes.insert(mapped_bit); + node_origins[mapped_bit] = ModIndex::PortInfo(cell, conn.first, offset); + cell_fanout[cell].insert(mapped_bit); + } + } + + int fanin = 0; + for (auto conn : cell->connections()) + { + if (!cell->input(conn.first)) continue; + for (auto bit : sigmap(conn.second)) + { + if (!bit.wire) continue; + for (auto fanout_bit : cell_fanout[cell]) + { + edges_fw[bit].insert(fanout_bit); + edges_bw[fanout_bit].insert(bit); + } + fanin++; + } + } + + if (fanin > order) + log_error("Cell %s (%s.%s) with fan-in %d cannot be mapped to a %d-LUT.\n", + cell->type.c_str(), log_id(module), log_id(cell), fanin, order); + } + } + + for (auto edge : edges_fw) + { + if (!nodes[edge.first]) + { + inputs.insert(edge.first); + nodes.insert(edge.first); + } + } + + for (auto node : nodes) + { + auto node_info = index.query(node); + if (node_info->is_output && !inputs[node]) + outputs.insert(node); + for (auto port : node_info->ports) + if (!cell_types[port.cell->type] && !inputs[node]) + outputs.insert(node); + } + + for (auto node : nodes) + labels[node] = -1; + for (auto input : inputs) + labels[input] = 0; + + if (debug) + { + dump_dot_graph("flowmap-init.dot"); + log("Dumped complete combinatorial graph to `flowmap-init.dot`.\n"); + } + + pool worklist = nodes; + int debug_num = 0; + while (!worklist.empty()) + { + auto sink = worklist.pop(); + if (labels[sink] != -1) + continue; + + bool inputs_have_labels = true; + for (auto sink_input : edges_bw[sink]) + { + if (labels[sink_input] == -1) + { + inputs_have_labels = false; + break; + } + } + if (!inputs_have_labels) + continue; + + if (debug) + { + debug_num++; + log("Examining subgraph %d rooted in %s.\n", debug_num, log_signal(sink)); + } + + pool subgraph = find_subgraph(sink); + + int p = 1; + for (auto subgraph_node : subgraph) + p = max(p, labels[subgraph_node]); + + FlowGraph flow_graph = build_flow_graph(sink, p); + int flow = flow_graph.maximum_flow(order); + pool x, xi; + if (flow <= order) + { + labels[sink] = p; + auto cut = flow_graph.edge_cut(); + x = cut.first; + xi = cut.second; + } + else + { + labels[sink] = p + 1; + x = subgraph; + x.erase(sink); + xi.insert(sink); + } + lut_gates[sink] = xi; + + pool k; + for (auto xi_node : xi) + { + for (auto xi_node_pred : edges_bw[xi_node]) + if (x[xi_node_pred]) + k.insert(xi_node_pred); + } + log_assert((int)k.size() <= order); + lut_inputs[sink] = k; + + if (debug) + { + log(" Maximum flow: %d. Assigned label %d.\n", flow, labels[sink]); + dump_dot_graph(stringf("flowmap-%d-sub.dot", debug_num), subgraph, {x, xi}); + log(" Dumped subgraph to `flowmap-%d-sub.dot`.\n", debug_num); + flow_graph.dump_dot_graph(stringf("flowmap-%d-flow.dot", debug_num)); + log(" Dumped flow graph to `flowmap-%d-flow.dot`.\n", debug_num); + log(" LUT packed:"); + for (auto xi_node : xi) + log(" %s", log_signal(xi_node)); + log(".\n"); + log(" LUT inputs:"); + for (auto k_node : k) + log(" %s", log_signal(k_node)); + log(".\n"); + } + + for (auto sink_succ : edges_fw[sink]) + worklist.insert(sink_succ); + } + + if (debug) + { + dump_dot_graph("flowmap-done.dot"); + log("Dumped complete combinatorial graph to `flowmap-done.dot`.\n"); + } + + int depth = 0; + for (auto label : labels) + depth = max(depth, label.second); + log("Maximum depth: %d levels.\n", depth); + + ConstEval ce(module); + for (auto input_node : inputs) + ce.stop(input_node); + + log("\n"); + log("Mapping cells.\n"); + + pool mapped_nodes; + worklist = outputs; + while (!worklist.empty()) + { + auto node = worklist.pop(); + if (node_origins.count(node)) + { + auto origin = node_origins[node]; + if (origin.cell->getPort(origin.port).size() == 1) + log("Mapping %s.%s.%s (%s).\n", + log_id(module), log_id(origin.cell), origin.port.c_str(), log_signal(node)); + else + log("Mapping %s.%s.%s [%d] (%s).\n", + log_id(module), log_id(origin.cell), origin.port.c_str(), origin.offset, log_signal(node)); + } + else + { + log("Mapping %s.%s.\n", log_id(module), log_signal(node)); + } + + for (auto gate_node : lut_gates[node]) + { + log_assert(node_origins.count(gate_node)); + + if (gate_node == node) + continue; + + auto gate_origin = node_origins[gate_node]; + if (gate_origin.cell->getPort(gate_origin.port).size() == 1) + log(" Packing %s.%s.%s (%s).\n", + log_id(module), log_id(gate_origin.cell), gate_origin.port.c_str(), log_signal(gate_node)); + else + log(" Packing %s.%s.%s [%d] (%s).\n", + log_id(module), log_id(gate_origin.cell), gate_origin.port.c_str(), gate_origin.offset, log_signal(gate_node)); + } + + vector input_nodes(lut_inputs[node].begin(), lut_inputs[node].end()); + RTLIL::Const lut_table(State::Sx, 1 << input_nodes.size()); + for (unsigned i = 0; i < (1 << input_nodes.size()); i++) + { + ce.push(); + for (size_t n = 0; n < input_nodes.size(); n++) + ce.set(input_nodes[n], ((i >> n) & 1) ? State::S1 : State::S0); + + RTLIL::SigSpec value = node, undef; + if (!ce.eval(value, undef)) + { + string env; + for (auto input_node : input_nodes) + env += stringf(" %s = %s\n", log_signal(input_node), log_signal(ce.values_map(input_node))); + log_error("Cannot evaluate %s because %s is not defined.\nEvaluation environment:\n%s", + log_signal(node), log_signal(undef), env.c_str()); + } + + lut_table[i] = value.as_bool() ? State::S1 : State::S0; + ce.pop(); + } + + RTLIL::SigSpec lut_a, lut_y = node; + for (auto input_node : input_nodes) + lut_a.append_bit(input_node); + + RTLIL::Cell *lut = module->addLut(NEW_ID, lut_a, lut_y, lut_table); + mapped_count++; + + for (auto gate_node : lut_gates[node]) + { + auto gate_origin = node_origins[gate_node]; + lut->add_strpool_attribute("\\src", gate_origin.cell->get_strpool_attribute("\\src")); + packed_count++; + } + + log(" Packed into a %d-LUT %s.%s.\n", (int)input_nodes.size(), log_id(module), log_id(lut)); + + mapped_nodes.insert(node); + for (auto input_node : input_nodes) + { + if (!mapped_nodes[input_node] && !inputs[input_node]) + worklist.insert(input_node); + } + } + + unique_packed_count += nodes.size(); + + for (auto node : mapped_nodes) + { + auto origin = node_origins[node]; + RTLIL::SigSpec driver = origin.cell->getPort(origin.port); + driver[origin.offset] = module->addWire(NEW_ID); + origin.cell->setPort(origin.port, driver); + } + } +}; + +static void split(std::vector &tokens, const std::string &text, char sep) +{ + size_t start = 0, end = 0; + while ((end = text.find(sep, start)) != std::string::npos) { + tokens.push_back(text.substr(start, end - start)); + start = end + 1; + } + tokens.push_back(text.substr(start)); +} + +struct FlowmapPass : public Pass { + FlowmapPass() : Pass("flowmap", "pack LUTs with FlowMap") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" flowmap [options] [selection]\n"); + log("\n"); + log("This pass uses the FlowMap technology mapping algorithm to pack logic gates\n"); + log("into k-LUTs with optimal depth. It allows mapping any circuit elements that can\n"); + log("be evaluated with the `eval` pass, including cells with multiple output ports\n"); + log("and multi-bit input and output ports.\n"); + log("\n"); + log(" -maxlut \n"); + log(" perform technology mapping for a k-LUT architecture. if not specified,\n"); + log(" defaults to 3.\n"); + log("\n"); + log(" -cells [,,...]\n"); + log(" map specified cells. if not specified, maps $_NOT_, $_AND_, $_OR_,\n"); + log(" $_XOR_ and $_MUX_, which are the outputs of the `simplemap` pass.\n"); + log("\n"); + log(" -debug\n"); + log(" dump intermediate graphs.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing FLOWMAP pass (pack LUTs with FlowMap).\n"); + + int order = 3; + vector cells; + bool debug = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-maxlut" && argidx + 1 < args.size()) + { + order = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-cells" && argidx + 1 < args.size()) + { + split(cells, args[++argidx], ','); + continue; + } + if (args[argidx] == "-debug") + { + debug = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + pool cell_types; + if (!cells.empty()) + { + for (auto &cell : cells) + cell_types.insert(cell); + } + else + { + cell_types = {"$_NOT_", "$_AND_", "$_OR_", "$_XOR_", "$_MUX_"}; + } + + int mapped_count = 0, packed_count = 0, unique_packed_count = 0; + for (auto module : design->selected_modules()) + { + FlowmapWorker worker(order, cell_types, debug, module); + mapped_count += worker.mapped_count; + packed_count += worker.packed_count; + unique_packed_count += worker.unique_packed_count; + } + + log("\n"); + log("Mapped %d LUTs.\n", mapped_count); + log("Packed %d cells %d times.\n", unique_packed_count, packed_count); + } +} FlowmapPass; + +PRIVATE_NAMESPACE_END From 7850a0c28a2a2d7761d49e23e139a93e837a3cca Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 4 Jan 2019 02:33:10 +0000 Subject: [PATCH 37/71] flowmap: add link to longer version of paper. NFC. --- passes/techmap/flowmap.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index cdca78db8..adfb70a69 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -19,7 +19,7 @@ // [[CITE]] // Jason Cong; Yuzheng Ding, "An Optimal Technology Mapping Algorithm for Delay Optimization in Lookup-Table Based FPGA Designs," -// Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on, vol. 13, no. 1, Jan 1994 +// Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on, Vol. 13, pp. 1-12, Jan. 1994. // doi: 10.1109/43.273754 // Required reading material: @@ -27,7 +27,8 @@ // Min-cut max-flow theorem: // https://www.coursera.org/lecture/algorithms-part2/maxflow-mincut-theorem-beb9G // FlowMap paper: -// http://cadlab.cs.ucla.edu/~cong/papers/iccad92.pdf +// http://cadlab.cs.ucla.edu/~cong/papers/iccad92.pdf (short version) +// https://limsk.ece.gatech.edu/book/papers/flowmap.pdf (long version) // Notes on implementation: // From fd21564deb73c6fe19c5985ae5c168f628f190d6 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 4 Jan 2019 02:46:27 +0000 Subject: [PATCH 38/71] flowmap: improve debug graph output. NFC. --- passes/techmap/flowmap.cc | 123 +++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 47 deletions(-) diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index adfb70a69..5d6b8b645 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -64,10 +64,10 @@ PRIVATE_NAMESPACE_BEGIN struct GraphStyle { string label; - string color; + string color, fillcolor; - GraphStyle(string label = "", string color = "black") : - label(label), color(color) {} + GraphStyle(string label = "", string color = "black", string fillcolor = "") : + label(label), color(color), fillcolor(fillcolor) {} }; static string dot_escape(string value) @@ -110,13 +110,11 @@ static void dump_dot_graph(string filename, if (outputs[node]) shape = "octagon"; auto prop = node_style(node); - string id; - if (node == SigBit()) - id = "(source)"; - else - id = log_signal(node); - fprintf(f, " n%d [ shape=%s, fontname=\"Monospace\", label=\"%s%s\", color=\"%s\" ];\n", - ids[node], shape.c_str(), dot_escape(id).c_str(), dot_escape(prop.label.c_str()).c_str(), prop.color.c_str()); + string style = ""; + if (!prop.fillcolor.empty()) + style = "filled"; + fprintf(f, " n%d [ shape=%s, fontname=\"Monospace\", label=\"%s\", color=\"%s\", fillcolor=\"%s\", style=\"%s\" ];\n", + ids[node], shape.c_str(), dot_escape(prop.label.c_str()).c_str(), prop.color.c_str(), prop.fillcolor.c_str(), style.c_str()); } fprintf(f, " { rank=\"source\"; "); @@ -138,8 +136,8 @@ static void dump_dot_graph(string filename, if (nodes[source] && nodes[sink]) { auto prop = edge_style(source, sink); - fprintf(f, " n%d -> n%d [ label=\"%s\", color=\"%s\" ];\n", - ids[source], ids[sink], dot_escape(prop.label.c_str()).c_str(), prop.color.c_str()); + fprintf(f, " n%d -> n%d [ label=\"%s\", color=\"%s\", fillcolor=\"%s\" ];\n", + ids[source], ids[sink], dot_escape(prop.label.c_str()).c_str(), prop.color.c_str(), prop.fillcolor.c_str()); } } } @@ -164,7 +162,7 @@ struct FlowGraph void dump_dot_graph(string filename) { auto node_style = [&](RTLIL::SigBit node) { - string label; + string label = (node == source) ? "(source)" : log_signal(node); for (auto collapsed_node : collapsed[node]) label += stringf(" %s", log_signal(collapsed_node)); int flow = node_flow[node]; @@ -423,28 +421,46 @@ struct FlowmapWorker int mapped_count = 0, packed_count = 0, unique_packed_count = 0; - void dump_dot_graph(string filename, pool subgraph = {}, pair, pool> cut = {}) + void dump_dot_graph(string filename, + pool subgraph_nodes = {}, dict> subgraph_edges = {}, + pair, pool> cut = {}, + dict> collapsed = {}) { - if (subgraph.empty()) - subgraph = nodes; + if (subgraph_nodes.empty()) + subgraph_nodes = nodes; + if (subgraph_edges.empty()) + subgraph_edges = edges_fw; auto node_style = [&](RTLIL::SigBit node) { - string label, color; + string label = log_signal(node); + for (auto collapsed_node : collapsed[node]) + if (collapsed_node != node) + label += stringf(" %s", log_signal(collapsed_node)); if (labels[node] == -1) - label = string("\n"); + label += "\nl=?"; else - label = stringf("\nl=%d", labels[node]); - color = "black"; - if (cut.first[node]) - color = "blue"; - if (cut.second[node]) - color = "red"; - return GraphStyle{label, color}; + label += stringf("\nl=%d", labels[node]); + if (cut.first.empty() && cut.second.empty()) + { + if (labels[node] == -1) + return GraphStyle{label}; + string fillcolor = stringf("/set311/%d", 1 + labels[node] % 11); + return GraphStyle{label, "", fillcolor}; + } + else + { + string color = "black"; + if (cut.first[node]) + color = "blue"; + if (cut.second[node]) + color = "red"; + return GraphStyle{label, color}; + } }; auto edge_style = [&](RTLIL::SigBit, RTLIL::SigBit) { return GraphStyle{}; }; - ::dump_dot_graph(filename, subgraph, edges_fw, inputs, outputs, node_style, edge_style, module->name.str()); + ::dump_dot_graph(filename, subgraph_nodes, subgraph_edges, inputs, outputs, node_style, edge_style, module->name.str()); } pool find_subgraph(RTLIL::SigBit sink) @@ -582,8 +598,8 @@ struct FlowmapWorker if (debug) { - dump_dot_graph("flowmap-init.dot"); - log("Dumped complete combinatorial graph to `flowmap-init.dot`.\n"); + dump_dot_graph("flowmap-initial.dot"); + log("Dumped complete initial graph to `flowmap-initial.dot`.\n"); } pool worklist = nodes; @@ -650,7 +666,7 @@ struct FlowmapWorker if (debug) { log(" Maximum flow: %d. Assigned label %d.\n", flow, labels[sink]); - dump_dot_graph(stringf("flowmap-%d-sub.dot", debug_num), subgraph, {x, xi}); + dump_dot_graph(stringf("flowmap-%d-sub.dot", debug_num), subgraph, {}, {x, xi}); log(" Dumped subgraph to `flowmap-%d-sub.dot`.\n", debug_num); flow_graph.dump_dot_graph(stringf("flowmap-%d-flow.dot", debug_num)); log(" Dumped flow graph to `flowmap-%d-flow.dot`.\n", debug_num); @@ -668,17 +684,40 @@ struct FlowmapWorker worklist.insert(sink_succ); } - if (debug) - { - dump_dot_graph("flowmap-done.dot"); - log("Dumped complete combinatorial graph to `flowmap-done.dot`.\n"); - } - int depth = 0; for (auto label : labels) depth = max(depth, label.second); log("Maximum depth: %d levels.\n", depth); + if (debug) + { + dump_dot_graph("flowmap-labeled.dot"); + log("Dumped complete labeled graph to `flowmap-labeled.dot`.\n"); + } + + pool lut_nodes; + dict> lut_edges; + worklist = outputs; + while (!worklist.empty()) + { + auto lut_node = worklist.pop(); + lut_nodes.insert(lut_node); + for (auto input_node : lut_inputs[lut_node]) + { + lut_edges[input_node].insert(lut_node); + if (!lut_nodes[input_node] && !inputs[input_node]) + worklist.insert(input_node); + } + } + + if (debug) + { + pool lut_and_input_nodes = lut_nodes; + lut_and_input_nodes.insert(inputs.begin(), inputs.end()); + dump_dot_graph("flowmap-packed.dot", lut_and_input_nodes, lut_edges, {}, lut_gates); + log("Dumped complete packed graph to `flowmap-packed.dot`.\n"); + } + ConstEval ce(module); for (auto input_node : inputs) ce.stop(input_node); @@ -687,10 +726,8 @@ struct FlowmapWorker log("Mapping cells.\n"); pool mapped_nodes; - worklist = outputs; - while (!worklist.empty()) + for (auto node : lut_nodes) { - auto node = worklist.pop(); if (node_origins.count(node)) { auto origin = node_origins[node]; @@ -750,22 +787,14 @@ struct FlowmapWorker RTLIL::Cell *lut = module->addLut(NEW_ID, lut_a, lut_y, lut_table); mapped_count++; - + mapped_nodes.insert(node); for (auto gate_node : lut_gates[node]) { auto gate_origin = node_origins[gate_node]; lut->add_strpool_attribute("\\src", gate_origin.cell->get_strpool_attribute("\\src")); packed_count++; } - log(" Packed into a %d-LUT %s.%s.\n", (int)input_nodes.size(), log_id(module), log_id(lut)); - - mapped_nodes.insert(node); - for (auto input_node : input_nodes) - { - if (!mapped_nodes[input_node] && !inputs[input_node]) - worklist.insert(input_node); - } } unique_packed_count += nodes.size(); From 3b17c9018acd9d8bee12e653745fb2f00105bd58 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 4 Jan 2019 11:37:25 +0100 Subject: [PATCH 39/71] Unify usage of noflatten among architectures --- techlibs/achronix/synth_achronix.cc | 4 ++-- techlibs/coolrunner2/synth_coolrunner2.cc | 2 +- techlibs/gowin/synth_gowin.cc | 14 +++++++++++--- techlibs/intel/synth_intel.cc | 4 ++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/techlibs/achronix/synth_achronix.cc b/techlibs/achronix/synth_achronix.cc index 3642e3bd3..626860d9c 100755 --- a/techlibs/achronix/synth_achronix.cc +++ b/techlibs/achronix/synth_achronix.cc @@ -95,8 +95,8 @@ struct SynthAchronixPass : public ScriptPass { run_to = args[argidx].substr(pos+1); continue; } - if (args[argidx] == "-flatten") { - flatten = true; + if (args[argidx] == "-noflatten") { + flatten = false; continue; } if (args[argidx] == "-retime") { diff --git a/techlibs/coolrunner2/synth_coolrunner2.cc b/techlibs/coolrunner2/synth_coolrunner2.cc index 810380d4a..21bbcaef4 100644 --- a/techlibs/coolrunner2/synth_coolrunner2.cc +++ b/techlibs/coolrunner2/synth_coolrunner2.cc @@ -129,7 +129,7 @@ struct SynthCoolrunner2Pass : public ScriptPass run(stringf("hierarchy -check %s", help_mode ? "-top " : top_opt.c_str())); } - if (check_label("flatten", "(unless -noflatten)") && flatten) + if (flatten && check_label("flatten", "(unless -noflatten)")) { run("proc"); run("flatten"); diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc index 9700b3898..96128a680 100644 --- a/techlibs/gowin/synth_gowin.cc +++ b/techlibs/gowin/synth_gowin.cc @@ -52,6 +52,9 @@ struct SynthGowinPass : public ScriptPass log(" -nobram\n"); log(" do not use BRAM cells in output netlist\n"); log("\n"); + log(" -noflatten\n"); + log(" do not flatten design before synthesis\n"); + log("\n"); log(" -retime\n"); log(" run 'abc' with -dff option\n"); log("\n"); @@ -62,14 +65,15 @@ struct SynthGowinPass : public ScriptPass } string top_opt, vout_file; - bool retime, nobram; + bool retime, flatten, nobram; void clear_flags() YS_OVERRIDE { top_opt = "-auto-top"; vout_file = ""; retime = false; - nobram = true; + flatten = true; + nobram = true; } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE @@ -104,6 +108,10 @@ struct SynthGowinPass : public ScriptPass nobram = true; continue; } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } break; } extra_args(args, argidx, design); @@ -127,7 +135,7 @@ struct SynthGowinPass : public ScriptPass run(stringf("hierarchy -check %s", help_mode ? "-top " : top_opt.c_str())); } - if (check_label("flatten") && check_label("flatten", "(unless -noflatten)")) + if (flatten && check_label("flatten", "(unless -noflatten)")) { run("proc"); run("flatten"); diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index d74f295ec..0f1d7a7b5 100644 --- a/techlibs/intel/synth_intel.cc +++ b/techlibs/intel/synth_intel.cc @@ -131,8 +131,8 @@ struct SynthIntelPass : public ScriptPass { nobram = true; continue; } - if (args[argidx] == "-flatten") { - flatten = true; + if (args[argidx] == "-noflatten") { + flatten = false; continue; } if (args[argidx] == "-retime") { From 9bc5cf08440188fa3dab1115f10a0859ee2c7252 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 4 Jan 2019 04:54:20 +0000 Subject: [PATCH 40/71] flowmap: cleanup for clarity. NFCI. --- passes/techmap/flowmap.cc | 260 ++++++++++++++++++++--------------- passes/tests/flowmap/flow.v | 22 +++ passes/tests/flowmap/flowp.v | 16 +++ 3 files changed, 185 insertions(+), 113 deletions(-) create mode 100644 passes/tests/flowmap/flow.v create mode 100644 passes/tests/flowmap/flowp.v diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index 5d6b8b645..1b53de4db 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -402,29 +402,34 @@ struct FlowGraph struct FlowmapWorker { int order; - pool cell_types; bool debug; RTLIL::Module *module; SigMap sigmap; ModIndex index; - pool cells; + + dict node_origins; pool nodes, inputs, outputs; dict> edges_fw, edges_bw; dict labels; - dict> lut_gates, lut_inputs; + pool lut_nodes; + dict> lut_gates; + dict> lut_edges_fw, lut_edges_bw; - dict node_origins; - dict> cell_fanout; + int gate_count = 0, lut_count = 0, packed_count = 0; + int gate_area = 0, lut_area = 0; - int mapped_count = 0, packed_count = 0, unique_packed_count = 0; + enum class GraphMode { + Label, + Cut, + }; - void dump_dot_graph(string filename, + void dump_dot_graph(string filename, GraphMode mode, pool subgraph_nodes = {}, dict> subgraph_edges = {}, - pair, pool> cut = {}, - dict> collapsed = {}) + dict> collapsed = {}, + pair, pool> cut = {}) { if (subgraph_nodes.empty()) subgraph_nodes = nodes; @@ -436,26 +441,29 @@ struct FlowmapWorker for (auto collapsed_node : collapsed[node]) if (collapsed_node != node) label += stringf(" %s", log_signal(collapsed_node)); - if (labels[node] == -1) - label += "\nl=?"; - else - label += stringf("\nl=%d", labels[node]); - if (cut.first.empty() && cut.second.empty()) + switch (mode) { - if (labels[node] == -1) + case GraphMode::Label: + if (labels[node] == -1) + { + label += "\nl=?"; + return GraphStyle{label}; + } + else + { + label += stringf("\nl=%d", labels[node]); + string fillcolor = stringf("/set311/%d", 1 + labels[node] % 11); + return GraphStyle{label, "", fillcolor}; + } + + case GraphMode::Cut: + if (cut.first[node]) + return GraphStyle{label, "blue"}; + if (cut.second[node]) + return GraphStyle{label, "red"}; return GraphStyle{label}; - string fillcolor = stringf("/set311/%d", 1 + labels[node] % 11); - return GraphStyle{label, "", fillcolor}; - } - else - { - string color = "black"; - if (cut.first[node]) - color = "blue"; - if (cut.second[node]) - color = "red"; - return GraphStyle{label, color}; } + return GraphStyle{label}; }; auto edge_style = [&](RTLIL::SigBit, RTLIL::SigBit) { return GraphStyle{}; @@ -519,57 +527,56 @@ struct FlowmapWorker return flow_graph; } - FlowmapWorker(int order, pool cell_types, bool debug, RTLIL::Module *module) : - order(order), cell_types(cell_types), debug(debug), module(module), sigmap(module), index(module) + void discover_nodes(pool cell_types) { - log("Labeling cells.\n"); for (auto cell : module->selected_cells()) { - if (cell_types[cell->type]) + if (!cell_types[cell->type]) + continue; + + if (!cell->known()) + log_error("Cell %s (%s.%s) is unknown.\n", cell->type.c_str(), log_id(module), log_id(cell)); + + pool fanout; + for (auto conn : cell->connections()) { - if (!cell->known()) + if (!cell->output(conn.first)) continue; + int offset = -1; + for (auto bit : conn.second) { - log_error("Cell %s (%s.%s) is unknown.\n", cell->type.c_str(), log_id(module), log_id(cell)); + offset++; + if (!bit.wire) continue; + auto mapped_bit = sigmap(bit); + if (nodes[mapped_bit]) + log_error("Multiple drivers found for wire %s.\n", log_signal(mapped_bit)); + nodes.insert(mapped_bit); + node_origins[mapped_bit] = ModIndex::PortInfo(cell, conn.first, offset); + fanout.insert(mapped_bit); } - cells.insert(cell); - - for (auto conn : cell->connections()) - { - if (!cell->output(conn.first)) continue; - int offset = -1; - for (auto bit : conn.second) - { - offset++; - if (!bit.wire) continue; - auto mapped_bit = sigmap(bit); - if (nodes[mapped_bit]) - log_error("Multiple drivers found for wire %s.\n", log_signal(mapped_bit)); - nodes.insert(mapped_bit); - node_origins[mapped_bit] = ModIndex::PortInfo(cell, conn.first, offset); - cell_fanout[cell].insert(mapped_bit); - } - } - - int fanin = 0; - for (auto conn : cell->connections()) - { - if (!cell->input(conn.first)) continue; - for (auto bit : sigmap(conn.second)) - { - if (!bit.wire) continue; - for (auto fanout_bit : cell_fanout[cell]) - { - edges_fw[bit].insert(fanout_bit); - edges_bw[fanout_bit].insert(bit); - } - fanin++; - } - } - - if (fanin > order) - log_error("Cell %s (%s.%s) with fan-in %d cannot be mapped to a %d-LUT.\n", - cell->type.c_str(), log_id(module), log_id(cell), fanin, order); } + + int fanin = 0; + for (auto conn : cell->connections()) + { + if (!cell->input(conn.first)) continue; + for (auto bit : sigmap(conn.second)) + { + if (!bit.wire) continue; + for (auto fanout_bit : fanout) + { + edges_fw[bit].insert(fanout_bit); + edges_bw[fanout_bit].insert(bit); + } + fanin++; + } + } + + if (fanin > order) + log_error("Cell %s (%s.%s) with fan-in %d cannot be mapped to a %d-LUT.\n", + cell->type.c_str(), log_id(module), log_id(cell), fanin, order); + + gate_count++; + gate_area += 1 << fanin; } for (auto edge : edges_fw) @@ -591,15 +598,23 @@ struct FlowmapWorker outputs.insert(node); } + if (debug) + { + dump_dot_graph("flowmap-initial.dot", GraphMode::Label); + log("Dumped initial graph to `flowmap-initial.dot`.\n"); + } + } + + void label_nodes() + { for (auto node : nodes) labels[node] = -1; for (auto input : inputs) - labels[input] = 0; - - if (debug) { - dump_dot_graph("flowmap-initial.dot"); - log("Dumped complete initial graph to `flowmap-initial.dot`.\n"); + if (input.wire->attributes.count("\\$flowmap_level")) + labels[input] = input.wire->attributes["\\$flowmap_level"].as_int(); + else + labels[input] = 0; } pool worklist = nodes; @@ -661,70 +676,73 @@ struct FlowmapWorker k.insert(xi_node_pred); } log_assert((int)k.size() <= order); - lut_inputs[sink] = k; + lut_edges_bw[sink] = k; + for (auto k_node : k) + lut_edges_fw[k_node].insert(sink); if (debug) { log(" Maximum flow: %d. Assigned label %d.\n", flow, labels[sink]); - dump_dot_graph(stringf("flowmap-%d-sub.dot", debug_num), subgraph, {}, {x, xi}); + dump_dot_graph(stringf("flowmap-%d-sub.dot", debug_num), GraphMode::Cut, subgraph, {}, {}, {x, xi}); log(" Dumped subgraph to `flowmap-%d-sub.dot`.\n", debug_num); flow_graph.dump_dot_graph(stringf("flowmap-%d-flow.dot", debug_num)); log(" Dumped flow graph to `flowmap-%d-flow.dot`.\n", debug_num); - log(" LUT packed:"); - for (auto xi_node : xi) - log(" %s", log_signal(xi_node)); - log(".\n"); log(" LUT inputs:"); for (auto k_node : k) log(" %s", log_signal(k_node)); log(".\n"); + log(" LUT packed gates:"); + for (auto xi_node : xi) + log(" %s", log_signal(xi_node)); + log(".\n"); } for (auto sink_succ : edges_fw[sink]) worklist.insert(sink_succ); } - int depth = 0; - for (auto label : labels) - depth = max(depth, label.second); - log("Maximum depth: %d levels.\n", depth); - if (debug) { - dump_dot_graph("flowmap-labeled.dot"); - log("Dumped complete labeled graph to `flowmap-labeled.dot`.\n"); + dump_dot_graph("flowmap-labeled.dot", GraphMode::Label); + log("Dumped labeled graph to `flowmap-labeled.dot`.\n"); } + } - pool lut_nodes; - dict> lut_edges; - worklist = outputs; + int pack_luts() + { + pool worklist = outputs; while (!worklist.empty()) { auto lut_node = worklist.pop(); lut_nodes.insert(lut_node); - for (auto input_node : lut_inputs[lut_node]) - { - lut_edges[input_node].insert(lut_node); + for (auto input_node : lut_edges_bw[lut_node]) if (!lut_nodes[input_node] && !inputs[input_node]) worklist.insert(input_node); - } } + int depth = 0; + for (auto label : labels) + depth = max(depth, label.second); + log("Solved to %d LUTs in %d levels.\n", (int)lut_nodes.size(), depth); + if (debug) { - pool lut_and_input_nodes = lut_nodes; + pool lut_and_input_nodes; + lut_and_input_nodes.insert(lut_nodes.begin(), lut_nodes.end()); lut_and_input_nodes.insert(inputs.begin(), inputs.end()); - dump_dot_graph("flowmap-packed.dot", lut_and_input_nodes, lut_edges, {}, lut_gates); - log("Dumped complete packed graph to `flowmap-packed.dot`.\n"); + dump_dot_graph("flowmap-packed.dot", GraphMode::Label, lut_and_input_nodes, lut_edges_fw, lut_gates); + log("Dumped packed graph to `flowmap-packed.dot`.\n"); } + return depth; + } + + void map_cells() + { ConstEval ce(module); for (auto input_node : inputs) ce.stop(input_node); - log("\n"); - log("Mapping cells.\n"); - pool mapped_nodes; for (auto node : lut_nodes) { @@ -759,7 +777,7 @@ struct FlowmapWorker log_id(module), log_id(gate_origin.cell), gate_origin.port.c_str(), gate_origin.offset, log_signal(gate_node)); } - vector input_nodes(lut_inputs[node].begin(), lut_inputs[node].end()); + vector input_nodes(lut_edges_bw[node].begin(), lut_edges_bw[node].end()); RTLIL::Const lut_table(State::Sx, 1 << input_nodes.size()); for (unsigned i = 0; i < (1 << input_nodes.size()); i++) { @@ -786,7 +804,6 @@ struct FlowmapWorker lut_a.append_bit(input_node); RTLIL::Cell *lut = module->addLut(NEW_ID, lut_a, lut_y, lut_table); - mapped_count++; mapped_nodes.insert(node); for (auto gate_node : lut_gates[node]) { @@ -794,11 +811,11 @@ struct FlowmapWorker lut->add_strpool_attribute("\\src", gate_origin.cell->get_strpool_attribute("\\src")); packed_count++; } + lut_count++; + lut_area += 1 << input_nodes.size(); log(" Packed into a %d-LUT %s.%s.\n", (int)input_nodes.size(), log_id(module), log_id(lut)); } - unique_packed_count += nodes.size(); - for (auto node : mapped_nodes) { auto origin = node_origins[node]; @@ -807,6 +824,19 @@ struct FlowmapWorker origin.cell->setPort(origin.port, driver); } } + + FlowmapWorker(int order, pool cell_types, bool debug, RTLIL::Module *module) : + order(order), debug(debug), module(module), sigmap(module), index(module) + { + log("Labeling cells.\n"); + discover_nodes(cell_types); + label_nodes(); + pack_luts(); + + log("\n"); + log("Mapping cells.\n"); + map_cells(); + } }; static void split(std::vector &tokens, const std::string &text, char sep) @@ -832,7 +862,7 @@ struct FlowmapPass : public Pass { log("be evaluated with the `eval` pass, including cells with multiple output ports\n"); log("and multi-bit input and output ports.\n"); log("\n"); - log(" -maxlut \n"); + log(" -maxlut k\n"); log(" perform technology mapping for a k-LUT architecture. if not specified,\n"); log(" defaults to 3.\n"); log("\n"); @@ -846,8 +876,6 @@ struct FlowmapPass : public Pass { } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - log_header(design, "Executing FLOWMAP pass (pack LUTs with FlowMap).\n"); - int order = 3; vector cells; bool debug = false; @@ -885,18 +913,24 @@ struct FlowmapPass : public Pass { cell_types = {"$_NOT_", "$_AND_", "$_OR_", "$_XOR_", "$_MUX_"}; } - int mapped_count = 0, packed_count = 0, unique_packed_count = 0; + log_header(design, "Executing FLOWMAP pass (pack LUTs with FlowMap).\n"); + + int gate_count = 0, lut_count = 0, packed_count = 0; + int gate_area = 0, lut_area = 0; for (auto module : design->selected_modules()) { FlowmapWorker worker(order, cell_types, debug, module); - mapped_count += worker.mapped_count; + gate_count += worker.gate_count; + lut_count += worker.lut_count; packed_count += worker.packed_count; - unique_packed_count += worker.unique_packed_count; + gate_area += worker.gate_area; + lut_area += worker.lut_area; } log("\n"); - log("Mapped %d LUTs.\n", mapped_count); - log("Packed %d cells %d times.\n", unique_packed_count, packed_count); + log("Mapped %d LUTs.\n", lut_count); + log("Packed %d cells; duplicated %d cells.\n", packed_count, packed_count - gate_count); + log("Solution has %.1f%% area overhead.\n", (lut_area - gate_area) * 100.0 / gate_area); } } FlowmapPass; diff --git a/passes/tests/flowmap/flow.v b/passes/tests/flowmap/flow.v new file mode 100644 index 000000000..297ef910e --- /dev/null +++ b/passes/tests/flowmap/flow.v @@ -0,0 +1,22 @@ +// Exact reproduction of Figure 2(a) from 10.1109/43.273754. +module top(...); + input a,b,c,d,e,f; + wire nA = b&c; + wire A = !nA; + wire nB = c|d; + wire B = !nB; + wire nC = e&f; + wire C = !nC; + wire D = A|B; + wire E = a&D; + wire nF = D&C; + wire F = !nF; + wire nG = F|B; + wire G = !nG; + wire H = a&F; + wire I = E|G; + wire J = G&C; + wire np = H&I; + output p = !np; + output q = A|J; +endmodule diff --git a/passes/tests/flowmap/flowp.v b/passes/tests/flowmap/flowp.v new file mode 100644 index 000000000..2fb40ffa4 --- /dev/null +++ b/passes/tests/flowmap/flowp.v @@ -0,0 +1,16 @@ +// Like flow.v, but results in a network identical to Figure 2(b). +module top(...); + input a,b,c,d,e,f; + wire A = b&c; + wire B = c|d; + wire C = e&f; + wire D = A|B; + wire E = a&D; + wire F = D&C; + wire G = F|B; + wire H = a&F; + wire I = E|G; + wire J = G&C; + output p = H&I; + output q = A|J; +endmodule From f5d23d4c7af33f73b1d9250e458b0932ff7a965b Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 4 Jan 2019 14:44:35 +0100 Subject: [PATCH 41/71] Update Verific default path Signed-off-by: Clifford Wolf --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 128539f6a..d83a71256 100644 --- a/Makefile +++ b/Makefile @@ -357,7 +357,7 @@ endif endif ifeq ($(ENABLE_VERIFIC),1) -VERIFIC_DIR ?= /usr/local/src/verific_lib_eval +VERIFIC_DIR ?= /usr/local/src/verific_lib VERIFIC_COMPONENTS ?= verilog vhdl database util containers hier_tree CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC ifeq ($(OS), Darwin) From 6d1e7e9403c81289492af889922df9aa3e3842b0 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 4 Jan 2019 15:03:29 +0100 Subject: [PATCH 42/71] Remove -m32 Verific eval lib build instructions Signed-off-by: Clifford Wolf --- frontends/verific/README | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/frontends/verific/README b/frontends/verific/README index b4c436a3a..c76cdd637 100644 --- a/frontends/verific/README +++ b/frontends/verific/README @@ -4,35 +4,6 @@ This directory contains Verific bindings for Yosys. See http://www.verific.com/ for details. -Building Yosys with the 32 bit Verific eval library on amd64: -============================================================= - -1.) Use a Makefile.conf like the following one: - ---snip-- -CONFIG := gcc -ENABLE_TCL := 0 -ENABLE_PLUGINS := 0 -ENABLE_VERIFIC := 1 -CXXFLAGS += -m32 -LDFLAGS += -m32 -VERIFIC_DIR = /usr/local/src/verific_lib_eval ---snap-- - - -2.) Install the necessary multilib packages - -Hint: On debian/ubuntu the multilib packages have names such as -libreadline-dev:i386 or lib32readline6-dev, depending on the -exact version of debian/ubuntu you are working with. - - -3.) Build and test - -make -j8 -./yosys -p 'verific -sv frontends/verific/example.sv; verific -import top' - - Verific Features that should be enabled in your Verific library =============================================================== From 50ef4561d49a471162799fd1f2cd42af408c0b73 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 4 Jan 2019 15:15:23 +0100 Subject: [PATCH 43/71] Fix cells_sim.v for Achronix FPGA --- techlibs/achronix/speedster22i/cells_sim.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/achronix/speedster22i/cells_sim.v b/techlibs/achronix/speedster22i/cells_sim.v index da23fed7e..a94dce9b1 100755 --- a/techlibs/achronix/speedster22i/cells_sim.v +++ b/techlibs/achronix/speedster22i/cells_sim.v @@ -61,7 +61,7 @@ reg [1:0] s1; end endfunction -always @(dataa_w or datab_w or datac_w or datad_w or cin_w) begin +always @(dataa_w or datab_w or datac_w or datad_w) begin combout_rt = lut_data(lut_function, dataa_w, datab_w, datac_w, datad_w); end From 2fcc1ee72edc4f94b4065696def810b38e503656 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 4 Jan 2019 21:18:03 +0000 Subject: [PATCH 44/71] flowmap: add -minlut option, to allow postprocessing with opt_lut. --- passes/techmap/flowmap.cc | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index 1b53de4db..8c127cb65 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -737,7 +737,7 @@ struct FlowmapWorker return depth; } - void map_cells() + void map_cells(int minlut) { ConstEval ce(module); for (auto input_node : inputs) @@ -778,7 +778,7 @@ struct FlowmapWorker } vector input_nodes(lut_edges_bw[node].begin(), lut_edges_bw[node].end()); - RTLIL::Const lut_table(State::Sx, 1 << input_nodes.size()); + RTLIL::Const lut_table(State::Sx, max(1 << input_nodes.size(), 1 << minlut)); for (unsigned i = 0; i < (1 << input_nodes.size()); i++) { ce.push(); @@ -802,6 +802,7 @@ struct FlowmapWorker RTLIL::SigSpec lut_a, lut_y = node; for (auto input_node : input_nodes) lut_a.append_bit(input_node); + lut_a.append(RTLIL::Const(State::Sx, minlut - input_nodes.size())); RTLIL::Cell *lut = module->addLut(NEW_ID, lut_a, lut_y, lut_table); mapped_nodes.insert(node); @@ -812,8 +813,12 @@ struct FlowmapWorker packed_count++; } lut_count++; - lut_area += 1 << input_nodes.size(); - log(" Packed into a %d-LUT %s.%s.\n", (int)input_nodes.size(), log_id(module), log_id(lut)); + lut_area += lut_table.size(); + + if ((int)input_nodes.size() >= minlut) + log(" Packed into a %d-LUT %s.%s.\n", (int)input_nodes.size(), log_id(module), log_id(lut)); + else + log(" Packed into a %d-LUT %s.%s (implemented as %d-LUT).\n", (int)input_nodes.size(), log_id(module), log_id(lut), minlut); } for (auto node : mapped_nodes) @@ -825,7 +830,7 @@ struct FlowmapWorker } } - FlowmapWorker(int order, pool cell_types, bool debug, RTLIL::Module *module) : + FlowmapWorker(int order, int minlut, pool cell_types, bool debug, RTLIL::Module *module) : order(order), debug(debug), module(module), sigmap(module), index(module) { log("Labeling cells.\n"); @@ -835,7 +840,7 @@ struct FlowmapWorker log("\n"); log("Mapping cells.\n"); - map_cells(); + map_cells(minlut); } }; @@ -866,6 +871,9 @@ struct FlowmapPass : public Pass { log(" perform technology mapping for a k-LUT architecture. if not specified,\n"); log(" defaults to 3.\n"); log("\n"); + log(" -minlut n\n"); + log(" only produce n-input or larger LUTs. if not specified, defaults to 1.\n"); + log("\n"); log(" -cells [,,...]\n"); log(" map specified cells. if not specified, maps $_NOT_, $_AND_, $_OR_,\n"); log(" $_XOR_ and $_MUX_, which are the outputs of the `simplemap` pass.\n"); @@ -877,6 +885,7 @@ struct FlowmapPass : public Pass { void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { int order = 3; + int minlut = 1; vector cells; bool debug = false; @@ -888,6 +897,11 @@ struct FlowmapPass : public Pass { order = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-minlut" && argidx + 1 < args.size()) + { + minlut = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-cells" && argidx + 1 < args.size()) { split(cells, args[++argidx], ','); @@ -919,7 +933,7 @@ struct FlowmapPass : public Pass { int gate_area = 0, lut_area = 0; for (auto module : design->selected_modules()) { - FlowmapWorker worker(order, cell_types, debug, module); + FlowmapWorker worker(order, minlut, cell_types, debug, module); gate_count += worker.gate_count; lut_count += worker.lut_count; packed_count += worker.packed_count; From 17ceab92a93f5d2ef0eb26f3fd04df65e4393f9f Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 5 Jan 2019 12:10:24 +0100 Subject: [PATCH 45/71] Bugfix in Verilog string handling Signed-off-by: Clifford Wolf --- frontends/verilog/verilog_lexer.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index f9118e142..1b1873e24 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -274,7 +274,7 @@ YOSYS_NAMESPACE_END yystr[j++] = yystr[i++]; } yystr[j] = 0; - frontend_verilog_yylval.string = new std::string(yystr); + frontend_verilog_yylval.string = new std::string(yystr, j); free(yystr); return TOK_STRING; } From f589ce86bac3169281a077248af328f6758ff0eb Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 5 Jan 2019 17:02:01 +0100 Subject: [PATCH 46/71] Add skeleton Yosys-Libero igloo2 example project Signed-off-by: Clifford Wolf --- examples/igloo2/.gitignore | 2 ++ examples/igloo2/example.v | 22 ++++++++++++++++++++++ examples/igloo2/example.ys | 2 ++ examples/igloo2/libero.sh | 4 ++++ examples/igloo2/libero.tcl | 14 ++++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 examples/igloo2/.gitignore create mode 100644 examples/igloo2/example.v create mode 100644 examples/igloo2/example.ys create mode 100644 examples/igloo2/libero.sh create mode 100644 examples/igloo2/libero.tcl diff --git a/examples/igloo2/.gitignore b/examples/igloo2/.gitignore new file mode 100644 index 000000000..ae86e69cc --- /dev/null +++ b/examples/igloo2/.gitignore @@ -0,0 +1,2 @@ +/example.edn +/work diff --git a/examples/igloo2/example.v b/examples/igloo2/example.v new file mode 100644 index 000000000..3eb7007c5 --- /dev/null +++ b/examples/igloo2/example.v @@ -0,0 +1,22 @@ +module top ( + input clk, + output LED1, + output LED2, + output LED3, + output LED4, + output LED5 +); + + localparam BITS = 5; + localparam LOG2DELAY = 22; + + reg [BITS+LOG2DELAY-1:0] counter = 0; + reg [BITS-1:0] outcnt; + + always @(posedge clk) begin + counter <= counter + 1; + outcnt <= counter >> LOG2DELAY; + end + + assign {LED1, LED2, LED3, LED4, LED5} = outcnt ^ (outcnt >> 1); +endmodule diff --git a/examples/igloo2/example.ys b/examples/igloo2/example.ys new file mode 100644 index 000000000..75a305d86 --- /dev/null +++ b/examples/igloo2/example.ys @@ -0,0 +1,2 @@ +read_verilog example.v +synth_sf2 -top top -edif example.edn diff --git a/examples/igloo2/libero.sh b/examples/igloo2/libero.sh new file mode 100644 index 000000000..582f6ccb9 --- /dev/null +++ b/examples/igloo2/libero.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -ex +rm -rf work +LM_LICENSE_FILE=1702@`hostname` /opt/microsemi/Libero_SoC_v11.9/Libero/bin/libero SCRIPT:libero.tcl diff --git a/examples/igloo2/libero.tcl b/examples/igloo2/libero.tcl new file mode 100644 index 000000000..cc1ab2403 --- /dev/null +++ b/examples/igloo2/libero.tcl @@ -0,0 +1,14 @@ +# Run with "libero SCRIPT:libero.tcl" + +new_project \ + -name top \ + -location work \ + -family IGLOO2 \ + -die PA4MGL500 \ + -package tq144 \ + -speed -1 \ + -hdl VERILOG + +import_files -edif {example.edn} +run_tool –name {COMPILE} +run_tool –name {PLACEROUTEN} From 62c90c4e171493afe0543ae269811a9ad2848802 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sun, 6 Jan 2019 14:40:10 +1300 Subject: [PATCH 47/71] Rename cells based on the wires they drive. --- passes/cmds/rename.cc | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 4b4af0a40..698ce7235 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -61,6 +61,42 @@ static std::string derive_name_from_src(const std::string &src, int counter) return stringf("\\%s$%d", src_base.c_str(), counter); } +static IdString derive_name_from_wire(const RTLIL::Cell &cell) +{ + // Find output + const SigSpec *output = nullptr; + int num_outputs = 0; + for (auto &connection : cell.connections()) { + if (cell.output(connection.first)) { + output = &connection.second; + num_outputs++; + } + } + + if (num_outputs != 1) // Skip cells thad drive multiple outputs + return cell.name; + + std::string name = ""; + for (auto &chunk : output->chunks()) { + // Skip cells that drive privately named wires + if (!chunk.wire || chunk.wire->name.str()[0] == '$') + return cell.name; + + if (name != "") + name += "$"; + + name += chunk.wire->name.str(); + if (chunk.wire->width != chunk.width) { + name += "["; + if (chunk.width != 1) + name += std::to_string(chunk.offset + chunk.width) + ":"; + name += std::to_string(chunk.offset) + "]"; + } + } + + return name + cell.type.str(); +} + struct RenamePass : public Pass { RenamePass() : Pass("rename", "rename object in the design") { } void help() YS_OVERRIDE @@ -77,6 +113,10 @@ struct RenamePass : public Pass { log("Assign names auto-generated from the src attribute to all selected wires and\n"); log("cells with private names.\n"); log("\n"); + log(" rename -wire [selection]\n"); + log("Assign auto-generated names based on the wires they drive to all selected\n"); + log("cells with private names. Ignores cells driving privatly named wires.\n"); + log("\n"); log(" rename -enumerate [-pattern ] [selection]\n"); log("\n"); log("Assign short auto-generated names to all selected wires and cells with private\n"); @@ -98,6 +138,7 @@ struct RenamePass : public Pass { { std::string pattern_prefix = "_", pattern_suffix = "_"; bool flag_src = false; + bool flag_wire = false; bool flag_enumerate = false; bool flag_hide = false; bool flag_top = false; @@ -112,6 +153,11 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-wire" && !got_mode) { + flag_wire = true; + got_mode = true; + continue; + } if (arg == "-enumerate" && !got_mode) { flag_enumerate = true; got_mode = true; @@ -167,6 +213,26 @@ struct RenamePass : public Pass { } } else + if (flag_wire) + { + extra_args(args, argidx, design); + + for (auto &mod : design->modules_) + { + RTLIL::Module *module = mod.second; + if (!design->selected(module)) + continue; + + dict new_cells; + for (auto &it : module->cells_) { + if (it.first[0] == '$' && design->selected(module, it.second)) + it.second->name = derive_name_from_wire(*it.second); + new_cells[it.second->name] = it.second; + } + module->cells_.swap(new_cells); + } + } + else if (flag_enumerate) { extra_args(args, argidx, design); From 8b44198e2366d304880e810ceee5975263db6aca Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 6 Jan 2019 19:51:37 +0000 Subject: [PATCH 48/71] flowmap: construct a max-volume max-flow min-cut, not just any one. --- passes/techmap/flowmap.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index 8c127cb65..be70b579b 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -51,7 +51,8 @@ // 3. The paper ambiguously states: "Moreover, we can find such a cut (X′′, X̅′′) by performing a depth first search starting at the source s, // and including in X′′ all the nodes which are reachable from s." This actually refers to a specific kind of search, mincut computation. // Mincut computation involves computing the set of nodes reachable from s by an undirected path with no full (i.e. zero capacity) forward -// edges or empty (i.e. no flow) backward edges. +// edges or empty (i.e. no flow) backward edges. In addition, the depth first search is required to compute a max-volume max-flow min-cut +// specifically, because a max-flow min-cut is not, in general, unique. #include "kernel/yosys.h" #include "kernel/sigtools.h" @@ -356,10 +357,12 @@ struct FlowGraph NodePrime source_prime = {source, true}; NodePrime sink_prime = {sink, false}; - pool worklist = {source_prime}, visited; + pool visited; + vector worklist = {source_prime}; while (!worklist.empty()) { - auto node_prime = worklist.pop(); + auto node_prime = worklist.back(); + worklist.pop_back(); if (visited[node_prime]) continue; visited.insert(node_prime); @@ -372,18 +375,18 @@ struct FlowGraph if (!node_prime.is_bottom) // top { if (node_flow[node_prime.node] < MAX_NODE_FLOW) - worklist.insert(node_prime.as_bottom()); + worklist.push_back(node_prime.as_bottom()); for (auto node_pred : edges_bw[node_prime.node]) if (edge_flow[{node_pred, node_prime.node}] > 0) - worklist.insert(NodePrime::bottom(node_pred)); + worklist.push_back(NodePrime::bottom(node_pred)); } else // bottom { if (node_flow[node_prime.node] > 0) - worklist.insert(node_prime.as_top()); + worklist.push_back(node_prime.as_top()); for (auto node_succ : edges_fw[node_prime.node]) if (true /* edge_flow[...] < ∞ */) - worklist.insert(NodePrime::top(node_succ)); + worklist.push_back(NodePrime::top(node_succ)); } } From a342d6db49b812efa1457f39dc9713bcda3d54ce Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 7 Jan 2019 00:11:49 +0000 Subject: [PATCH 49/71] bugpoint: new pass. A typical use of `bugpoint` would involve a script with a pass under test, e.g.: flowmap -relax -optarea 100 and would be invoked as: bugpoint -yosys ./yosys -script flowmap.ys -clean -cells This replaces the current design with the minimal design that still crashes the `flowmap.ys` script. `bugpoint` can also be used to perform generic design minimization using `select`, e.g. the following script: select i:* %x t:$_MUX_ %i -assert-max 0 would remove all parts of the design except for an unbroken path from an input to an output port that goes through exactly one $_MUX_ cell. (The condition is inverted.) --- passes/cmds/Makefile.inc | 2 +- passes/cmds/bugpoint.cc | 369 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 passes/cmds/bugpoint.cc diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 44a83b2b9..c8067a8be 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -29,4 +29,4 @@ OBJS += passes/cmds/chformal.o OBJS += passes/cmds/chtype.o OBJS += passes/cmds/blackbox.o OBJS += passes/cmds/ltp.o - +OBJS += passes/cmds/bugpoint.o diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc new file mode 100644 index 000000000..736e94274 --- /dev/null +++ b/passes/cmds/bugpoint.cc @@ -0,0 +1,369 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 whitequark + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "backends/ilang/ilang_backend.h" + +USING_YOSYS_NAMESPACE +using namespace ILANG_BACKEND; +PRIVATE_NAMESPACE_BEGIN + +struct BugpointPass : public Pass { + BugpointPass() : Pass("bugpoint", "minimize testcases") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" bugpoint [options]\n"); + log("\n"); + log("This command minimizes testcases that crash Yosys. It removes an arbitrary part\n"); + log("of the design and recursively invokes Yosys with a given script, repeating these\n"); + log("steps while it can find a smaller design that still causes a crash. Once this\n"); + log("command finishes, it replaces the current design with the smallest testcase it\n"); + log("was able to produce.\n"); + log("\n"); + log("It is possible to specify the kinds of design part that will be removed. If none\n"); + log("are specified, all parts of design will be removed.\n"); + log("\n"); + log(" -yosys \n"); + log(" use this Yosys binary. if not specified, `yosys` is used.\n"); + log("\n"); + log(" -script \n"); + log(" use this script to crash Yosys. required.\n"); + log("\n"); + log(" -grep \n"); + log(" only consider crashes that place this string in the log file.\n"); + log("\n"); + log(" -fast\n"); + log(" run `clean -purge` after each minimization step. converges faster, but\n"); + log(" produces larger testcases, and may fail to produce any testcase at all if\n"); + log(" the crash is related to dangling wires.\n"); + log("\n"); + log(" -clean\n"); + log(" run `clean -purge` before checking testcase and after finishing. produces\n"); + log(" smaller and more useful testcases, but may fail to produce any testcase\n"); + log(" at all if the crash is related to dangling wires.\n"); + log("\n"); + log(" -modules\n"); + log(" try to remove modules.\n"); + log("\n"); + log(" -ports\n"); + log(" try to remove module ports.\n"); + log("\n"); + log(" -cells\n"); + log(" try to remove cells.\n"); + log("\n"); + log(" -connections\n"); + log(" try to reconnect ports to 'x.\n"); + log("\n"); + } + + bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script) + { + design->sort(); + + std::ofstream f("bugpoint-case.il"); + ILANG_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false); + f.close(); + + string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd.c_str(), script.c_str()); + return system(yosys_cmdline.c_str()) == 0; + } + + bool check_logfile(string grep) + { + if (grep.empty()) + return true; + + std::ifstream f("bugpoint-case.log"); + while (!f.eof()) + { + string line; + getline(f, line); + if (line.find(grep) != std::string::npos) + return true; + } + return false; + } + + RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false) + { + if (!do_clean) + return design; + + RTLIL::Design *design_copy = new RTLIL::Design; + for (auto &it : design->modules_) + design_copy->add(it.second->clone()); + Pass::call(design_copy, "clean -purge"); + + if (do_delete) + delete design; + return design_copy; + } + + RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections) + { + RTLIL::Design *design_copy = new RTLIL::Design; + for (auto &it : design->modules_) + design_copy->add(it.second->clone()); + + int index = 0; + if (modules) + { + for (auto &it : design_copy->modules_) + { + if (it.second->get_bool_attribute("\\blackbox")) + continue; + + if (index++ == seed) + { + log("Trying to remove module %s.\n", it.first.c_str()); + design_copy->remove(it.second); + return design_copy; + } + } + } + if (ports) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_bool_attribute("\\blackbox")) + continue; + + for (auto wire : mod->wires()) + { + if (!stage2 && wire->get_bool_attribute("$bugpoint")) + continue; + + if (wire->port_input || wire->port_output) + { + if (index++ == seed) + { + log("Trying to remove module port %s.\n", log_signal(wire)); + wire->port_input = wire->port_output = false; + mod->fixup_ports(); + return design_copy; + } + } + } + } + } + if (cells) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_bool_attribute("\\blackbox")) + continue; + + for (auto &it : mod->cells_) + { + if (index++ == seed) + { + log("Trying to remove cell %s.%s.\n", mod->name.c_str(), it.first.c_str()); + mod->remove(it.second); + return design_copy; + } + } + } + } + if (connections) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_bool_attribute("\\blackbox")) + continue; + + for (auto cell : mod->cells()) + { + for (auto it : cell->connections_) + { + RTLIL::SigSpec port = cell->getPort(it.first); + bool is_undef = port.is_fully_undef(); + bool is_port = port.is_wire() && (port.as_wire()->port_input || port.as_wire()->port_output); + + if(is_undef || (!stage2 && is_port)) + continue; + + if (index++ == seed) + { + log("Trying to remove cell port %s.%s.%s.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str()); + RTLIL::SigSpec port_x(State::Sx, port.size()); + cell->unsetPort(it.first); + cell->setPort(it.first, port_x); + return design_copy; + } + + if (!stage2 && (cell->input(it.first) || cell->output(it.first)) && index++ == seed) + { + log("Trying to expose cell port %s.%s.%s as module port.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str()); + RTLIL::Wire *wire = mod->addWire(NEW_ID, port.size()); + wire->set_bool_attribute("$bugpoint"); + wire->port_input = cell->input(it.first); + wire->port_output = cell->output(it.first); + cell->unsetPort(it.first); + cell->setPort(it.first, wire); + mod->fixup_ports(); + return design_copy; + } + } + } + } + } + return NULL; + } + + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + string yosys_cmd = "yosys", script, grep; + bool fast = false, clean = false; + bool modules = false, ports = false, cells = false, connections = false, has_part = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-yosys" && argidx + 1 < args.size()) { + yosys_cmd = args[++argidx]; + continue; + } + if (args[argidx] == "-script" && argidx + 1 < args.size()) { + script = args[++argidx]; + continue; + } + if (args[argidx] == "-grep" && argidx + 1 < args.size()) { + grep = args[++argidx]; + continue; + } + if (args[argidx] == "-fast") { + fast = true; + continue; + } + if (args[argidx] == "-clean") { + clean = true; + continue; + } + if (args[argidx] == "-modules") { + modules = true; + has_part = true; + continue; + } + if (args[argidx] == "-ports") { + ports = true; + has_part = true; + continue; + } + if (args[argidx] == "-cells") { + cells = true; + has_part = true; + continue; + } + if (args[argidx] == "-connections") { + connections = true; + has_part = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!has_part) + { + modules = true; + ports = true; + cells = true; + connections = true; + } + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + RTLIL::Design *crashing_design = clean_design(design, clean); + if (run_yosys(crashing_design, yosys_cmd, script)) + log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n"); + if (!check_logfile(grep)) + log_cmd_error("The provided grep string is not found in the log file!\n"); + + int seed = 0, crashing_seed = seed; + bool found_something = false, stage2 = false; + while (true) + { + if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections)) + { + simplified = clean_design(simplified, fast, /*do_delete=*/true); + + bool crashes; + if (clean) + { + RTLIL::Design *testcase = clean_design(simplified); + crashes = !run_yosys(testcase, yosys_cmd, script); + delete testcase; + } + else + { + crashes = !run_yosys(simplified, yosys_cmd, script); + } + + if (crashes && check_logfile(grep)) + { + log("Testcase crashes.\n"); + if (crashing_design != design) + delete crashing_design; + crashing_design = simplified; + crashing_seed = seed; + found_something = true; + } + else + { + log("Testcase does not crash.\n"); + delete simplified; + seed++; + } + } + else + { + seed = 0; + if (found_something) + found_something = false; + else + { + if (!stage2) + { + log("Demoting introduced module ports.\n"); + stage2 = true; + } + else + { + log("Simplifications exhausted.\n"); + break; + } + } + } + } + + if (crashing_design != design) + { + Pass::call(design, "design -reset"); + crashing_design = clean_design(crashing_design, clean, /*do_delete=*/true); + for (auto &it : crashing_design->modules_) + design->add(it.second->clone()); + delete crashing_design; + } + } +} BugpointPass; + +PRIVATE_NAMESPACE_END From b5f6e786ea3affc6688ec9d229ae8642f2c9e151 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Mon, 7 Jan 2019 09:45:21 +0100 Subject: [PATCH 50/71] Switch "bugpoint" from system() to run_command() Signed-off-by: Clifford Wolf --- passes/cmds/bugpoint.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 736e94274..606276e64 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -83,7 +83,7 @@ struct BugpointPass : public Pass { f.close(); string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd.c_str(), script.c_str()); - return system(yosys_cmdline.c_str()) == 0; + return run_command(yosys_cmdline) == 0; } bool check_logfile(string grep) From 8a63fc51d31bca50c3d08130c3271a288af1cf4b Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Mon, 7 Jan 2019 10:01:11 +0100 Subject: [PATCH 51/71] Bugfix in $memrd sharing Signed-off-by: Clifford Wolf --- passes/opt/share.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index b80280829..c85c27427 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -710,8 +710,12 @@ struct ShareWorker RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); RTLIL::SigSpec addr1 = c1->getPort("\\ADDR"); RTLIL::SigSpec addr2 = c2->getPort("\\ADDR"); - if (addr1 != addr2) - supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act)); + if (GetSize(addr1) < GetSize(addr2)) + addr1.extend_u0(GetSize(addr2)); + else + addr2.extend_u0(GetSize(addr1)); + supercell->setPort("\\ADDR", addr1 != addr2 ? module->Mux(NEW_ID, addr2, addr1, act) : addr1); + supercell->parameters["\\ABITS"] = RTLIL::Const(GetSize(addr1)); supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA"))); supercell_aux.insert(supercell); return supercell; From f042559e9dbcc0738c6404903ac22da63cd27404 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Mon, 7 Jan 2019 10:07:28 +0100 Subject: [PATCH 52/71] Fix typo in manual Signed-off-by: Clifford Wolf --- manual/CHAPTER_CellLib.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index d40a600ed..e22664a82 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -437,7 +437,7 @@ otherwise. \begin{lstlisting}[mathescape,language=Verilog] always @($ClkEdge$ C, $RstEdge$ R) if (R == $RstLvl$) - Q <= $RstVa$l; + Q <= $RstVal$; else Q <= D; \end{lstlisting} From 211c26a4c9b9010380bfab9f02b79a25ee10ee29 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 4 Jan 2019 13:06:51 +0000 Subject: [PATCH 53/71] flowmap: implement depth relaxation. --- passes/techmap/flowmap.cc | 702 ++++++++++++++++++++++++++++++++-- passes/tests/flowmap/pack1.v | 11 + passes/tests/flowmap/pack1p.v | 11 + passes/tests/flowmap/pack2.v | 15 + passes/tests/flowmap/pack2p.v | 15 + passes/tests/flowmap/pack3.v | 15 + passes/tests/flowmap/pack3p.v | 15 + 7 files changed, 762 insertions(+), 22 deletions(-) create mode 100644 passes/tests/flowmap/pack1.v create mode 100644 passes/tests/flowmap/pack1p.v create mode 100644 passes/tests/flowmap/pack2.v create mode 100644 passes/tests/flowmap/pack2p.v create mode 100644 passes/tests/flowmap/pack3.v create mode 100644 passes/tests/flowmap/pack3p.v diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index be70b579b..f938efeee 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -17,11 +17,16 @@ * */ -// [[CITE]] +// [[CITE]] FlowMap algorithm // Jason Cong; Yuzheng Ding, "An Optimal Technology Mapping Algorithm for Delay Optimization in Lookup-Table Based FPGA Designs," // Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on, Vol. 13, pp. 1-12, Jan. 1994. // doi: 10.1109/43.273754 +// [[CITE]] FlowMap-r algorithm +// Jason Cong; Yuzheng Ding, "On Area/Depth Tradeoff in LUT-Based FPGA Technology Mapping," +// Very Large Scale Integration Systems, IEEE Transactions on, Vol. 2, June 1994. +// doi: 10.1109/92.28574 + // Required reading material: // // Min-cut max-flow theorem: @@ -29,11 +34,14 @@ // FlowMap paper: // http://cadlab.cs.ucla.edu/~cong/papers/iccad92.pdf (short version) // https://limsk.ece.gatech.edu/book/papers/flowmap.pdf (long version) +// FlowMap-r paper: +// http://cadlab.cs.ucla.edu/~cong/papers/dac93.pdf (short version) +// https://sci-hub.tw/10.1109/92.285741 (long version) -// Notes on implementation: +// Notes on correspondence between paper and implementation: // -// 1. In the paper, the nodes are logic elements (analogous to Yosys cells) and edges are wires. However, in our implementation, we use -// an inverted approach: the nodes are Yosys wire bits, and the edges are derived from (but aren't represented by) Yosys cells. +// 1. In the FlowMap paper, the nodes are logic elements (analogous to Yosys cells) and edges are wires. However, in our implementation, +// we use an inverted approach: the nodes are Yosys wire bits, and the edges are derived from (but aren't represented by) Yosys cells. // This may seem counterintuitive. Three observations may help understanding this. First, for a cell with a 1-bit Y output that is // the sole driver of its output net (which is the typical case), these representations are equivalent, because there is an exact // correspondence between cells and output wires. Second, in the paper, primary inputs (analogous to Yosys cell or module ports) are @@ -42,17 +50,50 @@ // such cells are supported without any additional effort; any Yosys cell with n output wire bits ends up being split into n flow graph // nodes. // -// 2. The paper introduces three networks: Nt, Nt', and Nt''. The network Nt is directly represented by a subgraph of RTLIL graph, +// 2. The FlowMap paper introduces three networks: Nt, Nt', and Nt''. The network Nt is directly represented by a subgraph of RTLIL graph, // which is parsed into an equivalent but easier to traverse representation in FlowmapWorker. The network Nt' is built explicitly // from a subgraph of Nt, and uses a similar representation in FlowGraph. The network Nt'' is implicit in FlowGraph, which is possible // because of the following observation: each Nt' node corresponds to an Nt'' edge of capacity 1, and each Nt' edge corresponds to // an Nt'' edge of capacity ∞. Therefore, we only need to explicitly record flow for Nt' edges and through Nt' nodes. // -// 3. The paper ambiguously states: "Moreover, we can find such a cut (X′′, X̅′′) by performing a depth first search starting at the source s, -// and including in X′′ all the nodes which are reachable from s." This actually refers to a specific kind of search, mincut computation. -// Mincut computation involves computing the set of nodes reachable from s by an undirected path with no full (i.e. zero capacity) forward -// edges or empty (i.e. no flow) backward edges. In addition, the depth first search is required to compute a max-volume max-flow min-cut -// specifically, because a max-flow min-cut is not, in general, unique. +// 3. The FlowMap paper ambiguously states: "Moreover, we can find such a cut (X′′, X̅′′) by performing a depth first search starting at +// the source s, and including in X′′ all the nodes which are reachable from s." This actually refers to a specific kind of search, +// min-cut computation. Min-cut computation involves computing the set of nodes reachable from s by an undirected path with no full +// (i.e. zero capacity) forward edges or empty (i.e. no flow) backward edges. In addition, the depth first search is required to compute +// a max-volume max-flow min-cut specifically, because a max-flow min-cut is not, in general, unique. + +// Notes on implementation: +// +// 1. To compute depth optimal packing, an intermediate representation is used, where each cell with n output bits is split into n graph +// nodes. Each such graph node is represented directly with the wire bit (RTLIL::SigBit instance) that corresponds to the output bit +// it is created from. Fan-in and fan-out are represented explicitly by edge lists derived from the RTLIL graph. This IR never changes +// after it has been computed. +// +// In terms of data, this IR is comprised of `inputs`, `outputs`, `nodes`, `edges_fw` and `edges_bw` fields. +// +// We call this IR "gate IR". +// +// 2. To compute area optimal packing, another intermediate representation is used, which consists of some K-feasible cone for every node +// that exists in the gate IR. Immediately after depth optimal packing with FlowMap, each such cone occupies the lowest possible depth, +// but this is not true in general, and transformations of this IR may change the cones, although each transformation has to keep each +// cone K-feasible. In this IR, LUT fan-in and fan-out are represented explicitly by edge lists; if a K-feasible cone chosen for node A +// includes nodes B and C, there are edges between all predecessors of A, B and C in the gate IR and node A in this IR. Moreover, in +// this IR, cones may be *realized* or *derealized*. Only realized cones will end up mapped to actual LUTs in the output of this pass. +// +// Intuitively, this IR contains (some, ideally but not necessarily optimal) LUT representation for each input cell. By starting at outputs +// and traversing the graph of this IR backwards, each K-feasible cone is converted to an actual LUT at the end of the pass. This is +// the same as iterating through each realized LUT. +// +// The following are the invariants of this IR: +// a) Each gate IR node corresponds to a K-feasible cut. +// b) Each realized LUT is reachable through backward edges from some output. +// c) The LUT fan-in is exactly the fan-in of its constituent gates minus the fan-out of its constituent gates. +// The invariants are kept even for derealized LUTs, since the whole point of this IR is ease of packing, unpacking, and repacking LUTs. +// +// In terms of data, this IR is comprised of `lut_nodes` (the set of all realized LUTs), `lut_gates` (the map from a LUT to its +// constituent gates), `lut_edges_fw` and `lut_edges_bw` fields. The `inputs` and `outputs` fields are shared with the gate IR. +// +// We call this IR "LUT IR". #include "kernel/yosys.h" #include "kernel/sigtools.h" @@ -405,7 +446,8 @@ struct FlowGraph struct FlowmapWorker { int order; - bool debug; + int r_alpha, r_beta, r_gamma; + bool debug, debug_relax; RTLIL::Module *module; SigMap sigmap; @@ -413,13 +455,16 @@ struct FlowmapWorker dict node_origins; + // Gate IR pool nodes, inputs, outputs; dict> edges_fw, edges_bw; dict labels; + // LUT IR pool lut_nodes; dict> lut_gates; dict> lut_edges_fw, lut_edges_bw; + dict lut_depths, lut_altitudes, lut_slacks; int gate_count = 0, lut_count = 0, packed_count = 0; int gate_area = 0, lut_area = 0; @@ -427,6 +472,7 @@ struct FlowmapWorker enum class GraphMode { Label, Cut, + Slack, }; void dump_dot_graph(string filename, GraphMode mode, @@ -465,6 +511,10 @@ struct FlowmapWorker if (cut.second[node]) return GraphStyle{label, "red"}; return GraphStyle{label}; + + case GraphMode::Slack: + label += stringf("\nd=%d a=%d\ns=%d", lut_depths[node], lut_altitudes[node], lut_slacks[node]); + return GraphStyle{label, lut_slacks[node] == 0 ? "red" : "black"}; } return GraphStyle{label}; }; @@ -474,6 +524,14 @@ struct FlowmapWorker ::dump_dot_graph(filename, subgraph_nodes, subgraph_edges, inputs, outputs, node_style, edge_style, module->name.str()); } + void dump_dot_lut_graph(string filename, GraphMode mode) + { + pool lut_and_input_nodes; + lut_and_input_nodes.insert(lut_nodes.begin(), lut_nodes.end()); + lut_and_input_nodes.insert(inputs.begin(), inputs.end()); + dump_dot_graph(filename, mode, lut_and_input_nodes, lut_edges_fw, lut_gates); + } + pool find_subgraph(RTLIL::SigBit sink) { pool subgraph; @@ -726,20 +784,560 @@ struct FlowmapWorker int depth = 0; for (auto label : labels) depth = max(depth, label.second); - log("Solved to %d LUTs in %d levels.\n", (int)lut_nodes.size(), depth); + log("Solved to %d LUTs with maximum depth %d.\n", (int)lut_nodes.size(), depth); if (debug) { - pool lut_and_input_nodes; - lut_and_input_nodes.insert(lut_nodes.begin(), lut_nodes.end()); - lut_and_input_nodes.insert(inputs.begin(), inputs.end()); - dump_dot_graph("flowmap-packed.dot", GraphMode::Label, lut_and_input_nodes, lut_edges_fw, lut_gates); + dump_dot_lut_graph("flowmap-packed.dot", GraphMode::Label); log("Dumped packed graph to `flowmap-packed.dot`.\n"); } return depth; } + void realize_derealize_lut(RTLIL::SigBit lut, pool *changed = nullptr) + { + pool worklist = {lut}; + while (!worklist.empty()) + { + auto lut = worklist.pop(); + if (inputs[lut]) + continue; + + bool realized_successors = false; + for (auto lut_succ : lut_edges_fw[lut]) + if (lut_nodes[lut_succ]) + realized_successors = true; + + if (realized_successors && !lut_nodes[lut]) + lut_nodes.insert(lut); + else if (!realized_successors && lut_nodes[lut]) + lut_nodes.erase(lut); + else + continue; + + for (auto lut_pred : lut_edges_bw[lut]) + worklist.insert(lut_pred); + + if (changed) + changed->insert(lut); + } + } + + void add_lut_edge(RTLIL::SigBit pred, RTLIL::SigBit succ, pool *changed = nullptr) + { + log_assert(!lut_edges_fw[pred][succ] && !lut_edges_bw[succ][pred]); + log_assert((int)lut_edges_bw[succ].size() < order); + + lut_edges_fw[pred].insert(succ); + lut_edges_bw[succ].insert(pred); + realize_derealize_lut(pred, changed); + + if (changed) + { + changed->insert(pred); + changed->insert(succ); + } + } + + void remove_lut_edge(RTLIL::SigBit pred, RTLIL::SigBit succ, pool *changed = nullptr) + { + log_assert(lut_edges_fw[pred][succ] && lut_edges_bw[succ][pred]); + + lut_edges_fw[pred].erase(succ); + lut_edges_bw[succ].erase(pred); + realize_derealize_lut(pred, changed); + + if (changed) + { + if (lut_nodes[pred]) + changed->insert(pred); + changed->insert(succ); + } + } + + pair, pool> cut_lut_at_gate(RTLIL::SigBit lut, RTLIL::SigBit lut_gate) + { + pool gate_inputs = lut_edges_bw[lut]; + pool other_inputs; + pool worklist = {lut}; + while (!worklist.empty()) + { + auto node = worklist.pop(); + for (auto node_pred : edges_bw[node]) + { + if (node_pred == lut_gate) + continue; + if (lut_gates[lut][node_pred]) + worklist.insert(node_pred); + else + { + gate_inputs.erase(node_pred); + other_inputs.insert(node_pred); + } + } + } + return {gate_inputs, other_inputs}; + } + + void compute_lut_distances(dict &lut_distances, bool forward, + pool initial = {}, pool *changed = nullptr) + { + pool terminals = forward ? inputs : outputs; + auto &lut_edges_next = forward ? lut_edges_fw : lut_edges_bw; + auto &lut_edges_prev = forward ? lut_edges_bw : lut_edges_fw; + + if (initial.empty()) + initial = terminals; + for (auto node : initial) + lut_distances.erase(node); + + pool worklist = initial; + while (!worklist.empty()) + { + auto lut = worklist.pop(); + int lut_distance = 0; + if (forward && inputs[lut]) + lut_distance = labels[lut]; // to support (* $flowmap_level=n *) + for (auto lut_prev : lut_edges_prev[lut]) + if ((lut_nodes[lut_prev] || inputs[lut_prev]) && lut_distances.count(lut_prev)) + lut_distance = max(lut_distance, lut_distances[lut_prev] + 1); + if (!lut_distances.count(lut) || lut_distances[lut] != lut_distance) + { + lut_distances[lut] = lut_distance; + if (changed != nullptr && !inputs[lut]) + changed->insert(lut); + for (auto lut_next : lut_edges_next[lut]) + if (lut_nodes[lut_next] || inputs[lut_next]) + worklist.insert(lut_next); + } + } + } + + void check_lut_distances(const dict &lut_distances, bool forward) + { + dict gold_lut_distances; + compute_lut_distances(gold_lut_distances, forward); + for (auto lut_distance : lut_distances) + if (lut_nodes[lut_distance.first]) + log_assert(lut_distance.second == gold_lut_distances[lut_distance.first]); + } + + // LUT depth is the length of the longest path from any input in LUT fan-in to LUT. + // LUT altitude (for lack of a better term) is the length of the longest path from LUT to any output in LUT fan-out. + void update_lut_depths_altitudes(pool worklist = {}, pool *changed = nullptr) + { + compute_lut_distances(lut_depths, /*forward=*/true, worklist, changed); + compute_lut_distances(lut_altitudes, /*forward=*/false, worklist, changed); + if (debug_relax && !worklist.empty()) { + check_lut_distances(lut_depths, /*forward=*/true); + check_lut_distances(lut_altitudes, /*forward=*/false); + } + } + + // LUT critical output set is the set of outputs whose depth will increase (equivalently, slack will decrease) if the depth of + // the LUT increases. (This is referred to as RPOv for LUTv in the paper.) + void compute_lut_critical_outputs(dict> &lut_critical_outputs, + pool worklist = {}) + { + if (worklist.empty()) + worklist = lut_nodes; + + while (!worklist.empty()) + { + bool updated_some = false; + for (auto lut : worklist) + { + if (outputs[lut]) + lut_critical_outputs[lut] = {lut}; + else + { + bool all_succ_computed = true; + lut_critical_outputs[lut] = {}; + for (auto lut_succ : lut_edges_fw[lut]) + { + if (lut_nodes[lut_succ] && lut_depths[lut_succ] == lut_depths[lut] + 1) + { + if (lut_critical_outputs.count(lut_succ)) + lut_critical_outputs[lut].insert(lut_critical_outputs[lut_succ].begin(), lut_critical_outputs[lut_succ].end()); + else + { + all_succ_computed = false; + break; + } + } + } + if (!all_succ_computed) + { + lut_critical_outputs.erase(lut); + continue; + } + } + worklist.erase(lut); + updated_some = true; + } + log_assert(updated_some); + } + } + + // Invalidating LUT critical output sets is tricky, because increasing the depth of a LUT may take other, adjacent LUTs off the critical + // path to the output. Conservatively, if we increase depth of some LUT, every LUT in its input cone needs to have its critical output + // set invalidated, too. + pool invalidate_lut_critical_outputs(dict> &lut_critical_outputs, + pool worklist) + { + pool changed; + while (!worklist.empty()) + { + auto lut = worklist.pop(); + changed.insert(lut); + lut_critical_outputs.erase(lut); + for (auto lut_pred : lut_edges_bw[lut]) + { + if (lut_nodes[lut_pred] && !changed[lut_pred]) + { + changed.insert(lut_pred); + worklist.insert(lut_pred); + } + } + } + return changed; + } + + void check_lut_critical_outputs(const dict> &lut_critical_outputs) + { + dict> gold_lut_critical_outputs; + compute_lut_critical_outputs(gold_lut_critical_outputs); + for (auto lut_critical_output : lut_critical_outputs) + if (lut_nodes[lut_critical_output.first]) + log_assert(lut_critical_output.second == gold_lut_critical_outputs[lut_critical_output.first]); + } + + void update_lut_critical_outputs(dict> &lut_critical_outputs, + pool worklist = {}) + { + if (!worklist.empty()) + { + pool invalidated = invalidate_lut_critical_outputs(lut_critical_outputs, worklist); + compute_lut_critical_outputs(lut_critical_outputs, invalidated); + check_lut_critical_outputs(lut_critical_outputs); + } + else + compute_lut_critical_outputs(lut_critical_outputs); + } + + void update_breaking_node_potentials(dict> &potentials, + const dict> &lut_critical_outputs) + { + for (auto lut : lut_nodes) + { + if (potentials.count(lut)) + continue; + if (lut_gates[lut].size() == 1 || lut_slacks[lut] == 0) + continue; + + if (debug_relax) + log(" Computing potentials for LUT %s.\n", log_signal(lut)); + + for (auto lut_gate : lut_gates[lut]) + { + if (lut == lut_gate) + continue; + + if (debug_relax) + log(" Considering breaking node %s.\n", log_signal(lut_gate)); + + int r_ex, r_im, r_slk; + + auto cut_inputs = cut_lut_at_gate(lut, lut_gate); + pool gate_inputs = cut_inputs.first, other_inputs = cut_inputs.second; + if (gate_inputs.empty() && (int)other_inputs.size() == order) + { + if (debug_relax) + log(" Breaking would result in a (k+1)-LUT.\n"); + continue; + } + + pool elim_fanin_luts; + for (auto gate_input : gate_inputs) + { + if (lut_edges_fw[gate_input].size() == 1) + { + log_assert(lut_edges_fw[gate_input][lut]); + elim_fanin_luts.insert(gate_input); + } + } + if (debug_relax) + { + if (!lut_nodes[lut_gate]) + log(" Breaking requires a new LUT.\n"); + if (!gate_inputs.empty()) + { + log(" Breaking eliminates LUT inputs"); + for (auto gate_input : gate_inputs) + log(" %s", log_signal(gate_input)); + log(".\n"); + } + if (!elim_fanin_luts.empty()) + { + log(" Breaking eliminates fan-in LUTs"); + for (auto elim_fanin_lut : elim_fanin_luts) + log(" %s", log_signal(elim_fanin_lut)); + log(".\n"); + } + } + r_ex = (lut_nodes[lut_gate] ? 0 : -1) + elim_fanin_luts.size(); + + pool> maybe_mergeable_luts; + + // Try to merge LUTv with one of its successors. + RTLIL::SigBit last_lut_succ; + int fanout = 0; + for (auto lut_succ : lut_edges_fw[lut]) + { + if (lut_nodes[lut_succ]) + { + fanout++; + last_lut_succ = lut_succ; + } + } + if (fanout == 1) + maybe_mergeable_luts.insert({lut, last_lut_succ}); + + // Try to merge LUTv with one of its predecessors. + for (auto lut_pred : other_inputs) + { + int fanout = 0; + for (auto lut_pred_succ : lut_edges_fw[lut_pred]) + if (lut_nodes[lut_pred_succ] || lut_pred_succ == lut_gate) + fanout++; + if (fanout == 1) + maybe_mergeable_luts.insert({lut_pred, lut}); + } + + // Try to merge LUTw with one of its predecessors. + for (auto lut_gate_pred : lut_edges_bw[lut_gate]) + { + int fanout = 0; + for (auto lut_gate_pred_succ : lut_edges_fw[lut_gate_pred]) + if (lut_nodes[lut_gate_pred_succ] || lut_gate_pred_succ == lut_gate) + fanout++; + if (fanout == 1) + maybe_mergeable_luts.insert({lut_gate_pred, lut_gate}); + } + + r_im = 0; + for (auto maybe_mergeable_pair : maybe_mergeable_luts) + { + log_assert(lut_edges_fw[maybe_mergeable_pair.first][maybe_mergeable_pair.second]); + pool unique_inputs; + for (auto fst_lut_pred : lut_edges_bw[maybe_mergeable_pair.first]) + if (lut_nodes[fst_lut_pred]) + unique_inputs.insert(fst_lut_pred); + for (auto snd_lut_pred : lut_edges_bw[maybe_mergeable_pair.second]) + if (lut_nodes[snd_lut_pred]) + unique_inputs.insert(snd_lut_pred); + unique_inputs.erase(maybe_mergeable_pair.first); + if ((int)unique_inputs.size() <= order) + { + if (debug_relax) + log(" Breaking may allow merging %s and %s.\n", + log_signal(maybe_mergeable_pair.first), log_signal(maybe_mergeable_pair.second)); + r_im++; + } + } + + int lut_gate_depth; + if (lut_nodes[lut_gate]) + lut_gate_depth = lut_depths[lut_gate]; + else + { + lut_gate_depth = 0; + for (auto lut_gate_pred : lut_edges_bw[lut_gate]) + lut_gate_depth = max(lut_gate_depth, lut_depths[lut_gate_pred] + 1); + } + if (lut_depths[lut] >= lut_gate_depth + 1) + r_slk = 0; + else + { + int depth_delta = lut_gate_depth + 1 - lut_depths[lut]; + if (depth_delta > lut_slacks[lut]) + { + if (debug_relax) + log(" Breaking would increase depth by %d, which is more than available slack.\n", depth_delta); + continue; + } + + if (debug_relax) + { + log(" Breaking increases depth of LUT by %d.\n", depth_delta); + if (lut_critical_outputs.at(lut).size()) + { + log(" Breaking decreases slack of outputs"); + for (auto lut_critical_output : lut_critical_outputs.at(lut)) + { + log(" %s", log_signal(lut_critical_output)); + log_assert(lut_slacks[lut_critical_output] > 0); + } + log(".\n"); + } + } + r_slk = lut_critical_outputs.at(lut).size() * depth_delta; + } + + int p = 100 * (r_alpha * r_ex + r_beta * r_im + r_gamma) / (r_slk + 1); + if (debug_relax) + log(" Potential for breaking node %s: %d (Rex=%d, Rim=%d, Rslk=%d).\n", + log_signal(lut_gate), p, r_ex, r_im, r_slk); + potentials[lut][lut_gate] = p; + } + } + } + + bool relax_depth_for_bound(bool first, int depth_bound, dict> &lut_critical_outputs) + { + for (auto node : lut_nodes) + { + lut_slacks[node] = depth_bound - (lut_depths[node] + lut_altitudes[node]); + log_assert(lut_slacks[node] >= 0); + } + if (debug) + { + dump_dot_lut_graph(stringf("flowmap-relax-%d-initial.dot", depth_bound), GraphMode::Slack); + log(" Dumped initial slack graph to `flowmap-relax-%d-initial.dot`.\n", depth_bound); + } + + dict> potentials; + for (int break_num = 1; ; break_num++) + { + update_breaking_node_potentials(potentials, lut_critical_outputs); + + if (potentials.empty()) + { + log(" Relaxed to %d LUTs.\n", (int)lut_nodes.size()); + if (!first && break_num == 1) + { + log(" Design fully relaxed.\n"); + return true; + } + else + { + log(" Slack exhausted.\n"); + break; + } + } + + RTLIL::SigBit breaking_lut, breaking_gate; + int best_potential = INT_MIN; + for (auto lut_gate_potentials : potentials) + { + for (auto gate_potential : lut_gate_potentials.second) + { + if (gate_potential.second > best_potential) + { + breaking_lut = lut_gate_potentials.first; + breaking_gate = gate_potential.first; + best_potential = gate_potential.second; + } + } + } + log(" Breaking LUT %s to %s LUT %s (potential %d).\n", + log_signal(breaking_lut), lut_nodes[breaking_gate] ? "reuse" : "extract", log_signal(breaking_gate), best_potential); + + if (debug_relax) + log(" Removing breaking gate %s from LUT.\n", log_signal(breaking_gate)); + lut_gates[breaking_lut].erase(breaking_gate); + + auto cut_inputs = cut_lut_at_gate(breaking_lut, breaking_gate); + pool gate_inputs = cut_inputs.first, other_inputs = cut_inputs.second; + + pool worklist = lut_gates[breaking_lut]; + pool elim_gates = gate_inputs; + while (!worklist.empty()) + { + auto lut_gate = worklist.pop(); + bool all_gate_preds_elim = true; + for (auto lut_gate_pred : edges_bw[lut_gate]) + if (!elim_gates[lut_gate_pred]) + all_gate_preds_elim = false; + if (all_gate_preds_elim) + { + if (debug_relax) + log(" Removing gate %s from LUT.\n", log_signal(lut_gate)); + lut_gates[breaking_lut].erase(lut_gate); + for (auto lut_gate_succ : edges_fw[lut_gate]) + worklist.insert(lut_gate_succ); + } + } + log_assert(!lut_gates[breaking_lut].empty()); + + pool directly_affected_nodes = {breaking_lut}; + for (auto gate_input : gate_inputs) + { + if (debug_relax) + log(" Removing LUT edge %s -> %s.\n", log_signal(gate_input), log_signal(breaking_lut)); + remove_lut_edge(gate_input, breaking_lut, &directly_affected_nodes); + } + if (debug_relax) + log(" Adding LUT edge %s -> %s.\n", log_signal(breaking_gate), log_signal(breaking_lut)); + add_lut_edge(breaking_gate, breaking_lut, &directly_affected_nodes); + + if (debug_relax) + log(" Updating slack and potentials.\n"); + + pool indirectly_affected_nodes = {}; + update_lut_depths_altitudes(directly_affected_nodes, &indirectly_affected_nodes); + update_lut_critical_outputs(lut_critical_outputs, indirectly_affected_nodes); + for (auto node : indirectly_affected_nodes) + { + lut_slacks[node] = depth_bound - (lut_depths[node] + lut_altitudes[node]); + log_assert(lut_slacks[node] >= 0); + if (debug_relax) + log(" LUT %s now has depth %d and slack %d.\n", log_signal(node), lut_depths[node], lut_slacks[node]); + } + + worklist = indirectly_affected_nodes; + pool visited; + while (!worklist.empty()) + { + auto node = worklist.pop(); + visited.insert(node); + potentials.erase(node); + // We are invalidating the entire output cone of the gate IR node, not just of the LUT IR node. This is done to also invalidate + // all LUTs that could contain one of the indirectly affected nodes as a *part* of them, as they may not be in the output cone + // of any of the LUT IR nodes, e.g. if we have a LUT IR node A and node B as predecessors of node C, where node B includes all + // gates from node A. + for (auto node_succ : edges_fw[node]) + if (!visited[node_succ]) + worklist.insert(node_succ); + } + + if (debug) + { + dump_dot_lut_graph(stringf("flowmap-relax-%d-break-%d.dot", depth_bound, break_num), GraphMode::Slack); + log(" Dumped slack graph after break %d to `flowmap-relax-%d-break-%d.dot`.\n", break_num, depth_bound, break_num); + } + } + + return false; + } + + void optimize_area(int depth, int optarea) + { + dict> lut_critical_outputs; + update_lut_depths_altitudes(); + update_lut_critical_outputs(lut_critical_outputs); + + for (int depth_bound = depth; depth_bound <= depth + optarea; depth_bound++) + { + log("Relaxing with depth bound %d.\n", depth_bound); + bool fully_relaxed = relax_depth_for_bound(depth_bound == depth, depth_bound, lut_critical_outputs); + + if (fully_relaxed) + break; + } + } + void map_cells(int minlut) { ConstEval ce(module); @@ -833,13 +1431,23 @@ struct FlowmapWorker } } - FlowmapWorker(int order, int minlut, pool cell_types, bool debug, RTLIL::Module *module) : - order(order), debug(debug), module(module), sigmap(module), index(module) + FlowmapWorker(int order, int minlut, pool cell_types, int r_alpha, int r_beta, int r_gamma, + bool relax, int optarea, bool debug, bool debug_relax, + RTLIL::Module *module) : + order(order), r_alpha(r_alpha), r_beta(r_beta), r_gamma(r_gamma), debug(debug), debug_relax(debug_relax), + module(module), sigmap(module), index(module) { log("Labeling cells.\n"); discover_nodes(cell_types); label_nodes(); - pack_luts(); + int depth = pack_luts(); + + if (relax) + { + log("\n"); + log("Optimizing area.\n"); + optimize_area(depth, optarea); + } log("\n"); log("Mapping cells.\n"); @@ -881,16 +1489,34 @@ struct FlowmapPass : public Pass { log(" map specified cells. if not specified, maps $_NOT_, $_AND_, $_OR_,\n"); log(" $_XOR_ and $_MUX_, which are the outputs of the `simplemap` pass.\n"); log("\n"); + log(" -relax\n"); + log(" perform depth relaxation and area minimization.\n"); + log("\n"); + log(" -r-alpha n, -r-beta n, -r-gamma n\n"); + log(" parameters of depth relaxation heuristic potential function.\n"); + log(" if not specified, alpha=8, beta=2, gamma=1.\n"); + log("\n"); + log(" -optarea n\n"); + log(" optimize for area by trading off at most n logic levels for fewer LUTs.\n"); + log(" n may be zero, to optimize for area without increasing depth.\n"); + log(" implies -relax.\n"); + log("\n"); log(" -debug\n"); log(" dump intermediate graphs.\n"); log("\n"); + log(" -debug-relax\n"); + log(" explain decisions performed during depth relaxation.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { int order = 3; int minlut = 1; vector cells; - bool debug = false; + bool relax = false; + int r_alpha = 8, r_beta = 2, r_gamma = 1; + int optarea = 0; + bool debug = false, debug_relax = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -910,11 +1536,42 @@ struct FlowmapPass : public Pass { split(cells, args[++argidx], ','); continue; } + if (args[argidx] == "-relax") + { + relax = true; + continue; + } + if (args[argidx] == "-r-alpha" && argidx + 1 < args.size()) + { + r_alpha = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-r-beta" && argidx + 1 < args.size()) + { + r_beta = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-r-gamma" && argidx + 1 < args.size()) + { + r_gamma = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-optarea" && argidx + 1 < args.size()) + { + relax = true; + optarea = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-debug") { debug = true; continue; } + if (args[argidx] == "-debug-relax") + { + debug = debug_relax = true; + continue; + } break; } extra_args(args, argidx, design); @@ -930,13 +1587,14 @@ struct FlowmapPass : public Pass { cell_types = {"$_NOT_", "$_AND_", "$_OR_", "$_XOR_", "$_MUX_"}; } - log_header(design, "Executing FLOWMAP pass (pack LUTs with FlowMap).\n"); + const char *algo_r = relax ? "-r" : ""; + log_header(design, "Executing FLOWMAP pass (pack LUTs with FlowMap%s).\n", algo_r); int gate_count = 0, lut_count = 0, packed_count = 0; int gate_area = 0, lut_area = 0; for (auto module : design->selected_modules()) { - FlowmapWorker worker(order, minlut, cell_types, debug, module); + FlowmapWorker worker(order, minlut, cell_types, r_alpha, r_beta, r_gamma, relax, optarea, debug, debug_relax, module); gate_count += worker.gate_count; lut_count += worker.lut_count; packed_count += worker.packed_count; diff --git a/passes/tests/flowmap/pack1.v b/passes/tests/flowmap/pack1.v new file mode 100644 index 000000000..9454edf3c --- /dev/null +++ b/passes/tests/flowmap/pack1.v @@ -0,0 +1,11 @@ +// Exact reproduction of Figure 3(a) from 10.1109/92.285741. +module top(...); + input a,b,c,d,e,f,g,h; + wire x = !(c|d); + wire y = !(e&f); + wire u = !(a&b); + wire v = !(x|y); + wire w = !(g&h); + output s = !(u|v); + output t = !(v|w); +endmodule diff --git a/passes/tests/flowmap/pack1p.v b/passes/tests/flowmap/pack1p.v new file mode 100644 index 000000000..fdb278833 --- /dev/null +++ b/passes/tests/flowmap/pack1p.v @@ -0,0 +1,11 @@ +// Like pack1.v, but results in a simpler network. +module top(...); + input a,b,c,d,e,f,g,h; + wire x = c|d; + wire y = e&f; + wire u = a&b; + wire v = x|y; + wire w = g&h; + output s = u|v; + output t = v|w; +endmodule diff --git a/passes/tests/flowmap/pack2.v b/passes/tests/flowmap/pack2.v new file mode 100644 index 000000000..445e4afb0 --- /dev/null +++ b/passes/tests/flowmap/pack2.v @@ -0,0 +1,15 @@ +// Exact reproduction of Figure 4(a) from 10.1109/92.285741. +module top(...); + (* $flowmap_level=1 *) input a; + (* $flowmap_level=1 *) input b; + (* $flowmap_level=2 *) input c; + (* $flowmap_level=1 *) input d; + (* $flowmap_level=3 *) input e; + (* $flowmap_level=1 *) input f; + wire u = !(a&b); + wire w = !(c|d); + wire v = !(u|w); + wire n0 = !(w&e); + wire n1 = !(n0|f); + output n2 = !(v&n1); +endmodule diff --git a/passes/tests/flowmap/pack2p.v b/passes/tests/flowmap/pack2p.v new file mode 100644 index 000000000..d4b41733d --- /dev/null +++ b/passes/tests/flowmap/pack2p.v @@ -0,0 +1,15 @@ +// Like pack2.v, but results in a simpler network. +module top(...); + (* $flowmap_level=1 *) input a; + (* $flowmap_level=1 *) input b; + (* $flowmap_level=2 *) input c; + (* $flowmap_level=1 *) input d; + (* $flowmap_level=3 *) input e; + (* $flowmap_level=1 *) input f; + wire u = a&b; + wire w = c|d; + wire v = u|w; + wire n0 = w&e; + wire n1 = n0|f; + output n2 = v&n1; +endmodule diff --git a/passes/tests/flowmap/pack3.v b/passes/tests/flowmap/pack3.v new file mode 100644 index 000000000..06147a1aa --- /dev/null +++ b/passes/tests/flowmap/pack3.v @@ -0,0 +1,15 @@ +// Exact reproduction of Figure 5(a) (bottom) from 10.1109/92.285741. +module top(...); + input a,b,c,d,e,f,g,h,i,j; + wire x = !(a&b); + wire y = !(c|d); + wire z = !(e|f); + wire n0 = !(g&h); + wire n1 = !(i|j); + wire w = !(x&y); + wire n2 = !(z&n0); + wire n3 = !(n0|n1); + wire n4 = !(n2|n3); + wire v = !(w|n5); + output u = !(w&v); +endmodule diff --git a/passes/tests/flowmap/pack3p.v b/passes/tests/flowmap/pack3p.v new file mode 100644 index 000000000..bc6ac1757 --- /dev/null +++ b/passes/tests/flowmap/pack3p.v @@ -0,0 +1,15 @@ +// Like pack2.v, but results in a simpler network. +module top(...); + input a,b,c,d,e,f,g,h,i,j; + wire x = a&b; + wire y = c|d; + wire z = e|f; + wire n0 = g&h; + wire n1 = i|j; + wire w = x&y; + wire n2 = z&n0; + wire n3 = n0|n1; + wire n4 = n2|n3; + wire v = w|n5; + output u = w&v; +endmodule From e792bd56b77066ee048057e5efdb5d0f517b043a Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 8 Jan 2019 02:05:06 +0000 Subject: [PATCH 54/71] flowmap: clean up terminology. * "map": group gates into LUTs; * "pack": replace gates with LUTs. This is important because we have FlowMap and DF-Map, and currently our messages are ambiguous. Also clean up some other log messages while we're at it. --- passes/techmap/flowmap.cc | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index f938efeee..ddbd7bf5d 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -769,7 +769,7 @@ struct FlowmapWorker } } - int pack_luts() + int map_luts() { pool worklist = outputs; while (!worklist.empty()) @@ -784,12 +784,12 @@ struct FlowmapWorker int depth = 0; for (auto label : labels) depth = max(depth, label.second); - log("Solved to %d LUTs with maximum depth %d.\n", (int)lut_nodes.size(), depth); + log("Mapped to %zu LUTs with maximum depth %d.\n", lut_nodes.size(), depth); if (debug) { - dump_dot_lut_graph("flowmap-packed.dot", GraphMode::Label); - log("Dumped packed graph to `flowmap-packed.dot`.\n"); + dump_dot_lut_graph("flowmap-mapped.dot", GraphMode::Label); + log("Dumped mapped graph to `flowmap-mapped.dot`.\n"); } return depth; @@ -1196,6 +1196,8 @@ struct FlowmapWorker bool relax_depth_for_bound(bool first, int depth_bound, dict> &lut_critical_outputs) { + size_t initial_count = lut_nodes.size(); + for (auto node : lut_nodes) { lut_slacks[node] = depth_bound - (lut_depths[node] + lut_altitudes[node]); @@ -1214,7 +1216,7 @@ struct FlowmapWorker if (potentials.empty()) { - log(" Relaxed to %d LUTs.\n", (int)lut_nodes.size()); + log(" Relaxed to %zu (+%zu) LUTs.\n", lut_nodes.size(), lut_nodes.size() - initial_count); if (!first && break_num == 1) { log(" Design fully relaxed.\n"); @@ -1338,7 +1340,7 @@ struct FlowmapWorker } } - void map_cells(int minlut) + void pack_cells(int minlut) { ConstEval ce(module); for (auto input_node : inputs) @@ -1351,15 +1353,15 @@ struct FlowmapWorker { auto origin = node_origins[node]; if (origin.cell->getPort(origin.port).size() == 1) - log("Mapping %s.%s.%s (%s).\n", + log("Packing %s.%s.%s (%s).\n", log_id(module), log_id(origin.cell), origin.port.c_str(), log_signal(node)); else - log("Mapping %s.%s.%s [%d] (%s).\n", + log("Packing %s.%s.%s [%d] (%s).\n", log_id(module), log_id(origin.cell), origin.port.c_str(), origin.offset, log_signal(node)); } else { - log("Mapping %s.%s.\n", log_id(module), log_signal(node)); + log("Packing %s.%s.\n", log_id(module), log_signal(node)); } for (auto gate_node : lut_gates[node]) @@ -1417,9 +1419,9 @@ struct FlowmapWorker lut_area += lut_table.size(); if ((int)input_nodes.size() >= minlut) - log(" Packed into a %d-LUT %s.%s.\n", (int)input_nodes.size(), log_id(module), log_id(lut)); + log(" Packed into a %zu-LUT %s.%s.\n", input_nodes.size(), log_id(module), log_id(lut)); else - log(" Packed into a %d-LUT %s.%s (implemented as %d-LUT).\n", (int)input_nodes.size(), log_id(module), log_id(lut), minlut); + log(" Packed into a %zu-LUT %s.%s (implemented as %d-LUT).\n", input_nodes.size(), log_id(module), log_id(lut), minlut); } for (auto node : mapped_nodes) @@ -1440,7 +1442,7 @@ struct FlowmapWorker log("Labeling cells.\n"); discover_nodes(cell_types); label_nodes(); - int depth = pack_luts(); + int depth = map_luts(); if (relax) { @@ -1450,8 +1452,8 @@ struct FlowmapWorker } log("\n"); - log("Mapping cells.\n"); - map_cells(minlut); + log("Packing cells.\n"); + pack_cells(minlut); } }; @@ -1603,9 +1605,8 @@ struct FlowmapPass : public Pass { } log("\n"); - log("Mapped %d LUTs.\n", lut_count); - log("Packed %d cells; duplicated %d cells.\n", packed_count, packed_count - gate_count); - log("Solution has %.1f%% area overhead.\n", (lut_area - gate_area) * 100.0 / gate_area); + log("Packed %d cells (%d of them duplicated) into %d LUTs.\n", packed_count, packed_count - gate_count, lut_count); + log("Solution takes %.1f%% of original gate area.\n", lut_area * 100.0 / gate_area); } } FlowmapPass; From 2a2e0a4722ded7628b71f436b94a06aebd57bb62 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 8 Jan 2019 20:16:36 +0100 Subject: [PATCH 55/71] Improve igloo2 example Signed-off-by: Clifford Wolf --- examples/igloo2/.gitignore | 3 ++- examples/igloo2/example.ys | 3 ++- examples/igloo2/libero.tcl | 27 ++++++++++++++++++++++--- examples/igloo2/{libero.sh => runme.sh} | 1 + 4 files changed, 29 insertions(+), 5 deletions(-) rename examples/igloo2/{libero.sh => runme.sh} (88%) diff --git a/examples/igloo2/.gitignore b/examples/igloo2/.gitignore index ae86e69cc..fa3c3d7ed 100644 --- a/examples/igloo2/.gitignore +++ b/examples/igloo2/.gitignore @@ -1,2 +1,3 @@ -/example.edn +/netlist.edn +/netlist.v /work diff --git a/examples/igloo2/example.ys b/examples/igloo2/example.ys index 75a305d86..872f97b99 100644 --- a/examples/igloo2/example.ys +++ b/examples/igloo2/example.ys @@ -1,2 +1,3 @@ read_verilog example.v -synth_sf2 -top top -edif example.edn +synth_sf2 -top top -edif netlist.edn +write_verilog netlist.v diff --git a/examples/igloo2/libero.tcl b/examples/igloo2/libero.tcl index cc1ab2403..9f6d3b792 100644 --- a/examples/igloo2/libero.tcl +++ b/examples/igloo2/libero.tcl @@ -9,6 +9,27 @@ new_project \ -speed -1 \ -hdl VERILOG -import_files -edif {example.edn} -run_tool –name {COMPILE} -run_tool –name {PLACEROUTEN} +# import_files -edif "[pwd]/netlist.edn" + +import_files -hdl_source "[pwd]/netlist.v" +set_root top + +save_project + +puts "**> SYNTHESIZE" +run_tool -name {SYNTHESIZE} +puts "<** SYNTHESIZE" + +puts "**> COMPILE" +run_tool -name {COMPILE} +puts "<** COMPILE" + +puts "**> PLACEROUTE" +run_tool -name {PLACEROUTE} +puts "<** PLACEROUTE" + +# puts "**> export_bitstream" +# export_bitstream_file -trusted_facility_file 1 -trusted_facility_file_components {FABRIC} +# puts "<** export_bitstream" + +exit 0 diff --git a/examples/igloo2/libero.sh b/examples/igloo2/runme.sh similarity index 88% rename from examples/igloo2/libero.sh rename to examples/igloo2/runme.sh index 582f6ccb9..4edfb5409 100644 --- a/examples/igloo2/libero.sh +++ b/examples/igloo2/runme.sh @@ -1,4 +1,5 @@ #!/bin/bash set -ex rm -rf work +yosys example.ys LM_LICENSE_FILE=1702@`hostname` /opt/microsemi/Libero_SoC_v11.9/Libero/bin/libero SCRIPT:libero.tcl From 7a45122168d06a416f1341e1adf0760cc4c462b8 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 14 Jan 2019 16:08:58 +0000 Subject: [PATCH 56/71] manual: explain $tribuf cell. --- manual/CHAPTER_CellLib.tex | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index e22664a82..464f7d1a9 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -119,6 +119,12 @@ than one bit from \B{S} is set the output is undefined. Cells of this type are u ``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by an optimization). +The {\tt \$tribuf} cell is used to implement tristate logic. Cells of this type have a \B{WIDTH} +parameter and inputs \B{A} and \B{EN} and an output \B{Y}. The \B{A} input and \B{Y} output are +\B{WIDTH} bits wide, and the \B{EN} input is one bit wide. When \B{EN} is 0, the output \B{Y} +is not driven. When \B{EN} is 1, the value from \B{A} input is sent to the \B{Y} output. Therefore, +the {\tt \$tribuf} cell implements the function \lstinline[language=Verilog]; Y = EN ? A : 'bz;. + Behavioural code with cascaded {\tt if-then-else}- and {\tt case}-statements usually results in trees of multiplexer cells. Many passes (from various optimizations to FSM extraction) heavily depend on these multiplexer trees to @@ -476,6 +482,10 @@ Add information about {\tt \$dffe}, {\tt \$dffsr}, {\tt \$dlatch}, and {\tt \$dl Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells. \end{fixme} +\begin{fixme} +Add information about {\tt \$\_TBUF\_} cells. +\end{fixme} + \begin{fixme} Add information about {\tt \$\_NAND\_}, {\tt \$\_NOR\_}, {\tt \$\_XNOR\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_ORNOT\_}, {\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells. From fc2dd7ec8e433bf54fffdb85e9aa2556fe3e39a2 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 14 Jan 2019 16:17:25 +0000 Subject: [PATCH 57/71] manual: document some gates. --- manual/CHAPTER_CellLib.tex | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 464f7d1a9..e64919182 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -404,9 +404,15 @@ Verilog & Cell Type \\ \hline \lstinline[language=Verilog]; Y = ~A; & {\tt \$\_NOT\_} \\ \lstinline[language=Verilog]; Y = A & B; & {\tt \$\_AND\_} \\ +\lstinline[language=Verilog]; Y = ~(A & B); & {\tt \$\_NAND\_} \\ +\lstinline[language=Verilog]; Y = A & ~B; & {\tt \$\_ANDNOT\_} \\ \lstinline[language=Verilog]; Y = A | B; & {\tt \$\_OR\_} \\ +\lstinline[language=Verilog]; Y = ~(A | B); & {\tt \$\_NOR\_} \\ +\lstinline[language=Verilog]; Y = A | ~B; & {\tt \$\_ORNOT\_} \\ \lstinline[language=Verilog]; Y = A ^ B; & {\tt \$\_XOR\_} \\ +\lstinline[language=Verilog]; Y = ~(A ^ B); & {\tt \$\_XNOR\_} \\ \lstinline[language=Verilog]; Y = S ? B : A; & {\tt \$\_MUX\_} \\ +\lstinline[language=Verilog]; Y = EN ? A : 'bz; & {\tt \$\_TBUF\_} \\ \hline \lstinline[language=Verilog]; always @(negedge C) Q <= D; & {\tt \$\_DFF\_N\_} \\ \lstinline[language=Verilog]; always @(posedge C) Q <= D; & {\tt \$\_DFF\_P\_} \\ @@ -429,9 +435,10 @@ $ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\ \end{table} Table~\ref{tab:CellLib_gates} lists all cell types used for gate level logic. The cell types -{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_OR\_}, {\tt \$\_XOR\_} and {\tt \$\_MUX\_} -are used to model combinatorial logic. The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} -represent d-type flip-flops. +{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_OR\_}, {\tt \$\_NOR\_}, +{\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_} and {\tt \$\_MUX\_} are used to model combinatorial logic. +The cell type {\tt \$\_TBUF\_} is used to model tristate logic. +The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops. The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_}, {\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement @@ -483,11 +490,6 @@ Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLA \end{fixme} \begin{fixme} -Add information about {\tt \$\_TBUF\_} cells. -\end{fixme} - -\begin{fixme} -Add information about {\tt \$\_NAND\_}, {\tt \$\_NOR\_}, {\tt \$\_XNOR\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_ORNOT\_}, -{\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells. +Add information about {\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells. \end{fixme} From 6c5049f016538e887476bb66d3f653155fa354ff Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 15 Jan 2019 10:55:27 +0100 Subject: [PATCH 58/71] Fix handling of $shiftx in Verilog back-end Signed-off-by: Clifford Wolf --- backends/verilog/verilog_backend.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 2537e18e5..8da3c0627 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -709,11 +709,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == "$shiftx") { + std::string temp_id = next_auto_id(); + f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort("\\A"))-1, temp_id.c_str()); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(";\n"); + f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort("\\Y")); - f << stringf(" = "); - dump_sigspec(f, cell->getPort("\\A")); - f << stringf("["); + f << stringf(" = %s[", temp_id.c_str()); if (cell->getParam("\\B_SIGNED").as_bool()) f << stringf("$signed("); dump_sigspec(f, cell->getPort("\\B")); From e70ebe557cd02f52f32a297fa63008dba25e4f6a Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 13 Jan 2019 17:00:58 +0100 Subject: [PATCH 59/71] Add optional nullstr argument to log_id() Signed-off-by: Clifford Wolf --- kernel/log.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/log.h b/kernel/log.h index e1f54a197..759939025 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -94,7 +94,9 @@ const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true); const char *log_const(const RTLIL::Const &value, bool autoint = true); const char *log_id(RTLIL::IdString id); -template static inline const char *log_id(T *obj) { +template static inline const char *log_id(T *obj, const char *nullstr = nullptr) { + if (nullstr && obj == nullptr) + return nullstr; return log_id(obj->name); } From 54dc33b90518d8962d949a8f8b25842f720ac670 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 17 Jan 2019 13:33:11 +0100 Subject: [PATCH 60/71] Add "write_edif -gndvccy" Signed-off-by: Clifford Wolf --- backends/edif/edif.cc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index d4e56a9eb..2d25f879d 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -106,6 +106,10 @@ struct EdifBackend : public Backend { log(" if the design contains constant nets. use \"hilomap\" to map to custom\n"); log(" constant drivers first)\n"); log("\n"); + log(" -gndvccy\n"); + log(" create \"GND\" and \"VCC\" cells with \"Y\" outputs. (the default is \"G\"\n"); + log(" for \"GND\" and \"P\" for \"VCC\".)\n"); + log("\n"); log(" -attrprop\n"); log(" create EDIF properties for cell attributes\n"); log("\n"); @@ -126,7 +130,7 @@ struct EdifBackend : public Backend { bool port_rename = false; bool attr_properties = false; std::map> lib_cell_ports; - bool nogndvcc = false; + bool nogndvcc = false, gndvccy = true; CellTypes ct(design); EdifNames edif_names; @@ -141,6 +145,10 @@ struct EdifBackend : public Backend { nogndvcc = true; continue; } + if (args[argidx] == "-gndvccy") { + gndvccy = true; + continue; + } if (args[argidx] == "-attrprop") { attr_properties = true; continue; @@ -211,7 +219,7 @@ struct EdifBackend : public Backend { *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); - *f << stringf(" (interface (port G (direction OUTPUT)))\n"); + *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'G'); *f << stringf(" )\n"); *f << stringf(" )\n"); @@ -219,7 +227,7 @@ struct EdifBackend : public Backend { *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); - *f << stringf(" (interface (port P (direction OUTPUT)))\n"); + *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'P'); *f << stringf(" )\n"); *f << stringf(" )\n"); } @@ -420,9 +428,9 @@ struct EdifBackend : public Backend { if (nogndvcc) log_error("Design contains constant nodes (map with \"hilomap\" first).\n"); if (sig == RTLIL::State::S0) - *f << stringf(" (portRef G (instanceRef GND))\n"); + *f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G'); if (sig == RTLIL::State::S1) - *f << stringf(" (portRef P (instanceRef VCC))\n"); + *f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P'); } *f << stringf(" ))\n"); } From 841ca74c90872985ae4e128608260b4a38d04762 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 17 Jan 2019 13:33:45 +0100 Subject: [PATCH 61/71] Add "synth_sf2 -vlog", fix "synth_sf2 -edif" Signed-off-by: Clifford Wolf --- techlibs/sf2/synth_sf2.cc | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/techlibs/sf2/synth_sf2.cc b/techlibs/sf2/synth_sf2.cc index 62b3cd0e2..6828a4298 100644 --- a/techlibs/sf2/synth_sf2.cc +++ b/techlibs/sf2/synth_sf2.cc @@ -44,6 +44,10 @@ struct SynthSf2Pass : public ScriptPass log(" write the design to the specified EDIF file. writing of an output file\n"); log(" is omitted if this parameter is not specified.\n"); log("\n"); + log(" -vlog \n"); + log(" write the design to the specified Verilog file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); log(" -json \n"); log(" write the design to the specified JSON file. writing of an output file\n"); log(" is omitted if this parameter is not specified.\n"); @@ -65,13 +69,14 @@ struct SynthSf2Pass : public ScriptPass log("\n"); } - string top_opt, edif_file, json_file; + string top_opt, edif_file, vlog_file, json_file; bool flatten, retime; void clear_flags() YS_OVERRIDE { top_opt = "-auto-top"; edif_file = ""; + vlog_file = ""; json_file = ""; flatten = true; retime = false; @@ -93,6 +98,10 @@ struct SynthSf2Pass : public ScriptPass edif_file = args[++argidx]; continue; } + if (args[argidx] == "-vlog" && argidx+1 < args.size()) { + vlog_file = args[++argidx]; + continue; + } if (args[argidx] == "-json" && argidx+1 < args.size()) { json_file = args[++argidx]; continue; @@ -192,7 +201,13 @@ struct SynthSf2Pass : public ScriptPass if (check_label("edif")) { if (!edif_file.empty() || help_mode) - run(stringf("write_edif %s", help_mode ? "" : edif_file.c_str())); + run(stringf("write_edif -gndvccy %s", help_mode ? "" : edif_file.c_str())); + } + + if (check_label("vlog")) + { + if (!vlog_file.empty() || help_mode) + run(stringf("write_verilog %s", help_mode ? "" : vlog_file.c_str())); } if (check_label("json")) From 9b277fc21ea455a0e0ca9b7acde039e90ddb380d Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 17 Jan 2019 13:35:52 +0100 Subject: [PATCH 62/71] Improve Igloo2 example Signed-off-by: Clifford Wolf --- examples/igloo2/.gitignore | 4 ++-- examples/igloo2/example.fp.pdc | 0 examples/igloo2/example.io.pdc | 0 examples/igloo2/example.sdc | 0 examples/igloo2/example.v | 2 +- examples/igloo2/example.ys | 4 ++-- examples/igloo2/libero.tcl | 42 +++++++++++++++++++++++++--------- examples/igloo2/runme.sh | 3 +-- 8 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 examples/igloo2/example.fp.pdc create mode 100644 examples/igloo2/example.io.pdc create mode 100644 examples/igloo2/example.sdc diff --git a/examples/igloo2/.gitignore b/examples/igloo2/.gitignore index fa3c3d7ed..ea58efc9f 100644 --- a/examples/igloo2/.gitignore +++ b/examples/igloo2/.gitignore @@ -1,3 +1,3 @@ /netlist.edn -/netlist.v -/work +/netlist.vm +/proj diff --git a/examples/igloo2/example.fp.pdc b/examples/igloo2/example.fp.pdc new file mode 100644 index 000000000..e69de29bb diff --git a/examples/igloo2/example.io.pdc b/examples/igloo2/example.io.pdc new file mode 100644 index 000000000..e69de29bb diff --git a/examples/igloo2/example.sdc b/examples/igloo2/example.sdc new file mode 100644 index 000000000..e69de29bb diff --git a/examples/igloo2/example.v b/examples/igloo2/example.v index 3eb7007c5..0e336e557 100644 --- a/examples/igloo2/example.v +++ b/examples/igloo2/example.v @@ -1,4 +1,4 @@ -module top ( +module example ( input clk, output LED1, output LED2, diff --git a/examples/igloo2/example.ys b/examples/igloo2/example.ys index 872f97b99..04ea02672 100644 --- a/examples/igloo2/example.ys +++ b/examples/igloo2/example.ys @@ -1,3 +1,3 @@ read_verilog example.v -synth_sf2 -top top -edif netlist.edn -write_verilog netlist.v +synth_sf2 -top example -edif netlist.edn +write_verilog netlist.vm diff --git a/examples/igloo2/libero.tcl b/examples/igloo2/libero.tcl index 9f6d3b792..b2090f402 100644 --- a/examples/igloo2/libero.tcl +++ b/examples/igloo2/libero.tcl @@ -1,24 +1,38 @@ # Run with "libero SCRIPT:libero.tcl" +file delete -force proj + new_project \ - -name top \ - -location work \ + -name example \ + -location proj \ + -block_mode 1 \ + -hdl "VERILOG" \ -family IGLOO2 \ -die PA4MGL500 \ -package tq144 \ - -speed -1 \ - -hdl VERILOG + -speed -1 -# import_files -edif "[pwd]/netlist.edn" +import_files -hdl_source {netlist.vm} +import_files -sdc {example.sdc} +import_files -io_pdc {example.io.pdc} +import_files -fp_pdc {example.fp.pdc} +set_option -synth 0 -import_files -hdl_source "[pwd]/netlist.v" -set_root top +organize_tool_files -tool PLACEROUTE \ + -file {proj/constraint/example.sdc} \ + -file {proj/constraint/io/example.io.pdc} \ + -file {proj/constraint/fp/example.fp.pdc} \ + -input_type constraint -save_project +organize_tool_files -tool VERIFYTIMING \ + -file {proj/constraint/example.sdc} \ + -input_type constraint -puts "**> SYNTHESIZE" -run_tool -name {SYNTHESIZE} -puts "<** SYNTHESIZE" +configure_tool -name PLACEROUTE \ + -params TDPR:true \ + -params PDPR:false \ + -params EFFORT_LEVEL:false \ + -params REPAIR_MIN_DELAY:false puts "**> COMPILE" run_tool -name {COMPILE} @@ -28,6 +42,12 @@ puts "**> PLACEROUTE" run_tool -name {PLACEROUTE} puts "<** PLACEROUTE" +puts "**> VERIFYTIMING" +run_tool -name {VERIFYTIMING} +puts "<** VERIFYTIMING" + +save_project + # puts "**> export_bitstream" # export_bitstream_file -trusted_facility_file 1 -trusted_facility_file_components {FABRIC} # puts "<** export_bitstream" diff --git a/examples/igloo2/runme.sh b/examples/igloo2/runme.sh index 4edfb5409..54247759f 100644 --- a/examples/igloo2/runme.sh +++ b/examples/igloo2/runme.sh @@ -1,5 +1,4 @@ #!/bin/bash set -ex -rm -rf work -yosys example.ys +yosys -p 'synth_sf2 -top example -edif netlist.edn -vlog netlist.vm' example.v LM_LICENSE_FILE=1702@`hostname` /opt/microsemi/Libero_SoC_v11.9/Libero/bin/libero SCRIPT:libero.tcl From db5765b443c26b5b2dc3ac56d5a448fc8b861d43 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 17 Jan 2019 14:38:37 +0100 Subject: [PATCH 63/71] Add SF2 IO buffer insertion Signed-off-by: Clifford Wolf --- examples/igloo2/example.v | 3 +- examples/igloo2/libero.tcl | 2 +- techlibs/sf2/Makefile.inc | 1 + techlibs/sf2/cells_sim.v | 21 ++++++ techlibs/sf2/sf2_iobs.cc | 130 +++++++++++++++++++++++++++++++++++++ techlibs/sf2/synth_sf2.cc | 17 ++++- 6 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 techlibs/sf2/sf2_iobs.cc diff --git a/examples/igloo2/example.v b/examples/igloo2/example.v index 0e336e557..1a1967d5a 100644 --- a/examples/igloo2/example.v +++ b/examples/igloo2/example.v @@ -1,5 +1,6 @@ module example ( input clk, + input EN, output LED1, output LED2, output LED3, @@ -14,7 +15,7 @@ module example ( reg [BITS-1:0] outcnt; always @(posedge clk) begin - counter <= counter + 1; + counter <= counter + EN; outcnt <= counter >> LOG2DELAY; end diff --git a/examples/igloo2/libero.tcl b/examples/igloo2/libero.tcl index b2090f402..952342a4c 100644 --- a/examples/igloo2/libero.tcl +++ b/examples/igloo2/libero.tcl @@ -5,7 +5,7 @@ file delete -force proj new_project \ -name example \ -location proj \ - -block_mode 1 \ + -block_mode 0 \ -hdl "VERILOG" \ -family IGLOO2 \ -die PA4MGL500 \ diff --git a/techlibs/sf2/Makefile.inc b/techlibs/sf2/Makefile.inc index cade49f37..cc3054ace 100644 --- a/techlibs/sf2/Makefile.inc +++ b/techlibs/sf2/Makefile.inc @@ -1,5 +1,6 @@ OBJS += techlibs/sf2/synth_sf2.o +OBJS += techlibs/sf2/sf2_iobs.o $(eval $(call add_share_file,share/sf2,techlibs/sf2/arith_map.v)) $(eval $(call add_share_file,share/sf2,techlibs/sf2/cells_map.v)) diff --git a/techlibs/sf2/cells_sim.v b/techlibs/sf2/cells_sim.v index b03b2c750..f967068af 100644 --- a/techlibs/sf2/cells_sim.v +++ b/techlibs/sf2/cells_sim.v @@ -73,3 +73,24 @@ module CFG4 ( parameter [15:0] INIT = 16'h0; assign Y = INIT >> {D, C, B, A}; endmodule + +module CLKBUF ( + input PAD, + output Y +); + assign Y = PAD; +endmodule + +module INBUF ( + input PAD, + output Y +); + assign Y = PAD; +endmodule + +module OUTBUF ( + input D, + output PAD +); + assign PAD = D; +endmodule diff --git a/techlibs/sf2/sf2_iobs.cc b/techlibs/sf2/sf2_iobs.cc new file mode 100644 index 000000000..27141430c --- /dev/null +++ b/techlibs/sf2/sf2_iobs.cc @@ -0,0 +1,130 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct Sf2IobsPass : public Pass { + Sf2IobsPass() : Pass("sf2_iobs", "SF2: insert IO buffers") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" sf2_iobs [options] [selection]\n"); + log("\n"); + log("Add SF2 I/O buffers to top module IOs as needed.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing sf2_iobs pass (insert IO buffers).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + Module *module = design->top_module(); + + if (module == nullptr) + log_cmd_error("No top module found.\n"); + + SigMap sigmap(module); + + pool clk_bits; + pool handled_io_bits; + dict rewrite_bits; + vector> pad_bits; + + for (auto cell : module->cells()) + { + if (cell->type == "\\SLE") { + for (auto bit : sigmap(cell->getPort("\\CLK"))) + clk_bits.insert(bit); + } + if (cell->type.in("\\INBUF", "\\OUTBUF", "\\CLKBUF")) { + for (auto bit : sigmap(cell->getPort("\\PAD"))) + handled_io_bits.insert(bit); + } + } + + for (auto wire : vector(module->wires())) + { + if (!wire->port_input && !wire->port_output) + continue; + + for (int index = 0; index < GetSize(wire); index++) + { + SigBit bit(wire, index); + SigBit canonical_bit = sigmap(bit); + + if (handled_io_bits.count(canonical_bit)) + continue; + + if (wire->port_input && wire->port_output) + log_error("Failed to add buffer for inout port bit %s.\n", log_signal(bit)); + + IdString buf_type, buf_port; + + if (wire->port_output) { + buf_type = "\\OUTBUF"; + buf_port = "\\D"; + } else if (clk_bits.count(canonical_bit)) { + buf_type = "\\CLKBUF"; + buf_port = "\\Y"; + } else { + buf_type = "\\INBUF"; + buf_port = "\\Y"; + } + + Cell *c = module->addCell(NEW_ID, buf_type); + SigBit new_bit = module->addWire(NEW_ID); + c->setPort(buf_port, new_bit); + pad_bits.push_back(make_pair(c, bit)); + rewrite_bits[canonical_bit] = new_bit; + + log("Added %s cell %s for port bit %s.\n", log_id(c->type), log_id(c), log_signal(bit)); + } + } + + auto rewrite_function = [&](SigSpec &s) { + for (auto &bit : s) { + SigBit canonical_bit = sigmap(bit); + if (rewrite_bits.count(canonical_bit)) + bit = rewrite_bits.at(canonical_bit); + } + }; + + module->rewrite_sigspecs(rewrite_function); + + for (auto &it : pad_bits) + it.first->setPort("\\PAD", it.second); + } +} Sf2IobsPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/sf2/synth_sf2.cc b/techlibs/sf2/synth_sf2.cc index 6828a4298..bdc20456d 100644 --- a/techlibs/sf2/synth_sf2.cc +++ b/techlibs/sf2/synth_sf2.cc @@ -60,6 +60,9 @@ struct SynthSf2Pass : public ScriptPass log(" -noflatten\n"); log(" do not flatten design before synthesis\n"); log("\n"); + log(" -noiobs\n"); + log(" run synthesis in \"block mode\", i.e. do not insert IO buffers\n"); + log("\n"); log(" -retime\n"); log(" run 'abc' with -dff option\n"); log("\n"); @@ -70,7 +73,7 @@ struct SynthSf2Pass : public ScriptPass } string top_opt, edif_file, vlog_file, json_file; - bool flatten, retime; + bool flatten, retime, iobs; void clear_flags() YS_OVERRIDE { @@ -80,6 +83,7 @@ struct SynthSf2Pass : public ScriptPass json_file = ""; flatten = true; retime = false; + iobs = true; } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE @@ -122,6 +126,10 @@ struct SynthSf2Pass : public ScriptPass retime = true; continue; } + if (args[argidx] == "-noiobs") { + iobs = false; + continue; + } break; } extra_args(args, argidx, design); @@ -191,6 +199,13 @@ struct SynthSf2Pass : public ScriptPass run("clean"); } + if (check_label("map_iobs")) + { + if (iobs || help_mode) + run("sf2_iobs", "(unless -noiobs)"); + run("clean"); + } + if (check_label("check")) { run("hierarchy -check"); From f3556e9f7ac6947be440a51699af773655de4911 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 17 Jan 2019 14:54:04 +0100 Subject: [PATCH 64/71] Cleanups in igloo2 example design Signed-off-by: Clifford Wolf --- examples/igloo2/example.fp.pdc | 0 examples/igloo2/example.io.pdc | 0 examples/igloo2/example.pdc | 1 + examples/igloo2/example.sdc | 1 + examples/igloo2/example.ys | 3 --- examples/igloo2/libero.tcl | 6 ++---- 6 files changed, 4 insertions(+), 7 deletions(-) delete mode 100644 examples/igloo2/example.fp.pdc delete mode 100644 examples/igloo2/example.io.pdc create mode 100644 examples/igloo2/example.pdc delete mode 100644 examples/igloo2/example.ys diff --git a/examples/igloo2/example.fp.pdc b/examples/igloo2/example.fp.pdc deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/igloo2/example.io.pdc b/examples/igloo2/example.io.pdc deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/igloo2/example.pdc b/examples/igloo2/example.pdc new file mode 100644 index 000000000..e6ffd53db --- /dev/null +++ b/examples/igloo2/example.pdc @@ -0,0 +1 @@ +# Add placement constraints here diff --git a/examples/igloo2/example.sdc b/examples/igloo2/example.sdc index e69de29bb..c6ff94161 100644 --- a/examples/igloo2/example.sdc +++ b/examples/igloo2/example.sdc @@ -0,0 +1 @@ +# Add timing constraints here diff --git a/examples/igloo2/example.ys b/examples/igloo2/example.ys deleted file mode 100644 index 04ea02672..000000000 --- a/examples/igloo2/example.ys +++ /dev/null @@ -1,3 +0,0 @@ -read_verilog example.v -synth_sf2 -top example -edif netlist.edn -write_verilog netlist.vm diff --git a/examples/igloo2/libero.tcl b/examples/igloo2/libero.tcl index 952342a4c..1f3476316 100644 --- a/examples/igloo2/libero.tcl +++ b/examples/igloo2/libero.tcl @@ -14,14 +14,12 @@ new_project \ import_files -hdl_source {netlist.vm} import_files -sdc {example.sdc} -import_files -io_pdc {example.io.pdc} -import_files -fp_pdc {example.fp.pdc} +import_files -io_pdc {example.pdc} set_option -synth 0 organize_tool_files -tool PLACEROUTE \ -file {proj/constraint/example.sdc} \ - -file {proj/constraint/io/example.io.pdc} \ - -file {proj/constraint/fp/example.fp.pdc} \ + -file {proj/constraint/io/example.pdc} \ -input_type constraint organize_tool_files -tool VERIFYTIMING \ From 95b6c3588222564b86cfa73f0601be44e3af5786 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 18 Jan 2019 23:22:02 +0000 Subject: [PATCH 65/71] proc_clean: fix fully def check to consider compare/signal length. Fixes #790. --- passes/proc/proc_clean.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 3919e4b9c..0e2f95226 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -82,9 +82,15 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did { if (max_depth != 0) proc_clean_case(cs, did_something, count, max_depth-1); + int size = 0; for (auto cmp : cs->compare) - if (!cmp.is_fully_def()) + { + size += cmp.size(); + if (cmp.is_fully_def()) all_fully_def = false; + } + if (sw->signal.size() != size) + all_fully_def = false; } if (all_fully_def) { From 58d059ccb7f32f4d061fe3d7caf57483fbf01516 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 23 Jan 2019 22:08:38 +0000 Subject: [PATCH 66/71] proc_clean: fix critical typo. --- passes/proc/proc_clean.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 0e2f95226..52141a8ec 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -86,7 +86,7 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did for (auto cmp : cs->compare) { size += cmp.size(); - if (cmp.is_fully_def()) + if (!cmp.is_fully_def()) all_fully_def = false; } if (sw->signal.size() != size) From 0de328da8ffea4902c52b286d7b38d67a714c742 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 25 Jan 2019 19:25:25 +0100 Subject: [PATCH 67/71] Fixed Anlogic simulation model --- techlibs/anlogic/cells_sim.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/anlogic/cells_sim.v b/techlibs/anlogic/cells_sim.v index 60a367928..058e76605 100644 --- a/techlibs/anlogic/cells_sim.v +++ b/techlibs/anlogic/cells_sim.v @@ -17,7 +17,7 @@ module AL_MAP_LUT1 ( ); parameter [1:0] INIT = 2'h0; parameter EQN = "(A)"; - assign Y = INIT >> A; + assign o = INIT >> a; endmodule module AL_MAP_LUT2 ( From 42c47a83dab873290040367455c48f619d83e2a3 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 26 Jan 2019 23:55:46 +0000 Subject: [PATCH 68/71] write_verilog: escape names that match SystemVerilog keywords. --- backends/verilog/verilog_backend.cc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 8da3c0627..fc38afbda 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -126,6 +126,33 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) break; } + const pool keywords = { + // IEEE 1800-2017 Annex B + "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", + "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", + "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", + "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", + "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", + "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", + "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", + "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", + "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", + "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", + "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", + "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", + "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", + "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", + "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", + "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", + "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", + "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", + "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", + "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", + "wildcard", "wire", "with", "within", "wor", "xnor", "xor", + }; + if (keywords.count(str)) + do_escape = true; + if (do_escape) return "\\" + std::string(str) + " "; return std::string(str); From 3d7925ad9f7840d5269b84d053ae808f36ccf762 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 27 Jan 2019 00:21:31 +0000 Subject: [PATCH 69/71] write_verilog: write $tribuf cell as ternary. --- backends/verilog/verilog_backend.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 8da3c0627..54281e32e 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -789,6 +789,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$tribuf") + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); + f << stringf(" = "); + dump_sigspec(f, cell->getPort("\\EN")); + f << stringf(" ? "); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(" : %d'bz;\n", cell->parameters.at("\\WIDTH").as_int()); + return true; + } + if (cell->type == "$slice") { f << stringf("%s" "assign ", indent.c_str()); From 9666cca9ddba2e6d242006e80d66277b8b4df0fd Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 27 Jan 2019 09:17:02 +0100 Subject: [PATCH 70/71] Remove asicworld tests for (unsupported) switch-level modelling Signed-off-by: Clifford Wolf --- tests/asicworld/code_hdl_models_misc1.v | 22 ------------------- .../asicworld/code_hdl_models_mux21_switch.v | 22 ------------------- tests/asicworld/code_hdl_models_nand_switch.v | 14 ------------ .../asicworld/code_hdl_models_t_gate_switch.v | 11 ---------- 4 files changed, 69 deletions(-) delete mode 100644 tests/asicworld/code_hdl_models_misc1.v delete mode 100644 tests/asicworld/code_hdl_models_mux21_switch.v delete mode 100644 tests/asicworld/code_hdl_models_nand_switch.v delete mode 100644 tests/asicworld/code_hdl_models_t_gate_switch.v diff --git a/tests/asicworld/code_hdl_models_misc1.v b/tests/asicworld/code_hdl_models_misc1.v deleted file mode 100644 index e3d9d5d64..000000000 --- a/tests/asicworld/code_hdl_models_misc1.v +++ /dev/null @@ -1,22 +0,0 @@ -module misc1 (a,b,c,d,y); -input a, b,c,d; -output y; - -wire net1,net2,net3; - -supply1 vdd; -supply0 vss; - -// y = !((a+b+c).d) - -pmos p1 (vdd,net1,a); -pmos p2 (net1,net2,b); -pmos p3 (net2,y,c); -pmos p4 (vdd,y,d); - -nmos n1 (vss,net3,a); -nmos n2 (vss,net3,b); -nmos n3 (vss,net3,c); -nmos n4 (net3,y,d); - -endmodule diff --git a/tests/asicworld/code_hdl_models_mux21_switch.v b/tests/asicworld/code_hdl_models_mux21_switch.v deleted file mode 100644 index 519c07fc5..000000000 --- a/tests/asicworld/code_hdl_models_mux21_switch.v +++ /dev/null @@ -1,22 +0,0 @@ -//----------------------------------------------------- -// Design Name : mux21_switch -// File Name : mux21_switch.v -// Function : 2:1 Mux using Switch Primitives -// Coder : Deepak Kumar Tala -//----------------------------------------------------- -module mux21_switch (out, ctrl, in1, in2); - - output out; - input ctrl, in1, in2; - wire w; - - supply1 power; - supply0 ground; - - pmos N1 (w, power, ctrl); - nmos N2 (w, ground, ctrl); - - cmos C1 (out, in1, w, ctrl); - cmos C2 (out, in2, ctrl, w); - -endmodule diff --git a/tests/asicworld/code_hdl_models_nand_switch.v b/tests/asicworld/code_hdl_models_nand_switch.v deleted file mode 100644 index 1ccdd3a7c..000000000 --- a/tests/asicworld/code_hdl_models_nand_switch.v +++ /dev/null @@ -1,14 +0,0 @@ -module nand_switch(a,b,out); -input a,b; -output out; - -supply0 vss; -supply1 vdd; -wire net1; - -pmos p1 (vdd,out,a); -pmos p2 (vdd,out,b); -nmos n1 (vss,net1,a); -nmos n2 (net1,out,b); - -endmodule \ No newline at end of file diff --git a/tests/asicworld/code_hdl_models_t_gate_switch.v b/tests/asicworld/code_hdl_models_t_gate_switch.v deleted file mode 100644 index 5a7e0eaff..000000000 --- a/tests/asicworld/code_hdl_models_t_gate_switch.v +++ /dev/null @@ -1,11 +0,0 @@ -module t_gate_switch (L,R,nC,C); - inout L; - inout R; - input nC; - input C; - - //Syntax: keyword unique_name (drain. source, gate); - pmos p1 (L,R,nC); - nmos p2 (L,R,C); - -endmodule From e112d2fbf5a31f00ef19e6d05f28fecc1e9c56b9 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 6 Feb 2019 16:35:59 +0100 Subject: [PATCH 71/71] Add missing blackslash-to-slash convertion to smtio.py (matching Smt2Worker::get_id() behavior) Signed-off-by: Clifford Wolf --- backends/smt2/smtio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index 68783e744..ab20a4af2 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -784,7 +784,7 @@ class SmtIo: def get_path(self, mod, path): assert mod in self.modinfo - path = path.split(".") + path = path.replace("\\", "/").split(".") for i in range(len(path)-1): first = ".".join(path[0:i+1])