From e7223be18c57063eb8f5d9d9af4ce64d8d9bfc76 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 2 Sep 2024 18:25:09 +1000 Subject: [PATCH] Gowin. Implement the UserFlash primitive Some Gowin chips have embedded flash memory accessible from the fabric. Here we add primitives that allow access to this memory. Signed-off-by: YRabbit --- himbaechel/uarch/gowin/constids.inc | 37 +++++++++- himbaechel/uarch/gowin/gowin.h | 12 +++- himbaechel/uarch/gowin/gowin_arch_gen.py | 16 ++++- himbaechel/uarch/gowin/pack.cc | 90 ++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 3 deletions(-) diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index fcadd941ef..62a4036057 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1193,7 +1193,42 @@ X(BOTTOM_IO_PORT_A) X(BOTTOM_IO_PORT_B) X(IOLOGIC_DUMMY) -// +// User Flash +X(INUSEN) +X(DIN) +X(DOUT) +X(XE) +X(YE) +X(SE) +X(PROG) +X(ERASE) +X(NVSTR) +X(XADR0) +X(XADR1) +X(XADR2) +X(XADR3) +X(XADR4) +X(XADR5) +X(XADR6) +X(XADR7) +X(XADR8) +X(YADR) +X(RA) +X(CA) +X(PA) +X(MODE) +X(SEQ) +X(RMODE) +X(WMODE) +X(RBYTESEL) +X(WBYTESEL) +X(FLASH96K) +X(FLASH256K) +X(FLASH608K) +X(FLASH128K) +X(FLASH64K) +X(FLASH64KZ) +X(FLASH96KA) // wire types X(GLOBAL_CLK) diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index c14949d665..81c825f4da 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -73,6 +73,14 @@ inline bool is_clkdiv2(const CellInfo *cell) { return type_is_clkdiv2(cell->type // Return true for HCLK Cells inline bool is_hclk(const CellInfo *cell) { return type_is_clkdiv2(cell->type) || type_is_clkdiv(cell->type); } +// Return true if a cell is a UserFlash +inline bool type_is_userflash(IdString cell_type) +{ + return cell_type.in(id_FLASH96K, id_FLASH256K, id_FLASH608K, id_FLASH128K, id_FLASH64K, id_FLASH64K, id_FLASH64KZ, + id_FLASH96KA); +} +inline bool is_userflash(const CellInfo *cell) { return type_is_userflash(cell->type); } + // ========================================== // extra data in the chip db // ========================================== @@ -155,7 +163,9 @@ enum BANDGAP_Z = 279, DQCE_Z = 280, // : 286 reserve for 6 DQCEs - DCS_Z = 286, // : 287 reserve for 2 DCSs + DCS_Z = 286, // : 288 reserve for 2 DCSs + + USERFLASH_Z = 288, // The two least significant bits encode Z for 9-bit adders and // multipliers, if they are equal to 0, then we get Z of their common diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 5f0e4ac2fd..5457fc621f 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -50,7 +50,9 @@ BANDGAP_Z = 279 DQCE_Z = 280 # : 286 reserve for 6 DQCEs -DCS_Z = 286 # : 287 reserve for 2 DCSs +DCS_Z = 286 # : 288 reserve for 2 DCSs + +USERFLASH_Z = 288 DSP_Z = 509 @@ -527,6 +529,18 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int): bel.flags = BEL_FLAG_GLOBAL tt.add_bel_pin(bel, "I", wire, PinType.INPUT) tt.add_bel_pin(bel, "O", wire_out, PinType.OUTPUT) + elif func == 'userflash': + bel = tt.create_bel("USERFLASH", desc['type'], USERFLASH_Z) + portmap = desc['ins'] + for port, wire in portmap.items(): + if not tt.has_wire(wire): + tt.create_wire(wire, "FLASH_IN") + tt.add_bel_pin(bel, port, wire, PinType.INPUT) + portmap = desc['outs'] + for port, wire in portmap.items(): + if not tt.has_wire(wire): + tt.create_wire(wire, "FLASH_OUT") + tt.add_bel_pin(bel, port, wire, PinType.OUTPUT) def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int): has_extra_func = (y, x) in db.extra_func diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index ec1ef8f15e..c62c9a5eab 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -3006,6 +3006,8 @@ struct GowinPacker // ========================================= void pack_dqce() { + log_info("Pack DQCE cells...\n"); + // At the placement stage, nothing can be said definitively about DQCE, // so we make user cells virtual but allocate all available bels by // creating and placing cells - we will use some of them after, and @@ -3039,6 +3041,8 @@ struct GowinPacker // ========================================= void pack_dcs() { + log_info("Pack DCS cells...\n"); + // At the placement stage, nothing can be said definitively about DCS, // so we make user cells virtual but allocate all available bels by // creating and placing cells - we will use some of them after, and @@ -3072,6 +3076,89 @@ struct GowinPacker } } + // ========================================= + // Enable UserFlash + // ========================================= + void pack_userflash() + { + log_info("Pack UserFlash cells...\n"); + + for (auto &cell : ctx->cells) { + auto &ci = *cell.second; + if (!is_userflash(&ci)) { + continue; + } + + if (ci.type.in(id_FLASH96K, id_FLASH256K, id_FLASH608K)) { + // enable + ci.addInput(id_INUSEN); + ci.connectPort(id_INUSEN, ctx->nets.at(ctx->id("$PACKER_GND")).get()); + } + // rename ports + for (int i = 0; i < 32; ++i) { + ci.renamePort(ctx->idf("DIN[%d]", i), ctx->idf("DIN%d", i)); + ci.renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + if (ci.type.in(id_FLASH96K)) { + for (int i = 0; i < 6; ++i) { + ci.renamePort(ctx->idf("RA[%d]", i), ctx->idf("RA%d", i)); + ci.renamePort(ctx->idf("CA[%d]", i), ctx->idf("CA%d", i)); + ci.renamePort(ctx->idf("PA[%d]", i), ctx->idf("PA%d", i)); + } + for (int i = 0; i < 2; ++i) { + ci.renamePort(ctx->idf("MODE[%d]", i), ctx->idf("MODE%d", i)); + ci.renamePort(ctx->idf("SEQ[%d]", i), ctx->idf("SEQ%d", i)); + ci.renamePort(ctx->idf("RMODE[%d]", i), ctx->idf("RMODE%d", i)); + ci.renamePort(ctx->idf("WMODE[%d]", i), ctx->idf("WMODE%d", i)); + ci.renamePort(ctx->idf("RBYTESEL[%d]", i), ctx->idf("RBYTESEL%d", i)); + ci.renamePort(ctx->idf("WBYTESEL[%d]", i), ctx->idf("WBYTESEL%d", i)); + } + } else { + for (int i = 0; i < 9; ++i) { + ci.renamePort(ctx->idf("XADR[%d]", i), ctx->idf("XADR%d", i)); + } + for (int i = 0; i < 6; ++i) { + ci.renamePort(ctx->idf("YADR[%d]", i), ctx->idf("YADR%d", i)); + } + } + // add invertor + int lut_idx = 0; + auto add_inv = [&](IdString port, PortType port_type) { + if (!port_used(&ci, port)) { + return; + } + + CellInfo *lut = ctx->createCell(create_aux_name(ci.name, lut_idx, "_lut$"), id_LUT4); + lut->addInput(id_I0); + lut->addOutput(id_F); + lut->setParam(id_INIT, 0x5555); + ++lut_idx; + + if (port_type == PORT_IN) { + ci.movePortTo(port, lut, id_I0); + lut->connectPorts(id_F, &ci, port); + } else { + ci.movePortTo(port, lut, id_F); + ci.connectPorts(port, lut, id_I0); + } + }; + for (auto pin : ci.ports) { + if (pin.second.type == PORT_OUT) { + add_inv(pin.first, PORT_OUT); + } else { + if (pin.first == id_INUSEN) { + continue; + } + if (ci.type == id_FLASH608K && pin.first.in(id_XADR0, id_XADR1, id_XADR2, id_XADR3, id_XADR4, + id_XADR5, id_XADR6, id_XADR7, id_XADR8)) { + continue; + } + add_inv(pin.first, PORT_IN); + } + } + } + } + void run(void) { handle_constants(); @@ -3124,6 +3211,9 @@ struct GowinPacker pack_buffered_nets(); ctx->check(); + pack_userflash(); + ctx->check(); + pack_dqce(); ctx->check();