diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 1add903340..032de7ac65 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -889,6 +889,7 @@ X(DID) X(WRE) // BSRAM +X(BLK_SEL) X(BSRAM_SUBTYPE) X(WRITE_MODE) X(READ_MODE) diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 728d6df21c..c815fe0a2c 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -98,6 +98,7 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { static constexpr int32_t HAS_SP32 = 1; static constexpr int32_t NEED_SP_FIX = 2; static constexpr int32_t NEED_BSRAM_OUTREG_FIX = 4; + static constexpr int32_t NEED_BLKSEL_FIX = 8; }); } // namespace diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 73a5ade1cb..cc19e3c09c 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -15,9 +15,10 @@ BEL_FLAG_SIMPLE_IO = 0x100 # Chip flags -CHIP_HAS_SP32 = 0x1 -CHIP_NEED_SP_FIX = 0x2 +CHIP_HAS_SP32 = 0x1 +CHIP_NEED_SP_FIX = 0x2 CHIP_NEED_BSRAM_OUTREG_FIX = 0x4 +CHIP_NEED_BLKSEL_FIX = 0x8 # Z of the bels # sync with C++ part! @@ -1021,6 +1022,8 @@ def main(): chip_flags |= CHIP_NEED_SP_FIX; if "NEED_BSRAM_OUTREG_FIX" in db.chip_flags: chip_flags |= CHIP_NEED_BSRAM_OUTREG_FIX; + if "NEED_BLKSEL_FIX" in db.chip_flags: + chip_flags |= CHIP_NEED_BLKSEL_FIX; X = db.cols; Y = db.rows; diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index ac6153af1f..603b6fc5b5 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -130,6 +130,12 @@ bool GowinUtils::need_BSRAM_OUTREG_fix(void) return extra->chip_flags & Extra_chip_data_POD::NEED_BSRAM_OUTREG_FIX; } +bool GowinUtils::need_BLKSEL_fix(void) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return extra->chip_flags & Extra_chip_data_POD::NEED_BLKSEL_FIX; +} + std::unique_ptr GowinUtils::create_cell(IdString name, IdString type) { NPNR_ASSERT(!ctx->cells.count(name)); diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index c4d3e628dd..65ae632e99 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -38,6 +38,7 @@ struct GowinUtils bool have_SP32(void); bool need_SP_fix(void); bool need_BSRAM_OUTREG_fix(void); + bool need_BLKSEL_fix(void); // DSP inline int get_dsp_18_z(int z) const { return z & (~3); } diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index ee1ce9f200..b0975b3a3f 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -1331,6 +1331,70 @@ struct GowinPacker } } + // We solve the BLKSEL problems that are observed on some chips by + // connecting the BLKSEL ports to constant networks so that this BSRAM will + // be selected, the actual selection is made by manipulating the Clock + // Enable pin using a LUT-based decoder. + void bsram_fix_blksel(CellInfo *ci, std::vector> &new_cells) + { + // is BSRAM enabled + NetInfo *ce_net = ci->getPort(id_CE); + if (ce_net == nullptr || ce_net->name == ctx->id("$PACKER_GND")) { + return; + } + + // port name, BLK_SEL parameter for this port + std::vector> dyn_blksel; + + int blk_sel_parameter = ci->params.at(id_BLK_SEL).as_int64(); + for (int i = 0; i < 3; ++i) { + IdString pin_name = ctx->idf("BLKSEL[%d]", i); + NetInfo *net = ci->getPort(pin_name); + if (net == nullptr || net->name == ctx->id("$PACKER_GND") || net->name == ctx->id("$PACKER_VCC")) { + continue; + } + dyn_blksel.push_back(std::make_pair(pin_name, (blk_sel_parameter >> i) & 1)); + } + + if (dyn_blksel.empty()) { + return; + } + + if (ctx->verbose) { + log_info(" apply the BSRAM BLKSEL fix\n"); + } + + // Make a decoder + auto lut_cell = gwu.create_cell(create_aux_name(ci->name, 0, "_blksel_lut$"), id_LUT4); + CellInfo *lut = lut_cell.get(); + lut->addInput(id_I3); + ci->movePortTo(id_CE, lut, id_I3); + lut->addOutput(id_F); + ci->connectPorts(id_CE, lut, id_F); + + NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); + NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); + + // Connected CE to I3 to make it easy to calculate the decoder + int init = 0x100; // CE == 0 --> F = 0 + // CE == 1 --> F = decoder result + int idx = 0; + for (auto &port : dyn_blksel) { + IdString lut_input_name = ctx->idf("I%d", idx); + ci->movePortTo(port.first, lut, lut_input_name); + if (port.second) { + init <<= (1 << idx); + ci->connectPort(port.first, vcc_net); + } else { + ci->connectPort(port.first, vss_net); + } + ++idx; + } + lut->setParam(id_INIT, init); + + new_cells.push_back(std::move(lut_cell)); + } + // Some chips cannot, for some reason, use internal BSRAM registers to // implement READ_MODE=1'b1 (pipeline) with a word width other than 32 or // 36 bits. @@ -1729,6 +1793,11 @@ struct GowinPacker bsram_fix_outreg(ci, new_cells); } + // Some chips have problems with BLKSEL ports + 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 // SP/SPX9s, which occupy two BSRAMs.