diff --git a/himbaechel/himbaechel_dbgen/chip.py b/himbaechel/himbaechel_dbgen/chip.py index f392cb73..9e4c7c25 100644 --- a/himbaechel/himbaechel_dbgen/chip.py +++ b/himbaechel/himbaechel_dbgen/chip.py @@ -531,6 +531,10 @@ class Chip: tinst.serialise_lists(f"tinst_{x}_{y}", bba) self.strs.serialise_lists(f"constids", bba) + if self.extra_data is not None: + self.extra_data.serialise_lists("extra_data", bba) + bba.label("extra_data") + self.extra_data.serialise("extra_data", bba) bba.label(f"tile_types") for i, tt in enumerate(self.tile_types): @@ -573,8 +577,11 @@ class Chip: bba.u32(0) # db-defined constids bba.ref("constids") - # extra data: not yet used - bba.u32(0) + # extra data + if self.extra_data is not None: + bba.ref("extra_data") + else: + bba.u32(0) def write_bba(self, filename): with open(filename, "w") as f: diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 6cec43f4..b5eb981b 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1060,6 +1060,8 @@ X(router) X(GOWIN_GND) X(GOWIN_VCC) X(PLL) +X(BOTTOM_IO_PORT_A) +X(BOTTOM_IO_PORT_B) // wire types X(GLOBAL_CLK) diff --git a/himbaechel/uarch/gowin/cst.cc b/himbaechel/uarch/gowin/cst.cc index 5b9ddaaa..8821eec8 100644 --- a/himbaechel/uarch/gowin/cst.cc +++ b/himbaechel/uarch/gowin/cst.cc @@ -47,7 +47,7 @@ struct GowinCstReader row = col; col = 1; } - int z = match[3].str()[0] - 'A'; + int z = match[3].str()[0] - 'A' + BelZ::IOBA_Z; return Loc(col - 1, row - 1, z); } diff --git a/himbaechel/uarch/gowin/gowin.cc b/himbaechel/uarch/gowin/gowin.cc index 6c836645..4d12361d 100644 --- a/himbaechel/uarch/gowin/gowin.cc +++ b/himbaechel/uarch/gowin/gowin.cc @@ -146,6 +146,7 @@ void GowinImpl::pack() } gowin_pack(ctx); } + void GowinImpl::prePlace() { assign_cell_info(); } void GowinImpl::postPlace() { diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index fd3c6dd9..2a931480 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -24,6 +24,33 @@ 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); } + +// extra data in the chip db +NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD { + int32_t wire_a_net; + int32_t wire_b_net; +}); + +NPNR_PACKED_STRUCT(struct Bottom_io_POD { + // simple OBUF + static constexpr int8_t NORMAL = 0; + RelSlice conditions; +}); + +NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { Bottom_io_POD bottom_io; }); + +inline IdString get_bottom_io_wire_a_net(const ChipInfoPOD *chip, int8_t condition) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(chip->extra_data.get()); + return IdString(extra->bottom_io.conditions[condition].wire_a_net); +} + +inline IdString get_bottom_io_wire_b_net(const ChipInfoPOD *chip, int8_t condition) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(chip->extra_data.get()); + return IdString(extra->bottom_io.conditions[condition].wire_b_net); +} + } // namespace // Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator @@ -39,6 +66,9 @@ enum ALU0_Z = 30, // :35, 6 ALU RAMW_Z = 36, // RAM16SDP4 + IOBA_Z = 50, + IOBB_Z = 51, // +IOBC...IOBL + PLL_Z = 275, GSR_Z = 276, VCC_Z = 277, diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 38e81275..1fee760b 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -22,11 +22,17 @@ MUX27_Z = 29 ALU0_Z = 30 # : 35, 6 ALUs RAMW_Z = 36 # RAM16SDP4 +IOBA_Z = 50 +IOBB_Z = 51 + PLL_Z = 275 GSR_Z = 276 VCC_Z = 277 GND_Z = 278 +# ======================================= +# Chipdb additional info +# ======================================= @dataclass class TileExtraData(BBAStruct): tile_class: IdString # The general functionality of the slightly different tiles, @@ -38,6 +44,45 @@ class TileExtraData(BBAStruct): def serialise(self, context: str, bba: BBAWriter): bba.u32(self.tile_class.index) +@dataclass +class BottomIOCnd(BBAStruct): + wire_a_net: IdString + wire_b_net: IdString + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u32(self.wire_a_net.index) + bba.u32(self.wire_b_net.index) + +@dataclass +class BottomIO(BBAStruct): + conditions: list[BottomIOCnd] = field(default_factory = list) + + def serialise_lists(self, context: str, bba: BBAWriter): + bba.label(f"{context}_conditions") + for i, cnd in enumerate(self.conditions): + cnd.serialise(f"{context}_cnd{i}", bba) + + def serialise(self, context: str, bba: BBAWriter): + bba.slice(f"{context}_conditions", len(self.conditions)) + +@dataclass +class ChipExtraData(BBAStruct): + strs: StringPool + bottom_io: BottomIO + + def create_bottom_io(self): + self.bottom_io = BottomIO() + + def add_bottom_io_cnd(self, net_a: str, net_b: str): + self.bottom_io.conditions.append(BottomIOCnd(self.strs.id(net_a), self.strs.id(net_b))) + + def serialise_lists(self, context: str, bba: BBAWriter): + self.bottom_io.serialise_lists(f"{context}_bottom_io", bba) + def serialise(self, context: str, bba: BBAWriter): + self.bottom_io.serialise(f"{context}_bottom_io", bba) + created_tiletypes = set() # u-turn at the rim @@ -224,9 +269,17 @@ def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int): tt.create_wire(portmap['I'], "IO_I") tt.create_wire(portmap['O'], "IO_O") # bels - io = tt.create_bel(name, "IOB", z = i) + io = tt.create_bel(name, "IOB", z = IOBA_Z + i) tt.add_bel_pin(io, "I", portmap['I'], PinType.INPUT) tt.add_bel_pin(io, "O", portmap['O'], PinType.OUTPUT) + # bottom io + if 'BOTTOM_IO_PORT_A' in portmap.keys(): + if not tt.has_wire(portmap['BOTTOM_IO_PORT_A']): + tt.create_wire(portmap['BOTTOM_IO_PORT_A'], "IO_I") + tt.create_wire(portmap['BOTTOM_IO_PORT_B'], "IO_I") + tt.add_bel_pin(io, "BOTTOM_IO_PORT_A", portmap['BOTTOM_IO_PORT_A'], PinType.INPUT) + tt.add_bel_pin(io, "BOTTOM_IO_PORT_B", portmap['BOTTOM_IO_PORT_B'], PinType.INPUT) + create_switch_matrix(tt, db, x, y) return (ttyp, tt) @@ -425,6 +478,13 @@ def create_packages(chip: Chip, db: chipdb): bank = int(db.pin_bank[io_loc]) pad = pkg.create_pad(pinno, tile, bel, pad_func, bank) +# Extra chip data +def create_extra_data(chip: Chip, db: chipdb): + chip.extra_data = ChipExtraData(chip.strs, None) + chip.extra_data.create_bottom_io() + for net_a, net_b in db.bottom_io[2]: + chip.extra_data.add_bottom_io_cnd(net_a, net_b) + def main(): parser = argparse.ArgumentParser(description='Make Gowin BBA') parser.add_argument('-d', '--device', required=True) @@ -492,6 +552,7 @@ def main(): # Create nodes between tiles create_nodes(ch, db) + create_extra_data(ch, db) ch.write_bba(args.output) if __name__ == '__main__': main() diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index ff9da757..c91763f5 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -21,9 +21,37 @@ struct GowinPacker // =================================== // IO // =================================== - void pack_iobs(void) + void config_bottom_row(CellInfo &ci, Loc loc, uint8_t cnd = Bottom_io_POD::NORMAL) + { + if (!ci.type.in(id_OBUF, id_TBUF, id_IOBUF)) { + return; + } + if (cnd == Bottom_io_POD::NORMAL && loc.z != BelZ::IOBA_Z) { + return; + } + auto connect_io_wire = [&](IdString port, IdString net_name) { + // XXX it is very convenient that nothing terrible happens in case + // of absence/presence of a port + ci.disconnectPort(port); + ci.addInput(port); + if (net_name == id_VSS) { + ci.connectPort(port, ctx->nets[ctx->id("$PACKER_GND")].get()); + } else { + NPNR_ASSERT(net_name == id_VCC); + ci.connectPort(port, ctx->nets[ctx->id("$PACKER_VCC")].get()); + } + }; + + IdString wire_a_net = get_bottom_io_wire_a_net(ctx->chip_info, cnd); + connect_io_wire(id_BOTTOM_IO_PORT_A, wire_a_net); + + IdString wire_b_net = get_bottom_io_wire_b_net(ctx->chip_info, cnd); + connect_io_wire(id_BOTTOM_IO_PORT_B, wire_b_net); + } + + // Attributes of deleted cells are copied + void trim_nextpnr_iobs(void) { - log_info("Pack IOBs...\n"); // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis const pool top_ports{ CellTypePort(id_IBUF, id_I), @@ -62,6 +90,34 @@ struct GowinPacker ctx->cells.erase(cell_name); } + BelId bind_io(CellInfo &ci) + { + BelId bel = ctx->getBelByName(IdStringList::parse(ctx, ci.attrs.at(id_BEL).as_string())); + ci.unsetAttr(id_BEL); + ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED); + return bel; + } + + void pack_iobs(void) + { + log_info("Pack IOBs...\n"); + trim_nextpnr_iobs(); + + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_IBUF, id_OBUF, id_IOBUF)) // XXX TBUF + continue; + if (ci.attrs.count(id_BEL) == 0) { + log_error("Unconstrained IO:%s\n", ctx->nameOf(&ci)); + } + BelId io_bel = bind_io(ci); + Loc io_loc = ctx->getBelLocation(io_bel); + if (io_loc.y == ctx->getGridDimY() - 1) { + config_bottom_row(ci, io_loc); + } + } + } + // =================================== // Constant nets // =================================== @@ -570,8 +626,8 @@ struct GowinPacker void run(void) { - pack_iobs(); handle_constants(); + pack_iobs(); pack_gsr(); pack_wideluts(); pack_alus();