Gowin. Implement the UserFlash primitive (#1357)

* 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 <rabbit@yrabbit.cyou>

* Gowin. Fix cell creation

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>

---------

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2024-09-04 20:55:35 +10:00 committed by GitHub
parent 2dc712130c
commit 4cf7afedf7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 159 additions and 3 deletions

View File

@ -1193,7 +1193,42 @@ X(BOTTOM_IO_PORT_A)
X(BOTTOM_IO_PORT_B) X(BOTTOM_IO_PORT_B)
X(IOLOGIC_DUMMY) 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 // wire types
X(GLOBAL_CLK) X(GLOBAL_CLK)

View File

@ -73,6 +73,14 @@ inline bool is_clkdiv2(const CellInfo *cell) { return type_is_clkdiv2(cell->type
// Return true for HCLK Cells // Return true for HCLK Cells
inline bool is_hclk(const CellInfo *cell) { return type_is_clkdiv2(cell->type) || type_is_clkdiv(cell->type); } 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 // extra data in the chip db
// ========================================== // ==========================================
@ -155,7 +163,9 @@ enum
BANDGAP_Z = 279, BANDGAP_Z = 279,
DQCE_Z = 280, // : 286 reserve for 6 DQCEs 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 // 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 // multipliers, if they are equal to 0, then we get Z of their common

View File

@ -50,7 +50,9 @@ GND_Z = 278
BANDGAP_Z = 279 BANDGAP_Z = 279
DQCE_Z = 280 # : 286 reserve for 6 DQCEs 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 DSP_Z = 509
@ -527,6 +529,18 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
bel.flags = BEL_FLAG_GLOBAL bel.flags = BEL_FLAG_GLOBAL
tt.add_bel_pin(bel, "I", wire, PinType.INPUT) tt.add_bel_pin(bel, "I", wire, PinType.INPUT)
tt.add_bel_pin(bel, "O", wire_out, PinType.OUTPUT) 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): def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
has_extra_func = (y, x) in db.extra_func has_extra_func = (y, x) in db.extra_func

View File

@ -3006,6 +3006,8 @@ struct GowinPacker
// ========================================= // =========================================
void pack_dqce() void pack_dqce()
{ {
log_info("Pack DQCE cells...\n");
// At the placement stage, nothing can be said definitively about DQCE, // At the placement stage, nothing can be said definitively about DQCE,
// so we make user cells virtual but allocate all available bels by // so we make user cells virtual but allocate all available bels by
// creating and placing cells - we will use some of them after, and // creating and placing cells - we will use some of them after, and
@ -3039,6 +3041,8 @@ struct GowinPacker
// ========================================= // =========================================
void pack_dcs() void pack_dcs()
{ {
log_info("Pack DCS cells...\n");
// At the placement stage, nothing can be said definitively about DCS, // At the placement stage, nothing can be said definitively about DCS,
// so we make user cells virtual but allocate all available bels by // so we make user cells virtual but allocate all available bels by
// creating and placing cells - we will use some of them after, and // creating and placing cells - we will use some of them after, and
@ -3072,6 +3076,96 @@ struct GowinPacker
} }
} }
// =========================================
// Enable UserFlash
// =========================================
void pack_userflash()
{
log_info("Pack UserFlash cells...\n");
std::vector<std::unique_ptr<CellInfo>> new_cells;
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;
}
std::unique_ptr<CellInfo> lut_cell =
gwu.create_cell(create_aux_name(ci.name, lut_idx, "_lut$"), id_LUT4);
new_cells.push_back(std::move(lut_cell));
CellInfo *lut = new_cells.back().get();
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);
}
}
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
void run(void) void run(void)
{ {
handle_constants(); handle_constants();
@ -3124,6 +3218,9 @@ struct GowinPacker
pack_buffered_nets(); pack_buffered_nets();
ctx->check(); ctx->check();
pack_userflash();
ctx->check();
pack_dqce(); pack_dqce();
ctx->check(); ctx->check();