mirror of https://github.com/YosysHQ/nextpnr.git
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:
parent
a76c5b5a0f
commit
81ccada239
|
|
@ -1313,6 +1313,9 @@ X(LSREN)
|
||||||
// EMCU
|
// EMCU
|
||||||
X(EMCU)
|
X(EMCU)
|
||||||
|
|
||||||
|
// I3C
|
||||||
|
X(I3C_IOBUF)
|
||||||
|
|
||||||
// MIPI
|
// MIPI
|
||||||
X(IL)
|
X(IL)
|
||||||
X(OH)
|
X(OH)
|
||||||
|
|
|
||||||
|
|
@ -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 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); }
|
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
|
// IOLOGIC input and output separately
|
||||||
|
|
||||||
inline bool type_is_iologico(IdString cell_type)
|
inline bool type_is_iologico(IdString cell_type)
|
||||||
|
|
@ -106,6 +110,9 @@ NPNR_PACKED_STRUCT(struct Tile_extra_data_POD {
|
||||||
int32_t class_id;
|
int32_t class_id;
|
||||||
int16_t io16_x_off;
|
int16_t io16_x_off;
|
||||||
int16_t io16_y_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 {
|
NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ CHIP_NEED_BSRAM_OUTREG_FIX = 0x4
|
||||||
CHIP_NEED_BLKSEL_FIX = 0x8
|
CHIP_NEED_BLKSEL_FIX = 0x8
|
||||||
CHIP_HAS_BANDGAP = 0x10
|
CHIP_HAS_BANDGAP = 0x10
|
||||||
|
|
||||||
|
# Tile flags
|
||||||
|
TILE_I3C_CAPABLE_IO = 0x1
|
||||||
|
|
||||||
# Z of the bels
|
# Z of the bels
|
||||||
# sync with C++ part!
|
# sync with C++ part!
|
||||||
LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1
|
LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1
|
||||||
|
|
@ -126,6 +129,7 @@ class TileExtraData(BBAStruct):
|
||||||
# then we assign them to the same LOGIC class.
|
# then we assign them to the same LOGIC class.
|
||||||
io16_x_off: int = 0 # OSER16/IDES16 offsets to the aux cell
|
io16_x_off: int = 0 # OSER16/IDES16 offsets to the aux cell
|
||||||
io16_y_off: int = 0
|
io16_y_off: int = 0
|
||||||
|
tile_flags: int = 0
|
||||||
|
|
||||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
pass
|
pass
|
||||||
|
|
@ -133,6 +137,7 @@ class TileExtraData(BBAStruct):
|
||||||
bba.u32(self.tile_class.index)
|
bba.u32(self.tile_class.index)
|
||||||
bba.u16(self.io16_x_off)
|
bba.u16(self.io16_x_off)
|
||||||
bba.u16(self.io16_y_off)
|
bba.u16(self.io16_y_off)
|
||||||
|
bba.u32(self.tile_flags)
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BottomIOCnd(BBAStruct):
|
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)
|
tt.add_bel_pin(bel, port, wire, PinType.OUTPUT)
|
||||||
else:
|
else:
|
||||||
tt.add_bel_pin(bel, port, wire, PinType.INPUT)
|
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':
|
elif func == 'mipi_obuf':
|
||||||
bel = tt.create_bel('MIPI_OBUF', 'MIPI_OBUF', MIPIOBUF_Z)
|
bel = tt.create_bel('MIPI_OBUF', 'MIPI_OBUF', MIPIOBUF_Z)
|
||||||
elif func == 'mipi_ibuf':
|
elif func == 'mipi_ibuf':
|
||||||
|
|
|
||||||
|
|
@ -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);
|
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
|
// pin functions: GCLKT_4, SSPI_CS, READY etc
|
||||||
IdStringList GowinUtils::get_pin_funcs(BelId io_bel)
|
IdStringList GowinUtils::get_pin_funcs(BelId io_bel)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ struct GowinUtils
|
||||||
// tile
|
// tile
|
||||||
IdString get_tile_class(int x, int y);
|
IdString get_tile_class(int x, int y);
|
||||||
Loc get_tile_io16_offs(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
|
// pin functions: GCLKT_4, SSPI_CS, READY etc
|
||||||
IdStringList get_pin_funcs(BelId io_bel);
|
IdStringList get_pin_funcs(BelId io_bel);
|
||||||
|
|
|
||||||
|
|
@ -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
|
// 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",
|
log_error("MIPI %s is not connected to the output pin or the pin is not constrained.\n",
|
||||||
ctx->nameOf(&ci));
|
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;
|
BelId iob_bel = out_iob->bel;
|
||||||
Loc iob_loc = ctx->getBelLocation(iob_bel);
|
Loc iob_loc = ctx->getBelLocation(iob_bel);
|
||||||
iob_loc.z = BelZ::MIPIOBUF_Z;
|
iob_loc.z = BelZ::MIPIOBUF_Z;
|
||||||
|
|
@ -383,6 +427,9 @@ struct GowinPacker
|
||||||
ctx->nameOf(&ci));
|
ctx->nameOf(&ci));
|
||||||
}
|
}
|
||||||
// check A IO placing
|
// 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;
|
BelId iob_bel = in_iob->bel;
|
||||||
Loc iob_loc = ctx->getBelLocation(iob_bel);
|
Loc iob_loc = ctx->getBelLocation(iob_bel);
|
||||||
if (iob_loc.z != BelZ::IOBA_Z) {
|
if (iob_loc.z != BelZ::IOBA_Z) {
|
||||||
|
|
@ -402,6 +449,9 @@ struct GowinPacker
|
||||||
ctx->nameOf(&ci));
|
ctx->nameOf(&ci));
|
||||||
}
|
}
|
||||||
// check B IO placing
|
// 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;
|
BelId iobb_bel = inb_iob->bel;
|
||||||
Loc iobb_loc = ctx->getBelLocation(iobb_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) {
|
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();
|
pack_iobs();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
pack_i3c();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_mipi();
|
pack_mipi();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue