From 91b0c4f90a71cb18413e1ffa1129184c8ecad67d Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 23 Jan 2024 14:50:36 +1000 Subject: [PATCH] gowin: Himbaechel. Deal with SP BSRAM ports. The OCE signal in the SP(X)9B primitive is intended to control the built-in output register. The documentation states that this port is invalid when READ_MODE=0 is used. However, it has been experimentally established that you cannot simply apply VCC or GND to it and forget it - the discrepancy between the signal on this port and the signal on the CE port leads to both skipping data reading and unnecessary reading after CE has switched to 0. Here we force these ports to be connected to the network, except in the case where the user controls the OCE signal using non-constant signals. Also: * All PIPs for clock spines are made inaccessible to the common router - in general, using these routes for signals that have not been processed by a special globals router is fraught with effects that are difficult to detect. * The INV primitive has been added purely to speed up development - this primitive is not generated by Yosys, but is almost always present in vendor output files. Signed-off-by: YRabbit --- himbaechel/uarch/gowin/constids.inc | 3 ++ himbaechel/uarch/gowin/globals.cc | 4 ++- himbaechel/uarch/gowin/gowin.cc | 6 ++++ himbaechel/uarch/gowin/gowin_utils.h | 15 ++++++++++ himbaechel/uarch/gowin/pack.cc | 42 ++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 70f0d9896b..f357783c72 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -934,6 +934,9 @@ X(GSR) X(GSR0) X(GSRI) +// inverter +X(INV) + // Oscillators X(OSC) X(OSCZ) diff --git a/himbaechel/uarch/gowin/globals.cc b/himbaechel/uarch/gowin/globals.cc index c818f762a3..0489a9e00c 100644 --- a/himbaechel/uarch/gowin/globals.cc +++ b/himbaechel/uarch/gowin/globals.cc @@ -40,6 +40,8 @@ struct GowinGlobalRouter GowinGlobalRouter(Context *ctx) : ctx(ctx) { gwu.init(ctx); }; + bool checkPipAvail(PipId pip) const { return gwu.is_global_pip(pip) || ctx->checkPipAvail(pip); }; + // allow io->global, global->global and global->tile clock bool global_pip_filter(PipId pip) const { @@ -91,7 +93,7 @@ struct GowinGlobalRouter // Search uphill pips for (PipId pip : ctx->getPipsUphill(cursor)) { // Skip pip if unavailable, and not because it's already used for this net - if (!ctx->checkPipAvail(pip) && ctx->getBoundPipNet(pip) != net) { + if (!checkPipAvail(pip) && ctx->getBoundPipNet(pip) != net) { continue; } WireId prev = ctx->getPipSrcWire(pip); diff --git a/himbaechel/uarch/gowin/gowin.cc b/himbaechel/uarch/gowin/gowin.cc index 920d86facf..cae8f1ab88 100644 --- a/himbaechel/uarch/gowin/gowin.cc +++ b/himbaechel/uarch/gowin/gowin.cc @@ -38,6 +38,9 @@ struct GowinImpl : HimbaechelAPI bool isValidBelForCellType(IdString cell_type, BelId bel) const override; + // wires + bool checkPipAvail(PipId pip) const override; + private: HimbaechelHelpers h; GowinUtils gwu; @@ -154,6 +157,9 @@ void GowinImpl::init(Context *ctx) } } +// We do not allow the use of global wires that bypass a special router. +bool GowinImpl::checkPipAvail(PipId pip) const { return !gwu.is_global_pip(pip); } + void GowinImpl::pack() { if (ctx->settings.count(ctx->id("cst.filename"))) { diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index 5d2c6571df..d93c602974 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -38,6 +38,21 @@ struct GowinUtils // wires inline bool is_wire_type_default(IdString wire_type) { return wire_type == IdString(); } + // If wire is an important part of the global network (like SPINExx) + inline bool is_global_wire(WireId wire) const + { + return ctx->getWireName(wire)[1].in( + id_SPINE0, id_SPINE1, id_SPINE2, id_SPINE3, id_SPINE4, id_SPINE5, id_SPINE6, id_SPINE7, id_SPINE8, + id_SPINE9, id_SPINE10, id_SPINE11, id_SPINE12, id_SPINE13, id_SPINE14, id_SPINE15, id_SPINE16, + id_SPINE17, id_SPINE18, id_SPINE19, id_SPINE20, id_SPINE21, id_SPINE22, id_SPINE23, id_SPINE24, + id_SPINE25, id_SPINE26, id_SPINE27, id_SPINE28, id_SPINE29, id_SPINE30, id_SPINE31); + } + + // pips + inline bool is_global_pip(PipId pip) const + { + return is_global_wire(ctx->getPipSrcWire(pip)) || is_global_wire(ctx->getPipDstWire(pip)); + } // chip dependent bool have_SP32(void); diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index 5b0cc18383..5a2867dedb 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -1294,6 +1294,24 @@ struct GowinPacker } } + // If the memory is controlled by the CE, then it is logical for the OCE to + // also respond to this signal, unless the OCE is controlled separately. + void bsram_handle_sp_oce(CellInfo *ci, IdString ce_pin, IdString oce_pin) + { + const NetInfo *net = ci->getPort(oce_pin); + NPNR_ASSERT(ci->getPort(ce_pin) != nullptr); + if (net == nullptr || net->name == ctx->id("$PACKER_VCC") || net->name == ctx->id("$PACKER_GND")) { + if (net != nullptr) { + ci->disconnectPort(oce_pin); + } + ci->copyPortTo(ce_pin, ci, oce_pin); + } + if (ctx->verbose) { + log_info("%s: %s = %s = %s\n", ctx->nameOf(ci), ce_pin.c_str(ctx), oce_pin.c_str(ctx), + ctx->nameOf(ci->getPort(oce_pin))); + } + } + void pack_ROM(CellInfo *ci) { int default_bw = 32; @@ -1512,6 +1530,8 @@ struct GowinPacker } int bit_width = ci->params.at(id_BIT_WIDTH).as_int64(); + bsram_handle_sp_oce(ci, id_CE, id_OCE); + // 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. @@ -1623,6 +1643,25 @@ struct GowinPacker } } + // =================================== + // Replace INV with LUT + // =================================== + void pack_inv(void) + { + log_info("Pack INV..\n"); + + for (auto &cell : ctx->cells) { + auto &ci = *cell.second; + + if (ci.type == id_INV) { + ci.type = id_LUT4; + ci.renamePort(id_O, id_F); + ci.renamePort(id_I, id_I3); // use D - it's simple for INIT + ci.params[id_INIT] = Property(0x00ff); + } + } + } + // =================================== // PLL // =================================== @@ -1700,6 +1739,9 @@ struct GowinPacker pack_gsr(); ctx->check(); + pack_inv(); + ctx->check(); + pack_wideluts(); ctx->check();