gowin: convert latches to DFFs with LATCH attribute during packing

Instead of teaching all DFF infrastructure about 12 DL latch types,
pack_latches() converts them to corresponding DFF types early and sets
a LATCH attribute. This attribute is picked up by gowin_pack to set
REGMODE=LATCH instead of FF.
This commit is contained in:
Justin Zaun 2026-03-02 14:30:30 -10:00
parent 7d5fb8c240
commit 0de6a67e80
7 changed files with 46 additions and 30 deletions

View File

@ -907,6 +907,7 @@ X(DLP)
X(DLPE)
X(DLNP)
X(DLNPE)
X(LATCH)
// Shadow RAM
X(ROM16)

View File

@ -981,15 +981,7 @@ inline bool incompatible_ffs(const CellInfo *ff, const CellInfo *adj_ff)
(ff->type == id_DFFNPE && adj_ff->type != id_DFFNCE) ||
(ff->type == id_DFFNCE && adj_ff->type != id_DFFNPE) || (ff->type == id_DFF && adj_ff->type != id_DFF) ||
(ff->type == id_DFFE && adj_ff->type != id_DFFE) || (ff->type == id_DFFN && adj_ff->type != id_DFFN) ||
(ff->type == id_DFFNE && adj_ff->type != id_DFFNE) ||
(ff->type == id_DL && adj_ff->type != id_DL) ||
(ff->type == id_DLE && adj_ff->type != id_DLE) ||
(ff->type == id_DLN && adj_ff->type != id_DLN) ||
(ff->type == id_DLNE && adj_ff->type != id_DLNE) ||
(ff->type == id_DLC && adj_ff->type != id_DLP) || (ff->type == id_DLP && adj_ff->type != id_DLC) ||
(ff->type == id_DLCE && adj_ff->type != id_DLPE) || (ff->type == id_DLPE && adj_ff->type != id_DLCE) ||
(ff->type == id_DLNC && adj_ff->type != id_DLNP) || (ff->type == id_DLNP && adj_ff->type != id_DLNC) ||
(ff->type == id_DLNCE && adj_ff->type != id_DLNPE) || (ff->type == id_DLNPE && adj_ff->type != id_DLNCE));
(ff->type == id_DFFNE && adj_ff->type != id_DFFNE));
}
// placement validation

View File

@ -9,20 +9,18 @@ namespace {
// Return true if a cell is a LUT
inline bool type_is_lut(IdString cell_type) { return cell_type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4); }
inline bool is_lut(const CellInfo *cell) { return type_is_lut(cell->type); }
// Return true if a cell is a latch
inline bool type_is_latch(IdString cell_type)
{
return cell_type.in(id_DL, id_DLE, id_DLN, id_DLNE, id_DLC, id_DLCE, id_DLNC, id_DLNCE, id_DLP, id_DLPE,
id_DLNP, id_DLNPE);
}
inline bool is_latch(const CellInfo *cell) { return type_is_latch(cell->type); }
// Return true if a cell is a DFF (or latch, which uses the same BEL sites)
// Return true if a cell is a DFF
inline bool type_is_dff(IdString cell_type)
{
return cell_type.in(id_DFF, id_DFFE, id_DFFN, id_DFFNE, id_DFFS, id_DFFSE, id_DFFNS, id_DFFNSE, id_DFFR, id_DFFRE,
id_DFFNR, id_DFFNRE, id_DFFP, id_DFFPE, id_DFFNP, id_DFFNPE, id_DFFC, id_DFFCE, id_DFFNC,
id_DFFNCE) ||
type_is_latch(cell_type);
id_DFFNCE);
}
// Return true if a cell is a latch (before packing converts them to DFFs)
inline bool type_is_latch(IdString cell_type)
{
return cell_type.in(id_DL, id_DLE, id_DLN, id_DLNE, id_DLC, id_DLCE, id_DLNC, id_DLNCE, id_DLP, id_DLPE,
id_DLNP, id_DLNPE);
}
inline bool is_dff(const CellInfo *cell) { return type_is_dff(cell->type); }
// Return true if a cell is a ALU

View File

@ -718,6 +718,9 @@ void GowinPacker::run(void)
pack_ssram();
ctx->check();
pack_latches();
ctx->check();
constrain_lutffs();
ctx->check();

View File

@ -107,6 +107,7 @@ struct GowinPacker
std::unique_ptr<CellInfo> alu_add_cout_block(Context *ctx, CellInfo *tail, NetInfo *cout_net);
std::unique_ptr<CellInfo> alu_add_dummy_block(Context *ctx, CellInfo *tail);
void optimize_alu_lut(CellInfo *ci, int mode);
void pack_latches(void);
void constrain_lutffs(void);
std::unique_ptr<CellInfo> ssram_make_lut(Context *ctx, CellInfo *ci, int index);

View File

@ -558,13 +558,7 @@ static bool incompatible_ffs(IdString type_a, IdString type_b)
(type_a == id_DFFNP && type_b != id_DFFNC) || (type_a == id_DFFNC && type_b != id_DFFNP) ||
(type_a == id_DFFNPE && type_b != id_DFFNCE) || (type_a == id_DFFNCE && type_b != id_DFFNPE) ||
(type_a == id_DFF && type_b != id_DFF) || (type_a == id_DFFN && type_b != id_DFFN) ||
(type_a == id_DFFE && type_b != id_DFFE) || (type_a == id_DFFNE && type_b != id_DFFNE) ||
(type_a == id_DL && type_b != id_DL) || (type_a == id_DLE && type_b != id_DLE) ||
(type_a == id_DLN && type_b != id_DLN) || (type_a == id_DLNE && type_b != id_DLNE) ||
(type_a == id_DLC && type_b != id_DLP) || (type_a == id_DLP && type_b != id_DLC) ||
(type_a == id_DLCE && type_b != id_DLPE) || (type_a == id_DLPE && type_b != id_DLCE) ||
(type_a == id_DLNC && type_b != id_DLNP) || (type_a == id_DLNP && type_b != id_DLNC) ||
(type_a == id_DLNCE && type_b != id_DLNPE) || (type_a == id_DLNPE && type_b != id_DLNCE));
(type_a == id_DFFE && type_b != id_DFFE) || (type_a == id_DFFNE && type_b != id_DFFNE));
}
void GowinPacker::pack_io_regs(void)

View File

@ -503,6 +503,36 @@ void GowinPacker::pack_alus(void)
}
}
// ===================================
// convert latches to DFFs with LATCH attribute
// ===================================
void GowinPacker::pack_latches(void)
{
// Latch-to-DFF type mapping: latches use the same BEL as DFFs,
// just with REGMODE set to LATCH instead of FF.
const dict<IdString, IdString> latch_to_dff = {
{id_DL, id_DFF}, {id_DLE, id_DFFE},
{id_DLN, id_DFFN}, {id_DLNE, id_DFFNE},
{id_DLC, id_DFFC}, {id_DLCE, id_DFFCE},
{id_DLNC, id_DFFNC}, {id_DLNCE, id_DFFNCE},
{id_DLP, id_DFFP}, {id_DLPE, id_DFFPE},
{id_DLNP, id_DFFNP}, {id_DLNPE, id_DFFNPE},
};
int converted = 0;
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
auto it = latch_to_dff.find(ci->type);
if (it != latch_to_dff.end()) {
ci->type = it->second;
ci->setAttr(id_LATCH, 1);
++converted;
}
}
if (converted)
log_info("Converted %d latches to DFFs.\n", converted);
}
// ===================================
// glue LUT and FF
// ===================================
@ -514,10 +544,7 @@ void GowinPacker::constrain_lutffs(void)
{id_DFFS, id_D}, {id_DFFSE, id_D}, {id_DFFNS, id_D}, {id_DFFNSE, id_D},
{id_DFFR, id_D}, {id_DFFRE, id_D}, {id_DFFNR, id_D}, {id_DFFNRE, id_D},
{id_DFFP, id_D}, {id_DFFPE, id_D}, {id_DFFNP, id_D}, {id_DFFNPE, id_D},
{id_DFFC, id_D}, {id_DFFCE, id_D}, {id_DFFNC, id_D}, {id_DFFNCE, id_D},
{id_DL, id_D}, {id_DLE, id_D}, {id_DLN, id_D}, {id_DLNE, id_D},
{id_DLC, id_D}, {id_DLCE, id_D}, {id_DLNC, id_D}, {id_DLNCE, id_D},
{id_DLP, id_D}, {id_DLPE, id_D}, {id_DLNP, id_D}, {id_DLNPE, id_D}};
{id_DFFC, id_D}, {id_DFFCE, id_D}, {id_DFFNC, id_D}, {id_DFFNCE, id_D}};
int lutffs = h.constrain_cell_pairs(lut_outs, dff_ins, 1, 1);
log_info("Constrained %d LUTFF pairs.\n", lutffs);