diff --git a/himbaechel/uarch/gatemate/bitstream.cc b/himbaechel/uarch/gatemate/bitstream.cc index 1d1ec90c..2b0762ba 100644 --- a/himbaechel/uarch/gatemate/bitstream.cc +++ b/himbaechel/uarch/gatemate/bitstream.cc @@ -101,14 +101,17 @@ struct BitstreamBackend cc.configs[0].add_word("GPIO.BANK_W1", int_to_bitvector(1, 1)); cc.configs[0].add_word("GPIO.BANK_W2", int_to_bitvector(1, 1)); for (auto &cell : ctx->cells) { + CfgLoc loc = getConfigLoc(ctx, cell.second.get()->bel.tile); + auto ¶ms = cell.second.get()->params; switch (cell.second->type.index) { - case id_GPIO.index: { - CfgLoc loc = getConfigLoc(ctx, cell.second.get()->bel.tile); - cc.tiles[loc].add_word( - "GPIO.INIT", - str_to_bitvector(str_or_default(cell.second.get()->params, ctx->id("INIT"), ""), 72)); + case id_CC_IBUF.index: + case id_CC_TOBUF.index: + case id_CC_OBUF.index: + case id_CC_IOBUF.index: + for (auto &p : params) { + cc.tiles[loc].add_word(stringf("GPIO.%s", p.first.c_str(ctx)), p.second.as_bits()); + } break; - } default: break; } diff --git a/himbaechel/uarch/gatemate/ccf.cc b/himbaechel/uarch/gatemate/ccf.cc index dbaf56c6..4d2daaf8 100644 --- a/himbaechel/uarch/gatemate/ccf.cc +++ b/himbaechel/uarch/gatemate/ccf.cc @@ -77,7 +77,7 @@ struct GateMateCCFReader log_error("Value '%s' can not be defined for default GPIO in line %d.\n", name.c_str(), lineno); if (ctx->get_package_pin_bel(ctx->id(value)) == BelId()) log_error("Unknown location '%s' used in line %d.\n", value.c_str(), lineno); - props->emplace(ctx->id(name.c_str()), Property(value)); + props->emplace(ctx->id("PIN_NAME"), Property(value)); } else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" || name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") { if (value == "TRUE") { diff --git a/himbaechel/uarch/gatemate/constids.inc b/himbaechel/uarch/gatemate/constids.inc index 43431bfe..4f3e0d5b 100644 --- a/himbaechel/uarch/gatemate/constids.inc +++ b/himbaechel/uarch/gatemate/constids.inc @@ -46,11 +46,50 @@ X(CC_IDDR) X(CC_ODDR) X(I) X(O) +X(IO) X(DI) X(DO) X(OE) X(Y) X(A) +X(T) +X(I_P) +X(I_N) +X(O_P) +X(O_N) +X(IO_P) +X(IO_N) X(OUT3) X(OUT4) + +X(PIN_NAME) +X(PIN_NAME_P) +X(PIN_NAME_N) +X(V_IO) +X(PULLUP) +X(PULLDOWN) +X(KEEPER) +X(SCHMITT_TRIGGER) +X(DELAY_IBF) +X(FF_IBF) +X(DRIVE) +X(SLEW) +X(DELAY_OBF) +X(FF_OBF) +X(LVDS_RTERM) +X(LVDS_BOOST) + +X(OE_ENABLE) +X(OUT_SIGNAL) +X(INPUT_ENABLE) +X(OUT23_14_SEL) + +X(INIT_L00) +X(INIT_L01) +X(INIT_L02) +X(INIT_L03) +X(INIT_L10) +X(INIT_L11) +X(INIT_L20) +X(INIT_L30) diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc index 17661f28..e0585475 100644 --- a/himbaechel/uarch/gatemate/gatemate.cc +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -65,6 +65,24 @@ void GateMateImpl::setupArchContext() } } +// Bel bucket functions +IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const +{ + if (cell_type.in(id_CC_IBUF, id_CC_IBUF, id_CC_TOBUF, id_CC_IOBUF)) + return id_GPIO; + else + return cell_type; +} + +bool GateMateImpl::isValidBelForCellType(IdString cell_type, BelId bel) const +{ + IdString bel_type = ctx->getBelType(bel); + if (bel_type == id_GPIO) + return cell_type.in(id_CC_IBUF, id_CC_IBUF, id_CC_TOBUF, id_CC_IOBUF); + else + return (bel_type == cell_type); +} + struct GateMateArch : HimbaechelArch { GateMateArch() : HimbaechelArch("gatemate") {}; diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index d6c15995..99c0cb88 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -50,6 +50,9 @@ struct GateMateImpl : HimbaechelAPI bool read_bitstream(const std::string &device, const std::string &filename); void parse_ccf(const std::string &filename); + + IdString getBelBucketForCellType(IdString cell_type) const; + bool isValidBelForCellType(IdString cell_type, BelId bel) const; }; NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/gen/arch_gen.py b/himbaechel/uarch/gatemate/gen/arch_gen.py index cd2f8c8c..5d7f0cdf 100644 --- a/himbaechel/uarch/gatemate/gen/arch_gen.py +++ b/himbaechel/uarch/gatemate/gen/arch_gen.py @@ -81,6 +81,76 @@ def main(): tt.create_pip("CPE.IN1", "CPE.OUT2") tt.create_pip("CPE.IN1", "CPE.RAM_O1") tt.create_pip("CPE.IN1", "CPE.RAM_O2") + if "GPIO" in type_name: + tt.create_wire("GPIO.OUT_D1", "WIRE_INTERNAL") + tt.create_wire("GPIO.OUT_D2", "WIRE_INTERNAL") + tt.create_wire("GPIO.OUT_Q1", "WIRE_INTERNAL") + tt.create_wire("GPIO.OUT_Q2", "WIRE_INTERNAL") + tt.create_wire("GPIO.OUT_CLK","WIRE_INTERNAL") + tt.create_wire("GPIO.CLK_INT","WIRE_INTERNAL") + + pp = tt.create_pip("GPIO.OUT1", "GPIO.OUT_D1") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT1_4"), 1, 0, False) + pp = tt.create_pip("GPIO.OUT4", "GPIO.OUT_D1") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT1_4"), 1, 1, False) + + pp = tt.create_pip("GPIO.OUT2", "GPIO.OUT_D2") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT2_3"), 1, 0, False) + pp = tt.create_pip("GPIO.OUT3", "GPIO.OUT_D2") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT2_3"), 1, 1, False) + + pp = tt.create_pip("GPIO.OUT_D1","GPIO.DO") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT23_14_SEL"), 1, 0, False) + pp = tt.create_pip("GPIO.OUT_D2","GPIO.DO") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT23_14_SEL"), 1, 1, False) + + + pp = tt.create_pip("GPIO.OUT2","GPIO.OE") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OE_SIGNAL"), 2, 1, False) + pp = tt.create_pip("GPIO.OUT3","GPIO.OE") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OE_SIGNAL"), 2, 2, False) + pp = tt.create_pip("GPIO.OUT4","GPIO.OE") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OE_SIGNAL"), 2, 3, False) + + pp = tt.create_pip("GPIO.OUT4", "GPIO.CLK_INT") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.CLK_1_4"), 1, 0, False) + pp = tt.create_pip("GPIO.OUT1", "GPIO.CLK_INT") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.CLK_1_4"), 1, 1, False) + + pp = tt.create_pip("GPIO.CLK_INT", "GPIO.OUT_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.SEL_OUT_CLOCK"), 1, 1, False) + pp = tt.create_pip("GPIO.CLOCK1", "GPIO.OUT_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT_CLOCK"), 2, 0, False) + pp = tt.create_pip("GPIO.CLOCK2", "GPIO.OUT_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT_CLOCK"), 2, 1, False) + pp = tt.create_pip("GPIO.CLOCK3", "GPIO.OUT_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT_CLOCK"), 2, 2, False) + pp = tt.create_pip("GPIO.CLOCK4", "GPIO.OUT_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT_CLOCK"), 2, 3, False) + + + tt.create_wire("GPIO.IN_D1", "WIRE_INTERNAL") + tt.create_wire("GPIO.IN_D2", "WIRE_INTERNAL") + tt.create_wire("GPIO.IN_Q1", "WIRE_INTERNAL") + tt.create_wire("GPIO.IN_Q2", "WIRE_INTERNAL") + tt.create_wire("GPIO.IN_CLK","WIRE_INTERNAL") + + pp = tt.create_pip("GPIO.CLK_INT", "GPIO.IN_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.SEL_IN_CLOCK"), 1, 1, False) + pp = tt.create_pip("GPIO.CLOCK1", "GPIO.IN_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.IN_CLOCK"), 2, 0, False) + pp = tt.create_pip("GPIO.CLOCK2", "GPIO.IN_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.IN_CLOCK"), 2, 1, False) + pp = tt.create_pip("GPIO.CLOCK3", "GPIO.IN_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.IN_CLOCK"), 2, 2, False) + pp = tt.create_pip("GPIO.CLOCK4", "GPIO.IN_CLK") + pp.extra_data = PipExtraData(ch.strs.id("GPIO.IN_CLOCK"), 2, 3, False) + + + tt.create_pip("GPIO.DI", "GPIO.IN1") + tt.create_pip("GPIO.DI", "GPIO.IN2") + + # Setup tile grid for x in range(die.max_col() + 3): diff --git a/himbaechel/uarch/gatemate/pack.cc b/himbaechel/uarch/gatemate/pack.cc index 9cc100d3..ac96347d 100644 --- a/himbaechel/uarch/gatemate/pack.cc +++ b/himbaechel/uarch/gatemate/pack.cc @@ -24,6 +24,15 @@ NEXTPNR_NAMESPACE_BEGIN +void GateMatePacker::disconnect_if_gnd(CellInfo *cell, IdString input) +{ + NetInfo *net = cell->getPort(input); + if (!net) + return; + if (net->name.in(ctx->id("$PACKER_GND"))) { + cell->disconnectPort(input); + } +} void GateMatePacker::pack_io() { // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis @@ -80,8 +89,15 @@ void GateMatePacker::pack_io() // Copy attributes to real IO buffer for (auto &attrs : ci->attrs) top_port.cell->attrs[attrs.first] = attrs.second; - for (auto ¶ms : ci->params) + for (auto ¶ms : ci->params) { + if (top_port.cell->params.count(params.first)) { + if (top_port.cell->params[params.first] != params.second) { + log_warning("Overriding parameter '%s' with value '%s' for cell '%s'.\n", + params.first.c_str(ctx), params.second.c_str(), ctx->nameOf(top_port.cell)); + } + } top_port.cell->params[params.first] = params.second; + } // Make sure that top level net is set correctly port.second.net = top_port.cell->ports.at(top_port.port).net; @@ -94,26 +110,72 @@ void GateMatePacker::pack_io() for (auto &cell : ctx->cells) { CellInfo &ci = *cell.second; - if (!ci.type.in(id_CC_IBUF, id_CC_OBUF)) + if (!ci.type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF)) continue; std::string loc; - if (ci.params.count(ctx->id("LOC")) == 0) { + if (ci.params.count(ctx->id("PIN_NAME")) == 0) { log_warning("IO signal name '%s' is not defined in CCF file and will be auto-placed.\n", ctx->nameOf(&ci)); } else { - loc = ci.params.at(ctx->id("LOC")).to_string(); + loc = ci.params.at(ctx->id("PIN_NAME")).to_string(); } - if (ci.type == id_CC_IBUF) { - ci.renamePort(id_I, id_DI); - ci.renamePort(id_Y, id_IN1); - ci.params[ctx->id("INIT")] = - Property("000000000000000000000001000000000000000000000000000000000000000001010000"); + disconnect_if_gnd(&ci, id_T); + if (ci.type == id_CC_TOBUF && !ci.getPort(id_T)) + ci.type = id_CC_OBUF; + + std::vector keys; + for (auto &p : ci.params) { + if (p.first.in(id_PIN_NAME, id_V_IO)) { + keys.push_back(p.first); + continue; + } + if (ci.type.in(id_CC_IBUF, id_CC_IOBUF) && + p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER, id_SCHMITT_TRIGGER, id_DELAY_IBF, id_FF_IBF)) + continue; + if (ci.type.in(id_CC_TOBUF) && p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER)) + continue; + if (ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF) && + p.first.in(id_DRIVE, id_SLEW, id_DELAY_OBF, id_FF_OBF)) + continue; + log_warning("Removing unsupported parameter '%s' for type '%s'.\n", p.first.c_str(ctx), ci.type.c_str(ctx)); + keys.push_back(p.first); } - if (ci.type == id_CC_OBUF) { - ci.renamePort(id_O, id_DO); - ci.renamePort(id_A, id_OUT2); - ci.params[ctx->id("INIT")] = - Property("000000000000000000000000000000000000000100000000000000010000100100000000"); + for (auto key : keys) + ci.params.erase(key); + + if ((ci.params.count(id_KEEPER) + ci.params.count(id_PULLUP) + ci.params.count(id_PULLDOWN)) > 1) + log_error("PULLUP, PULLDOWN and KEEPER are mutually exclusive parameters.\n"); + + // DELAY_IBF and DELAY_OBF must be set depending of type + // Also we need to enable input/output + if (ci.type.in(id_CC_IBUF, id_CC_IOBUF)) { + ci.params[id_DELAY_IBF] = Property(1 << int_or_default(ci.params, id_DELAY_IBF, 0), 16); + ci.params[id_INPUT_ENABLE] = Property(Property::State::S1); + } + if (ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF)) { + ci.params[id_DELAY_OBF] = Property(1 << int_or_default(ci.params, id_DELAY_OBF, 0), 16); + ci.params[id_OE_ENABLE] = Property(Property::State::S1); + } + + // Disconnect PADs + ci.disconnectPort(id_IO); + ci.disconnectPort(id_I); + ci.disconnectPort(id_O); + + // Remap ports to GPIO bel + ci.renamePort(id_A, id_DO); + ci.renamePort(id_Y, id_DI); + ci.renamePort(id_T, id_OE); + + NetInfo *do_net = ci.getPort(id_DO); + if (do_net) { + if (do_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + ci.params[id_OUT23_14_SEL] = + Property(do_net->name == ctx->id("$PACKER_VCC") ? Property::State::S1 : Property::State::S0); + ci.disconnectPort(id_DO); + } else { + ci.params[id_OUT_SIGNAL] = Property(Property::State::S1); + } } if (!loc.empty()) { BelId bel = ctx->get_package_pin_bel(ctx->id(loc)); @@ -124,9 +186,50 @@ void GateMatePacker::pack_io() ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_FIXED); } - ci.type = id_GPIO; } } + +void GateMatePacker::pack_constants() +{ + log_info("Packing constants..\n"); + // Replace constants with LUTs + const dict vcc_params = { + {id_INIT_L00, Property(0xf, 4)}, {id_INIT_L01, Property(0xf, 4)}, {id_INIT_L02, Property(0xf, 4)}}; + const dict gnd_params = { + {id_INIT_L00, Property(0x0, 4)}, {id_INIT_L01, Property(0x0, 4)}, {id_INIT_L02, Property(0x0, 4)}}; + + h.replace_constants(CellTypePort(id_CPE, id_OUT1), CellTypePort(id_CPE, id_OUT1), vcc_params, gnd_params); +} + +void GateMatePacker::remove_constants() +{ + log_info("Removing constants..\n"); + auto fnd_cell = ctx->cells.find(ctx->id("$PACKER_VCC_DRV")); + if (fnd_cell != ctx->cells.end()) { + auto fnd_net = ctx->nets.find(ctx->id("$PACKER_VCC")); + if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries() == 0) { + BelId bel = (*fnd_cell).second.get()->bel; + if (bel != BelId()) + ctx->unbindBel(bel); + ctx->cells.erase(fnd_cell); + ctx->nets.erase(fnd_net); + log_info(" Removed unused VCC cell\n"); + } + } + fnd_cell = ctx->cells.find(ctx->id("$PACKER_GND_DRV")); + if (fnd_cell != ctx->cells.end()) { + auto fnd_net = ctx->nets.find(ctx->id("$PACKER_GND")); + if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries() == 0) { + BelId bel = (*fnd_cell).second.get()->bel; + if (bel != BelId()) + ctx->unbindBel(bel); + ctx->cells.erase(fnd_cell); + ctx->nets.erase(fnd_net); + log_info(" Removed unused GND cell\n"); + } + } +} + void GateMateImpl::pack() { const ArchArgs &args = ctx->args; @@ -135,7 +238,9 @@ void GateMateImpl::pack() } GateMatePacker packer(ctx, this); + packer.pack_constants(); packer.pack_io(); + packer.remove_constants(); } NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/pack.h b/himbaechel/uarch/gatemate/pack.h index 219484b8..473e6fee 100644 --- a/himbaechel/uarch/gatemate/pack.h +++ b/himbaechel/uarch/gatemate/pack.h @@ -29,6 +29,10 @@ struct GateMatePacker GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); }; void pack_io(); + void pack_constants(); + + void disconnect_if_gnd(CellInfo *cell, IdString input); + void remove_constants(); Context *ctx; GateMateImpl *uarch;