mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-30 19:22:31 +00:00 
			
		
		
		
	gatemate: Add LUT tree library script
Co-authored-by: Claire Xenia Wolf <claire@clairexen.net> Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
		
							parent
							
								
									7c756c9959
								
							
						
					
					
						commit
						38a24ec5cc
					
				
					 6 changed files with 591 additions and 6 deletions
				
			
		
							
								
								
									
										4
									
								
								techlibs/gatemate/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								techlibs/gatemate/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | lut_tree_cells.genlib | ||||||
|  | lut_tree_map.v | ||||||
|  | lut_tree_lib.mk | ||||||
|  | 
 | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| 
 | 
 | ||||||
| OBJS += techlibs/gatemate/synth_gatemate.o | OBJS += techlibs/gatemate/synth_gatemate.o | ||||||
|  | OBJS += techlibs/gatemate/gatemate_foldinv.o | ||||||
| 
 | 
 | ||||||
| $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/reg_map.v)) | $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/reg_map.v)) | ||||||
| $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/mux_map.v)) | $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/mux_map.v)) | ||||||
|  | @ -12,3 +13,18 @@ $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_map.v)) | ||||||
| $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams.txt)) | $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams.txt)) | ||||||
| $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_init_20.vh)) | $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_init_20.vh)) | ||||||
| $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_init_40.vh)) | $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_init_40.vh)) | ||||||
|  | $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/inv_map.v)) | ||||||
|  | 
 | ||||||
|  | EXTRA_OBJS += techlibs/gatemate/lut_tree_lib.mk | ||||||
|  | .SECONDARY: techlibs/gatemate/lut_tree_lib.mk | ||||||
|  | 
 | ||||||
|  | techlibs/gatemate/lut_tree_lib.mk: techlibs/gatemate/make_lut_tree_lib.py | ||||||
|  | 	$(Q) mkdir -p techlibs/gatemate | ||||||
|  | 	$(P) $(PYTHON_EXECUTABLE) $< | ||||||
|  | 	$(Q) touch $@ | ||||||
|  | 
 | ||||||
|  | techlibs/gatemate/lut_tree_cells.genlib: techlibs/gatemate/lut_tree_lib.mk | ||||||
|  | techlibs/gatemate/lut_tree_map.v: techlibs/gatemate/lut_tree_lib.mk | ||||||
|  | 
 | ||||||
|  | $(eval $(call add_gen_share_file,share/gatemate,techlibs/gatemate/lut_tree_cells.genlib)) | ||||||
|  | $(eval $(call add_gen_share_file,share/gatemate,techlibs/gatemate/lut_tree_map.v)) | ||||||
|  |  | ||||||
							
								
								
									
										218
									
								
								techlibs/gatemate/gatemate_foldinv.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								techlibs/gatemate/gatemate_foldinv.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,218 @@ | ||||||
