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:
parent
2dc712130c
commit
4cf7afedf7
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user