diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 233fc2ed..239157ea 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -30,7 +30,8 @@ inline bool is_diffio(const CellInfo *cell) { return type_is_diffio(cell->type); inline bool type_is_iologic(IdString cell_type) { - return cell_type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8, id_OSER10, id_OVIDEO); + return cell_type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8, id_OSER10, id_OVIDEO, id_IDDR, id_IDDRC, id_IDES4, + id_IDES8, id_IDES10, id_IVIDEO); } inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(cell->type); } @@ -77,6 +78,7 @@ enum IOLOGICA_Z = 70, + OSC_Z = 274, PLL_Z = 275, GSR_Z = 276, VCC_Z = 277, diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 2f38a960..4e44369b 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -30,6 +30,7 @@ IOBB_Z = 51 IOLOGICA_Z = 70 +OSC_Z = 274 PLL_Z = 275 GSR_Z = 276 VCC_Z = 277 @@ -292,6 +293,19 @@ def create_corner_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int): tt.create_wire(portmap['GSRI'], "GSRI") io = tt.create_bel("GSR", "GSR", z = GSR_Z) tt.add_bel_pin(io, "GSRI", portmap['GSRI'], PinType.INPUT) + if (y, x) in db.extra_cell_func: + funcs = db.extra_cell_func[(y, x)] + if 'osc' in funcs: + osc_type = funcs['osc']['type'] + portmap = db.grid[y][x].bels[osc_type].portmap + for port, wire in portmap.items(): + tt.create_wire(wire, port) + io = tt.create_bel(osc_type, osc_type, z = OSC_Z) + for port, wire in portmap.items(): + if 'OUT' in port: + tt.add_bel_pin(io, port, wire, PinType.OUTPUT) + else: + tt.add_bel_pin(io, port, wire, PinType.INPUT) return (tiletype, tt) diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index 064d3e3a..4ce98106 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -451,9 +451,9 @@ 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) + void create_aux_iologic_cells(CellInfo &ci, IdString mode) { - if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4)) { + if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_IDDR, id_IDDRC, id_IDES4)) { return; } IdString aux_name = create_aux_iologic_name(ci.name); @@ -472,12 +472,101 @@ struct GowinPacker 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(ctx->id("OUTMODE"), Property("DDRENABLE")); + 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))); ctx->bindBel(bel, aux, PlaceStrength::STRENGTH_LOCKED); } + void reconnect_ides_outs(CellInfo *ci) + { + switch (ci->type.hash()) { + case ID_IDDR: /* fall-through*/ + case ID_IDDRC: + ci->renamePort(id_Q1, id_Q9); + ci->renamePort(id_Q0, id_Q8); + break; + case ID_IDES4: + ci->renamePort(id_Q3, id_Q9); + ci->renamePort(id_Q2, id_Q8); + ci->renamePort(id_Q1, id_Q7); + ci->renamePort(id_Q0, id_Q6); + break; + case ID_IVIDEO: + ci->renamePort(id_Q6, id_Q9); + ci->renamePort(id_Q5, id_Q8); + ci->renamePort(id_Q4, id_Q7); + ci->renamePort(id_Q3, id_Q6); + ci->renamePort(id_Q2, id_Q5); + ci->renamePort(id_Q1, id_Q4); + ci->renamePort(id_Q0, id_Q3); + break; + case ID_IDES8: + ci->renamePort(id_Q7, id_Q9); + ci->renamePort(id_Q6, id_Q8); + ci->renamePort(id_Q5, id_Q7); + ci->renamePort(id_Q4, id_Q6); + ci->renamePort(id_Q3, id_Q5); + ci->renamePort(id_Q2, id_Q4); + ci->renamePort(id_Q1, id_Q3); + ci->renamePort(id_Q0, id_Q2); + break; + default: + break; + } + } + + void pack_ides_iol(CellInfo &ci, std::vector &cells_to_remove, std::vector &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()); + BelId iob_bel = in_iob->bel; + + BelId l_bel = get_iologic_bel(in_iob); + if (l_bel == BelId()) { + log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_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), + ctx->nameOfBel(l_bel), ctx->nameOf(ctx->getBoundBelCell(l_bel))); + } + ctx->bindBel(l_bel, &ci, PlaceStrength::STRENGTH_LOCKED); + std::string in_mode; + switch (ci.type.hash()) { + case ID_IDDR: + case ID_IDDRC: + in_mode = "IDDRX1"; + break; + case ID_IDES4: + in_mode = "IDDRX2"; + break; + case ID_IDES8: + in_mode = "IDDRX4"; + break; + case ID_IDES10: + in_mode = "IDDRX5"; + break; + case ID_IVIDEO: + in_mode = "VIDEORX"; + break; + } + 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 + nets_to_remove.push_back(ci.getPort(in_port)->name); + in_iob->disconnectPort(id_O); + ci.disconnectPort(in_port); + set_daaj_nets(ci, iob_bel); + reconnect_ides_outs(&ci); + + make_iob_nets(*in_iob); + } + void pack_iologic() { log_info("Pack IO logic...\n"); @@ -493,12 +582,17 @@ struct GowinPacker } 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); + create_aux_iologic_cells(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); + create_aux_iologic_cells(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")); continue; } }