|  | /*
 | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2021  gatecat <gatecat@ds0.me> | ||||||
|  |  *  Copyright (C) 2021  Cologne Chip AG <support@colognechip.com> | ||||||
|  |  * | ||||||
|  |  *  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 LUTPin { | ||||||
|  |     int input_bit; | ||||||
|  |     IdString init_param; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct LUTType { | ||||||
|  |     dict<IdString, LUTPin> inputs; | ||||||
|  |     IdString output_param; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const dict<IdString, LUTType> lut_types = { | ||||||
|  |     {ID(CC_LUT2), {{ | ||||||
|  |             {ID(I0), {0, ID(INIT)}}, | ||||||
|  |             {ID(I1), {1, ID(INIT)}}, | ||||||
|  |         }, ID(INIT)}}, | ||||||
|  |     {ID(CC_L2T4), {{ | ||||||
|  |             {ID(I0), {0, ID(INIT_L00)}}, | ||||||
|  |             {ID(I1), {1, ID(INIT_L00)}}, | ||||||
|  |             {ID(I2), {0, ID(INIT_L01)}}, | ||||||
|  |             {ID(I3), {1, ID(INIT_L01)}}, | ||||||
|  |         }, ID(INIT_L10)}}, | ||||||
|  |     {ID(CC_L2T5), {{ | ||||||
|  |             {ID(I0), {0, ID(INIT_L02)}}, | ||||||
|  |             {ID(I1), {1, ID(INIT_L02)}}, | ||||||
|  |             {ID(I2), {0, ID(INIT_L03)}}, | ||||||
|  |             {ID(I3), {1, ID(INIT_L03)}}, | ||||||
|  |             {ID(I4), {0, ID(INIT_L20)}}, | ||||||
|  |         }, ID(INIT_L20)}}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct FoldInvWorker { | ||||||
|  |     FoldInvWorker(Module *module) : module(module), sigmap(module) {}; | ||||||
|  |     Module *module; | ||||||
|  |     SigMap sigmap; | ||||||
|  | 
 | ||||||
|  |     // Mapping from inverter output to inverter input
 | ||||||
|  |     dict<SigBit, SigBit> inverted_bits; | ||||||
|  |     // Mapping from inverter input to inverter
 | ||||||
|  |     dict<SigBit, Cell*> inverter_input; | ||||||
|  | 
 | ||||||
|  |     void find_inverted_bits() | ||||||
|  |     { | ||||||
|  |         for (auto cell : module->selected_cells()) { | ||||||
|  |             if (cell->type != ID($__CC_NOT)) | ||||||
|  |                 continue; | ||||||
|  |             SigBit a = sigmap(cell->getPort(ID::A)[0]); | ||||||
|  |             SigBit y = sigmap(cell->getPort(ID::Y)[0]); | ||||||
|  |             inverted_bits[y] = a; | ||||||
|  |             inverter_input[a] = cell; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Const invert_lut_input(Const lut, int bit) | ||||||
|  |     { | ||||||
|  |         Const result(State::S0, GetSize(lut)); | ||||||
|  |         for (int i = 0; i < GetSize(lut); i++) { | ||||||
|  |             int j = i ^ (1 << bit); | ||||||
|  |             result[j] = lut[i]; | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Const invert_lut_output(Const lut) | ||||||
|  |     { | ||||||
|  |         Const result(State::S0, GetSize(lut)); | ||||||
|  |         for (int i = 0; i < GetSize(lut); i++) | ||||||
|  |             result[i] = (lut[i] == State::S1) ? State::S0 : State::S1; | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void fold_input_inverters() | ||||||
|  |     { | ||||||
|  |         for (auto cell : module->selected_cells()) { | ||||||
|  |             auto found_type = lut_types.find(cell->type); | ||||||
|  |             if (found_type == lut_types.end()) | ||||||
|  |                 continue; | ||||||
|  |             const auto &type = found_type->second; | ||||||
|  |             for (const auto &ipin : type.inputs) { | ||||||
|  |                 if (!cell->hasPort(ipin.first)) | ||||||
|  |                     continue; | ||||||
|  |                 auto sig = cell->getPort(ipin.first); | ||||||
|  |                 if (GetSize(sig) == 0) | ||||||
|  |                     continue; | ||||||
|  |                 SigBit bit = sigmap(sig[0]); | ||||||
|  |                 auto inv = inverted_bits.find(bit); | ||||||
|  |                 if (inv == inverted_bits.end()) | ||||||
|  |                     continue; // not the output of an inverter
 | ||||||
|  |                 // Rewire to inverter input
 | ||||||
|  |                 cell->unsetPort(ipin.first); | ||||||
|  |                 cell->setPort(ipin.first, inv->second); | ||||||
|  |                 // Rewrite init
 | ||||||
|  |                 cell->setParam(ipin.second.init_param, | ||||||
|  |                     invert_lut_input(cell->getParam(ipin.second.init_param), ipin.second.input_bit)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void fold_output_inverters() | ||||||
|  |     { | ||||||
|  |         pool<SigBit> used_bits; | ||||||
|  |         // Find bits that are actually used
 | ||||||
|  |         for (auto cell : module->selected_cells()) { | ||||||
|  |             for (auto conn : cell->connections()) { | ||||||
|  |                 if (cell->output(conn.first)) | ||||||
|  |                     continue; | ||||||
|  |                 for (auto bit : sigmap(conn.second)) | ||||||
|  |                     used_bits.insert(bit); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         // Find LUTs driving inverters
 | ||||||
|  |         // (create a vector to avoid iterate-and-modify issues)
 | ||||||
|  |         std::vector<std::pair<Cell *, Cell*>> lut_inv; | ||||||
|  |         for (auto cell : module->selected_cells()) { | ||||||
|  |             auto found_type = lut_types.find(cell->type); | ||||||
|  |             if (found_type == lut_types.end()) | ||||||
|  |                 continue; | ||||||
|  |             if (!cell->hasPort(ID::O)) | ||||||
|  |                 continue; | ||||||
|  |             auto o_sig = cell->getPort(ID::O); | ||||||
|  |             if (GetSize(o_sig) == 0) | ||||||
|  |                 continue; | ||||||
|  |             SigBit o = sigmap(o_sig[0]); | ||||||
|  |             auto found_inv = inverter_input.find(o); | ||||||
|  |             if (found_inv == inverter_input.end()) | ||||||
|  |                 continue; // doesn't drive an inverter
 | ||||||
|  |             lut_inv.emplace_back(cell, found_inv->second); | ||||||
|  |         } | ||||||
|  |         for (auto pair : lut_inv) { | ||||||
|  |             Cell *orig_lut = pair.first; | ||||||
|  |             Cell *inv = pair.second; | ||||||
|  |             // Find the inverter output
 | ||||||
|  |             SigBit inv_y = sigmap(inv->getPort(ID::Y)[0]); | ||||||
|  |             // Inverter output might not actually be used; if all users were folded into inputs already
 | ||||||
|  |             if (!used_bits.count(inv_y)) | ||||||
|  |                 continue; | ||||||
|  |             // Create a duplicate of the LUT with an inverted output
 | ||||||
|  |             // (if the uninverted version becomes unused it will be swept away)
 | ||||||
|  |             Cell *dup_lut = module->addCell(NEW_ID, orig_lut->type); | ||||||
|  |             inv->unsetPort(ID::Y); | ||||||
|  |             dup_lut->setPort(ID::O, inv_y); | ||||||
|  |             for (auto conn : orig_lut->connections()) { | ||||||
|  |                 if (conn.first == ID::O) | ||||||
|  |                     continue; | ||||||
|  |                 dup_lut->setPort(conn.first, conn.second); | ||||||
|  |             } | ||||||
|  |             for (auto param : orig_lut->parameters) { | ||||||
|  |                 if (param.first == lut_types.at(orig_lut->type).output_param) | ||||||
|  |                     dup_lut->parameters[param.first] = invert_lut_output(param.second); | ||||||
|  |                 else | ||||||
|  |                     dup_lut->parameters[param.first] = param.second; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void operator()() | ||||||
|  |     { | ||||||
|  |         find_inverted_bits(); | ||||||
|  |         fold_input_inverters(); | ||||||
|  |         fold_output_inverters(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct GatemateFoldInvPass : public Pass { | ||||||
|  |     GatemateFoldInvPass() : Pass("gatemate_foldinv", "fold inverters into Gatemate LUT trees") { } | ||||||
|  |     void help() override | ||||||
|  |     { | ||||||
|  |         //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||||
|  |         log("\n"); | ||||||
|  |         log("    gatemate_foldinv [selection]\n"); | ||||||
|  |         log("\n"); | ||||||
|  |         log("\n"); | ||||||
|  |         log("This pass searches for $__CC_NOT cells and folds them into CC_LUT2, CC_L2T4\n"); | ||||||
|  |         log("and CC_L2T5 cells as created by LUT tree mapping.\n"); | ||||||
|  |         log("\n"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void execute(std::vector<std::string> args, RTLIL::Design *design) override | ||||||
|  |     { | ||||||
|  |         log_header(design, "Executing GATEMATE_FOLDINV pass (folding LUT tree inverters).\n"); | ||||||
|  | 
 | ||||||
|  |         size_t argidx = 1; | ||||||
|  |         extra_args(args, argidx, design); | ||||||
|  | 
 | ||||||
|  |         for (Module *module : design->selected_modules()) { | ||||||
|  |             FoldInvWorker worker(module); | ||||||
|  |             worker(); | ||||||
|  |         }         | ||||||
|  |     } | ||||||
|  | } GatemateFoldInvPass; | ||||||
|  | 
 | ||||||
|  | PRIVATE_NAMESPACE_END | ||||||
|  | 
 | ||||||
							
								
								
									
										4
									
								
								techlibs/gatemate/inv_map.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								techlibs/gatemate/inv_map.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | // Any inverters not folded into LUTs are mapped to a LUT of their own
 | ||||||
|  | module \$__CC_NOT (input A, output Y); | ||||||
|  | 	CC_LUT1 #(.INIT(2'b01)) _TECHMAP_REPLACE_ (.I0(A), .O(Y)); | ||||||
|  | endmodule | ||||||
							
								
								
									
										323
									
								
								techlibs/gatemate/make_lut_tree_lib.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								techlibs/gatemate/make_lut_tree_lib.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,323 @@ | ||||||
|  | #!/usr/bin/env python3 | ||||||
|  | 
 | ||||||
|  | class FNode: | ||||||
|  |     def __init__(self, fun, *args): | ||||||
|  |         self.fun = fun | ||||||
|  |         self.args = args | ||||||
|  | 
 | ||||||
|  |         if len(self.args) == 0: | ||||||
|  |             assert fun not in ("BUF", "NOT", "AND", "OR", "XOR", "MUX") | ||||||
|  | 
 | ||||||
|  |         if len(self.args) == 1: | ||||||
|  |             assert fun in ("BUF", "NOT") | ||||||
|  | 
 | ||||||
|  |         if len(self.args) == 2: | ||||||
|  |             assert fun in ("AND", "OR", "XOR") | ||||||
|  | 
 | ||||||
|  |         if len(self.args) == 3: | ||||||
|  |             assert fun in ("MUX") | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         if len(self.args) == 0: | ||||||
|  |             return self.fun | ||||||
|  |         if self.fun == "NOT" and len(self.args[0].args) == 0: | ||||||
|  |             return "!" + self.args[0].fun | ||||||
|  |         return self.fun + "(" + ",".join([str(a) for a in self.args]) + ")" | ||||||
|  | 
 | ||||||
|  |     def as_genlib_term(self): | ||||||
|  |         if len(self.args) == 0: | ||||||
|  |             return self.fun | ||||||
|  |         if self.fun == "NOT": | ||||||
|  |             assert len(self.args[0].args) == 0 | ||||||
|  |             return "!" + self.args[0].fun | ||||||
|  |         if self.fun == "AND": | ||||||
|  |             return "(" + self.args[0].as_genlib_term() + "*" + self.args[1].as_genlib_term() + ")" | ||||||
|  |         if self.fun == "OR": | ||||||
|  |             return "(" + self.args[0].as_genlib_term() + "+" + self.args[1].as_genlib_term() + ")" | ||||||
|  |         assert False | ||||||
|  | 
 | ||||||
|  |     def mapMux(self): | ||||||
|  |         if self.fun == "MUX": | ||||||
|  |             A, B, C = self.args | ||||||
|  |             return OR(AND(A, NOT(C)), AND(B, C)).mapMux() | ||||||
|  |         return FNode(self.fun, *[a.mapMux() for a in self.args]) | ||||||
|  | 
 | ||||||
|  |     def mapXor(self): | ||||||
|  |         if self.fun == "XOR": | ||||||
|  |             A, B = self.args | ||||||
|  |             return OR(AND(A, NOT(B)), AND(NOT(A), B)).mapXor() | ||||||
|  |         return FNode(self.fun, *[a.mapXor() for a in self.args]) | ||||||
|  | 
 | ||||||
|  |     def mapNot(self): | ||||||
|  |         if self.fun == "BUF": | ||||||
|  |             return self.arg1.mapNot() | ||||||
|  |         if self.fun == "NOT": | ||||||
|  |             if self.args[0].fun == "AND": | ||||||
|  |                 return OR(NOT(self.args[0].args[0]),NOT(self.args[0].args[1])).mapNot() | ||||||
|  |             if self.args[0].fun == "OR": | ||||||
|  |                 return AND(NOT(self.args[0].args[0]),NOT(self.args[0].args[1])).mapNot() | ||||||
|  |             if self.args[0].fun == "NOT": | ||||||
|  |                 return self.args[0].args[0].mapNot() | ||||||
|  |         return FNode(self.fun, *[a.mapNot() for a in self.args]) | ||||||
|  | 
 | ||||||
|  |     def map(self): | ||||||
|  |         n = self | ||||||
|  |         n = n.mapMux() | ||||||
|  |         n = n.mapXor() | ||||||
|  |         n = n.mapNot() | ||||||
|  |         return n | ||||||
|  | 
 | ||||||
|  |     def isInv(self): | ||||||
|  |         if len(self.args) == 0: | ||||||
|  |             return False | ||||||
|  |         if self.fun == "XOR": | ||||||
|  |             return False | ||||||
|  |         if self.fun == "NOT": | ||||||
|  |             return self.args[0].isNonInv() | ||||||
|  |         for a in self.args: | ||||||
|  |             if not a.isInv(): | ||||||
|  |                 return False | ||||||
|  |         return True | ||||||
|  | 
 | ||||||
|  |     def isNonInv(self): | ||||||
|  |         if len(self.args) == 0: | ||||||
|  |             return True | ||||||
|  |         if self.fun == "XOR": | ||||||
|  |             return False | ||||||
|  |         if self.fun == "NOT": | ||||||
|  |             return self.args[0].isInv() | ||||||
|  |         for a in self.args: | ||||||
|  |             if not a.isNonInv(): | ||||||
|  |                 return False | ||||||
|  |         return True | ||||||
|  | 
 | ||||||
|  | A = FNode("A") | ||||||
|  | B = FNode("B") | ||||||
|  | C = FNode("C") | ||||||
|  | D = FNode("D") | ||||||
|  | E = FNode("E") | ||||||
|  | 
 | ||||||
|  | def BUF(arg): return FNode("BUF", arg) | ||||||
|  | def NOT(arg): return FNode("NOT", arg) | ||||||
|  | def AND(arg1, arg2): return FNode("AND", arg1, arg2) | ||||||
|  | def  OR(arg1, arg2): return FNode( "OR", arg1, arg2) | ||||||
|  | def XOR(arg1, arg2): return FNode("XOR", arg1, arg2) | ||||||
|  | def MUX(arg1, arg2, arg3): return FNode("MUX", arg1, arg2, arg3) | ||||||
|  | 
 | ||||||
|  | # Genlib Format: | ||||||
|  | # | ||||||
|  | # GATE <cell-name> <cell-area> <cell-logic-function> | ||||||
|  | # | ||||||
|  | # PIN <pin-name> <phase> <input-load> <max-load> | ||||||
|  | #     <rise-block-delay> <rise-fanout-delay> | ||||||
|  | #     <fall-block-delay> <fall-fanout-delay> | ||||||
|  | # | ||||||
|  | # phase: | ||||||
|  | #     INV, NONINV, or UNKNOWN | ||||||
|  | # | ||||||
|  | # cell-logic-function: | ||||||
|  | #     <output> = <term with *(AND), +(OR), !(NOT)> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | cells = [ | ||||||
|  |     ["$__CC_BUF", 5, A], | ||||||
|  |     ["$__CC_NOT", 0, NOT(A)], | ||||||
|  |     ["$__CC_MUX", 5, MUX(A, B, C)], | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | base_cells = [ | ||||||
|  |     ["$__CC2_A", AND(A, B)], | ||||||
|  |     ["$__CC2_O",  OR(A, B)], | ||||||
|  |     ["$__CC2_X", XOR(A, B)], | ||||||
|  | 
 | ||||||
|  |     ["$__CC3_AA", AND(AND(A, B), C)], | ||||||
|  |     ["$__CC3_OO",  OR( OR(A, B), C)], | ||||||
|  |     ["$__CC3_XX", XOR(XOR(A, B), C)], | ||||||
|  |     ["$__CC3_AO",  OR(AND(A, B), C)], | ||||||
|  |     ["$__CC3_OA", AND( OR(A, B), C)], | ||||||
|  |     ["$__CC3_AX", XOR(AND(A, B), C)], | ||||||
|  |     ["$__CC3_XA", AND(XOR(A, B), C)], | ||||||
|  | 
 | ||||||
|  | #   ["$__CC3_AAA", AND(AND(A,B),AND(A,C))], | ||||||
|  | #   ["$__CC3_AXA", XOR(AND(A,B),AND(A,C))], | ||||||
|  | #   ["$__CC3_XAX", AND(XOR(A,B),XOR(A,C))], | ||||||
|  | #   ["$__CC3_AAX", AND(AND(A,B),XOR(A,C))], | ||||||
|  | #   ["$__CC3_AXX", XOR(AND(A,B),XOR(A,C))], | ||||||
|  | #   ["$__CC3_XXX", XOR(XOR(A,B),XOR(A,C))], | ||||||
|  | #   ["$__CC3_AAO", AND(AND(A,B), OR(A,C))], | ||||||
|  | #   ["$__CC3_AOA",  OR(AND(A,B),AND(A,C))], | ||||||
|  | #   ["$__CC3_AOX",  OR(AND(A,B),XOR(A,C))], | ||||||
|  | 
 | ||||||
|  | #   ["$__CC3_AAA_N", AND(AND(A,B),AND(NOT(A),C))], | ||||||
|  | #   ["$__CC3_AXA_N", XOR(AND(A,B),AND(NOT(A),C))], | ||||||
|  | #   ["$__CC3_XAX_N", AND(XOR(A,B),XOR(NOT(A),C))], | ||||||
|  | #   ["$__CC3_AAX_N", AND(AND(A,B),XOR(NOT(A),C))], | ||||||
|  | #   ["$__CC3_AXX_N", XOR(AND(A,B),XOR(NOT(A),C))], | ||||||
|  | #   ["$__CC3_XXX_N", XOR(XOR(A,B),XOR(NOT(A),C))], | ||||||
|  | #   ["$__CC3_AAO_N", AND(AND(A,B), OR(NOT(A),C))], | ||||||
|  | #   ["$__CC3_AOA_N",  OR(AND(A,B),AND(NOT(A),C))], | ||||||
|  | #   ["$__CC3_AOX_N",  OR(AND(A,B),XOR(NOT(A),C))], | ||||||
|  | 
 | ||||||
|  |     ["$__CC4_AAA", AND(AND(A,B),AND(C,D))], | ||||||
|  |     ["$__CC4_AXA", XOR(AND(A,B),AND(C,D))], | ||||||
|  |     ["$__CC4_XAX", AND(XOR(A,B),XOR(C,D))], | ||||||
|  |     ["$__CC4_AAX", AND(AND(A,B),XOR(C,D))], | ||||||
|  |     ["$__CC4_AXX", XOR(AND(A,B),XOR(C,D))], | ||||||
|  |     ["$__CC4_XXX", XOR(XOR(A,B),XOR(C,D))], | ||||||
|  |     ["$__CC4_AAO", AND(AND(A,B), OR(C,D))], | ||||||
|  |     ["$__CC4_AOA",  OR(AND(A,B),AND(C,D))], | ||||||
|  |     ["$__CC4_AOX",  OR(AND(A,B),XOR(C,D))], | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | for name, expr in base_cells: | ||||||
|  |     cells.append([name, 10, expr]) | ||||||
|  | 
 | ||||||
|  |     name = (name | ||||||
|  |         .replace("$__CC4_", "$__CC5_") | ||||||
|  |         .replace("$__CC3_", "$__CC4_") | ||||||
|  |         .replace("$__CC2_", "$__CC3_")) | ||||||
|  | 
 | ||||||
|  |     # Cells such as $__CC4_AA_A are redundant, as $__CC4_AAA is equivalent | ||||||
|  |     if name not in ("$__CC4_AA", "$__CC3_A"): | ||||||
|  |         cells.append([name + "_A", 12, AND(E, expr)]) | ||||||
|  |     if name not in ("$__CC4_OO", "$__CC3_O"): | ||||||
|  |         cells.append([name + "_O", 12,  OR(E, expr)]) | ||||||
|  |     if name not in ("$__CC4_XX", "$__CC3_X"): | ||||||
|  |         cells.append([name + "_X", 12, XOR(E, expr)]) | ||||||
|  | 
 | ||||||
|  | with open("techlibs/gatemate/lut_tree_cells.genlib", "w") as glf: | ||||||
|  |     def mkGate(name, cost, expr, max_load=9999, block_delay = 10, fanout_delay = 5): | ||||||
|  |         name = name.replace(" ", "") | ||||||
|  |         expr = expr.map() | ||||||
|  | 
 | ||||||
|  |         phase = "UNKNOWN" | ||||||
|  |         if expr.isInv(): phase = "INV" | ||||||
|  |         if expr.isNonInv(): phase = "NONINV" | ||||||
|  | 
 | ||||||
|  |         print("", file=glf) | ||||||
|  |         print("GATE %s %d Y=%s;" % (name, cost, expr.as_genlib_term()), file=glf) | ||||||
|  |         print("PIN * %s 1 %d %d %d %d %d" % (phase, max_load, block_delay, fanout_delay, block_delay, fanout_delay), file=glf) | ||||||
|  |     print("GATE $__ZERO 0 Y=CONST0;", file=glf) | ||||||
|  |     print("GATE $__ONE 0 Y=CONST1;", file=glf) | ||||||
|  |     for name, cost, expr in cells: | ||||||
|  |         mkGate(name, cost, expr) | ||||||
|  | 
 | ||||||
|  | class LUTTreeNode: | ||||||
|  |     def __init__(self, name, width, inputs=None): | ||||||
|  |         self.name = name | ||||||
|  |         self.width = width | ||||||
|  |         self.inputs = inputs | ||||||
|  |     def is_input(self): | ||||||
|  |         return self.width == 0 | ||||||
|  |     def map(self, expr, params, ports): | ||||||
|  |         if self.is_input(): | ||||||
|  |             # Input to LUT tree | ||||||
|  |             if expr is None: | ||||||
|  |                 ports[self.name] = "" # disconnected input | ||||||
|  |             else: | ||||||
|  |                 assert(len(expr.args) == 0) | ||||||
|  |                 ports[self.name] = expr.fun | ||||||
|  |             return | ||||||
|  |         if expr is None: | ||||||
|  |             # Unused part of tree | ||||||
|  |             params[self.name] = "4'b0000" | ||||||
|  |             for i in self.inputs: | ||||||
|  |                 i.map(None, params, ports) | ||||||
|  |             return | ||||||
|  |         elif len(expr.args) == 0: | ||||||
|  |             # Input to the expression; but not LUT tree | ||||||
|  |             # Insert a route through | ||||||
|  |             params[self.name] = "4'b1010" | ||||||
|  |             self.inputs[0].map(expr, params, ports) | ||||||
|  |             for i in self.inputs[1:]: | ||||||
|  |                 i.map(None, params, ports) | ||||||
|  |             return | ||||||
|  |         # Map uphill LUTs; uninverting arguments and keeping track of that if needed | ||||||
|  |         arg_inv = [] | ||||||
|  |         for (i, arg) in zip(self.inputs, expr.args): | ||||||
|  |             if arg.fun == "NOT": | ||||||
|  |                 i.map(arg.args[0], params, ports) | ||||||
|  |                 arg_inv.append(True) | ||||||
|  |             else: | ||||||
|  |                 i.map(arg, params, ports) | ||||||
|  |                 arg_inv.append(False) | ||||||
|  |         # Determine base init value | ||||||
|  |         assert self.width == 2 | ||||||
|  |         if expr.fun == "AND": | ||||||
|  |             init = 0b1000 | ||||||
|  |         elif expr.fun == "OR": | ||||||
|  |             init = 0b1110 | ||||||
|  |         elif expr.fun == "XOR": | ||||||
|  |             init = 0b0110 | ||||||
|  |         else: | ||||||
|  |             assert False, expr.fun | ||||||
|  |         # Swap bits if init inverted | ||||||
|  |         swapped_init = 0b0000 | ||||||
|  |         for b in range(4): | ||||||
|  |             if ((init >> b) & 0x1) == 0: continue | ||||||
|  |             for i in range(2): | ||||||
|  |                 if arg_inv[i]: | ||||||
|  |                     b ^= (1 << i) | ||||||
|  |             swapped_init |= (1 << b) | ||||||
|  |         # Set init param | ||||||
|  |         params[self.name] = "4'b{:04b}".format(swapped_init) | ||||||
|  | 
 | ||||||
|  | def LUT2(name, i0, i1): return LUTTreeNode(name, 2, [i0, i1]) | ||||||
|  | def I(name): return LUTTreeNode(name, 0) | ||||||
|  | 
 | ||||||
|  | lut_prims = { | ||||||
|  |     "CC_LUT2": LUT2("INIT", I("I0"), I("I1")), | ||||||
|  |     "CC_L2T4": LUT2( | ||||||
|  |         "INIT_L10", | ||||||
|  |         LUT2("INIT_L00", I("I0"), I("I1")), | ||||||
|  |         LUT2("INIT_L01", I("I2"), I("I3")), | ||||||
|  |     ), | ||||||
|  |     "CC_L2T5": LUT2( | ||||||
|  |         "INIT_L20", I("I4"), LUT2("INIT_L11", | ||||||
|  |             LUT2("INIT_L02", I("I0"), I("I1")), | ||||||
|  |             LUT2("INIT_L03", I("I2"), I("I3")), | ||||||
|  |         ) | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | with open("techlibs/gatemate/lut_tree_map.v", "w") as vf: | ||||||
|  |     # Non-automatic rules | ||||||
|  |     print(""" | ||||||
|  | module \\$__ZERO (output Y); assign Y = 1'b0; endmodule | ||||||
|  | module \\$__ONE (output Y); assign Y = 1'b1; endmodule | ||||||
|  | 
 | ||||||
|  | module \\$__CC_BUF (input A, output Y); assign Y = A; endmodule | ||||||
|  | 
 | ||||||
|  | module \\$__CC_MUX (input A, B, C, output Y); | ||||||
|  |     CC_MX2 _TECHMAP_REPLACE_ ( | ||||||
|  |         .D0(A), .D1(B), .S0(C), | ||||||
|  |         .Y(Y) | ||||||
|  |     ); | ||||||
|  | endmodule | ||||||
|  | """, file=vf) | ||||||
|  |     for name, cost, expr in cells: | ||||||
|  |         expr = expr.mapMux().mapNot() # Don't map XOR | ||||||
|  |         if name in ("$__CC_BUF", "$__CC_NOT", "$__CC_MUX"): | ||||||
|  |             # Special cases | ||||||
|  |             continue | ||||||
|  |         if name.startswith("$__CC2_"): | ||||||
|  |             prim = "CC_LUT2" | ||||||
|  |         elif name.startswith("$__CC5_") or (name.startswith("$__CC4_") and cost == 12): | ||||||
|  |             prim = "CC_L2T5" | ||||||
|  |         else: | ||||||
|  |             prim = "CC_L2T4" | ||||||
|  |         ports = {} | ||||||
|  |         params = {} | ||||||
|  |         lut_prims[prim].map(expr, params, ports) | ||||||
|  |         print("", file=vf) | ||||||
|  |         print("module \\%s (input %s, output Y);" % (name, | ||||||
|  |             ", ".join(sorted(set(x for x in ports.values() if x)))), file=vf) | ||||||
|  |         print("    %s #(" % prim, file=vf) | ||||||
|  |         for k, v in sorted(params.items(), key=lambda x: x[0]): | ||||||
|  |             print("        .%s(%s)," % (k, v), file=vf) | ||||||
|  |         print("    ) _TECHMAP_REPLACE_ (", file=vf) | ||||||
|  |         print("         %s," % ", ".join(".%s(%s)" % (k, v) for k, v in sorted(ports.items(), key=lambda x:x[0])), | ||||||
|  |             file=vf) | ||||||
|  |         print("         .O(Y)", file=vf) | ||||||
|  |         print("    );", file=vf) | ||||||
|  |         print("endmodule", file=vf) | ||||||
|  | @ -67,7 +67,10 @@ struct SynthGateMatePass : public ScriptPass | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -nomx8, -nomx4\n"); | 		log("    -nomx8, -nomx4\n"); | ||||||
| 		log("        do not use CC_MX{8,4} multiplexer cells in output netlist.\n"); | 		log("        do not use CC_MX{8,4} multiplexer cells in output netlist.\n"); | ||||||
| 		log("\n");; | 		log("\n"); | ||||||
|  | 		log("    -luttree\n"); | ||||||
|  | 		log("        use new LUT tree mapping approach (EXPERIMENTAL).\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("    -dff\n"); | 		log("    -dff\n"); | ||||||
| 		log("        run 'abc' with -dff option\n"); | 		log("        run 'abc' with -dff option\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | @ -87,7 +90,7 @@ struct SynthGateMatePass : public ScriptPass | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	string top_opt, vlog_file, json_file; | 	string top_opt, vlog_file, json_file; | ||||||
| 	bool noflatten, nobram, noaddf, nomult, nomx4, nomx8, dff, retime, noiopad, noclkbuf; | 	bool noflatten, nobram, noaddf, nomult, nomx4, nomx8, luttree, dff, retime, noiopad, noclkbuf; | ||||||
| 
 | 
 | ||||||
| 	void clear_flags() override | 	void clear_flags() override | ||||||
| 	{ | 	{ | ||||||
|  | @ -100,6 +103,7 @@ struct SynthGateMatePass : public ScriptPass | ||||||
| 		nomult = false; | 		nomult = false; | ||||||
| 		nomx4 = false; | 		nomx4 = false; | ||||||
| 		nomx8 = false; | 		nomx8 = false; | ||||||
|  | 		luttree = false; | ||||||
| 		dff = false; | 		dff = false; | ||||||
| 		retime = false; | 		retime = false; | ||||||
| 		noiopad = false; | 		noiopad = false; | ||||||
|  | @ -158,6 +162,10 @@ struct SynthGateMatePass : public ScriptPass | ||||||
| 				nomx8 = true; | 				nomx8 = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-luttree") { | ||||||
|  | 				luttree = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			if (args[argidx] == "-dff") { | 			if (args[argidx] == "-dff") { | ||||||
| 				dff = true; | 				dff = true; | ||||||
| 				continue; | 				continue; | ||||||
|  | @ -298,11 +306,23 @@ struct SynthGateMatePass : public ScriptPass | ||||||
| 
 | 
 | ||||||
| 		if (check_label("map_luts")) | 		if (check_label("map_luts")) | ||||||
| 		{ | 		{ | ||||||
| 			std::string abc_args = " -dress -lut 4"; | 			if (luttree || help_mode) { | ||||||
| 			if (dff) { | 				std::string abc_args = " -genlib +/gatemate/lut_tree_cells.genlib"; | ||||||
| 				abc_args += " -dff"; | 				if (dff) { | ||||||
|  | 					abc_args += " -dff"; | ||||||
|  | 				} | ||||||
|  | 				run("abc " + abc_args, "(with -luttree)"); | ||||||
|  | 				run("techmap -map +/gatemate/lut_tree_map.v", "(with -luttree)"); | ||||||
|  | 				run("gatemate_foldinv", "(with -luttree)"); | ||||||
|  | 				run("techmap -map +/gatemate/inv_map.v", "(with -luttree)"); | ||||||
|  | 			} | ||||||
|  | 			if (!luttree || help_mode) { | ||||||
|  | 				std::string abc_args = " -dress -lut 4"; | ||||||
|  | 				if (dff) { | ||||||
|  | 					abc_args += " -dff"; | ||||||
|  | 				} | ||||||
|  | 				run("abc " + abc_args, "(without -luttree)"); | ||||||
| 			} | 			} | ||||||
| 			run("abc " + abc_args); |  | ||||||
| 			run("clean"); | 			run("clean"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue