From 0de6a67e8048365fb47b23a503e14e588db91934 Mon Sep 17 00:00:00 2001 From: Justin Zaun Date: Mon, 2 Mar 2026 14:30:30 -1000 Subject: [PATCH] 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. --- himbaechel/uarch/gowin/constids.inc | 1 + himbaechel/uarch/gowin/gowin.cc | 10 +-------- himbaechel/uarch/gowin/gowin.h | 18 +++++++-------- himbaechel/uarch/gowin/pack.cc | 3 +++ himbaechel/uarch/gowin/pack.h | 1 + himbaechel/uarch/gowin/pack_io.cc | 8 +------ himbaechel/uarch/gowin/pack_luts.cc | 35 +++++++++++++++++++++++++---- 7 files changed, 46 insertions(+), 30 deletions(-) diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 7f7d024b..5e802d40 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -907,6 +907,7 @@ X(DLP) X(DLPE) X(DLNP) X(DLNPE) +X(LATCH) // Shadow RAM X(ROM16) diff --git a/himbaechel/uarch/gowin/gowin.cc b/himbaechel/uarch/gowin/gowin.cc index 1ad37383..0904127c 100644 --- a/himbaechel/uarch/gowin/gowin.cc +++ b/himbaechel/uarch/gowin/gowin.cc @@ -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 diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index cc16c2a4..1ea79f33 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -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 diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index 1101af36..0b80d68a 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -718,6 +718,9 @@ void GowinPacker::run(void) pack_ssram(); ctx->check(); + pack_latches(); + ctx->check(); + constrain_lutffs(); ctx->check(); diff --git a/himbaechel/uarch/gowin/pack.h b/himbaechel/uarch/gowin/pack.h index d3f7eae5..1d254106 100644 --- a/himbaechel/uarch/gowin/pack.h +++ b/himbaechel/uarch/gowin/pack.h @@ -107,6 +107,7 @@ struct GowinPacker std::unique_ptr alu_add_cout_block(Context *ctx, CellInfo *tail, NetInfo *cout_net); std::unique_ptr 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 ssram_make_lut(Context *ctx, CellInfo *ci, int index); diff --git a/himbaechel/uarch/gowin/pack_io.cc b/himbaechel/uarch/gowin/pack_io.cc index f7855945..26807fa7 100644 --- a/himbaechel/uarch/gowin/pack_io.cc +++ b/himbaechel/uarch/gowin/pack_io.cc @@ -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) diff --git a/himbaechel/uarch/gowin/pack_luts.cc b/himbaechel/uarch/gowin/pack_luts.cc index 4b6df1e6..914da663 100644 --- a/himbaechel/uarch/gowin/pack_luts.cc +++ b/himbaechel/uarch/gowin/pack_luts.cc @@ -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 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);