gowin: Himbaechel. Add a wideluts
- MUX2_LUT5, MUX2_LUT6, MUX2_LUT7 and MUX2_LUT8 support; - storing a common class of files in extra_data; - misc fixes. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
f7fbe0db04
commit
c82654d003
@ -19,17 +19,7 @@ void GowinImpl::init(Context *ctx)
|
||||
// Arch::archArgsToId
|
||||
}
|
||||
|
||||
void GowinImpl::prePlace()
|
||||
{
|
||||
ctx->cells.at(ctx->id("leds_OBUF_O"))->setAttr(ctx->id("BEL"), std::string("X0Y14/IOBA"));
|
||||
ctx->cells.at(ctx->id("leds_OBUF_O_1"))->setAttr(ctx->id("BEL"), std::string("X0Y15/IOBB"));
|
||||
ctx->cells.at(ctx->id("leds_OBUF_O_2"))->setAttr(ctx->id("BEL"), std::string("X0Y20/IOBB"));
|
||||
ctx->cells.at(ctx->id("leds_OBUF_O_3"))->setAttr(ctx->id("BEL"), std::string("X0Y21/IOBB"));
|
||||
ctx->cells.at(ctx->id("leds_OBUF_O_4"))->setAttr(ctx->id("BEL"), std::string("X0Y24/IOBB"));
|
||||
ctx->cells.at(ctx->id("leds_OBUF_O_5"))->setAttr(ctx->id("BEL"), std::string("X0Y25/IOBB"));
|
||||
ctx->cells.at(ctx->id("rst_IBUF_I"))->setAttr(ctx->id("BEL"), std::string("X0Y4/IOBA"));
|
||||
assign_cell_info();
|
||||
}
|
||||
void GowinImpl::prePlace() { assign_cell_info(); }
|
||||
|
||||
void GowinImpl::pack()
|
||||
{
|
||||
@ -47,15 +37,17 @@ void GowinImpl::pack()
|
||||
// disconnect the constant LUT inputs
|
||||
mod_lut_inputs();
|
||||
|
||||
pack_wideluts();
|
||||
|
||||
// Constrain directly connected LUTs and FFs together to use dedicated resources
|
||||
int lutffs = h.constrain_cell_pairs(
|
||||
pool<CellTypePort>{{id_LUT1, id_F}, {id_LUT2, id_F}, {id_LUT3, id_F}, {id_LUT4, id_F}},
|
||||
pool<CellTypePort>{{id_DFF, id_D}, {id_DFFE, id_D}, {id_DFFN, id_D}, {id_DFFNE, id_D},
|
||||
{id_DFFS, id_D}, {id_DFFSE, id_D}, {id_DFFNS, id_D}, {id_DFFNSE, id_D},
|
||||
{id_DFFR, id_D}, {id_DFFRE, id_D}, {id_DFFNR, id_D}, {id_DFFNRE, id_D},
|
||||
{id_DFFP, id_D}, {id_DFFPE, id_D}, {id_DFFNP, id_D}, {id_DFFNPE, id_D},
|
||||
{id_DFFC, id_D}, {id_DFFCE, id_D}, {id_DFFNC, id_D}, {id_DFFNCE, id_D}},
|
||||
1);
|
||||
auto lut_outs = pool<CellTypePort>{{id_LUT1, id_F}, {id_LUT2, id_F}, {id_LUT3, id_F}, {id_LUT4, id_F}};
|
||||
auto dff_ins = pool<CellTypePort>{{id_DFF, id_D}, {id_DFFE, id_D}, {id_DFFN, id_D}, {id_DFFNE, id_D},
|
||||
{id_DFFS, id_D}, {id_DFFSE, id_D}, {id_DFFNS, id_D}, {id_DFFNSE, id_D},
|
||||
{id_DFFR, id_D}, {id_DFFRE, id_D}, {id_DFFNR, id_D}, {id_DFFNRE, id_D},
|
||||
{id_DFFP, id_D}, {id_DFFPE, id_D}, {id_DFFNP, id_D}, {id_DFFNPE, id_D},
|
||||
{id_DFFC, id_D}, {id_DFFCE, id_D}, {id_DFFNC, id_D}, {id_DFFNCE, id_D}};
|
||||
|
||||
int lutffs = h.constrain_cell_pairs(lut_outs, dff_ins, 1);
|
||||
log_info("Constrained %d LUTFF pairs.\n", lutffs);
|
||||
}
|
||||
|
||||
@ -237,4 +229,101 @@ void GowinImpl::mod_lut_inputs(void)
|
||||
}
|
||||
}
|
||||
|
||||
// make cluster from LUTs and MUXes
|
||||
void GowinImpl::pack_wideluts(void)
|
||||
{
|
||||
// children's offsets
|
||||
struct _children
|
||||
{
|
||||
IdString port;
|
||||
int dx, dz;
|
||||
} mux_inputs[4][2] = {{{id_I0, 1, -7}, {id_I1, 0, -7}},
|
||||
{{id_I0, 0, 4}, {id_I1, 0, -4}},
|
||||
{{id_I0, 0, 2}, {id_I1, 0, -2}},
|
||||
{{id_I0, 0, -BelZ::MUX20_Z}, {id_I1, 0, 2 - BelZ::MUX20_Z}}};
|
||||
typedef std::function<void(CellInfo &, CellInfo *, int, int)> recurse_func_t;
|
||||
recurse_func_t make_cluster = [&, this](CellInfo &ci_root, CellInfo *ci_cursor, int dx, int dz) {
|
||||
_children *inputs;
|
||||
if (is_lut(ci_cursor)) {
|
||||
return;
|
||||
}
|
||||
switch (ci_cursor->type.hash()) {
|
||||
case ID_MUX2_LUT8:
|
||||
inputs = mux_inputs[0];
|
||||
break;
|
||||
case ID_MUX2_LUT7:
|
||||
inputs = mux_inputs[1];
|
||||
break;
|
||||
case ID_MUX2_LUT6:
|
||||
inputs = mux_inputs[2];
|
||||
break;
|
||||
case ID_MUX2_LUT5:
|
||||
inputs = mux_inputs[3];
|
||||
break;
|
||||
default:
|
||||
log_error("Bad MUX2 node:%s\n", ctx->nameOf(ci_cursor));
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// input src
|
||||
NetInfo *in = ci_cursor->getPort(inputs[i].port);
|
||||
NPNR_ASSERT(in && in->driver.cell);
|
||||
int child_dx = dx + inputs[i].dx;
|
||||
int child_dz = dz + inputs[i].dz;
|
||||
ci_root.constr_children.push_back(in->driver.cell);
|
||||
in->driver.cell->cluster = ci_root.name;
|
||||
in->driver.cell->constr_abs_z = false;
|
||||
in->driver.cell->constr_x = child_dx;
|
||||
in->driver.cell->constr_y = 0;
|
||||
in->driver.cell->constr_z = child_dz;
|
||||
make_cluster(ci_root, in->driver.cell, child_dx, child_dz);
|
||||
}
|
||||
};
|
||||
|
||||
// look for MUX2
|
||||
// MUX2_LUT8 create right away, collect others
|
||||
std::vector<IdString> muxes[3];
|
||||
int packed[4] = {0, 0, 0, 0};
|
||||
for (auto &cell : ctx->cells) {
|
||||
auto &ci = *cell.second;
|
||||
if (ci.cluster != ClusterId()) {
|
||||
continue;
|
||||
}
|
||||
if (ci.type == id_MUX2_LUT8) {
|
||||
ci.cluster = ci.name;
|
||||
ci.constr_abs_z = 0;
|
||||
make_cluster(ci, &ci, 0, 0);
|
||||
++packed[0];
|
||||
continue;
|
||||
}
|
||||
if (ci.type.in(id_MUX2_LUT7, id_MUX2_LUT6, id_MUX2_LUT5)) {
|
||||
switch (ci.type.hash()) {
|
||||
case ID_MUX2_LUT7:
|
||||
muxes[0].push_back(cell.first);
|
||||
break;
|
||||
case ID_MUX2_LUT6:
|
||||
muxes[1].push_back(cell.first);
|
||||
break;
|
||||
default: // ID_MUX2_LUT5
|
||||
muxes[2].push_back(cell.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// create others
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (IdString cell_name : muxes[i]) {
|
||||
auto &ci = *ctx->cells.at(cell_name);
|
||||
if (ci.cluster != ClusterId()) {
|
||||
continue;
|
||||
}
|
||||
ci.cluster = ci.name;
|
||||
ci.constr_abs_z = 0;
|
||||
make_cluster(ci, &ci, 0, 0);
|
||||
++packed[i + 1];
|
||||
}
|
||||
}
|
||||
log_info("Packed MUX2_LUT8:%d, MUX2_LU7:%d, MUX2_LUT6:%d, MUX2_LUT5:%d\n", packed[0], packed[1], packed[2],
|
||||
packed[3]);
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -45,6 +45,8 @@ struct GowinImpl : HimbaechelAPI
|
||||
// modify LUTs with constant inputs
|
||||
void mod_lut_inputs(void);
|
||||
|
||||
void pack_wideluts(void);
|
||||
|
||||
// Return true if a cell is a LUT
|
||||
inline bool type_is_lut(IdString cell_type) const { return cell_type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4); }
|
||||
inline bool is_lut(const CellInfo *cell) const { return type_is_lut(cell->type); }
|
||||
@ -68,5 +70,21 @@ struct GowinArch : HimbaechelArch
|
||||
} exampleArch;
|
||||
} // namespace
|
||||
|
||||
// Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator
|
||||
namespace BelZ {
|
||||
enum
|
||||
{
|
||||
LUT0_Z = 0,
|
||||
LUT7_Z = 14,
|
||||
MUX20_Z = 16,
|
||||
MUX21_Z = 18,
|
||||
MUX23_Z = 22,
|
||||
MUX27_Z = 29,
|
||||
|
||||
VCC_Z = 277,
|
||||
VSS_Z = 278
|
||||
};
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
||||
|
@ -12,8 +12,27 @@ from himbaechel_dbgen.chip import *
|
||||
from apycula import chipdb
|
||||
|
||||
# Z of the bels
|
||||
VCC_Z = 277
|
||||
GND_Z = 288
|
||||
# sync with C++ part!
|
||||
LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1
|
||||
LUT7_Z = 14
|
||||
MUX20_Z = 16
|
||||
MUX21_Z = 18
|
||||
MUX23_Z = 22
|
||||
MUX27_Z = 29
|
||||
|
||||
VCC_Z = 277
|
||||
GND_Z = 278
|
||||
|
||||
@dataclass
|
||||
class TileExtraData(BBAStruct):
|
||||
tile_class: IdString # The general functionality of the slightly different tiles,
|
||||
# let's say the behavior of LUT+DFF in the tiles are completely identical,
|
||||
# but one of them also contains clock-wire switches,
|
||||
# then we assign them to the same LOGIC class.
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
pass
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
bba.u32(self.tile_class.index)
|
||||
|
||||
created_tiletypes = set()
|
||||
|
||||
@ -72,6 +91,10 @@ def create_nodes(chip: Chip, db: chipdb):
|
||||
nodes.append([NodeWire(x, y, f'{d}8{i}0'),
|
||||
NodeWire(*uturn(db, x + offs[0] * 4, y + offs[1] * 4, f'{d}8{i}4')),
|
||||
NodeWire(*uturn(db, x + offs[0] * 8, y + offs[1] * 8, f'{d}8{i}8'))])
|
||||
# I0 for MUX2_LUT8
|
||||
if x < X - 1 and chip.tile_type_at(x, y).extra_data.tile_class == chip.strs.id('LOGIC') and chip.tile_type_at(x, y).extra_data.tile_class == chip.strs.id('LOGIC'):
|
||||
nodes.append([NodeWire(x, y, 'OF30'),
|
||||
NodeWire(x + 1, y, 'OF3')])
|
||||
for node in nodes:
|
||||
chip.add_node(node)
|
||||
# VCC and VSS sources in the all tiles
|
||||
@ -99,7 +122,9 @@ 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):
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp
|
||||
tt = chip.create_tile_type(f"NULL_{ttyp}")
|
||||
typename = "NULL"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return ttyp
|
||||
|
||||
@ -107,7 +132,9 @@ def create_null_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:
|
||||
return ttyp
|
||||
tt = chip.create_tile_type(f"CORNER_{ttyp}")
|
||||
typename = "CORNER"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
if x == 0 and y == 0:
|
||||
# GND is the logic low level generator
|
||||
@ -126,7 +153,10 @@ def create_corner_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:
|
||||
return ttyp
|
||||
tt = chip.create_tile_type(f"IO_{ttyp}")
|
||||
typename = "IO"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
for i in range(2):
|
||||
name = ['IOBA', 'IOBB'][i]
|
||||
# wires
|
||||
@ -142,13 +172,15 @@ def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
|
||||
# XXX lut+dff only for now
|
||||
def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
N = 8
|
||||
lut_inputs = ['A', 'B', 'C', 'D']
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp
|
||||
tt = chip.create_tile_type(f"LOGIC_{ttyp}")
|
||||
typename = "LOGIC"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
# setup wires
|
||||
for i in range(N):
|
||||
for i in range(8):
|
||||
for inp_name in lut_inputs:
|
||||
tt.create_wire(f"{inp_name}{i}", "LUT_INPUT")
|
||||
tt.create_wire(f"F{i}", "LUT_OUT")
|
||||
@ -162,9 +194,13 @@ def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
tt.create_wire(f"CLK{j}", "TILE_CLK")
|
||||
tt.create_wire(f"LSR{j}", "TILE_LSR")
|
||||
tt.create_wire(f"CE{j}", "TILE_CE")
|
||||
for j in range(8):
|
||||
tt.create_wire(f"OF{j}", "MUX_OUT")
|
||||
tt.create_wire(f"SEL{j}", "MUX_SEL")
|
||||
tt.create_wire("OF30", "MUX_OUT")
|
||||
|
||||
# create logic cells
|
||||
for i in range(N):
|
||||
for i in range(8):
|
||||
# LUT
|
||||
lut = tt.create_bel(f"LUT{i}", "LUT4", z = (i * 2 + 0))
|
||||
for j, inp_name in enumerate(lut_inputs):
|
||||
@ -186,6 +222,30 @@ def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
tt.add_bel_pin(ff, "PRESET", f"LSR{i // 2}", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "CLEAR", f"LSR{i // 2}", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "CE", f"CE{i // 2}", PinType.INPUT)
|
||||
# wide luts
|
||||
for i in range(4):
|
||||
ff = tt.create_bel(f"MUX{i * 2}", "MUX2_LUT5", z = MUX20_Z + i * 4)
|
||||
tt.add_bel_pin(ff, "I0", f"F{i * 2}", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "I1", f"F{i * 2 + 1}", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "O", f"OF{i * 2}", PinType.OUTPUT)
|
||||
tt.add_bel_pin(ff, "S0", f"SEL{i * 2}", PinType.INPUT)
|
||||
for i in range(2):
|
||||
ff = tt.create_bel(f"MUX{i * 4 + 1}", "MUX2_LUT6", z = MUX21_Z + i * 8)
|
||||
tt.add_bel_pin(ff, "I0", f"OF{i * 4 + 2}", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "I1", f"OF{i * 4}", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "O", f"OF{i * 4 + 1}", PinType.OUTPUT)
|
||||
tt.add_bel_pin(ff, "S0", f"SEL{i * 4 + 1}", PinType.INPUT)
|
||||
ff = tt.create_bel(f"MUX3", "MUX2_LUT7", z = MUX23_Z)
|
||||
tt.add_bel_pin(ff, "I0", f"OF5", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "I1", f"OF1", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "O", f"OF3", PinType.OUTPUT)
|
||||
tt.add_bel_pin(ff, "S0", f"SEL3", PinType.INPUT)
|
||||
ff = tt.create_bel(f"MUX7", "MUX2_LUT8", z = MUX27_Z)
|
||||
tt.add_bel_pin(ff, "I0", f"OF30", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "I1", f"OF3", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, "O", f"OF7", PinType.OUTPUT)
|
||||
tt.add_bel_pin(ff, "S0", f"SEL7", PinType.INPUT)
|
||||
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return ttyp
|
||||
|
||||
@ -212,7 +272,7 @@ def main():
|
||||
# these differences (in case it turns out later that there is a slightly
|
||||
# different routing or something like that).
|
||||
logic_tiletypes = {12, 13, 14, 15, 16, 17}
|
||||
io_tiletypes = {53, 58, 64} # Tangnano9k leds tiles and clock ;)
|
||||
io_tiletypes = {53, 55, 58, 59, 64, 65}
|
||||
# Setup tile grid
|
||||
for x in range(X):
|
||||
for y in range(Y):
|
||||
|
Loading…
Reference in New Issue
Block a user