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:
parent
a823543932
commit
0994e11b73
@ -175,7 +175,7 @@ void GowinImpl::postRoute()
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
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()) {
|
||||
// mark FCLK<-HCLK connections
|
||||
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];
|
||||
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)));
|
||||
if (ctx->debug) {
|
||||
log_info("set IOLOGIC_FCLK to %s\n", up_wire_name.c_str(ctx));
|
||||
}
|
||||
}
|
||||
if (ctx->debug) {
|
||||
log_info("HCLK user cell:%s, port:%s, wire:%s, pip:%s, up wire:%s\n",
|
||||
|
@ -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 is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); }
|
||||
|
||||
// ==========================================
|
||||
// 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 {
|
||||
int32_t wire_a_net;
|
||||
int32_t wire_b_net;
|
||||
@ -77,6 +85,8 @@ enum
|
||||
IOBB_Z = 51, // +IOBC...IOBL
|
||||
|
||||
IOLOGICA_Z = 70,
|
||||
IDES16_Z = 72,
|
||||
OSER16_Z = 73,
|
||||
|
||||
OSC_Z = 274,
|
||||
PLL_Z = 275,
|
||||
|
@ -29,6 +29,8 @@ IOBA_Z = 50
|
||||
IOBB_Z = 51
|
||||
|
||||
IOLOGICA_Z = 70
|
||||
IDES16_Z = 72
|
||||
OSER16_Z = 73
|
||||
|
||||
OSC_Z = 274
|
||||
PLL_Z = 275
|
||||
@ -45,10 +47,15 @@ class TileExtraData(BBAStruct):
|
||||
# let's say the behavior of LUT+DFF in the tiles are completely identical,
|
||||
# but one of them also contains clock-wire switches,
|
||||
# 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):
|
||||
pass
|
||||
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)
|
||||
|
||||
@dataclass
|
||||
class BottomIOCnd(BBAStruct):
|
||||
@ -262,6 +269,28 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
||||
tt.create_wire(wire, "GSRI")
|
||||
bel = tt.create_bel("GSR", "GSR", z = GSR_Z)
|
||||
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):
|
||||
has_extra_func = (y, x) in db.extra_func
|
||||
|
@ -11,6 +11,24 @@
|
||||
|
||||
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
|
||||
IdStringList GowinUtils::get_pin_funcs(BelId bel)
|
||||
{
|
||||
|
@ -19,6 +19,10 @@ struct GowinUtils
|
||||
|
||||
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
|
||||
IdStringList get_pin_funcs(BelId bel);
|
||||
|
||||
|
@ -332,7 +332,19 @@ struct GowinPacker
|
||||
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.
|
||||
IdString out_port = id_Q0;
|
||||
@ -346,6 +358,9 @@ struct GowinPacker
|
||||
if (l_bel == BelId()) {
|
||||
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)) {
|
||||
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);
|
||||
|
||||
// mark IOB as used by IOLOGIC
|
||||
out_iob->setParam(id_IOLOGIC_IOB, 1);
|
||||
// disconnect Q output: it is wired internally
|
||||
nets_to_remove.push_back(ci.getPort(out_port)->name);
|
||||
out_iob->disconnectPort(id_I);
|
||||
@ -390,8 +403,7 @@ struct GowinPacker
|
||||
make_iob_nets(*out_iob);
|
||||
}
|
||||
|
||||
void pack_single_output_iol(CellInfo &ci, std::vector<IdString> &cells_to_remove,
|
||||
std::vector<IdString> &nets_to_remove)
|
||||
void pack_single_output_iol(CellInfo &ci, std::vector<IdString> &nets_to_remove)
|
||||
{
|
||||
IdString out_port = id_Q;
|
||||
|
||||
@ -403,6 +415,9 @@ struct GowinPacker
|
||||
if (l_bel == BelId()) {
|
||||
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)) {
|
||||
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);
|
||||
|
||||
// mark IOB as used by IOLOGIC
|
||||
out_iob->setParam(id_IOLOGIC_IOB, 1);
|
||||
// disconnect Q output: it is wired internally
|
||||
nets_to_remove.push_back(ci.getPort(out_port)->name);
|
||||
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; }
|
||||
|
||||
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)) {
|
||||
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 io_bel = gwu.get_io_bel_from_iologic(bel);
|
||||
if (!ctx->checkBelAvail(io_bel)) {
|
||||
@ -467,15 +480,18 @@ struct GowinPacker
|
||||
}
|
||||
|
||||
ctx->createCell(aux_name, id_IOLOGIC_DUMMY);
|
||||
CellInfo *aux = ctx->cells[aux_name].get();
|
||||
aux->addInput(id_PCLK);
|
||||
ci.copyPortTo(id_PCLK, ctx->cells.at(aux_name).get(), id_PCLK);
|
||||
aux->addInput(id_RESET);
|
||||
ci.copyPortTo(id_RESET, ctx->cells.at(aux_name).get(), id_RESET);
|
||||
ctx->cells.at(aux_name)->setParam(mode, Property("DDRENABLE"));
|
||||
ctx->cells.at(aux_name)->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY"));
|
||||
ctx->cells.at(aux_name)->setAttr(ctx->id("MAIN_CELL"), Property(ci.name.str(ctx)));
|
||||
CellInfo *aux = ctx->cells.at(aux_name).get();
|
||||
ci.copyPortTo(id_PCLK, aux, id_PCLK);
|
||||
ci.copyPortTo(id_RESET, aux, id_RESET);
|
||||
if (io16) {
|
||||
aux->setParam(mode, Property("DDRENABLE16"));
|
||||
} else {
|
||||
aux->setParam(mode, Property("DDRENABLE"));
|
||||
}
|
||||
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);
|
||||
return aux;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -528,6 +544,9 @@ struct GowinPacker
|
||||
if (l_bel == BelId()) {
|
||||
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)) {
|
||||
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);
|
||||
|
||||
// mark IOB as used by IOLOGIC
|
||||
in_iob->setParam(id_IOLOGIC_IOB, 1);
|
||||
// disconnect Q output: it is wired internally
|
||||
// disconnect D input: it is wired internally
|
||||
nets_to_remove.push_back(ci.getPort(in_port)->name);
|
||||
in_iob->disconnectPort(id_O);
|
||||
ci.disconnectPort(in_port);
|
||||
@ -570,7 +587,7 @@ struct GowinPacker
|
||||
void pack_iologic()
|
||||
{
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8)) {
|
||||
pack_bi_output_iol(ci, cells_to_remove, nets_to_remove);
|
||||
create_aux_iologic_cells(ci, ctx->id("OUTMODE"));
|
||||
pack_bi_output_iol(ci, nets_to_remove);
|
||||
create_aux_iologic_cell(ci, ctx->id("OUTMODE"));
|
||||
continue;
|
||||
}
|
||||
if (ci.type.in(id_OVIDEO, id_OSER10)) {
|
||||
pack_single_output_iol(ci, cells_to_remove, nets_to_remove);
|
||||
create_aux_iologic_cells(ci, ctx->id("OUTMODE"));
|
||||
pack_single_output_iol(ci, nets_to_remove);
|
||||
create_aux_iologic_cell(ci, ctx->id("OUTMODE"));
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
create_aux_iologic_cells(ci, ctx->id("INMODE"));
|
||||
pack_ides_iol(ci, nets_to_remove);
|
||||
create_aux_iologic_cell(ci, ctx->id("INMODE"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cell : cells_to_remove) {
|
||||
ctx->cells.erase(cell);
|
||||
for (auto net : nets_to_remove) {
|
||||
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) {
|
||||
ctx->nets.erase(net);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// Constant nets
|
||||
// ===================================
|
||||
@ -1123,6 +1324,9 @@ struct GowinPacker
|
||||
pack_iologic();
|
||||
ctx->check();
|
||||
|
||||
pack_io16();
|
||||
ctx->check();
|
||||
|
||||
pack_gsr();
|
||||
ctx->check();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user