From a84172b23bce1429dedef1eb08fdb25393ba72b6 Mon Sep 17 00:00:00 2001
From: Andrew Zonenberg <azonenberg@drawersteak.com>
Date: Wed, 13 Sep 2017 10:58:41 -0700
Subject: [PATCH] Initial support for extraction of counters with clock enable

---
 passes/techmap/extract_counter.cc | 75 +++++++++++++++++++++++----
 techlibs/greenpak4/cells_map.v    | 86 +++++++++++++++++++++++--------
 2 files changed, 131 insertions(+), 30 deletions(-)

diff --git a/passes/techmap/extract_counter.cc b/passes/techmap/extract_counter.cc
index de374ab2b..ec34d9cd1 100644
--- a/passes/techmap/extract_counter.cc
+++ b/passes/techmap/extract_counter.cc
@@ -92,9 +92,11 @@ struct CounterExtraction
 	int width;						//counter width
 	RTLIL::Wire* rwire;				//the register output
 	bool has_reset;					//true if we have a reset
+	bool has_ce;					//true if we have a clock enable
 	RTLIL::SigSpec rst;				//reset pin
 	int count_value;				//value we count from
-	RTLIL::SigSpec clk;				//clock signal
+	RTLIL::SigSpec ce;				//clock signal
+	RTLIL::SigSpec clk;				//clock enable, if any
 	RTLIL::SigSpec outsig;			//counter output signal
 	RTLIL::Cell* count_mux;			//counter mux
 	RTLIL::Cell* count_reg;			//counter register
