aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPepijn de Vos <pepijndevos@gmail.com>2022-06-05 16:59:06 +0200
committerPepijn de Vos <pepijndevos@gmail.com>2022-06-05 16:59:06 +0200
commit490dddf636bc50945ee6e6858f7c1942faa3aaf5 (patch)
tree379153bbfaffd6968dbe1def362e6e1fa72df036
parent997af6d7202b08c457c56db958c5e39605e74295 (diff)
downloadnextpnr-490dddf636bc50945ee6e6858f7c1942faa3aaf5.tar.gz
nextpnr-490dddf636bc50945ee6e6858f7c1942faa3aaf5.tar.bz2
nextpnr-490dddf636bc50945ee6e6858f7c1942faa3aaf5.zip
WIP shadowram
-rw-r--r--gowin/arch.cc36
-rw-r--r--gowin/arch.h2
-rw-r--r--gowin/cells.cc33
-rw-r--r--gowin/cells.h8
-rw-r--r--gowin/constids.inc13
-rw-r--r--gowin/pack.cc83
6 files changed, 175 insertions, 0 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc
index 4fc2cd43..d554bd64 100644
--- a/gowin/arch.cc
+++ b/gowin/arch.cc
@@ -791,6 +791,39 @@ void Arch::read_cst(std::istream &in)
settings[id_cst] = 1;
}
+void Arch::addShadowRamBels(const DatabasePOD *db, int row, int col)
+{
+ IdString belname, bel_id;
+ char buf[32];
+ snprintf(buf, 32, "R%dC%d_RAMW", row + 1, col + 1);
+ belname = id(buf);
+ addBel(belname, id_RAMW, Loc(col, row, BelZ::lutram_0_z), false);
+
+ snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, 4);
+ addBelInput(belname, id_A4, id(buf));
+ snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, 4);
+ addBelInput(belname, id_B4, id(buf));
+ snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, 4);
+ addBelInput(belname, id_C4, id(buf));
+ snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, 4);
+ addBelInput(belname, id_D4, id(buf));
+
+ snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, 5);
+ addBelInput(belname, id_A5, id(buf));
+ snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, 5);
+ addBelInput(belname, id_B5, id(buf));
+ snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, 5);
+ addBelInput(belname, id_C5, id(buf));
+ snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, 5);
+ addBelInput(belname, id_D5, id(buf));
+
+ snprintf(buf, 32, "R%dC%d_CLK%d", row + 1, col + 1, 2);
+ addBelInput(belname, id_CLK, id(buf));
+ snprintf(buf, 32, "R%dC%d_LSR%d", row + 1, col + 1, 2);
+ addBelInput(belname, id_LSR, id(buf));
+}
+
+
// Add all MUXes for the cell
void Arch::addMuxBels(const DatabasePOD *db, int row, int col)
{
@@ -1112,6 +1145,9 @@ Arch::Arch(ArchArgs args) : args(args)
if (z == 0) {
addMuxBels(db, row, col);
}
+ if (z == 4) {
+ addShadowRamBels(db, row, col);
+ }
if (z % 2 == 0) {
snprintf(buf, 32, "R%dC%d_LUT_GRP%d", row + 1, col + 1, z);
grpname = id(buf);
diff --git a/gowin/arch.h b/gowin/arch.h
index 14181d79..80f887bb 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -344,6 +344,7 @@ struct Arch : BaseArch<ArchRanges>
DelayQuad getWireTypeDelay(IdString wire);
void read_cst(std::istream &in);
void addMuxBels(const DatabasePOD *db, int row, int col);
+ void addShadowRamBels(const DatabasePOD *db, int row, int col);
// ---------------------------------------------------------------
// Common Arch API. Every arch must provide the following methods.
@@ -485,6 +486,7 @@ enum
{
mux_0_z = 10, // start Z for the MUX2LUT5 bels
iologic_0_z = 20, // start Z for the IOLOGIC bels
+ lutram_0_z = 30, // start Z for the IOLOGIC bels
vcc_0_z = 277, // virtual VCC bel Z
gnd_0_z = 278, // virtual VSS bel Z
osc_z = 280 // Z for the oscillator bels
diff --git a/gowin/cells.cc b/gowin/cells.cc
index c3b21782..8285dc4e 100644
--- a/gowin/cells.cc
+++ b/gowin/cells.cc
@@ -166,4 +166,37 @@ void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool<IdString> &to
}
}
+void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw)
+{
+ if (ramw->hierpath == IdString())
+ ramw->hierpath = ramw->hierpath;
+ ram->movePortTo(ctx->id("WAD[0]"), ramw, id_A4);
+ ram->movePortTo(ctx->id("WAD[1]"), ramw, id_B4);
+ ram->movePortTo(ctx->id("WAD[2]"), ramw, id_C4);
+ ram->movePortTo(ctx->id("WAD[3]"), ramw, id_D4);
+
+ ram->movePortTo(ctx->id("DI[0]"), ramw, id_A5);
+ ram->movePortTo(ctx->id("DI[1]"), ramw, id_B5);
+ ram->movePortTo(ctx->id("DI[2]"), ramw, id_C5);
+ ram->movePortTo(ctx->id("DI[3]"), ramw, id_D5);
+
+ ram->movePortTo(ctx->id("CLK"), ramw, id_CLK);
+ ram->movePortTo(ctx->id("WRE"), ramw, id_LSR);
+}
+
+void sram_to_slice(Context *ctx, CellInfo *ram, CellInfo *slice, int index)
+{
+ char buf1[32];
+ if (slice->hierpath == IdString())
+ slice->hierpath = slice->hierpath;
+
+ snprintf(buf1, 32, "DO[%d]", index);
+ ram->movePortTo(ctx->id(buf1), slice, id_F);
+
+ ram->copyPortTo(ctx->id("RAD[0]"), slice, id_A);
+ ram->copyPortTo(ctx->id("RAD[1]"), slice, id_B);
+ ram->copyPortTo(ctx->id("RAD[2]"), slice, id_C);
+ ram->copyPortTo(ctx->id("RAD[3]"), slice, id_D);
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/gowin/cells.h b/gowin/cells.h
index 8f0636b8..b6d86497 100644
--- a/gowin/cells.h
+++ b/gowin/cells.h
@@ -111,6 +111,8 @@ inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SLICE; }
+inline bool is_sram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_RAM16SDP4; }
+
// Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports
// as needed. Set no_dff if a DFF is not being used, so that the output
// can be reconnected
@@ -125,6 +127,12 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l
// Convert a Gowin IO buffer to a IOB bel
void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool<IdString> &todelete_cells);
+// Convert RAM16 to write port
+void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw);
+
+// Convert RAM16 to slice
+void sram_to_slice(Context *ctx, CellInfo *ram, CellInfo *slice, int index);
+
NEXTPNR_NAMESPACE_END
#endif
diff --git a/gowin/constids.inc b/gowin/constids.inc
index 3691c506..d9ca1f0c 100644
--- a/gowin/constids.inc
+++ b/gowin/constids.inc
@@ -748,6 +748,19 @@ X(DFFNPE)
X(DFFNC)
X(DFFNCE)
+// Shadow RAM
+X(RAMW)
+X(RAM16SDP4)
+X(WADA)
+X(WADB)
+X(WADC)
+X(WADD)
+X(DIA)
+X(DIB)
+X(DIC)
+X(DID)
+X(WRE)
+
// IOB types
X(IBUF)
X(OBUF)
diff --git a/gowin/pack.cc b/gowin/pack.cc
index 4adfec1a..fa88772a 100644
--- a/gowin/pack.cc
+++ b/gowin/pack.cc
@@ -692,6 +692,89 @@ static void pack_gsr(Context *ctx)
}
}
+// Pack shadow RAM
+void pack_sram(Context *ctx)
+{
+ pool<IdString> packed_cells;
+ std::vector<std::unique_ptr<CellInfo>> new_cells;
+
+ for (auto &cell : ctx->cells) {
+ CellInfo *ci = cell.second.get();
+ if (is_sram(ctx, ci)) {
+
+ // Create RAMW slice
+ std::unique_ptr<CellInfo> ramw_slice =
+ create_generic_cell(ctx, id_RAMW, ci->name.str(ctx) + "$RAMW_SLICE");
+ sram_to_ramw_split(ctx, ci, ramw_slice.get());
+
+ // Create actual RAM slices
+ std::unique_ptr<CellInfo> ram_comb[4];
+ for (int i = 0; i < 4; i++) {
+ ram_comb[i] = create_generic_cell(ctx, id_SLICE,
+ ci->name.str(ctx) + "$SRAM_SLICE" + std::to_string(i));
+ sram_to_slice(ctx, ci, ram_comb[i].get(), i);
+ }
+ // Create 'block' SLICEs as a placement hint that these cells are mutually exclusive with the RAMW
+ std::unique_ptr<CellInfo> ramw_block[2];
+ for (int i = 0; i < 2; i++) {
+ ramw_block[i] = create_generic_cell(ctx, id_SLICE,
+ ci->name.str(ctx) + "$RAMW_BLOCK" + std::to_string(i));
+ ramw_block[i]->params[id_FF_TYPE] = std::string("RAMW_BLOCK");
+ }
+
+ // Disconnect ports of original cell after packing
+ // ci->disconnectPort(id_WCK);
+ // ci->disconnectPort(id_WRE);
+
+ for (int i = 0; i < 4; i++)
+ ci->disconnectPort(ctx->id(stringf("RAD[%d]", i)));
+
+ // Setup placement constraints
+ // Use the 0th bit as an anchor
+ ram_comb[0]->constr_abs_z = true;
+ ram_comb[0]->constr_z = 0;
+ ram_comb[0]->cluster = ram_comb[0]->name;
+ for (int i = 1; i < 4; i++) {
+ ram_comb[i]->cluster = ram_comb[0]->name;
+ ram_comb[i]->constr_abs_z = true;
+ ram_comb[i]->constr_x = 0;
+ ram_comb[i]->constr_y = 0;
+ ram_comb[i]->constr_z = i;
+ ram_comb[0]->constr_children.push_back(ram_comb[i].get());
+ }
+ for (int i = 0; i < 2; i++) {
+ ramw_block[i]->cluster = ram_comb[0]->name;
+ ramw_block[i]->constr_abs_z = true;
+ ramw_block[i]->constr_x = 0;
+ ramw_block[i]->constr_y = 0;
+ ramw_block[i]->constr_z = i + 4;
+ ram_comb[0]->constr_children.push_back(ramw_block[i].get());
+ }
+
+ ramw_slice->cluster = ram_comb[0]->name;
+ ramw_slice->constr_abs_z = true;
+ ramw_slice->constr_x = 0;
+ ramw_slice->constr_y = 0;
+ ramw_slice->constr_z = 4;
+ ram_comb[0]->constr_children.push_back(ramw_slice.get());
+
+ for (int i = 0; i < 4; i++)
+ new_cells.push_back(std::move(ram_comb[i]));
+ for (int i = 0; i < 2; i++)
+ new_cells.push_back(std::move(ramw_block[i]));
+ new_cells.push_back(std::move(ramw_slice));
+ packed_cells.insert(ci->name);
+ }
+ }
+ for (auto pcell : packed_cells) {
+ ctx->cells.erase(pcell);
+ }
+ for (auto &ncell : new_cells) {
+ ctx->cells[ncell->name] = std::move(ncell);
+ }
+}
+
+
static bool is_nextpnr_iob(const Context *ctx, CellInfo *cell)
{
return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||