gowin: Himbaechel. Add OSER16 and IDES16

Information about what function (main or auxiliary) the cell performs in
these primitives is transmitted through the tile's extra data. And this
also allows us to remove the calculation of the coordinates of the
auxiliary cell on the go.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2023-08-13 22:05:18 +10:00 committed by myrtle
parent a823543932
commit 0994e11b73
6 changed files with 300 additions and 32 deletions

View File

@ -175,7 +175,7 @@ void GowinImpl::postRoute()
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
auto ci = cell.second.get(); auto ci = cell.second.get();
if (is_iologic(ci) && !ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC)) { if (ci->type == id_IOLOGIC || (is_iologic(ci) && !ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC))) {
if (visited_hclk_users.find(ci->name) == visited_hclk_users.end()) { if (visited_hclk_users.find(ci->name) == visited_hclk_users.end()) {
// mark FCLK<-HCLK connections // mark FCLK<-HCLK connections
const NetInfo *h_net = ci->getPort(id_FCLK); const NetInfo *h_net = ci->getPort(id_FCLK);
@ -192,6 +192,9 @@ void GowinImpl::postRoute()
IdString up_wire_name = ctx->getWireName(ctx->getPipSrcWire(up_pip))[1]; IdString up_wire_name = ctx->getWireName(ctx->getPipSrcWire(up_pip))[1];
if (up_wire_name.in(id_HCLK_OUT0, id_HCLK_OUT1, id_HCLK_OUT2, id_HCLK_OUT3)) { if (up_wire_name.in(id_HCLK_OUT0, id_HCLK_OUT1, id_HCLK_OUT2, id_HCLK_OUT3)) {
user.cell->setAttr(id_IOLOGIC_FCLK, Property(up_wire_name.str(ctx))); user.cell->setAttr(id_IOLOGIC_FCLK, Property(up_wire_name.str(ctx)));
if (ctx->debug) {
log_info("set IOLOGIC_FCLK to %s\n", up_wire_name.c_str(ctx));
}
} }
if (ctx->debug) { if (ctx->debug) {
log_info("HCLK user cell:%s, port:%s, wire:%s, pip:%s, up wire:%s\n", log_info("HCLK user cell:%s, port:%s, wire:%s, pip:%s, up wire:%s\n",

View File

@ -39,7 +39,15 @@ inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(cell->type
inline bool type_is_ssram(IdString cell_type) { return cell_type.in(id_RAM16SDP1, id_RAM16SDP2, id_RAM16SDP4); } 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); } inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); }
// ==========================================
// extra data in the chip db // extra data in the chip db
// ==========================================
NPNR_PACKED_STRUCT(struct Tile_extra_data_POD {
int32_t class_id;
int16_t io16_x_off;
int16_t io16_y_off;
});
NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD { NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD {
int32_t wire_a_net; int32_t wire_a_net;
int32_t wire_b_net; int32_t wire_b_net;
@ -77,6 +85,8 @@ enum
IOBB_Z = 51, // +IOBC...IOBL IOBB_Z = 51, // +IOBC...IOBL
IOLOGICA_Z = 70, IOLOGICA_Z = 70,
IDES16_Z = 72,
OSER16_Z = 73,
OSC_Z = 274, OSC_Z = 274,
PLL_Z = 275, PLL_Z = 275,

View File

@ -29,6 +29,8 @@ IOBA_Z = 50
IOBB_Z = 51 IOBB_Z = 51
IOLOGICA_Z = 70 IOLOGICA_Z = 70
IDES16_Z = 72
OSER16_Z = 73
OSC_Z = 274 OSC_Z = 274
PLL_Z = 275 PLL_Z = 275
@ -45,10 +47,15 @@ class TileExtraData(BBAStruct):
# let's say the behavior of LUT+DFF in the tiles are completely identical, # let's say the behavior of LUT+DFF in the tiles are completely identical,
# but one of them also contains clock-wire switches, # but one of them also contains clock-wire switches,
# 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_y_off: int = 0
def serialise_lists(self, context: str, bba: BBAWriter): def serialise_lists(self, context: str, bba: BBAWriter):
pass pass
def serialise(self, context: str, bba: BBAWriter): def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.tile_class.index) bba.u32(self.tile_class.index)
bba.u16(self.io16_x_off)
bba.u16(self.io16_y_off)
@dataclass @dataclass
class BottomIOCnd(BBAStruct): class BottomIOCnd(BBAStruct):
@ -262,6 +269,28 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
tt.create_wire(wire, "GSRI") tt.create_wire(wire, "GSRI")
bel = tt.create_bel("GSR", "GSR", z = GSR_Z) bel = tt.create_bel("GSR", "GSR", z = GSR_Z)
tt.add_bel_pin(bel, "GSRI", wire, PinType.INPUT) tt.add_bel_pin(bel, "GSRI", wire, PinType.INPUT)
if func == 'io16':
role = desc['role']
if role == 'MAIN':
y_off, x_off = desc['pair']
tt.extra_data.io16_x_off = x_off
tt.extra_data.io16_y_off = y_off
for io_type, z in {('IDES16', IDES16_Z), ('OSER16', OSER16_Z)}:
bel = tt.create_bel(io_type, io_type, z = z)
portmap = db.grid[y][x].bels[io_type].portmap
for port, wire in portmap.items():
if port == 'FCLK': # XXX compatibility
wire = 'FCLKA'
if not tt.has_wire(wire):
if port in {'CLK', 'PCLK'}:
tt.create_wire(wire, "TILE_CLK")
else:
tt.create_wire(wire, "IOL_PORT")
if 'OUT' in port:
tt.add_bel_pin(bel, port, wire, PinType.OUTPUT)
else:
tt.add_bel_pin(bel, port, wire, PinType.INPUT)
def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int): def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
has_extra_func = (y, x) in db.extra_func has_extra_func = (y, x) in db.extra_func

View File

@ -11,6 +11,24 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
// tile extra data
IdString GowinUtils::get_tile_class(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 IdString(extra->class_id);
}
// oser16/ides16 aux cell offsets
Loc GowinUtils::get_tile_io16_offs(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 Loc(extra->io16_x_off, extra->io16_y_off, 0);
}
// pin functions: GCLKT_4, SSPI_CS, READY etc // pin functions: GCLKT_4, SSPI_CS, READY etc
IdStringList GowinUtils::get_pin_funcs(BelId bel) IdStringList GowinUtils::get_pin_funcs(BelId bel)
{ {

View File

@ -19,6 +19,10 @@ struct GowinUtils
void init(Context *ctx) { this->ctx = ctx; } void init(Context *ctx) { this->ctx = ctx; }
// tile
IdString get_tile_class(int x, int y);
Loc get_tile_io16_offs(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 bel); IdStringList get_pin_funcs(BelId bel);

View File

@ -332,7 +332,19 @@ struct GowinPacker
return ctx->getBelByLocation(loc); return ctx->getBelByLocation(loc);
} }
void pack_bi_output_iol(CellInfo &ci, std::vector<IdString> &cells_to_remove, std::vector<IdString> &nets_to_remove) void check_iologic_placement(CellInfo &ci, Loc iob_loc, int diff /* 1 - diff */)
{
if (ci.type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC) || diff) {
return;
}
BelId l_bel = ctx->getBelByLocation(Loc(iob_loc.x, iob_loc.y, BelZ::IOBA_Z + 1 - (iob_loc.z - BelZ::IOBA_Z)));
if (!ctx->checkBelAvail(l_bel)) {
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
ctx->nameOfBel(l_bel), ctx->nameOf(ctx->getBoundBelCell(l_bel)));
}
}
void pack_bi_output_iol(CellInfo &ci, std::vector<IdString> &nets_to_remove)
{ {
// These primitives have an additional pin to control the tri-state iob - Q1. // These primitives have an additional pin to control the tri-state iob - Q1.
IdString out_port = id_Q0; IdString out_port = id_Q0;
@ -346,6 +358,9 @@ struct GowinPacker
if (l_bel == BelId()) { if (l_bel == BelId()) {
log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel)); log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel));
} }
// mark IOB as used by IOLOGIC
out_iob->setParam(id_IOLOGIC_IOB, 1);
check_iologic_placement(ci, ctx->getBelLocation(iob_bel), out_iob->attrs.count(id_DIFF_TYPE));
if (!ctx->checkBelAvail(l_bel)) { if (!ctx->checkBelAvail(l_bel)) {
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
@ -367,8 +382,6 @@ struct GowinPacker
} }
ci.setParam(ctx->id("OUTMODE"), out_mode); ci.setParam(ctx->id("OUTMODE"), out_mode);
// mark IOB as used by IOLOGIC
out_iob->setParam(id_IOLOGIC_IOB, 1);
// disconnect Q output: it is wired internally // disconnect Q output: it is wired internally
nets_to_remove.push_back(ci.getPort(out_port)->name); nets_to_remove.push_back(ci.getPort(out_port)->name);
out_iob->disconnectPort(id_I); out_iob->disconnectPort(id_I);
@ -390,8 +403,7 @@ struct GowinPacker
make_iob_nets(*out_iob); make_iob_nets(*out_iob);
} }
void pack_single_output_iol(CellInfo &ci, std::vector<IdString> &cells_to_remove, void pack_single_output_iol(CellInfo &ci, std::vector<IdString> &nets_to_remove)
std::vector<IdString> &nets_to_remove)
{ {
IdString out_port = id_Q; IdString out_port = id_Q;
@ -403,6 +415,9 @@ struct GowinPacker
if (l_bel == BelId()) { if (l_bel == BelId()) {
log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel)); log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel));
} }
// mark IOB as used by IOLOGIC
out_iob->setParam(id_IOLOGIC_IOB, 1);
check_iologic_placement(ci, ctx->getBelLocation(iob_bel), out_iob->attrs.count(id_DIFF_TYPE));
if (!ctx->checkBelAvail(l_bel)) { if (!ctx->checkBelAvail(l_bel)) {
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
@ -420,8 +435,6 @@ struct GowinPacker
} }
ci.setParam(ctx->id("OUTMODE"), out_mode); ci.setParam(ctx->id("OUTMODE"), out_mode);
// mark IOB as used by IOLOGIC
out_iob->setParam(id_IOLOGIC_IOB, 1);
// disconnect Q output: it is wired internally // disconnect Q output: it is wired internally
nets_to_remove.push_back(ci.getPort(out_port)->name); nets_to_remove.push_back(ci.getPort(out_port)->name);
out_iob->disconnectPort(id_I); out_iob->disconnectPort(id_I);
@ -451,12 +464,12 @@ struct GowinPacker
bool is_diff_io(BelId bel) { return ctx->getBoundBelCell(bel)->attrs.count(id_DIFF_TYPE) != 0; } bool is_diff_io(BelId bel) { return ctx->getBoundBelCell(bel)->attrs.count(id_DIFF_TYPE) != 0; }
void create_aux_iologic_cells(CellInfo &ci, IdString mode) CellInfo *create_aux_iologic_cell(CellInfo &ci, IdString mode, bool io16 = false, int idx = 0)
{ {
if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_IDDR, id_IDDRC, id_IDES4)) { if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_IDDR, id_IDDRC, id_IDES4)) {
return; return nullptr;
} }
IdString aux_name = create_aux_iologic_name(ci.name); IdString aux_name = create_aux_iologic_name(ci.name, idx);
BelId bel = get_aux_iologic_bel(ci); BelId bel = get_aux_iologic_bel(ci);
BelId io_bel = gwu.get_io_bel_from_iologic(bel); BelId io_bel = gwu.get_io_bel_from_iologic(bel);
if (!ctx->checkBelAvail(io_bel)) { if (!ctx->checkBelAvail(io_bel)) {
@ -467,15 +480,18 @@ struct GowinPacker
} }
ctx->createCell(aux_name, id_IOLOGIC_DUMMY); ctx->createCell(aux_name, id_IOLOGIC_DUMMY);
CellInfo *aux = ctx->cells[aux_name].get(); CellInfo *aux = ctx->cells.at(aux_name).get();
aux->addInput(id_PCLK); ci.copyPortTo(id_PCLK, aux, id_PCLK);
ci.copyPortTo(id_PCLK, ctx->cells.at(aux_name).get(), id_PCLK); ci.copyPortTo(id_RESET, aux, id_RESET);
aux->addInput(id_RESET); if (io16) {
ci.copyPortTo(id_RESET, ctx->cells.at(aux_name).get(), id_RESET); aux->setParam(mode, Property("DDRENABLE16"));
ctx->cells.at(aux_name)->setParam(mode, Property("DDRENABLE")); } else {
ctx->cells.at(aux_name)->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY")); aux->setParam(mode, Property("DDRENABLE"));
ctx->cells.at(aux_name)->setAttr(ctx->id("MAIN_CELL"), Property(ci.name.str(ctx))); }
aux->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY"));
aux->setAttr(ctx->id("MAIN_CELL"), Property(ci.name.str(ctx)));
ctx->bindBel(bel, aux, PlaceStrength::STRENGTH_LOCKED); ctx->bindBel(bel, aux, PlaceStrength::STRENGTH_LOCKED);
return aux;
} }
void reconnect_ides_outs(CellInfo *ci) void reconnect_ides_outs(CellInfo *ci)
@ -516,7 +532,7 @@ struct GowinPacker
} }
} }
void pack_ides_iol(CellInfo &ci, std::vector<IdString> &cells_to_remove, std::vector<IdString> &nets_to_remove) void pack_ides_iol(CellInfo &ci, std::vector<IdString> &nets_to_remove)
{ {
IdString in_port = id_D; IdString in_port = id_D;
@ -528,6 +544,9 @@ struct GowinPacker
if (l_bel == BelId()) { if (l_bel == BelId()) {
log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel)); log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel));
} }
// mark IOB as used by IOLOGIC
in_iob->setParam(id_IOLOGIC_IOB, 1);
check_iologic_placement(ci, ctx->getBelLocation(iob_bel), in_iob->attrs.count(id_DIFF_TYPE));
if (!ctx->checkBelAvail(l_bel)) { if (!ctx->checkBelAvail(l_bel)) {
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
@ -555,9 +574,7 @@ struct GowinPacker
} }
ci.setParam(ctx->id("INMODE"), in_mode); ci.setParam(ctx->id("INMODE"), in_mode);
// mark IOB as used by IOLOGIC // disconnect D input: it is wired internally
in_iob->setParam(id_IOLOGIC_IOB, 1);
// disconnect Q output: it is wired internally
nets_to_remove.push_back(ci.getPort(in_port)->name); nets_to_remove.push_back(ci.getPort(in_port)->name);
in_iob->disconnectPort(id_O); in_iob->disconnectPort(id_O);
ci.disconnectPort(in_port); ci.disconnectPort(in_port);
@ -570,7 +587,7 @@ struct GowinPacker
void pack_iologic() void pack_iologic()
{ {
log_info("Pack IO logic...\n"); log_info("Pack IO logic...\n");
std::vector<IdString> cells_to_remove, nets_to_remove; std::vector<IdString> nets_to_remove;
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second; CellInfo &ci = *cell.second;
@ -581,29 +598,213 @@ struct GowinPacker
log_info("pack %s of type %s.\n", ctx->nameOf(&ci), ci.type.c_str(ctx)); log_info("pack %s of type %s.\n", ctx->nameOf(&ci), ci.type.c_str(ctx));
} }
if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8)) { if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8)) {
pack_bi_output_iol(ci, cells_to_remove, nets_to_remove); pack_bi_output_iol(ci, nets_to_remove);
create_aux_iologic_cells(ci, ctx->id("OUTMODE")); create_aux_iologic_cell(ci, ctx->id("OUTMODE"));
continue; continue;
} }
if (ci.type.in(id_OVIDEO, id_OSER10)) { if (ci.type.in(id_OVIDEO, id_OSER10)) {
pack_single_output_iol(ci, cells_to_remove, nets_to_remove); pack_single_output_iol(ci, nets_to_remove);
create_aux_iologic_cells(ci, ctx->id("OUTMODE")); create_aux_iologic_cell(ci, ctx->id("OUTMODE"));
continue; continue;
} }
if (ci.type.in(id_IDDR, id_IDDRC, id_IDES4, id_IDES8, id_IDES10, id_IVIDEO)) { if (ci.type.in(id_IDDR, id_IDDRC, id_IDES4, id_IDES8, id_IDES10, id_IVIDEO)) {
pack_ides_iol(ci, cells_to_remove, nets_to_remove); pack_ides_iol(ci, nets_to_remove);
create_aux_iologic_cells(ci, ctx->id("INMODE")); create_aux_iologic_cell(ci, ctx->id("INMODE"));
continue; continue;
} }
} }
for (auto cell : cells_to_remove) { for (auto net : nets_to_remove) {
ctx->cells.erase(cell); ctx->nets.erase(net);
}
}
// ===================================
// IDES16 / OSER16
// ===================================
void check_io16_placement(CellInfo &ci, Loc main_loc, Loc aux_off, int diff /* 1 - diff */)
{
int mod[][3] = {{0, 0, 1}, {1, 1, 0}, {1, 1, 1}};
for (int i = diff; i < 3; ++i) {
Loc aux_loc(main_loc.x + mod[i][0] * aux_off.x, main_loc.y + mod[i][1] * aux_off.y, main_loc.z + mod[i][2]);
BelId l_bel = ctx->getBelByLocation(aux_loc);
if (!ctx->checkBelAvail(l_bel)) {
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
ctx->nameOfBel(l_bel), ctx->nameOf(ctx->getBoundBelCell(l_bel)));
}
}
}
void pack_oser16(CellInfo &ci, std::vector<IdString> &nets_to_remove)
{
IdString out_port = id_Q;
CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(out_port).net, is_iob, id_I, true);
NPNR_ASSERT(out_iob != nullptr && out_iob->bel != BelId());
// mark IOB as used by IOLOGIC
out_iob->setParam(id_IOLOGIC_IOB, 1);
BelId iob_bel = out_iob->bel;
Loc iob_loc = ctx->getBelLocation(iob_bel);
Loc aux_offset = gwu.get_tile_io16_offs(iob_loc.x, iob_loc.y);
if (aux_offset.x == 0 && aux_offset.y == 0) {
log_error("OSER16 %s can not be placed here\n", ctx->nameOf(&ci));
}
check_io16_placement(ci, iob_loc, aux_offset, out_iob->attrs.count(id_DIFF_TYPE));
BelId main_bel = ctx->getBelByLocation(Loc(iob_loc.x, iob_loc.y, BelZ::OSER16_Z));
ctx->bindBel(main_bel, &ci, PlaceStrength::STRENGTH_LOCKED);
// disconnect Q output: it is wired internally
nets_to_remove.push_back(ci.getPort(out_port)->name);
out_iob->disconnectPort(id_I);
ci.disconnectPort(out_port);
// to simplify packaging, the parts of the OSER16 are presented as IOLOGIC cells
// and one of these aux cells is declared as main
IdString main_name = create_aux_iologic_name(ci.name);
IdString aux_name = create_aux_iologic_name(ci.name, 1);
ctx->createCell(aux_name, id_IOLOGIC_DUMMY);
CellInfo *aux = ctx->cells.at(aux_name).get();
aux->setAttr(ctx->id("MAIN_CELL"), Property(main_name.str(ctx)));
aux->setParam(ctx->id("OUTMODE"), Property("ODDRX8"));
aux->setParam(ctx->id("UPDATE"), Property("SAME"));
aux->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY"));
ci.copyPortTo(id_PCLK, aux, id_PCLK);
ci.copyPortTo(id_RESET, aux, id_RESET);
ctx->bindBel(ctx->getBelByLocation(Loc(iob_loc.x, iob_loc.y, BelZ::IOLOGICA_Z)), aux,
PlaceStrength::STRENGTH_LOCKED);
// make aux cell in the first cell
aux = create_aux_iologic_cell(*aux, ctx->id("OUTMODE"), true, 2);
aux->setAttr(ctx->id("MAIN_CELL"), Property(main_name.str(ctx)));
aux->setParam(ctx->id("UPDATE"), Property("SAME"));
// make cell in the next location
ctx->createCell(main_name, id_IOLOGIC);
aux = ctx->cells.at(main_name).get();
aux->setAttr(ctx->id("MAIN_CELL"), Property(main_name.str(ctx)));
aux->setParam(ctx->id("OUTMODE"), Property("DDRENABLE16"));
aux->setParam(ctx->id("UPDATE"), Property("SAME"));
aux->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY"));
ci.copyPortTo(id_PCLK, aux, id_PCLK);
ci.copyPortTo(id_RESET, aux, id_RESET);
ci.movePortTo(id_FCLK, aux, id_FCLK);
ci.movePortTo(id_D12, aux, id_D0);
ci.movePortTo(id_D13, aux, id_D1);
ci.movePortTo(id_D14, aux, id_D2);
ci.movePortTo(id_D15, aux, id_D3);
Loc next_io16(iob_loc.x + aux_offset.x, iob_loc.y + aux_offset.y, BelZ::IOLOGICA_Z);
ctx->bindBel(ctx->getBelByLocation(next_io16), aux, PlaceStrength::STRENGTH_LOCKED);
Loc io_loc = ctx->getBelLocation(iob_bel);
if (io_loc.y == ctx->getGridDimY() - 1) {
config_bottom_row(*out_iob, io_loc, Bottom_io_POD::DDR);
}
make_iob_nets(*out_iob);
}
void pack_ides16(CellInfo &ci, std::vector<IdString> &nets_to_remove)
{
IdString in_port = id_D;
CellInfo *in_iob = net_driven_by(ctx, ci.ports.at(in_port).net, is_iob, id_O);
NPNR_ASSERT(in_iob != nullptr && in_iob->bel != BelId());
// mark IOB as used by IOLOGIC
in_iob->setParam(id_IOLOGIC_IOB, 1);
BelId iob_bel = in_iob->bel;
Loc iob_loc = ctx->getBelLocation(iob_bel);
Loc aux_offset = gwu.get_tile_io16_offs(iob_loc.x, iob_loc.y);
if (aux_offset.x == 0 && aux_offset.y == 0) {
log_error("IDES16 %s can not be placed here\n", ctx->nameOf(&ci));
}
check_io16_placement(ci, iob_loc, aux_offset, in_iob->attrs.count(id_DIFF_TYPE));
BelId main_bel = ctx->getBelByLocation(Loc(iob_loc.x, iob_loc.y, BelZ::IDES16_Z));
ctx->bindBel(main_bel, &ci, PlaceStrength::STRENGTH_LOCKED);
// disconnect Q output: it is wired internally
nets_to_remove.push_back(ci.getPort(in_port)->name);
in_iob->disconnectPort(id_O);
ci.disconnectPort(in_port);
// to simplify packaging, the parts of the IDES16 are presented as IOLOGIC cells
// and one of these aux cells is declared as main
IdString main_name = create_aux_iologic_name(ci.name);
IdString aux_name = create_aux_iologic_name(ci.name, 1);
ctx->createCell(aux_name, id_IOLOGIC_DUMMY);
CellInfo *aux = ctx->cells.at(aux_name).get();
aux->setAttr(ctx->id("MAIN_CELL"), Property(main_name.str(ctx)));
aux->setParam(ctx->id("INMODE"), Property("IDDRX8"));
aux->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY"));
ci.copyPortTo(id_PCLK, aux, id_PCLK);
ci.copyPortTo(id_RESET, aux, id_RESET);
ctx->bindBel(ctx->getBelByLocation(Loc(iob_loc.x, iob_loc.y, BelZ::IOLOGICA_Z)), aux,
PlaceStrength::STRENGTH_LOCKED);
// make aux cell in the first cell
aux = create_aux_iologic_cell(*aux, ctx->id("INMODE"), true, 2);
aux->setAttr(ctx->id("MAIN_CELL"), Property(main_name.str(ctx)));
ci.copyPortTo(id_CALIB, aux, id_CALIB);
// make cell in the next location
ctx->createCell(main_name, id_IOLOGIC);
aux = ctx->cells.at(main_name).get();
aux->setAttr(ctx->id("MAIN_CELL"), Property(main_name.str(ctx)));
aux->setParam(ctx->id("INMODE"), Property("DDRENABLE16"));
aux->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY"));
ci.copyPortTo(id_PCLK, aux, id_PCLK);
ci.copyPortTo(id_RESET, aux, id_RESET);
ci.copyPortTo(id_CALIB, aux, id_CALIB);
ci.movePortTo(id_FCLK, aux, id_FCLK);
ci.movePortTo(id_Q0, aux, id_Q6);
ci.movePortTo(id_Q1, aux, id_Q7);
ci.movePortTo(id_Q2, aux, id_Q8);
ci.movePortTo(id_Q3, aux, id_Q9);
Loc next_io16(iob_loc.x + aux_offset.x, iob_loc.y + aux_offset.y, BelZ::IOLOGICA_Z);
ctx->bindBel(ctx->getBelByLocation(next_io16), aux, PlaceStrength::STRENGTH_LOCKED);
make_iob_nets(*in_iob);
}
void pack_io16(void)
{
std::vector<IdString> nets_to_remove;
log_info("Pack DESER16 logic...\n");
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (ci.type == id_OSER16) {
if (ctx->debug) {
log_info("pack %s of type %s.\n", ctx->nameOf(&ci), ci.type.c_str(ctx));
}
pack_oser16(ci, nets_to_remove);
continue;
}
if (ci.type == id_IDES16) {
if (ctx->debug) {
log_info("pack %s of type %s.\n", ctx->nameOf(&ci), ci.type.c_str(ctx));
}
pack_ides16(ci, nets_to_remove);
continue;
}
} }
for (auto net : nets_to_remove) { for (auto net : nets_to_remove) {
ctx->nets.erase(net); ctx->nets.erase(net);
} }
} }
// =================================== // ===================================
// Constant nets // Constant nets
// =================================== // ===================================
@ -1123,6 +1324,9 @@ struct GowinPacker
pack_iologic(); pack_iologic();
ctx->check(); ctx->check();
pack_io16();
ctx->check();
pack_gsr(); pack_gsr();
ctx->check(); ctx->check();