aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--passes/techmap/extract_counter.cc75
-rw-r--r--techlibs/greenpak4/cells_map.v86
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