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)
// BSRAM
X(AUX)
X(BLK_SEL)
X(BSRAM_SUBTYPE)
X(WRITE_MODE)
@ -941,6 +942,8 @@ X(WREA)
X(WREB)
X(CLKA)
X(CLKB)
X(ADA)
X(ADB)
// DSP
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_CIN_MUX = 512;
static constexpr int32_t NEED_BSRAM_RESET_FIX = 1024;
static constexpr int32_t NEED_SDP_FIX = 2048;
});
} // namespace

View File

@ -29,6 +29,7 @@ CHIP_HAS_PINCFG = 0x80
CHIP_HAS_DFF67 = 0x100
CHIP_HAS_CIN_MUX = 0x200
CHIP_NEED_BSRAM_RESET_FIX = 0x400
CHIP_NEED_SDP_FIX = 0x800
# Tile flags
TILE_I3C_CAPABLE_IO = 0x1
@ -1617,6 +1618,8 @@ def main():
chip_flags |= CHIP_HAS_CIN_MUX;
if "NEED_BSRAM_RESET_FIX" in db.chip_flags:
chip_flags |= CHIP_NEED_BSRAM_RESET_FIX;
if "NEED_SDP_FIX" in db.chip_flags:
chip_flags |= CHIP_NEED_SDP_FIX;
X = db.cols;
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;
}
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)
{
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
bool has_SP32(void);
bool need_SP_fix(void);
bool need_SDP_fix(void);
bool need_BSRAM_OUTREG_fix(void);
bool need_BSRAM_RESET_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;
if (ci->type == id_SDPB) {
@ -2593,6 +2669,26 @@ struct GowinPacker
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 *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
@ -2608,23 +2704,19 @@ struct GowinPacker
ci->copyPortTo(id_OCE, ci, id_OCEB);
// Port A
ci->addInput(id_WRE);
ci->connectPort(id_WRE, vcc_net);
if (!ci->params.count(id_BIT_WIDTH_0)) {
ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32));
// If misconnected RESET
if (gwu.need_BSRAM_RESET_fix()) {
ci->renamePort(id_RESET, id_RESETB);
}
int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64();
bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d");
// Port A
ci->addInput(id_WREA);
ci->connectPort(id_WREA, vcc_net);
// Port B
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();
if (bit_width == 32 || bit_width == 36) {
ci->connectPort(id_WREB, vcc_net);
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d");
@ -2632,13 +2724,7 @@ struct GowinPacker
ci->connectPort(id_WREB, vss_net);
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d", 18);
}
// 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);
}
bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d");
}
void pack_DPB(CellInfo *ci)
@ -2678,6 +2764,10 @@ struct GowinPacker
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();
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).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);
CellInfo *sp = sp_cell.get();
sp->setAttr(id_AUX, 1);
ci->copyPortTo(id_CLK, sp, id_CLK);
ci->copyPortTo(id_OCE, sp, id_OCE);
@ -2695,38 +2786,32 @@ struct GowinPacker
ci->copyPortTo(id_WRE, sp, id_WRE);
// XXX Separate "byte enable" port
ci->movePortTo(ctx->id("AD[2]"), sp, ctx->id("AD0"));
ci->movePortTo(ctx->id("AD[3]"), sp, ctx->id("AD1"));
ci->movePortTo(ctx->id("AD[2]"), sp, ctx->id("AD[0]"));
ci->movePortTo(ctx->id("AD[3]"), sp, ctx->id("AD[1]"));
ci->connectPort(ctx->id("AD[2]"), vss_net);
ci->connectPort(ctx->id("AD[3]"), vss_net);
sp->addInput(ctx->id("AD2"));
sp->connectPort(ctx->id("AD2"), vss_net);
sp->addInput(ctx->id("AD3"));
sp->connectPort(ctx->id("AD3"), vss_net);
sp->addInput(ctx->id("AD[2]"));
sp->connectPort(ctx->id("AD[2]"), vss_net);
sp->addInput(ctx->id("AD[3]"));
sp->connectPort(ctx->id("AD[3]"), vss_net);
ci->disconnectPort(ctx->id("AD[4]"));
ci->connectPort(ctx->id("AD[4]"), vss_net);
sp->addInput(ctx->id("AD4"));
sp->connectPort(ctx->id("AD4"), vcc_net);
sp->addInput(ctx->id("AD[4]"));
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->setAttr(id_BSRAM_SUBTYPE, ci->attrs.at(id_BSRAM_SUBTYPE));
if (bw == 32) {
ci->setParam(id_BIT_WIDTH, Property(16, 32));
sp->setParam(id_BIT_WIDTH, Property(16, 32));
ci->movePortBusTo(id_DI, 16, true, sp, id_DI, 0, false, 16);
ci->movePortBusTo(id_DO, 16, true, sp, id_DO, 0, false, 16);
} else {
ci->setParam(id_BIT_WIDTH, Property(18, 32));
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);
bw /= 2;
ci->setParam(id_BIT_WIDTH, Property(bw, 32));
sp->setParam(id_BIT_WIDTH, Property(bw, 32));
ci->movePortBusTo(id_DI, bw, true, sp, id_DI, 0, true, bw);
ci->movePortBusTo(id_DO, bw, true, sp, id_DO, 0, true, bw);
ci->copyPortBusTo(ctx->id("BLKSEL"), 0, true, sp, ctx->id("BLKSEL"), 0, true, 3);
new_cells.push_back(std::move(sp_cell));
}
@ -2746,6 +2831,7 @@ struct GowinPacker
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
if (!ci->attrs.count(id_AUX)) {
// XXX strange WRE<->CE relations
// Gowin IDE adds two LUTs to the WRE and CE signals. The logic is
// unclear, but without them effects occur. Perhaps this is a
@ -2763,6 +2849,7 @@ struct GowinPacker
if (gwu.need_BLKSEL_fix()) {
bsram_fix_blksel(ci, new_cells);
}
}
// XXX UG285-1.3.6_E Gowin BSRAM & SSRAM User Guide:
// For GW1N-9/GW1NR-9/GW1NS-4 series, 32/36-bit SP/SPX9 is divided into two
@ -2805,9 +2892,7 @@ struct GowinPacker
std::vector<std::unique_ptr<CellInfo>> new_cells;
log_info("Pack BSRAMs...\n");
for (auto &cell : ctx->cells) {
auto ci = cell.second.get();
if (is_bsram(ci)) {
auto do_bsram = [&](CellInfo *ci) {
if (ctx->verbose) {
log_info(" pack %s\n", ci->type.c_str(ctx));
}
@ -2819,7 +2904,7 @@ struct GowinPacker
break;
case ID_SDPX9B: /* fallthrough */
case ID_SDPB:
pack_SDPB(ci);
pack_SDPB(ci, new_cells);
ci->type = id_SDP;
break;
case ID_DPX9B: /* fallthrough */
@ -2835,10 +2920,21 @@ struct GowinPacker
default:
log_error("Unsupported BSRAM type '%s'\n", ci->type.c_str(ctx));
}
};
for (auto &cell : ctx->cells) {
auto ci = cell.second.get();
if (is_bsram(ci)) {
do_bsram(ci);
}
}
// Process new cells. New cells should not generate more.
for (auto &cell : new_cells) {
auto ci = cell.get();
if (is_bsram(ci)) {
do_bsram(ci);
}
ctx->cells[cell->name] = std::move(cell);
}
}