Gowin. Add BSRAM SDP fix. (#1575)

In the GW5A series, the primitive SemiDual Port BSRAM cannot function
when the width of any of the ports is 32/36 bits - it is necessary to
divide one block into two identical ones, each of which will be
responsible for 16 bits.

Here, we perform such a division and, in addition, ensure that the new
cells resulting from the division undergo the same packing procedure as
the original ones.

Naturally, with some reservations (the AUX attribute is responsible for
this) - in the case of SP, when service elements are added, it makes
sense to do this immediately for 32-bit SP and only then divide.

Also, SDPs are currently being corrected for cases where both ports are
‘problematic’, but it may happen that one port is 32 and the other is,
say, 1/2/4/8/16. This has been left for the future.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2025-10-13 19:07:39 +10:00 committed by GitHub
parent 5194b5cc0a
commit c7836625b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 192 additions and 82 deletions

View File

@ -910,6 +910,7 @@ X(DID)
X(WRE) X(WRE)
// BSRAM // BSRAM
X(AUX)
X(BLK_SEL) X(BLK_SEL)
X(BSRAM_SUBTYPE) X(BSRAM_SUBTYPE)
X(WRITE_MODE) X(WRITE_MODE)
@ -941,6 +942,8 @@ X(WREA)
X(WREB) X(WREB)
X(CLKA) X(CLKA)
X(CLKB) X(CLKB)
X(ADA)
X(ADB)
// DSP // DSP
X(ALU54D) X(ALU54D)

View File

@ -204,6 +204,7 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
static constexpr int32_t HAS_DFF67 = 256; static constexpr int32_t HAS_DFF67 = 256;
static constexpr int32_t HAS_CIN_MUX = 512; static constexpr int32_t HAS_CIN_MUX = 512;
static constexpr int32_t NEED_BSRAM_RESET_FIX = 1024; static constexpr int32_t NEED_BSRAM_RESET_FIX = 1024;
static constexpr int32_t NEED_SDP_FIX = 2048;
}); });
} // namespace } // namespace

View File

@ -29,6 +29,7 @@ CHIP_HAS_PINCFG = 0x80
CHIP_HAS_DFF67 = 0x100 CHIP_HAS_DFF67 = 0x100
CHIP_HAS_CIN_MUX = 0x200 CHIP_HAS_CIN_MUX = 0x200
CHIP_NEED_BSRAM_RESET_FIX = 0x400 CHIP_NEED_BSRAM_RESET_FIX = 0x400
CHIP_NEED_SDP_FIX = 0x800
# Tile flags # Tile flags
TILE_I3C_CAPABLE_IO = 0x1 TILE_I3C_CAPABLE_IO = 0x1
@ -1617,6 +1618,8 @@ def main():
chip_flags |= CHIP_HAS_CIN_MUX; chip_flags |= CHIP_HAS_CIN_MUX;
if "NEED_BSRAM_RESET_FIX" in db.chip_flags: if "NEED_BSRAM_RESET_FIX" in db.chip_flags:
chip_flags |= CHIP_NEED_BSRAM_RESET_FIX; chip_flags |= CHIP_NEED_BSRAM_RESET_FIX;
if "NEED_SDP_FIX" in db.chip_flags:
chip_flags |= CHIP_NEED_SDP_FIX;
X = db.cols; X = db.cols;
Y = db.rows; Y = db.rows;

View File

@ -380,6 +380,12 @@ bool GowinUtils::need_SP_fix(void)
return extra->chip_flags & Extra_chip_data_POD::NEED_SP_FIX; return extra->chip_flags & Extra_chip_data_POD::NEED_SP_FIX;
} }
bool GowinUtils::need_SDP_fix(void)
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
return extra->chip_flags & Extra_chip_data_POD::NEED_SDP_FIX;
}
bool GowinUtils::need_BSRAM_OUTREG_fix(void) bool GowinUtils::need_BSRAM_OUTREG_fix(void)
{ {
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get()); const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());