@@ -190,12 +192,43 @@ int counter_tryextract(
 		return 13;
 	extract.underflow_inv = underflow_inv;
 
-	//Y connection of the mux must have exactly one load, the counter's internal register
+	//Y connection of the mux must have exactly one load, the counter's internal register, if there's no clock enable
+	//If we have a clock enable, Y drives the B input of a mux. A of that mux must come from our register
 	const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y"));
 	pool<Cell*> muxy_loads = get_other_cells(muxy, index, count_mux);
 	if(muxy_loads.size() != 1)
 		return 14;
-	Cell* count_reg = *muxy_loads.begin();
+	Cell* muxload = *muxy_loads.begin();
+	Cell* count_reg = muxload;
+	Cell* cemux = NULL;
+	RTLIL::SigSpec cey;
+	if(muxload->type == "$mux")
+	{
+		//This mux is probably a clock enable mux.
+		//Find our count register (should be our only load)
+		cemux = muxload;
+		cey = sigmap(cemux->getPort("\\Y"));
+		pool<Cell*> cey_loads = get_other_cells(cey, index, cemux);
+		if(cey_loads.size() != 1)
+			return 24;
+		count_reg = *cey_loads.begin();
+
+		//Mux should have A driven by count Q, and B by muxy
+		//TODO: if A and B are swapped, CE polarity is inverted
+		if(sigmap(cemux->getPort("\\B")) != muxy)
+			return 24;
+		if(sigmap(cemux->getPort("\\A")) != sigmap(count_reg->getPort("\\Q")))
+			return 24;
+		if(sigmap(cemux->getPort("\\Y")) != sigmap(count_reg->getPort("\\D")))
+			return 24;
+
+		//Select of the mux is our clock enable
+		extract.has_ce = true;
+		extract.ce = sigmap(cemux->getPort("\\S"));
+	}
+	else
+		extract.has_ce = false;
+
 	extract.count_reg = count_reg;
 	if(count_reg->type == "$dff")
 		extract.has_reset = false;
@@ -216,17 +249,30 @@ int counter_tryextract(
 	//TODO: support synchronous reset
 	else
 		return 15;
-	if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D"))
+
+	//Sanity check that we use the ALU output properly
+	if(extract.has_ce)
+	{
+		if(!is_full_bus(muxy, index, count_mux, "\\Y", cemux, "\\B"))
+			return 16;
+		if(!is_full_bus(cey, index, cemux, "\\Y", count_reg, "\\D"))
+			return 16;
+	}
+	else if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D"))
 		return 16;
 
 	//TODO: Verify count_reg CLK_POLARITY is 1
 
 	//Register output must have exactly two loads, the inverter and ALU
 	//(unless we have a parallel output!)
+	//If we have a clock enable, 3 is OK
 	const RTLIL::SigSpec qport = count_reg->getPort("\\Q");
 	const RTLIL::SigSpec cnout = sigmap(qport);
 	pool<Cell*> cnout_loads = get_other_cells(cnout, index, count_reg);
-	if(cnout_loads.size() > 2)
+	unsigned int max_loads = 2;
+	if(extract.has_ce)
+		max_loads = 3;
+	if(cnout_loads.size() > max_loads)
 	{
 		//If we specified a limited set of cells for parallel output, check that we only drive them
 		if(!parallel_cells.empty())
@@ -237,6 +283,8 @@ int counter_tryextract(
 					continue;
 				if(c == cell)
 					continue;
+				if(c == muxload)
+					continue;
 
 				//Make sure we're in the whitelist
 				if( parallel_cells.find(c->type) == parallel_cells.end())
@@ -346,7 +394,7 @@ void counter_worker(
 	//Do nothing, unless extraction was forced in which case give an error
 	if(reason != 0)
 	{
-		static const char* reasons[24]=
+		static const char* reasons[25]=
 		{
 			"no problem",									//0
 			"counter is too large/small",					//1
@@ -371,7 +419,8 @@ void counter_worker(
 			"No init value found",							//20
 			"Underflow value is not equal to init value",	//21
 			"Reset polarity is not positive",				//22
-			"Reset is not to zero"							//23
+			"Reset is not to zero",							//23
+			"Clock enable configuration is unsupported"		//24
 		};
 
 		if(force_extract)
@@ -424,9 +473,17 @@ void counter_worker(
 	cell->setPort("\\CLK", extract.clk);
 	cell->setPort("\\OUT", extract.outsig);
 
-	//Hook up hard-wired ports (for now CE and up/down are not supported), default to no parallel output
+	//Hook up clock enable
+	if(extract.has_ce)
+	{
+		cell->setParam("\\HAS_CE", RTLIL::Const(1));
+		cell->setPort("\\CE", extract.ce);
+	}
+	else
+		cell->setParam("\\HAS_CE", RTLIL::Const(0));
+
+	//Hook up hard-wired ports (for now up/down are not supported), default to no parallel output
 	cell->setParam("\\HAS_POUT", RTLIL::Const(0));
-	cell->setParam("\\HAS_CE", RTLIL::Const(0));
 	cell->setParam("\\DIRECTION", RTLIL::Const("DOWN"));
 	cell->setPort("\\CE", RTLIL::Const(1));
 	cell->setPort("\\UP", RTLIL::Const(0));
diff --git a/techlibs/greenpak4/cells_map.v b/techlibs/greenpak4/cells_map.v
index b0ec9fd3e..9897d4e35 100644
--- a/techlibs/greenpak4/cells_map.v
+++ b/techlibs/greenpak4/cells_map.v
@@ -161,8 +161,8 @@ module \$__COUNT_ (CE, CLK, OUT, POUT, RST, UP);
 	parameter WIDTH = 8;
 	parameter DIRECTION = "DOWN";
 
-	//If we have a CE, or DIRECTION other than DOWN fail... GP_COUNTx_ADV is not supported yet
-	if(HAS_CE || (DIRECTION != "DOWN") ) begin
+	//If we have a DIRECTION other than DOWN fail... GP_COUNTx_ADV is not supported yet
+	if(DIRECTION != "DOWN") begin
 		initial begin
 			$display("ERROR: \$__COUNT_ support for GP_COUNTx_ADV is not yet implemented. This counter should never have been extracted (bug in extract_counter pass?).");
 			$finish;
@@ -187,28 +187,72 @@ module \$__COUNT_ (CE, CLK, OUT, POUT, RST, UP);
 
 	//Looks like a legal counter! Do something with it
 	else if(WIDTH <= 8) begin
-		GP_COUNT8 #(
-			.COUNT_TO(COUNT_TO),
-			.RESET_MODE(RESET_MODE),
-			.CLKIN_DIVIDE(1)
-		) _TECHMAP_REPLACE_ (
-			.CLK(CLK),
-			.RST(RST),
-			.OUT(OUT),
-			.POUT(POUT)
-		);
+		if(HAS_CE) begin
+			wire ce_not;
+			GP_INV ceinv(
+				.IN(CE),
+				.OUT(ce_not)
+			);
+			GP_COUNT8_ADV #(
+				.COUNT_TO(COUNT_TO),
+				.RESET_MODE(RESET_MODE),
+				.RESET_VALUE("COUNT_TO"),
+				.CLKIN_DIVIDE(1)
+			) _TECHMAP_REPLACE_ (
+				.CLK(CLK),
+				.RST(RST),
+				.OUT(OUT),
+				.UP(1'b0),		//always count down for now
+				.KEEP(ce_not),
+				.POUT(POUT)
+			);
+		end
+		else begin
+			GP_COUNT8 #(
+				.COUNT_TO(COUNT_TO),
+				.RESET_MODE(RESET_MODE),
+				.CLKIN_DIVIDE(1)
+			) _TECHMAP_REPLACE_ (
+				.CLK(CLK),
+				.RST(RST),
+				.OUT(OUT),
+				.POUT(POUT)
+			);
+		end
 	end
 
 	else begin
-		GP_COUNT14 #(
-			.COUNT_TO(COUNT_TO),
-			.RESET_MODE(RESET_MODE),
-			.CLKIN_DIVIDE(1)
-		) _TECHMAP_REPLACE_ (
-			.CLK(CLK),
-			.RST(RST),
-			.OUT(OUT)
-		);
+		if(HAS_CE) begin
+			wire ce_not;
+			GP_INV ceinv(
+				.IN(CE),
+				.OUT(ce_not)
+			);
+			GP_COUNT14_ADV #(
+				.COUNT_TO(COUNT_TO),
+				.RESET_MODE(RESET_MODE),
+				.RESET_VALUE("COUNT_TO"),
+				.CLKIN_DIVIDE(1)
+			) _TECHMAP_REPLACE_ (
+				.CLK(CLK),
+				.RST(RST),
+				.OUT(OUT),
+				.UP(1'b0),		//always count down for now
+				.KEEP(ce_not),
+				.POUT(POUT)
+			);
+		end
+		else begin
+			GP_COUNT14 #(
+				.COUNT_TO(COUNT_TO),
+				.RESET_MODE(RESET_MODE),
+				.CLKIN_DIVIDE(1)
+			) _TECHMAP_REPLACE_ (
+				.CLK(CLK),
+				.RST(RST),
+				.OUT(OUT)
+			);
+		end
 	end
 
 endmodule