diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 7bcf534aae..1add903340 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -892,6 +892,7 @@ X(WRE) X(BSRAM_SUBTYPE) X(WRITE_MODE) X(READ_MODE) +X(RESET_MODE) X(BIT_WIDTH) X(BIT_WIDTH_0) X(BIT_WIDTH_1) diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index e2d34d5064..728d6df21c 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -97,6 +97,7 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { // chip flags static constexpr int32_t HAS_SP32 = 1; static constexpr int32_t NEED_SP_FIX = 2; + static constexpr int32_t NEED_BSRAM_OUTREG_FIX = 4; }); } // namespace diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 4dd2e77934..73a5ade1cb 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -15,8 +15,9 @@ 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 # Z of the bels # sync with C++ part! @@ -1018,6 +1019,8 @@ def main(): chip_flags |= CHIP_HAS_SP32; if "NEED_SP_FIX" in db.chip_flags: chip_flags |= CHIP_NEED_SP_FIX; + if "NEED_BSRAM_OUTREG_FIX" in db.chip_flags: + chip_flags |= CHIP_NEED_BSRAM_OUTREG_FIX; X = db.cols; Y = db.rows; diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index 9443f6e6ab..ac6153af1f 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -124,6 +124,12 @@ bool GowinUtils::need_SP_fix(void) return extra->chip_flags & Extra_chip_data_POD::NEED_SP_FIX; } +bool GowinUtils::need_BSRAM_OUTREG_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_BSRAM_OUTREG_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 632a4bfd46..c4d3e628dd 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -37,6 +37,7 @@ struct GowinUtils // BSRAM bool have_SP32(void); bool need_SP_fix(void); + bool need_BSRAM_OUTREG_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 04a54eef45..ee1ce9f200 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -1331,6 +1331,73 @@ struct GowinPacker } } + // 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. + // We work around this by adding an external DFF and using BSRAM + // as READ_MODE=1'b0 (bypass). + void bsram_fix_outreg(CellInfo *ci, std::vector> &new_cells) + { + int bit_width = ci->params.at(id_BIT_WIDTH).as_int64(); + if (bit_width == 32 || bit_width == 36) { + 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); + if (ce_net == nullptr || oce_net == nullptr) { + return; + } + if (ce_net->name == ctx->id("$PACKER_GND") || oce_net->name == ctx->id("$PACKER_GND")) { + return; + } + + if (ctx->verbose) { + log_info(" apply the BSRAM OUTREG fix\n"); + } + ci->setParam(id_READ_MODE, 0); + ci->disconnectPort(id_OCE); + ci->connectPort(id_OCE, ce_net); + + NetInfo *reset_net = ci->getPort(id_RESET); + 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); + const NetInfo *net = ci->getPort(do_name); + if (net != nullptr) { + if (net->users.empty()) { + ci->disconnectPort(do_name); + continue; + } + + // create DFF + auto cache_dff_cell = gwu.create_cell(create_aux_name(ci->name, i, "_cache_dff$"), dff_type); + CellInfo *cache_dff = cache_dff_cell.get(); + cache_dff->addInput(id_CE); + cache_dff->connectPort(id_CE, oce_net); + + cache_dff->addInput(reset_port); + cache_dff->connectPort(reset_port, reset_net); + + ci->copyPortTo(id_CLK, cache_dff, id_CLK); + + cache_dff->addOutput(id_Q); + ci->movePortTo(do_name, cache_dff, id_Q); + + cache_dff->addInput(id_D); + ci->connectPorts(do_name, cache_dff, id_D); + + new_cells.push_back(std::move(cache_dff_cell)); + } + } + } + // Analysis of the images generated by the IDE showed that some components // are being added at the input and output of the BSRAM. Two LUTs are // added on the WRE and CE inputs (strangely, OCE is not affected), a pair @@ -1342,6 +1409,9 @@ struct GowinPacker { int bit_width = ci->params.at(id_BIT_WIDTH).as_int64(); + if (ctx->verbose) { + log_info(" apply the SP fix\n"); + } // create WRE LUT auto wre_lut_cell = gwu.create_cell(create_aux_name(ci->name, 0, "_wre_lut$"), id_LUT4); CellInfo *wre_lut = wre_lut_cell.get(); @@ -1654,6 +1724,11 @@ struct GowinPacker bsram_fix_sp(ci, new_cells); } + // Some chips have faulty output registers + if (gwu.need_BSRAM_OUTREG_fix()) { + bsram_fix_outreg(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.