diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 055c6dc20d..a33dc5791a 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1313,6 +1313,9 @@ X(LSREN) // EMCU X(EMCU) +// I3C +X(I3C_IOBUF) + // MIPI X(IL) X(OH) diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index c9a0781b59..b064c3622a 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -36,6 +36,10 @@ inline bool is_diffio(const CellInfo *cell) { return type_is_diffio(cell->type); inline bool type_is_mipi(IdString cell_type) { return cell_type.in(id_MIPI_OBUF, id_MIPI_OBUF_A, id_MIPI_IBUF); } inline bool is_mipi(const CellInfo *cell) { return type_is_mipi(cell->type); } +// I3C +inline bool type_is_i3c(IdString cell_type) { return cell_type.in(id_I3C_IOBUF); } +inline bool is_i3c(const CellInfo *cell) { return type_is_i3c(cell->type); } + // IOLOGIC input and output separately inline bool type_is_iologico(IdString cell_type) @@ -106,6 +110,7 @@ NPNR_PACKED_STRUCT(struct Tile_extra_data_POD { int32_t class_id; int16_t io16_x_off; int16_t io16_y_off; + int32_t i3c_capable; }); NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD { diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 9bd27e0059..3bb7fad596 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -126,6 +126,7 @@ class TileExtraData(BBAStruct): # then we assign them to the same LOGIC class. io16_x_off: int = 0 # OSER16/IDES16 offsets to the aux cell io16_y_off: int = 0 + i3c_capable: int = 0 # IO can be used as I3C def serialise_lists(self, context: str, bba: BBAWriter): pass @@ -133,6 +134,7 @@ def serialise(self, context: str, bba: BBAWriter): bba.u32(self.tile_class.index) bba.u16(self.io16_x_off) bba.u16(self.io16_y_off) + bba.u32(self.i3c_capable) @dataclass class BottomIOCnd(BBAStruct): @@ -582,6 +584,8 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int): tt.add_bel_pin(bel, port, wire, PinType.OUTPUT) else: tt.add_bel_pin(bel, port, wire, PinType.INPUT) + elif func == 'i3c_capable': + tt.extra_data.i3c_capable = 1 elif func == 'mipi_obuf': bel = tt.create_bel('MIPI_OBUF', 'MIPI_OBUF', MIPIOBUF_Z) elif func == 'mipi_ibuf': diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index ed3571a689..b7ca7aef7c 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -30,6 +30,15 @@ Loc GowinUtils::get_tile_io16_offs(int x, int y) return Loc(extra->io16_x_off, extra->io16_y_off, 0); } +// oser16/ides16 aux cell offsets +bool GowinUtils::get_i3c_capable(int x, int y) +{ + int tile = tile_by_xy(ctx->chip_info, x, y); + const Tile_extra_data_POD *extra = + reinterpret_cast(chip_tile_info(ctx->chip_info, tile).extra_data.get()); + return extra->i3c_capable != 0; +} + // pin functions: GCLKT_4, SSPI_CS, READY etc IdStringList GowinUtils::get_pin_funcs(BelId io_bel) { diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index ff622804f4..40894319c7 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -23,6 +23,7 @@ struct GowinUtils // tile IdString get_tile_class(int x, int y); Loc get_tile_io16_offs(int x, int y); + bool get_i3c_capable(int x, int y); // pin functions: GCLKT_4, SSPI_CS, READY etc IdStringList get_pin_funcs(BelId io_bel); diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index c8b23ef699..b2c67c3017 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -308,6 +308,47 @@ struct GowinPacker } } + // =================================== + // I3C + // =================================== + void pack_i3c(void) + { + log_info("Pack I3C IOs...\n"); + std::vector cells_to_remove; + + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!is_i3c(&ci)) { + continue; + } + // check for I3C-capable pin A + CellInfo *iob = net_only_drives(ctx, ci.ports.at(id_IO).net, is_iob, id_I); + if (iob == nullptr || iob->bel == BelId()) { + log_error("I3C %s IO is not connected to the input pin or the pin is not constrained.\n", + ctx->nameOf(&ci)); + } + BelId iob_bel = iob->bel; + Loc iob_loc = ctx->getBelLocation(iob_bel); + + if (!gwu.get_i3c_capable(iob_loc.x, iob_loc.y)) { + log_error("Can't place %s. Not I3C capable X%dY%d.\n", ctx->nameOf(&ci), iob_loc.x, iob_loc.y); + } + ci.disconnectPort(id_IO); + iob->disconnectPort(id_I); + ci.movePortTo(id_I, iob, id_I); + ci.movePortTo(id_O, iob, id_O); + iob->disconnectPort(id_OEN); + ci.movePortTo(id_MODESEL, iob, id_OEN); + + iob->setParam(id_I3C_IOBUF, 1); + cells_to_remove.push_back(ci.name); + } + + for (auto cell : cells_to_remove) { + ctx->cells.erase(cell); + } + } + // =================================== // MIPI IO // =================================== @@ -330,6 +371,9 @@ struct GowinPacker log_error("MIPI %s is not connected to the output pin or the pin is not constrained.\n", ctx->nameOf(&ci)); } + if (out_iob->params.count(id_I3C_IOBUF)) { + log_error("Can't place MIPI %s. Conflict with I3C %s.\n", ctx->nameOf(&ci), ctx->nameOf(out_iob)); + } BelId iob_bel = out_iob->bel; Loc iob_loc = ctx->getBelLocation(iob_bel); iob_loc.z = BelZ::MIPIOBUF_Z; @@ -383,6 +427,9 @@ struct GowinPacker ctx->nameOf(&ci)); } // check A IO placing + if (in_iob->params.count(id_I3C_IOBUF)) { + log_error("Can't place MIPI %s. Conflict with I3C %s.\n", ctx->nameOf(&ci), ctx->nameOf(in_iob)); + } BelId iob_bel = in_iob->bel; Loc iob_loc = ctx->getBelLocation(iob_bel); if (iob_loc.z != BelZ::IOBA_Z) { @@ -402,6 +449,9 @@ struct GowinPacker ctx->nameOf(&ci)); } // check B IO placing + if (inb_iob->params.count(id_I3C_IOBUF)) { + log_error("Can't place MIPI %s. Conflict with I3C %s.\n", ctx->nameOf(&ci), ctx->nameOf(inb_iob)); + } BelId iobb_bel = inb_iob->bel; Loc iobb_loc = ctx->getBelLocation(iobb_bel); if (iobb_loc.z != BelZ::IOBB_Z || iobb_loc.x != iob_loc.x || iobb_loc.y != iob_loc.y) { @@ -4108,6 +4158,9 @@ struct GowinPacker pack_iobs(); ctx->check(); + pack_i3c(); + ctx->check(); + pack_mipi(); ctx->check();