From e9b7da5a0f493109f9b5a04bda039c9d080794fb Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 10 Mar 2026 14:09:08 +1000 Subject: [PATCH] GOWIN. Fix DP when READ_MODE=1 Dual Port has a defective output register. This only manifests itself at small data widths and only on -C chips. That is, Tangprimer20k (GW2A-18) works perfectly, while Tangnano20k (GW2A-18C) stutters. The same story with GW1N-9 and GW1N-9C. Fortunately, the fix has long been included in nextpnr for SDP memory, so all that remains is to call the same function for Dual Port. Signed-off-by: YRabbit --- himbaechel/uarch/gowin/constids.inc | 4 ++ himbaechel/uarch/gowin/pack.h | 5 ++- himbaechel/uarch/gowin/pack_bsram.cc | 62 +++++++++++++--------------- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 527521bc..4c1fd416 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -917,6 +917,8 @@ X(BLK_SEL) X(BSRAM_SUBTYPE) X(WRITE_MODE) X(READ_MODE) +X(READ_MODE0) +X(READ_MODE1) X(RESET_MODE) X(BIT_WIDTH) X(BIT_WIDTH_0) @@ -1139,6 +1141,8 @@ X(LOCK) X(AD) X(DI) X(DO) +X(DOA) +X(DOB) X(P16A) X(P16B) X(P16C) diff --git a/himbaechel/uarch/gowin/pack.h b/himbaechel/uarch/gowin/pack.h index d3f7eae5..8b15b18e 100644 --- a/himbaechel/uarch/gowin/pack.h +++ b/himbaechel/uarch/gowin/pack.h @@ -113,12 +113,13 @@ struct GowinPacker // BSRAM void bsram_rename_ports(CellInfo *ci, int bit_width, char const *from, char const *to, int offset = 0); void bsram_fix_blksel(CellInfo *ci, std::vector> &new_cells); - void bsram_fix_outreg(CellInfo *ci, std::vector> &new_cells); + void bsram_fix_outreg(CellInfo *ci, int, IdString, IdString, IdString, IdString, IdString, IdString read_mode_param, + std::vector> &new_cells); void bsram_fix_sp(CellInfo *ci, std::vector> &new_cells); void pack_ROM(CellInfo *ci); void divide_sdp(CellInfo *ci, std::vector> &new_cells); void pack_SDPB(CellInfo *ci, std::vector> &new_cells); - void pack_DPB(CellInfo *ci); + void pack_DPB(CellInfo *ci, std::vector> &new_cells); void divide_sp(CellInfo *ci, std::vector> &new_cells); void pack_SP(CellInfo *ci, std::vector> &new_cells); diff --git a/himbaechel/uarch/gowin/pack_bsram.cc b/himbaechel/uarch/gowin/pack_bsram.cc index 1e3ad5d4..c9c8d110 100644 --- a/himbaechel/uarch/gowin/pack_bsram.cc +++ b/himbaechel/uarch/gowin/pack_bsram.cc @@ -97,18 +97,15 @@ void GowinPacker::bsram_fix_blksel(CellInfo *ci, std::vector> &new_cells) +void GowinPacker::bsram_fix_outreg(CellInfo *ci, int bit_width, IdString ce_pin, IdString oce_pin, IdString clk_pin, + IdString reset_pin, IdString do_pin, IdString read_mode_param, + std::vector> &new_cells) { - int bit_width = ci->params.at(id_BIT_WIDTH).as_int64(); - if (bit_width == 32 || bit_width == 36) { + if (ci->params.at(read_mode_param).as_int64() == 0) { return; } - int read_mode = ci->params.at(id_READ_MODE).as_int64(); - if (read_mode == 0) { - return; - } - NetInfo *ce_net = ci->getPort(id_CE); - NetInfo *oce_net = ci->getPort(id_OCE); + NetInfo *ce_net = ci->getPort(ce_pin); + NetInfo *oce_net = ci->getPort(oce_pin); if (ce_net == nullptr || oce_net == nullptr) { return; } @@ -119,17 +116,17 @@ void GowinPacker::bsram_fix_outreg(CellInfo *ci, std::vectorverbose) { log_info(" apply the BSRAM OUTREG fix\n"); } - ci->setParam(id_READ_MODE, 0); - ci->disconnectPort(id_OCE); - ci->connectPort(id_OCE, ce_net); + ci->setParam(read_mode_param, 0); + ci->disconnectPort(oce_pin); + ci->connectPort(oce_pin, ce_net); - NetInfo *reset_net = ci->getPort(id_RESET); + NetInfo *reset_net = ci->getPort(reset_pin); bool sync_reset = ci->params.at(id_RESET_MODE).as_string() == std::string("SYNC"); IdString dff_type = sync_reset ? id_DFFRE : id_DFFCE; IdString reset_port = sync_reset ? id_RESET : id_CLEAR; for (int i = 0; i < bit_width; ++i) { - IdString do_name = ctx->idf("DO[%d]", i); + IdString do_name = ctx->idf("%s[%d]", do_pin.c_str(ctx), i); const NetInfo *net = ci->getPort(do_name); if (net != nullptr) { if (net->users.empty()) { @@ -146,7 +143,7 @@ void GowinPacker::bsram_fix_outreg(CellInfo *ci, std::vectoraddInput(reset_port); cache_dff->connectPort(reset_port, reset_net); - ci->copyPortTo(id_CLK, cache_dff, id_CLK); + ci->copyPortTo(clk_pin, cache_dff, id_CLK); cache_dff->addOutput(id_Q); ci->movePortTo(do_name, cache_dff, id_Q); @@ -430,16 +427,6 @@ void GowinPacker::pack_SDPB(CellInfo *ci, std::vector> NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); - for (int i = 0; i < 14; ++i) { - ci->renamePort(ctx->idf("ADA[%d]", i), ctx->idf("ADA%d", i)); - ci->renamePort(ctx->idf("ADB[%d]", i), ctx->idf("ADB%d", i)); - } - - for (int i = 0; i < 3; ++i) { - ci->renamePort(ctx->idf("BLKSELA[%d]", i), ctx->idf("BLKSELA%d", i)); - ci->renamePort(ctx->idf("BLKSELB[%d]", i), ctx->idf("BLKSELB%d", i)); - } - ci->copyPortTo(id_OCE, ci, id_OCEB); // If misconnected RESET @@ -463,9 +450,10 @@ void GowinPacker::pack_SDPB(CellInfo *ci, std::vector> bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d", 18); } bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d"); + gwu.remove_brackets(ci); } -void GowinPacker::pack_DPB(CellInfo *ci) +void GowinPacker::pack_DPB(CellInfo *ci, std::vector> &new_cells) { int default_bw = 16; if (ci->type == id_DPB) { @@ -480,8 +468,11 @@ void GowinPacker::pack_DPB(CellInfo *ci) } int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64(); bsram_rename_ports(ci, bit_width, "DIA[%d]", "DIA%d"); - bsram_rename_ports(ci, bit_width, "DOA[%d]", "DOA%d"); + if (bit_width < 16 && gwu.need_BSRAM_OUTREG_fix()) { + bsram_fix_outreg(ci, bit_width, id_CEA, id_OCEA, id_CLKA, id_RESETA, id_DOA, id_READ_MODE0, new_cells); + } + bsram_rename_ports(ci, bit_width, "DOA[%d]", "DOA%d"); // In BYPASS mode, the OCE signal is dictated by CE. if (gwu.need_BSRAM_DP_CE_fix()) { if (bit_width <= 9) { @@ -489,12 +480,15 @@ void GowinPacker::pack_DPB(CellInfo *ci) ci->copyPortTo(id_CEA, ci, id_OCEA); } } - if (!ci->params.count(id_BIT_WIDTH_1)) { ci->setParam(id_BIT_WIDTH_1, Property(default_bw, 32)); } bit_width = ci->params.at(id_BIT_WIDTH_1).as_int64(); bsram_rename_ports(ci, bit_width, "DIB[%d]", "DIB%d"); + + if (bit_width < 16 && gwu.need_BSRAM_OUTREG_fix()) { + bsram_fix_outreg(ci, bit_width, id_CEB, id_OCEB, id_CLKB, id_RESETB, id_DOB, id_READ_MODE1, new_cells); + } bsram_rename_ports(ci, bit_width, "DOB[%d]", "DOB%d"); // In BYPASS mode, the OCE signal is dictated by CE. @@ -586,15 +580,15 @@ void GowinPacker::pack_SP(CellInfo *ci, std::vector> & bsram_fix_sp(ci, new_cells); } - // Some chips have faulty output registers - if (gwu.need_BSRAM_OUTREG_fix()) { - bsram_fix_outreg(ci, new_cells); - } - // Some chips have problems with BLKSEL ports if (gwu.need_BLKSEL_fix()) { bsram_fix_blksel(ci, new_cells); } + + // Some chips have faulty output registers + if (bit_width < 32 && gwu.need_BSRAM_OUTREG_fix()) { + bsram_fix_outreg(ci, bit_width, id_CE, id_OCE, id_CLK, id_RESET, id_DO, id_READ_MODE, new_cells); + } } // XXX UG285-1.3.6_E Gowin BSRAM & SSRAM User Guide: @@ -670,7 +664,7 @@ void GowinPacker::pack_bsram(void) break; case ID_DPX9B: /* fallthrough */ case ID_DPB: - pack_DPB(ci); + pack_DPB(ci, new_cells); ci->type = id_DP; break; case ID_SPX9: /* fallthrough */