diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 178e625b..70f0d989 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1022,6 +1022,9 @@ X(CLKOUTP) X(CLKOUTD) X(CLKOUTD3) X(LOCK) +X(AD) +X(DI) +X(DO) // PLL parameters X(CLKOUTPS) @@ -1133,3 +1136,4 @@ X(HCLK_OUT3) X(BUFG) X(CLOCK) + diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 3e8a3ec6..79fc4ea7 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -39,7 +39,7 @@ inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(cell->type inline bool type_is_ssram(IdString cell_type) { return cell_type.in(id_RAM16SDP1, id_RAM16SDP2, id_RAM16SDP4); } inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); } -// Return true if a cell is a SSRAM +// Return true if a cell is a BSRAM inline bool type_is_bsram(IdString cell_type) { return cell_type.in(id_SP, id_SPX9, id_pROM, id_pROMX9, id_ROM, id_SDP, id_SDPB, id_SDPX9B, id_DP, id_DPB, @@ -70,8 +70,11 @@ NPNR_PACKED_STRUCT(struct Bottom_io_POD { }); NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { + int32_t chip_flags; Bottom_io_POD bottom_io; RelSlice diff_io_types; + // chip flags + static constexpr int32_t HAS_SP32 = 0; }); } // namespace diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 533928b3..413801f4 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -14,6 +14,9 @@ from apycula import chipdb # Bel flags BEL_FLAG_SIMPLE_IO = 0x100 +# Chip flags +CHIP_HAS_SP32 = 0x1 + # Z of the bels # sync with C++ part! LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1 @@ -86,6 +89,7 @@ class BottomIO(BBAStruct): @dataclass class ChipExtraData(BBAStruct): strs: StringPool + flags: int bottom_io: BottomIO diff_io_types: list[IdString] = field(default_factory = list) @@ -105,6 +109,7 @@ class ChipExtraData(BBAStruct): bba.u32(diff_io_type.index) def serialise(self, context: str, bba: BBAWriter): + bba.u32(self.flags) self.bottom_io.serialise(f"{context}_bottom_io", bba) bba.slice(f"{context}_diff_io_types", len(self.diff_io_types)) @@ -691,8 +696,8 @@ def create_packages(chip: Chip, db: chipdb): pad = pkg.create_pad(pinno, tile, bel, pad_func, bank) # Extra chip data -def create_extra_data(chip: Chip, db: chipdb): - chip.extra_data = ChipExtraData(chip.strs, None) +def create_extra_data(chip: Chip, db: chipdb, chip_flags: int): + chip.extra_data = ChipExtraData(chip.strs, chip_flags, None) chip.extra_data.create_bottom_io() for net_a, net_b in db.bottom_io[2]: chip.extra_data.add_bottom_io_cnd(net_a, net_b) @@ -710,6 +715,10 @@ def main(): with gzip.open(importlib.resources.files("apycula").joinpath(f"{device}.pickle"), 'rb') as f: db = pickle.load(f) + chip_flags = 0; + if device not in {"GW1NS-4", "GW1N-9"}: + chip_flags &= CHIP_HAS_SP32; + X = db.cols; Y = db.rows; @@ -753,7 +762,7 @@ def main(): # Create nodes between tiles create_nodes(ch, db) - create_extra_data(ch, db) + create_extra_data(ch, db, chip_flags) ch.write_bba(args.output) if __name__ == '__main__': main() diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index 16b39d47..e19c64c8 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -91,4 +91,17 @@ IdString GowinUtils::get_bottom_io_wire_b_net(int8_t condition) const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); return IdString(extra->bottom_io.conditions[condition].wire_b_net); } + +bool GowinUtils::have_SP32(void) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return extra->chip_flags & Extra_chip_data_POD::HAS_SP32; +} + +std::unique_ptr GowinUtils::create_cell(IdString name, IdString type) +{ + NPNR_ASSERT(!ctx->cells.count(name)); + return std::make_unique(ctx, name, type); +} + NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index 3dbb7010..5d2c6571 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -38,6 +38,12 @@ struct GowinUtils // wires inline bool is_wire_type_default(IdString wire_type) { return wire_type == IdString(); } + + // chip dependent + bool have_SP32(void); + + // make cell but do not include it in the list of chip cells. + std::unique_ptr create_cell(IdString name, IdString type); }; NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index b3080793..5b0cc183 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -118,7 +118,7 @@ struct GowinPacker log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci), ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port)); for (const auto &attr : ci.attrs) { - i->driver.cell->attrs[attr.first] = attr.second; + i->driver.cell->setAttr(attr.first, attr.second); } } NetInfo *o = ci.getPort(id_O); @@ -128,7 +128,7 @@ struct GowinPacker log_error("Top-level port '%s' driving illegal port %s.%s\n", ctx->nameOf(&ci), ctx->nameOf(usr.cell), ctx->nameOf(usr.port)); for (const auto &attr : ci.attrs) { - usr.cell->attrs[attr.first] = attr.second; + usr.cell->setAttr(attr.first, attr.second); } // network/port attributes that can be set in the // restriction file and that need to be transferred to real @@ -150,7 +150,7 @@ struct GowinPacker log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci), ctx->nameOf(io->driver.cell), ctx->nameOf(io->driver.port)); for (const auto &attr : ci.attrs) { - io->driver.cell->attrs[attr.first] = attr.second; + io->driver.cell->setAttr(attr.first, attr.second); } } ci.disconnectPort(id_I); @@ -1011,13 +1011,13 @@ struct GowinPacker cin_ci->connectPort(id_COUT, cout_net); if (cin_net->name == ctx->id("$PACKER_GND")) { - cin_ci->params[id_ALU_MODE] = std::string("C2L"); + cin_ci->setParam(id_ALU_MODE, std::string("C2L")); cin_ci->addInput(id_I2); cin_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get()); return cin_ci; } if (cin_net->name == ctx->id("$PACKER_VCC")) { - cin_ci->params[id_ALU_MODE] = std::string("ONE2C"); + cin_ci->setParam(id_ALU_MODE, std::string("ONE2C")); cin_ci->addInput(id_I2); cin_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get()); return cin_ci; @@ -1031,7 +1031,7 @@ struct GowinPacker cin_ci->connectPort(id_I3, cin_net); cin_ci->addInput(id_I2); cin_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get()); - cin_ci->params[id_ALU_MODE] = std::string("0"); // ADD + cin_ci->setParam(id_ALU_MODE, std::string("0")); // ADD return cin_ci; } @@ -1054,7 +1054,7 @@ struct GowinPacker cout_ci->addInput(id_I2); cout_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get()); - cout_ci->params[id_ALU_MODE] = std::string("C2L"); + cout_ci->setParam(id_ALU_MODE, std::string("C2L")); return cout_ci; } @@ -1065,7 +1065,7 @@ struct GowinPacker IdString name_id = ctx->id(name); auto dummy_ci = std::make_unique(ctx, name_id, id_ALU); - dummy_ci->params[id_ALU_MODE] = std::string("C2L"); + dummy_ci->setParam(id_ALU_MODE, std::string("C2L")); return dummy_ci; } @@ -1116,7 +1116,7 @@ struct GowinPacker ci->constr_y = 0; ci->constr_z = alu_chain_len % 6; // XXX mode 0 - ADD - if (ci->params[id_ALU_MODE].as_int64() == 0) { + if (ci->params.at(id_ALU_MODE).as_int64() == 0) { ci->renamePort(id_I3, id_I2); ci->renamePort(id_I0, id_I3); ci->renamePort(id_I2, id_I0); @@ -1210,9 +1210,9 @@ struct GowinPacker } IdString init_name = ctx->idf("INIT_%d", index); if (ci->params.count(init_name)) { - lut_ci->params[id_INIT] = ci->params.at(init_name); + lut_ci->setParam(id_INIT, ci->params.at(init_name)); } else { - lut_ci->params[id_INIT] = std::string("1111111111111111"); + lut_ci->setParam(id_INIT, std::string("1111111111111111")); } return lut_ci; } @@ -1296,12 +1296,14 @@ struct GowinPacker void pack_ROM(CellInfo *ci) { + int default_bw = 32; // XXX use block 111 ci->setParam(ctx->id("BLK_SEL"), Property(7, 32)); if (ci->type == id_pROM) { ci->setAttr(id_BSRAM_SUBTYPE, Property("")); } else { ci->setAttr(id_BSRAM_SUBTYPE, Property("X9")); + default_bw = 36; } NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); @@ -1321,10 +1323,10 @@ struct GowinPacker ci->connectPort(id_WREB, vss_net); if (!ci->params.count(id_BIT_WIDTH)) { - ci->setParam(id_BIT_WIDTH, Property(32, 32)); + ci->setParam(id_BIT_WIDTH, Property(default_bw, 32)); } - int bit_width = ci->params[id_BIT_WIDTH].as_int64(); + int bit_width = ci->params.at(id_BIT_WIDTH).as_int64(); if (bit_width == 32 || bit_width == 36) { ci->copyPortTo(id_CLK, ci, id_CLKB); ci->copyPortTo(id_CE, ci, id_CEB); @@ -1354,10 +1356,12 @@ struct GowinPacker void pack_SDPB(CellInfo *ci) { + int default_bw = 32; if (ci->type == id_SDPB) { ci->setAttr(id_BSRAM_SUBTYPE, Property("")); } else { ci->setAttr(id_BSRAM_SUBTYPE, Property("X9")); + default_bw = 36; } NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); @@ -1380,18 +1384,18 @@ struct GowinPacker ci->connectPort(id_WRE, vcc_net); if (!ci->params.count(id_BIT_WIDTH_0)) { - ci->setParam(id_BIT_WIDTH_0, Property(32, 32)); + ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32)); } - int bit_width = ci->params[id_BIT_WIDTH_0].as_int64(); + int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64(); bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d"); // Port B ci->addInput(id_WREB); if (!ci->params.count(id_BIT_WIDTH_1)) { - ci->setParam(id_BIT_WIDTH_1, Property(32, 32)); + ci->setParam(id_BIT_WIDTH_1, Property(default_bw, 32)); } - bit_width = ci->params[id_BIT_WIDTH_1].as_int64(); + 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"); @@ -1403,10 +1407,12 @@ struct GowinPacker void pack_DPB(CellInfo *ci) { + int default_bw = 16; if (ci->type == id_DPB) { ci->setAttr(id_BSRAM_SUBTYPE, Property("")); } else { ci->setAttr(id_BSRAM_SUBTYPE, Property("X9")); + default_bw = 18; } for (int i = 0; i < 14; ++i) { @@ -1420,44 +1426,109 @@ struct GowinPacker } if (!ci->params.count(id_BIT_WIDTH_0)) { - ci->setParam(id_BIT_WIDTH_0, Property(16, 32)); - } - int bit_width = ci->params[id_BIT_WIDTH_0].as_int64(); - if (bit_width == 32 || bit_width == 36) { - log_error("Bit width %d is not supported\n", bit_width); + ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32)); } + 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 (!ci->params.count(id_BIT_WIDTH_1)) { - ci->setParam(id_BIT_WIDTH_1, Property(16, 32)); - } - bit_width = ci->params[id_BIT_WIDTH_1].as_int64(); - if (bit_width == 32 || bit_width == 36) { - log_error("Bit width %d is not supported\n", bit_width); + 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"); bsram_rename_ports(ci, bit_width, "DOB[%d]", "DOB%d"); } - void pack_SP(CellInfo *ci) + void divide_sp(CellInfo *ci, std::vector> &new_cells) { + 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(); + + IdString cell_type = id_SP; + if (bw == 36) { + cell_type = id_SPX9; + } + IdString name = ctx->idf("%s_AUX", ctx->nameOf(ci)); + + auto sp_cell = gwu.create_cell(name, cell_type); + CellInfo *sp = sp_cell.get(); + + ci->copyPortTo(id_CLK, sp, id_CLK); + ci->copyPortTo(id_OCE, sp, id_OCE); + ci->copyPortTo(id_CE, sp, id_CE); + ci->copyPortTo(id_RESET, sp, id_RESET); + 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->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); + + 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); + + ci->copyPortBusTo(id_AD, 5, true, sp, id_AD, 5, false, 14 - 5 + 1); + + 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); + + new_cells.push_back(std::move(sp_cell)); + } + + void pack_SP(CellInfo *ci, std::vector> &new_cells) + { + int default_bw = 32; if (ci->type == id_SP) { ci->setAttr(id_BSRAM_SUBTYPE, Property("")); } else { ci->setAttr(id_BSRAM_SUBTYPE, Property("X9")); + default_bw = 36; + } + if (!ci->params.count(id_BIT_WIDTH)) { + ci->setParam(id_BIT_WIDTH, Property(default_bw, 32)); + } + + int bit_width = ci->params.at(id_BIT_WIDTH).as_int64(); + // 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 + // SP/SPX9s, which occupy two BSRAMs. + // So divide it here + if ((bit_width == 32 || bit_width == 36) && !gwu.have_SP32()) { + divide_sp(ci, new_cells); + bit_width = ci->params.at(id_BIT_WIDTH).as_int64(); } NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); for (int i = 0; i < 3; ++i) { ci->renamePort(ctx->idf("BLKSEL[%d]", i), ctx->idf("BLKSEL%d", i)); - ci->copyPortTo(ctx->idf("BLKSEL%d", i), ci, ctx->idf("BLKSELB%d", i)); + if (bit_width == 32 || bit_width == 36) { + ci->copyPortTo(ctx->idf("BLKSEL%d", i), ci, ctx->idf("BLKSELB%d", i)); + } } - if (!ci->params.count(id_BIT_WIDTH)) { - ci->setParam(id_BIT_WIDTH, Property(32, 32)); - } - int bit_width = ci->params[id_BIT_WIDTH].as_int64(); for (int i = 0; i < 14; ++i) { ci->renamePort(ctx->idf("AD[%d]", i), ctx->idf("AD%d", i)); if (bit_width == 32 || bit_width == 36) { @@ -1479,11 +1550,11 @@ struct GowinPacker void pack_bsram(void) { + std::vector> new_cells; log_info("Pack BSRAMs...\n"); for (auto &cell : ctx->cells) { auto ci = cell.second.get(); - if (is_bsram(ci)) { if (ctx->verbose) { log_info(" pack %s\n", ci->type.c_str(ctx)); @@ -1506,7 +1577,7 @@ struct GowinPacker break; case ID_SPX9: /* fallthrough */ case ID_SP: - pack_SP(ci); + pack_SP(ci, new_cells); ci->type = id_SP; break; default: @@ -1514,6 +1585,10 @@ struct GowinPacker } } } + + for (auto &cell : new_cells) { + ctx->cells[cell->name] = std::move(cell); + } } // ===================================