Gowin. Add I3C io buffer. (#1445)

* Gowin. Add I3C io buffer.

A buffer is added that can operate as a normal IOBUF in PUSH-PULL mode
or switch to open-drain IOBUF mode.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>

* Gowin. Turn a variable into a set of flags

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>

---------

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2025-01-29 23:02:21 +10:00 committed by GitHub
parent a76c5b5a0f
commit 81ccada239
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 80 additions and 0 deletions

View File

@ -1313,6 +1313,9 @@ X(LSREN)
// EMCU
X(EMCU)
// I3C
X(I3C_IOBUF)
// MIPI
X(IL)
X(OH)

View File

@ -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,9 @@ NPNR_PACKED_STRUCT(struct Tile_extra_data_POD {
int32_t class_id;
int16_t io16_x_off;
int16_t io16_y_off;
int32_t tile_flags;
// tile flags
static constexpr int32_t TILE_I3C_CAPABLE_IO = 1;
});
NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD {

View File

@ -21,6 +21,9 @@ CHIP_NEED_BSRAM_OUTREG_FIX = 0x4
CHIP_NEED_BLKSEL_FIX = 0x8
CHIP_HAS_BANDGAP = 0x10
# Tile flags
TILE_I3C_CAPABLE_IO = 0x1
# Z of the bels
# sync with C++ part!
LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1
@ -126,6 +129,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
tile_flags: int = 0
def serialise_lists(self, context: str, bba: BBAWriter):
pass
@ -133,6 +137,7 @@ class TileExtraData(BBAStruct):
bba.u32(self.tile_class.index)
bba.u16(self.io16_x_off)
bba.u16(self.io16_y_off)
bba.u32(self.tile_flags)
@dataclass
class BottomIOCnd(BBAStruct):
@ -582,6 +587,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.tile_flags |= TILE_I3C_CAPABLE_IO
elif func == 'mipi_obuf':
bel = tt.create_bel('MIPI_OBUF', 'MIPI_OBUF', MIPIOBUF_Z)
elif func == 'mipi_ibuf':

View File

@ -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<const Tile_extra_data_POD *>(chip_tile_info(ctx->chip_info, tile).extra_data.get());
return extra->tile_flags & Tile_extra_data_POD::TILE_I3C_CAPABLE_IO;
}
// pin functions: GCLKT_4, SSPI_CS, READY etc
IdStringList GowinUtils::get_pin_funcs(BelId io_bel)
{

View File

@ -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);

View File

@ -308,6 +308,47 @@ struct GowinPacker
}
}
// ===================================
// I3C
// ===================================
void pack_i3c(void)
{
log_info("Pack I3C IOs...\n");
std::vector<IdString> 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();