View File

@ -99,6 +99,7 @@ struct GowinUtils
// BSRAM // BSRAM
bool has_SP32(void); bool has_SP32(void);
bool need_SP_fix(void); bool need_SP_fix(void);
bool need_SDP_fix(void);
bool need_BSRAM_OUTREG_fix(void); bool need_BSRAM_OUTREG_fix(void);
bool need_BSRAM_RESET_fix(void); bool need_BSRAM_RESET_fix(void);
bool need_BLKSEL_fix(void); bool need_BLKSEL_fix(void);

View File

@ -2583,7 +2583,83 @@ struct GowinPacker
} }
} }
void pack_SDPB(CellInfo *ci) void divide_sdp(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
{
if (ctx->verbose) {
log_info(" divide SDP\n");
}
int bw = ci->params.at(id_BIT_WIDTH_0).as_int64();
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
IdString cell_type = bw == 32 ? id_SDPB : id_SDPX9B;
IdString name = ctx->idf("%s_AUX", ctx->nameOf(ci));
auto sdp_cell = gwu.create_cell(name, cell_type);
CellInfo *sdp = sdp_cell.get();
sdp->setAttr(id_AUX, 1);
int new_bw = bw / 2;
ci->setParam(id_BIT_WIDTH_0, new_bw);
ci->setParam(id_BIT_WIDTH_1, new_bw);
sdp->params = ci->params;
sdp->setParam(id_BIT_WIDTH_0, new_bw);
sdp->setParam(id_BIT_WIDTH_1, new_bw);
// copy control ports
ci->copyPortBusTo(ctx->id("BLKSELA"), 0, true, sdp, ctx->id("BLKSELA"), 0, true, 3);
ci->copyPortBusTo(ctx->id("BLKSELB"), 0, true, sdp, ctx->id("BLKSELB"), 0, true, 3);
ci->copyPortTo(id_CEA, sdp, id_CEA);
ci->copyPortTo(id_CEB, sdp, id_CEB);
ci->copyPortTo(id_CLKA, sdp, id_CLKA);
ci->copyPortTo(id_CLKB, sdp, id_CLKB);
ci->copyPortTo(id_OCE, sdp, id_OCE);
ci->copyPortTo(id_RESET, sdp, id_RESET);
// Separate port A
ci->movePortTo(ctx->id("ADA[2]"), sdp, ctx->id("ADA[0]"));
ci->movePortTo(ctx->id("ADA[3]"), sdp, ctx->id("ADA[1]"));
ci->addInput(ctx->id("ADA[2]"));
ci->addInput(ctx->id("ADA[3]"));
ci->connectPort(ctx->id("ADA[2]"), vss_net);
ci->connectPort(ctx->id("ADA[3]"), vss_net);
sdp->addInput(ctx->id("ADA[2]"));
sdp->addInput(ctx->id("ADA[3]"));
sdp->connectPort(ctx->id("ADA[2]"), vss_net);
sdp->connectPort(ctx->id("ADA[3]"), vss_net);
ci->disconnectPort(ctx->id("ADA[4]"));
ci->connectPort(ctx->id("ADA[4]"), vss_net);
sdp->addInput(ctx->id("ADA[4]"));
sdp->connectPort(ctx->id("ADA[4]"), vcc_net);
ci->copyPortBusTo(id_ADA, 5, true, sdp, id_ADA, 5, true, 9);
// Separate port B
for (int i = 0; i < 4; ++i) {
IdString port = ctx->idf("ADB[%d]", i);
ci->disconnectPort(port);
ci->connectPort(port, vss_net);
ci->copyPortTo(port, sdp, port);
}
ci->disconnectPort(ctx->id("ADB[4]"));
ci->connectPort(ctx->id("ADB[4]"), vss_net);
sdp->addInput(ctx->id("ADB[4]"));
sdp->connectPort(ctx->id("ADB[4]"), vcc_net);
ci->copyPortBusTo(id_ADB, 5, true, sdp, id_ADB, 5, true, 9);
ci->movePortBusTo(id_DI, new_bw, true, sdp, id_DI, 0, true, new_bw);
ci->movePortBusTo(id_DO, new_bw, true, sdp, id_DO, 0, true, new_bw);
new_cells.push_back(std::move(sdp_cell));
}
void pack_SDPB(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
{ {
int default_bw = 32; int default_bw = 32;
if (ci->type == id_SDPB) { if (ci->type == id_SDPB) {
@ -2593,6 +2669,26 @@ struct GowinPacker
default_bw = 36; default_bw = 36;
} }
if (!ci->params.count(id_BIT_WIDTH_0)) {
ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32));
}
if (!ci->params.count(id_BIT_WIDTH_1)) {
ci->setParam(id_BIT_WIDTH_1, Property(default_bw, 32));
}
int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64();
if ((bit_width == 32 || bit_width == 36) && gwu.need_SDP_fix()) {
int bit_width_b = ci->params.at(id_BIT_WIDTH_1).as_int64();
if (bit_width == bit_width_b) {
divide_sdp(ci, new_cells);
} else {
log_error("The fix for SDP when ports A and B have different bit widths has not yet been implemented. "
"Cell'%s'\n",
ci->type.c_str(ctx));
}
}
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
@ -2608,23 +2704,19 @@ struct GowinPacker
ci->copyPortTo(id_OCE, ci, id_OCEB); ci->copyPortTo(id_OCE, ci, id_OCEB);
// Port A // If misconnected RESET
ci->addInput(id_WRE); if (gwu.need_BSRAM_RESET_fix()) {
ci->connectPort(id_WRE, vcc_net); ci->renamePort(id_RESET, id_RESETB);
if (!ci->params.count(id_BIT_WIDTH_0)) {
ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32));
} }
int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64(); // Port A
bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d"); ci->addInput(id_WREA);
ci->connectPort(id_WREA, vcc_net);
// Port B // Port B
ci->addInput(id_WREB); ci->addInput(id_WREB);
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(); bit_width = ci->params.at(id_BIT_WIDTH_1).as_int64();
if (bit_width == 32 || bit_width == 36) { if (bit_width == 32 || bit_width == 36) {
ci->connectPort(id_WREB, vcc_net); ci->connectPort(id_WREB, vcc_net);
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d"); bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d");
@ -2632,13 +2724,7 @@ struct GowinPacker
ci->connectPort(id_WREB, vss_net); ci->connectPort(id_WREB, vss_net);
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d", 18); bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d", 18);
} }
bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d");
// If misconnected RESET
if (gwu.need_BSRAM_RESET_fix()) {
ci->renamePort(id_RESET, id_RESETB);
ci->addInput(id_RESET);
ci->connectPort(id_RESET, vcc_net);
}
} }
void pack_DPB(CellInfo *ci) void pack_DPB(CellInfo *ci)
@ -2678,6 +2764,10 @@ struct GowinPacker
void divide_sp(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells) void divide_sp(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
{ {
if (ctx->verbose) {
log_info(" divide SP\n");
}
int bw = ci->params.at(id_BIT_WIDTH).as_int64(); int bw = ci->params.at(id_BIT_WIDTH).as_int64();
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
@ -2687,6 +2777,7 @@ struct GowinPacker
auto sp_cell = gwu.create_cell(name, cell_type); auto sp_cell = gwu.create_cell(name, cell_type);
CellInfo *sp = sp_cell.get(); CellInfo *sp = sp_cell.get();
sp->setAttr(id_AUX, 1);
ci->copyPortTo(id_CLK, sp, id_CLK); ci->copyPortTo(id_CLK, sp, id_CLK);
ci->copyPortTo(id_OCE, sp, id_OCE); ci->copyPortTo(id_OCE, sp, id_OCE);
@ -2695,38 +2786,32 @@ struct GowinPacker
ci->copyPortTo(id_WRE, sp, id_WRE); ci->copyPortTo(id_WRE, sp, id_WRE);
// XXX Separate "byte enable" port // XXX Separate "byte enable" port
ci->movePortTo(ctx->id("AD[2]"), sp, ctx->id("AD0")); ci->movePortTo(ctx->id("AD[2]"), sp, ctx->id("AD[0]"));
ci->movePortTo(ctx->id("AD[3]"), sp, ctx->id("AD1")); ci->movePortTo(ctx->id("AD[3]"), sp, ctx->id("AD[1]"));
ci->connectPort(ctx->id("AD[2]"), vss_net); ci->connectPort(ctx->id("AD[2]"), vss_net);
ci->connectPort(ctx->id("AD[3]"), vss_net); ci->connectPort(ctx->id("AD[3]"), vss_net);
sp->addInput(ctx->id("AD2")); sp->addInput(ctx->id("AD[2]"));
sp->connectPort(ctx->id("AD2"), vss_net); sp->connectPort(ctx->id("AD[2]"), vss_net);
sp->addInput(ctx->id("AD3")); sp->addInput(ctx->id("AD[3]"));
sp->connectPort(ctx->id("AD3"), vss_net); sp->connectPort(ctx->id("AD[3]"), vss_net);
ci->disconnectPort(ctx->id("AD[4]")); ci->disconnectPort(ctx->id("AD[4]"));
ci->connectPort(ctx->id("AD[4]"), vss_net); ci->connectPort(ctx->id("AD[4]"), vss_net);
sp->addInput(ctx->id("AD4")); sp->addInput(ctx->id("AD[4]"));
sp->connectPort(ctx->id("AD4"), vcc_net); sp->connectPort(ctx->id("AD[4]"), vcc_net);
ci->copyPortBusTo(id_AD, 5, true, sp, id_AD, 5, false, 14 - 5 + 1); ci->copyPortBusTo(id_AD, 5, true, sp, id_AD, 5, true, 9);
sp->params = ci->params; sp->params = ci->params;
sp->setAttr(id_BSRAM_SUBTYPE, ci->attrs.at(id_BSRAM_SUBTYPE));
if (bw == 32) { bw /= 2;
ci->setParam(id_BIT_WIDTH, Property(16, 32)); ci->setParam(id_BIT_WIDTH, Property(bw, 32));
sp->setParam(id_BIT_WIDTH, Property(16, 32)); sp->setParam(id_BIT_WIDTH, Property(bw, 32));
ci->movePortBusTo(id_DI, 16, true, sp, id_DI, 0, false, 16); ci->movePortBusTo(id_DI, bw, true, sp, id_DI, 0, true, bw);
ci->movePortBusTo(id_DO, 16, true, sp, id_DO, 0, false, 16); ci->movePortBusTo(id_DO, bw, true, sp, id_DO, 0, true, bw);
} else {
ci->setParam(id_BIT_WIDTH, Property(18, 32)); ci->copyPortBusTo(ctx->id("BLKSEL"), 0, true, sp, ctx->id("BLKSEL"), 0, true, 3);
sp->setParam(id_BIT_WIDTH, Property(18, 32));
ci->movePortBusTo(id_DI, 18, true, sp, id_DI, 0, false, 18);
ci->movePortBusTo(id_DO, 18, true, sp, id_DO, 0, false, 18);
}
ci->copyPortBusTo(ctx->id("BLKSEL"), 0, true, sp, ctx->id("BLKSEL"), 0, false, 3);
new_cells.push_back(std::move(sp_cell)); new_cells.push_back(std::move(sp_cell));
} }
@ -2746,22 +2831,24 @@ struct GowinPacker
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64(); int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
// XXX strange WRE<->CE relations if (!ci->attrs.count(id_AUX)) {
// Gowin IDE adds two LUTs to the WRE and CE signals. The logic is // XXX strange WRE<->CE relations
// unclear, but without them effects occur. Perhaps this is a // Gowin IDE adds two LUTs to the WRE and CE signals. The logic is
// correction of some BSRAM defects. // unclear, but without them effects occur. Perhaps this is a
if (gwu.need_SP_fix()) { // correction of some BSRAM defects.
bsram_fix_sp(ci, new_cells); if (gwu.need_SP_fix()) {
} bsram_fix_sp(ci, new_cells);
}
// Some chips have faulty output registers // Some chips have faulty output registers
if (gwu.need_BSRAM_OUTREG_fix()) { if (gwu.need_BSRAM_OUTREG_fix()) {
bsram_fix_outreg(ci, new_cells); bsram_fix_outreg(ci, new_cells);
} }
// Some chips have problems with BLKSEL ports // Some chips have problems with BLKSEL ports
if (gwu.need_BLKSEL_fix()) { if (gwu.need_BLKSEL_fix()) {
bsram_fix_blksel(ci, new_cells); bsram_fix_blksel(ci, new_cells);
}
} }
// XXX UG285-1.3.6_E Gowin BSRAM & SSRAM User Guide: // XXX UG285-1.3.6_E Gowin BSRAM & SSRAM User Guide:
@ -2805,40 +2892,49 @@ struct GowinPacker
std::vector<std::unique_ptr<CellInfo>> new_cells; std::vector<std::unique_ptr<CellInfo>> new_cells;
log_info("Pack BSRAMs...\n"); log_info("Pack BSRAMs...\n");
auto do_bsram = [&](CellInfo *ci) {
if (ctx->verbose) {
log_info(" pack %s\n", ci->type.c_str(ctx));
}
switch (ci->type.hash()) {
case ID_pROMX9: /* fallthrough */
case ID_pROM:
pack_ROM(ci);
ci->type = id_ROM;
break;
case ID_SDPX9B: /* fallthrough */
case ID_SDPB:
pack_SDPB(ci, new_cells);
ci->type = id_SDP;
break;
case ID_DPX9B: /* fallthrough */
case ID_DPB:
pack_DPB(ci);
ci->type = id_DP;
break;
case ID_SPX9: /* fallthrough */
case ID_SP:
pack_SP(ci, new_cells);
ci->type = id_SP;
break;
default:
log_error("Unsupported BSRAM type '%s'\n", ci->type.c_str(ctx));
}
};
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
auto ci = cell.second.get(); auto ci = cell.second.get();
if (is_bsram(ci)) { if (is_bsram(ci)) {
if (ctx->verbose) { do_bsram(ci);
log_info(" pack %s\n", ci->type.c_str(ctx));
}
switch (ci->type.hash()) {
case ID_pROMX9: /* fallthrough */
case ID_pROM:
pack_ROM(ci);
ci->type = id_ROM;
break;
case ID_SDPX9B: /* fallthrough */
case ID_SDPB:
pack_SDPB(ci);
ci->type = id_SDP;
break;
case ID_DPX9B: /* fallthrough */
case ID_DPB:
pack_DPB(ci);
ci->type = id_DP;
break;
case ID_SPX9: /* fallthrough */
case ID_SP:
pack_SP(ci, new_cells);
ci->type = id_SP;
break;
default:
log_error("Unsupported BSRAM type '%s'\n", ci->type.c_str(ctx));
}
} }
} }
// Process new cells. New cells should not generate more.
for (auto &cell : new_cells) { for (auto &cell : new_cells) {
auto ci = cell.get();
if (is_bsram(ci)) {
do_bsram(ci);
}
ctx->cells[cell->name] = std::move(cell); ctx->cells[cell->name] = std::move(cell);
} }
} }