gowin: Himbaechel. Add the LUTRAM
- RAM16SDP1, RAM16SDP2 and RAM16SDP4 support; - Reading in these primitives is asynchronous, but we have taken measures so that DFF Bels remain unoccupied and they can be used to implement synchronous reading. - misc fixes. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
c9b23a01db
commit
c4b3268e90
@ -873,6 +873,8 @@ X(DFFNCE)
|
|||||||
X(RAM16)
|
X(RAM16)
|
||||||
X(RAMW)
|
X(RAMW)
|
||||||
X(RAM16SDP4)
|
X(RAM16SDP4)
|
||||||
|
X(RAM16SDP2)
|
||||||
|
X(RAM16SDP1)
|
||||||
X(WADA)
|
X(WADA)
|
||||||
X(WADB)
|
X(WADB)
|
||||||
X(WADC)
|
X(WADC)
|
||||||
|
@ -80,12 +80,15 @@ bool GowinImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
IdString bel_type = ctx->getBelType(bel);
|
IdString bel_type = ctx->getBelType(bel);
|
||||||
if (bel_type.in(id_LUT4, id_DFF)) {
|
switch (bel_type.hash()) {
|
||||||
|
case ID_LUT4: /* fall-through */
|
||||||
|
case ID_DFF:
|
||||||
return slice_valid(l.x, l.y, l.z / 2);
|
return slice_valid(l.x, l.y, l.z / 2);
|
||||||
} else {
|
case ID_ALU:
|
||||||
if (bel_type == id_ALU) {
|
|
||||||
return slice_valid(l.x, l.y, l.z - BelZ::ALU0_Z);
|
return slice_valid(l.x, l.y, l.z - BelZ::ALU0_Z);
|
||||||
}
|
case ID_RAM16SDP4:
|
||||||
|
// only slices 4 and 5 are critical for RAM
|
||||||
|
return slice_valid(l.x, l.y, l.z - BelZ::RAMW_Z + 5) && slice_valid(l.x, l.y, l.z - BelZ::RAMW_Z + 4);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -102,6 +105,9 @@ IdString GowinImpl::getBelBucketForCellType(IdString cell_type) const
|
|||||||
if (type_is_dff(cell_type)) {
|
if (type_is_dff(cell_type)) {
|
||||||
return id_DFF;
|
return id_DFF;
|
||||||
}
|
}
|
||||||
|
if (type_is_ssram(cell_type)) {
|
||||||
|
return id_RAM16SDP4;
|
||||||
|
}
|
||||||
if (cell_type == id_GOWIN_GND) {
|
if (cell_type == id_GOWIN_GND) {
|
||||||
return id_GND;
|
return id_GND;
|
||||||
}
|
}
|
||||||
@ -123,6 +129,9 @@ bool GowinImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
|
|||||||
if (bel_type == id_DFF) {
|
if (bel_type == id_DFF) {
|
||||||
return type_is_dff(cell_type);
|
return type_is_dff(cell_type);
|
||||||
}
|
}
|
||||||
|
if (bel_type == id_RAM16SDP4) {
|
||||||
|
return type_is_ssram(cell_type);
|
||||||
|
}
|
||||||
if (bel_type == id_GND) {
|
if (bel_type == id_GND) {
|
||||||
return cell_type == id_GOWIN_GND;
|
return cell_type == id_GOWIN_GND;
|
||||||
}
|
}
|
||||||
@ -167,11 +176,20 @@ bool GowinImpl::slice_valid(int x, int y, int z) const
|
|||||||
const CellInfo *lut = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2)));
|
const CellInfo *lut = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2)));
|
||||||
const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2 + 1)));
|
const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2 + 1)));
|
||||||
const CellInfo *alu = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z + BelZ::ALU0_Z)));
|
const CellInfo *alu = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z + BelZ::ALU0_Z)));
|
||||||
|
const CellInfo *ramw =
|
||||||
|
(z == 4 || z == 5) ? ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, BelZ::RAMW_Z))) : nullptr;
|
||||||
|
|
||||||
if (alu && lut) {
|
if (alu && lut) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ramw) {
|
||||||
|
if (alu || ff || lut) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// check for ALU/LUT in the adjacent cell
|
// check for ALU/LUT in the adjacent cell
|
||||||
int adj_lut_z = (1 - (z & 1) * 2 + z) * 2;
|
int adj_lut_z = (1 - (z & 1) * 2 + z) * 2;
|
||||||
int adj_alu_z = adj_lut_z / 2 + BelZ::ALU0_Z;
|
int adj_alu_z = adj_lut_z / 2 + BelZ::ALU0_Z;
|
||||||
|
@ -20,6 +20,10 @@ inline bool is_dff(const CellInfo *cell) { return type_is_dff(cell->type); }
|
|||||||
// Return true if a cell is a ALU
|
// Return true if a cell is a ALU
|
||||||
inline bool type_is_alu(IdString cell_type) { return cell_type == id_ALU; }
|
inline bool type_is_alu(IdString cell_type) { return cell_type == id_ALU; }
|
||||||
inline bool is_alu(const CellInfo *cell) { return type_is_alu(cell->type); }
|
inline bool is_alu(const CellInfo *cell) { return type_is_alu(cell->type); }
|
||||||
|
|
||||||
|
// Return true if a cell is a SSRAM
|
||||||
|
inline bool type_is_ssram(IdString cell_type) { return cell_type.in(id_RAM16SDP1, id_RAM16SDP2, id_RAM16SDP4); }
|
||||||
|
inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); }
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator
|
// Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator
|
||||||
@ -33,6 +37,7 @@ enum
|
|||||||
MUX23_Z = 22,
|
MUX23_Z = 22,
|
||||||
MUX27_Z = 29,
|
MUX27_Z = 29,
|
||||||
ALU0_Z = 30, // :35, 6 ALU
|
ALU0_Z = 30, // :35, 6 ALU
|
||||||
|
RAMW_Z = 36, // RAM16SDP4
|
||||||
|
|
||||||
VCC_Z = 277,
|
VCC_Z = 277,
|
||||||
VSS_Z = 278
|
VSS_Z = 278
|
||||||
|
@ -20,6 +20,7 @@ MUX21_Z = 18
|
|||||||
MUX23_Z = 22
|
MUX23_Z = 22
|
||||||
MUX27_Z = 29
|
MUX27_Z = 29
|
||||||
ALU0_Z = 30 # : 35, 6 ALUs
|
ALU0_Z = 30 # : 35, 6 ALUs
|
||||||
|
RAMW_Z = 36 # RAM16SDP4
|
||||||
|
|
||||||
VCC_Z = 277
|
VCC_Z = 277
|
||||||
GND_Z = 278
|
GND_Z = 278
|
||||||
@ -137,17 +138,17 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
|||||||
|
|
||||||
def create_null_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
def create_null_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||||
if ttyp in created_tiletypes:
|
if ttyp in created_tiletypes:
|
||||||
return ttyp
|
return ttyp, None
|
||||||
typename = "NULL"
|
typename = "NULL"
|
||||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||||
create_switch_matrix(tt, db, x, y)
|
create_switch_matrix(tt, db, x, y)
|
||||||
return ttyp
|
return (ttyp, tt)
|
||||||
|
|
||||||
# responsible nodes, there will be IO banks, configuration, etc.
|
# responsible nodes, there will be IO banks, configuration, etc.
|
||||||
def create_corner_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
def create_corner_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||||
if ttyp in created_tiletypes:
|
if ttyp in created_tiletypes:
|
||||||
return ttyp
|
return ttyp, None
|
||||||
typename = "CORNER"
|
typename = "CORNER"
|
||||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||||
@ -163,12 +164,12 @@ def create_corner_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
|||||||
tt.add_bel_pin(gnd, "V", "VCC", PinType.OUTPUT)
|
tt.add_bel_pin(gnd, "V", "VCC", PinType.OUTPUT)
|
||||||
|
|
||||||
create_switch_matrix(tt, db, x, y)
|
create_switch_matrix(tt, db, x, y)
|
||||||
return ttyp
|
return (ttyp, tt)
|
||||||
|
|
||||||
# simple IO - only A and B
|
# simple IO - only A and B
|
||||||
def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||||
if ttyp in created_tiletypes:
|
if ttyp in created_tiletypes:
|
||||||
return ttyp
|
return ttyp, None
|
||||||
typename = "IO"
|
typename = "IO"
|
||||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||||
@ -184,12 +185,12 @@ def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
|||||||
tt.add_bel_pin(io, "I", portmap['I'], PinType.INPUT)
|
tt.add_bel_pin(io, "I", portmap['I'], PinType.INPUT)
|
||||||
tt.add_bel_pin(io, "O", portmap['O'], PinType.OUTPUT)
|
tt.add_bel_pin(io, "O", portmap['O'], PinType.OUTPUT)
|
||||||
create_switch_matrix(tt, db, x, y)
|
create_switch_matrix(tt, db, x, y)
|
||||||
return ttyp
|
return (ttyp, tt)
|
||||||
|
|
||||||
# logic: luts, dffs, alu etc
|
# logic: luts, dffs, alu etc
|
||||||
def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||||
if ttyp in created_tiletypes:
|
if ttyp in created_tiletypes:
|
||||||
return ttyp
|
return ttyp, None
|
||||||
typename = "LOGIC"
|
typename = "LOGIC"
|
||||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||||
@ -281,7 +282,30 @@ def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
|||||||
tt.add_bel_pin(ff, "S0", f"SEL7", PinType.INPUT)
|
tt.add_bel_pin(ff, "S0", f"SEL7", PinType.INPUT)
|
||||||
|
|
||||||
create_switch_matrix(tt, db, x, y)
|
create_switch_matrix(tt, db, x, y)
|
||||||
return ttyp
|
return (ttyp, tt)
|
||||||
|
|
||||||
|
def create_ssram_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||||
|
if ttyp in created_tiletypes:
|
||||||
|
return ttyp, None
|
||||||
|
# SSRAM is LUT based, so it's logic-like
|
||||||
|
ttyp, tt = create_logic_tiletype(chip, db, x, y, ttyp)
|
||||||
|
|
||||||
|
lut_inputs = ['A', 'B', 'C', 'D']
|
||||||
|
ff = tt.create_bel(f"RAM16SDP4", "RAM16SDP4", z = RAMW_Z)
|
||||||
|
for i in range(4):
|
||||||
|
tt.add_bel_pin(ff, f"DI[{i}]", f"{lut_inputs[i]}5", PinType.INPUT)
|
||||||
|
tt.add_bel_pin(ff, f"WAD[{i}]", f"{lut_inputs[i]}4", PinType.INPUT)
|
||||||
|
# RAD[0] is RAD[0] is assumed to be connected to A3, A2, A1 and A0. But
|
||||||
|
# for now we connect it only to A0, the others will be connected
|
||||||
|
# directly during packing. RAD[1...3] - similarly.
|
||||||
|
tt.add_bel_pin(ff, f"RAD[{i}]", f"{lut_inputs[i]}0", PinType.INPUT)
|
||||||
|
tt.add_bel_pin(ff, f"DO[{i}]", f"F{i}", PinType.OUTPUT)
|
||||||
|
|
||||||
|
|
||||||
|
tt.add_bel_pin(ff, f"CLK", "CLK2", PinType.INPUT)
|
||||||
|
tt.add_bel_pin(ff, f"CE", "CE2", PinType.INPUT)
|
||||||
|
tt.add_bel_pin(ff, f"WRE", "LSR2", PinType.INPUT)
|
||||||
|
return (ttyp, tt)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description='Make Gowin BBA')
|
parser = argparse.ArgumentParser(description='Make Gowin BBA')
|
||||||
@ -305,28 +329,33 @@ def main():
|
|||||||
# The manufacturer distinguishes by externally identical tiles, so keep
|
# The manufacturer distinguishes by externally identical tiles, so keep
|
||||||
# these differences (in case it turns out later that there is a slightly
|
# these differences (in case it turns out later that there is a slightly
|
||||||
# different routing or something like that).
|
# different routing or something like that).
|
||||||
logic_tiletypes = {12, 13, 14, 15, 16, 17}
|
logic_tiletypes = {12, 13, 14, 15, 16}
|
||||||
io_tiletypes = {53, 55, 58, 59, 64, 65}
|
io_tiletypes = {53, 55, 58, 59, 64, 65}
|
||||||
|
ssram_tiletypes = {17, 18, 19}
|
||||||
# Setup tile grid
|
# Setup tile grid
|
||||||
for x in range(X):
|
for x in range(X):
|
||||||
for y in range(Y):
|
for y in range(Y):
|
||||||
ttyp = db.grid[y][x].ttyp
|
ttyp = db.grid[y][x].ttyp
|
||||||
if (x == 0 or x == X - 1) and (y == 0 or y == Y - 1):
|
if (x == 0 or x == X - 1) and (y == 0 or y == Y - 1):
|
||||||
assert ttyp not in created_tiletypes, "Duplication of corner types"
|
assert ttyp not in created_tiletypes, "Duplication of corner types"
|
||||||
ttyp = create_corner_tiletype(ch, db, x, y, ttyp)
|
ttyp, _ = create_corner_tiletype(ch, db, x, y, ttyp)
|
||||||
created_tiletypes.add(ttyp)
|
created_tiletypes.add(ttyp)
|
||||||
ch.set_tile_type(x, y, f"CORNER_{ttyp}")
|
ch.set_tile_type(x, y, f"CORNER_{ttyp}")
|
||||||
continue
|
continue
|
||||||
if ttyp in logic_tiletypes:
|
if ttyp in logic_tiletypes:
|
||||||
ttyp = create_logic_tiletype(ch, db, x, y, ttyp)
|
ttyp, _ = create_logic_tiletype(ch, db, x, y, ttyp)
|
||||||
|
created_tiletypes.add(ttyp)
|
||||||
|
ch.set_tile_type(x, y, f"LOGIC_{ttyp}")
|
||||||
|
elif ttyp in ssram_tiletypes:
|
||||||
|
ttyp, _ = create_ssram_tiletype(ch, db, x, y, ttyp)
|
||||||
created_tiletypes.add(ttyp)
|
created_tiletypes.add(ttyp)
|
||||||
ch.set_tile_type(x, y, f"LOGIC_{ttyp}")
|
ch.set_tile_type(x, y, f"LOGIC_{ttyp}")
|
||||||
elif ttyp in io_tiletypes:
|
elif ttyp in io_tiletypes:
|
||||||
ttyp = create_io_tiletype(ch, db, x, y, ttyp)
|
ttyp, _ = create_io_tiletype(ch, db, x, y, ttyp)
|
||||||
created_tiletypes.add(ttyp)
|
created_tiletypes.add(ttyp)
|
||||||
ch.set_tile_type(x, y, f"IO_{ttyp}")
|
ch.set_tile_type(x, y, f"IO_{ttyp}")
|
||||||
else:
|
else:
|
||||||
ttyp = create_null_tiletype(ch, db, x, y, ttyp)
|
ttyp, _ = create_null_tiletype(ch, db, x, y, ttyp)
|
||||||
created_tiletypes.add(ttyp)
|
created_tiletypes.add(ttyp)
|
||||||
ch.set_tile_type(x, y, f"NULL_{ttyp}")
|
ch.set_tile_type(x, y, f"NULL_{ttyp}")
|
||||||
|
|
||||||
|
@ -384,9 +384,91 @@ struct GowinPacker
|
|||||||
|
|
||||||
int lutffs = h.constrain_cell_pairs(lut_outs, dff_ins, 1);
|
int lutffs = h.constrain_cell_pairs(lut_outs, dff_ins, 1);
|
||||||
log_info("Constrained %d LUTFF pairs.\n", lutffs);
|
log_info("Constrained %d LUTFF pairs.\n", lutffs);
|
||||||
|
}
|
||||||
|
|
||||||
log_info("Pack ALUs...\n");
|
// ===================================
|
||||||
pack_alus();
|
// SSRAM cluster
|
||||||
|
// ===================================
|
||||||
|
// create ALU filler block
|
||||||
|
std::unique_ptr<CellInfo> ssram_make_lut(Context *ctx, CellInfo *ci, int index)
|
||||||
|
{
|
||||||
|
IdString name_id = ctx->idf("%s_LUT%d", ci->name.c_str(ctx), index);
|
||||||
|
auto lut_ci = std::make_unique<CellInfo>(ctx, name_id, id_LUT4);
|
||||||
|
if (index) {
|
||||||
|
for (IdString port : {id_I0, id_I1, id_I2, id_I3}) {
|
||||||
|
lut_ci->addInput(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IdString init_name = ctx->idf("INIT_%d", index);
|
||||||
|
if (ci->params.count(init_name)) {
|
||||||
|
lut_ci->params[id_INIT] = ci->params.at(init_name);
|
||||||
|
} else {
|
||||||
|
lut_ci->params[id_INIT] = std::string("1111111111111111");
|
||||||
|
}
|
||||||
|
return lut_ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pack_ram16sdp4(void)
|
||||||
|
{
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||||||
|
|
||||||
|
log_info("Pack RAMs...\n");
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
auto ci = cell.second.get();
|
||||||
|
if (ci->cluster != ClusterId()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_ssram(ci)) {
|
||||||
|
// make cluster root
|
||||||
|
ci->cluster = ci->name;
|
||||||
|
ci->constr_abs_z = true;
|
||||||
|
ci->constr_x = 0;
|
||||||
|
ci->constr_y = 0;
|
||||||
|
ci->constr_z = BelZ::RAMW_Z;
|
||||||
|
|
||||||
|
ci->addInput(id_CE);
|
||||||
|
ci->connectPort(id_CE, ctx->nets[ctx->id("$PACKER_VCC")].get());
|
||||||
|
|
||||||
|
// RAD networks
|
||||||
|
NetInfo *rad[4];
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
rad[i] = ci->getPort(ctx->idf("RAD[%d]", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// active LUTs
|
||||||
|
int luts_num = 4;
|
||||||
|
if (ci->type == id_RAM16SDP1) {
|
||||||
|
luts_num = 1;
|
||||||
|
} else {
|
||||||
|
if (ci->type == id_RAM16SDP2) {
|
||||||
|
luts_num = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make actual storage cells
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
new_cells.push_back(std::move(ssram_make_lut(ctx, ci, i)));
|
||||||
|
CellInfo *lut_ci = new_cells.back().get();
|
||||||
|
ci->constr_children.push_back(lut_ci);
|
||||||
|
lut_ci->cluster = ci->name;
|
||||||
|
lut_ci->constr_abs_z = true;
|
||||||
|
lut_ci->constr_x = 0;
|
||||||
|
lut_ci->constr_y = 0;
|
||||||
|
lut_ci->constr_z = i * 2;
|
||||||
|
// inputs
|
||||||
|
// LUT0 is already connected when generating the base
|
||||||
|
if (i && i < luts_num) {
|
||||||
|
for (int j = 0; j < 4; ++j) {
|
||||||
|
lut_ci->connectPort(ctx->idf("I%d", j), rad[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto &ncell : new_cells) {
|
||||||
|
ctx->cells[ncell->name] = std::move(ncell);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(void)
|
void run(void)
|
||||||
@ -396,6 +478,7 @@ struct GowinPacker
|
|||||||
pack_wideluts();
|
pack_wideluts();
|
||||||
pack_alus();
|
pack_alus();
|
||||||
constrain_lutffs();
|
constrain_lutffs();
|
||||||
|
pack_ram16sdp4();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user