gowin: add hardware latch support (DL/DLN/DLC/DLP variants)

Add simulation models, techmap, and dfflegalize rules for Gowin
DL-series latch primitives. Latches use the same physical BEL as
DFFs with REGMODE set to LATCH. All 12 variants are supported:
DL, DLE, DLN, DLNE, DLC, DLCE, DLNC, DLNCE, DLP, DLPE, DLNP, DLNPE.
This commit is contained in:
Justin Zaun 2026-03-01 16:11:30 -10:00
parent 1d3f9b7905
commit 8e97f34d31
5 changed files with 175 additions and 4 deletions

View File

@ -3,6 +3,7 @@ OBJS += techlibs/gowin/synth_gowin.o
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_map.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_sim.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_latch.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_xtra_gw1n.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_xtra_gw2a.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_xtra_gw5a.v))

View File

@ -0,0 +1,37 @@
`default_nettype none
// DL D Latch with Positive Gate
module \$_DLATCH_P_ (input E, D, output Q);
DL _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLN D Latch with Negative Gate
module \$_DLATCH_N_ (input E, D, output Q);
DLN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLC D Latch with Positive Gate and Asynchronous Clear
module \$_DLATCH_PP0_ (input E, R, D, output Q);
DLC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E), .CLEAR(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLNC D Latch with Negative Gate and Asynchronous Clear
module \$_DLATCH_NP0_ (input E, R, D, output Q);
DLNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E), .CLEAR(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLP D Latch with Positive Gate and Asynchronous Preset
module \$_DLATCH_PP1_ (input E, R, D, output Q);
DLP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E), .PRESET(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule
// DLNP D Latch with Negative Gate and Asynchronous Preset
module \$_DLATCH_NP1_ (input E, R, D, output Q);
DLNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(E), .PRESET(R));
wire _TECHMAP_REMOVEINIT_Q_ = 1;
endmodule

View File

@ -540,6 +540,113 @@ module DFFNCE (output reg Q, input D, CLK, CE, CLEAR);
end
endmodule // DFFNCE (negative clock edge; asynchronous clear; clock enable)
// Latch sim cells
// Gate signal uses CLK port name to match the physical DFF BEL pin
(* lib_whitebox *)
module DL (output reg Q, input D, CLK);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLK) Q <= D;
endmodule
(* lib_whitebox *)
module DLN (output reg Q, input D, CLK);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (!CLK) Q <= D;
endmodule
(* lib_whitebox *)
module DLE (output reg Q, input D, CLK, CE);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLK && CE) Q <= D;
endmodule
(* lib_whitebox *)
module DLNE (output reg Q, input D, CLK, CE);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (!CLK && CE) Q <= D;
endmodule
(* lib_whitebox *)
module DLC (output reg Q, input D, CLK, CLEAR);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLEAR) Q <= 1'b0;
else if (CLK) Q <= D;
endmodule
(* lib_whitebox *)
module DLCE (output reg Q, input D, CLK, CE, CLEAR);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLEAR) Q <= 1'b0;
else if (CLK && CE) Q <= D;
endmodule
(* lib_whitebox *)
module DLNC (output reg Q, input D, CLK, CLEAR);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLEAR) Q <= 1'b0;
else if (!CLK) Q <= D;
endmodule
(* lib_whitebox *)
module DLNCE (output reg Q, input D, CLK, CE, CLEAR);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (CLEAR) Q <= 1'b0;
else if (!CLK && CE) Q <= D;
endmodule
(* lib_whitebox *)
module DLP (output reg Q, input D, CLK, PRESET);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (PRESET) Q <= 1'b1;
else if (CLK) Q <= D;
endmodule
(* lib_whitebox *)
module DLPE (output reg Q, input D, CLK, CE, PRESET);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (PRESET) Q <= 1'b1;
else if (CLK && CE) Q <= D;
endmodule
(* lib_whitebox *)
module DLNP (output reg Q, input D, CLK, PRESET);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (PRESET) Q <= 1'b1;
else if (!CLK) Q <= D;
endmodule
(* lib_whitebox *)
module DLNPE (output reg Q, input D, CLK, CE, PRESET);
parameter [0:0] INIT = 1'b0;
initial Q = INIT;
always @*
if (PRESET) Q <= 1'b1;
else if (!CLK && CE) Q <= D;
endmodule
// TODO add more DFF sim cells
module VCC(output V);

View File

@ -340,17 +340,18 @@ struct SynthGowinPass : public ScriptPass
run("opt_clean");
if (family == "gw5a") {
if (strict_gw5a_dffs) {
run("dfflegalize -cell $_SDFFE_PP?P_ r -cell $_DFFE_PP?P_ r");
run("dfflegalize -cell $_SDFFE_PP?P_ r -cell $_DFFE_PP?P_ r -cell $_DLATCH_?_ x -cell $_DLATCH_?P?_ x");
} else {
run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFFE_PP?P_ r -cell $_DFFE_PP?P_ r");
run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFFE_PP?P_ r -cell $_DFFE_PP?P_ r -cell $_DLATCH_?_ x -cell $_DLATCH_?P?_ x");
}
} else {
if (nodffe)
run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFF_?P?_ r -cell $_DFF_?P?_ r");
run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFF_?P?_ r -cell $_DFF_?P?_ r -cell $_DLATCH_?_ x -cell $_DLATCH_?P?_ x");
else
run("dfflegalize -cell $_DFF_?_ 0 -cell $_DFFE_?P_ 0 -cell $_SDFF_?P?_ r -cell $_SDFFE_?P?P_ r -cell $_DFF_?P?_ r -cell $_DFFE_?P?P_ r");
run("dfflegalize -cell $_DFF_?_ 0 -cell $_DFFE_?P_ 0 -cell $_SDFF_?P?_ r -cell $_SDFFE_?P?P_ r -cell $_DFF_?P?_ r -cell $_DFFE_?P?P_ r -cell $_DLATCH_?_ x -cell $_DLATCH_?P?_ x");
}
run("techmap -map +/gowin/cells_map.v");
run("techmap -map +/gowin/cells_latch.v");
run("opt_expr -mux_undef");
run("simplemap");
}

View File

@ -0,0 +1,25 @@
read_verilog ../common/latches.v
design -save read
hierarchy -top latchp
proc
equiv_opt -async2sync -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd latchp # Constrain all select calls below inside the top module
select -assert-count 1 t:DL
select -assert-count 3 t:IBUF
select -assert-count 1 t:OBUF
select -assert-none t:DL t:IBUF t:OBUF %% t:* %D
design -load read
hierarchy -top latchn
proc
equiv_opt -async2sync -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd latchn # Constrain all select calls below inside the top module
select -assert-count 1 t:DLN
select -assert-count 3 t:IBUF
select -assert-count 1 t:OBUF
select -assert-none t:DLN t:IBUF t:OBUF %% t:* %D