diff options
Diffstat (limited to 'gowin/arch.cc')
-rw-r--r-- | gowin/arch.cc | 1237 |
1 files changed, 1237 insertions, 0 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc new file mode 100644 index 00000000..8b39cd27 --- /dev/null +++ b/gowin/arch.cc @@ -0,0 +1,1237 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com> + * Copyright (C) 2020 Pepijn de Vos <pepijn@symbioticeda.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <iostream> +#include <math.h> +#include <regex> +#include "embed.h" +#include "nextpnr.h" +#include "placer1.h" +#include "placer_heap.h" +#include "router1.h" +#include "router2.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +WireInfo &Arch::wire_info(IdString wire) +{ + auto w = wires.find(wire); + if (w == wires.end()) + NPNR_ASSERT_FALSE_STR("no wire named " + wire.str(this)); + return w->second; +} + +PipInfo &Arch::pip_info(IdString pip) +{ + auto p = pips.find(pip); + if (p == pips.end()) + NPNR_ASSERT_FALSE_STR("no pip named " + pip.str(this)); + return p->second; +} + +BelInfo &Arch::bel_info(IdString bel) +{ + auto b = bels.find(bel); + if (b == bels.end()) + NPNR_ASSERT_FALSE_STR("no bel named " + bel.str(this)); + return b->second; +} + +void Arch::addWire(IdString name, IdString type, int x, int y) +{ + // std::cout << name.str(this) << std::endl; + NPNR_ASSERT(wires.count(name) == 0); + WireInfo &wi = wires[name]; + wi.name = name; + wi.type = type; + wi.x = x; + wi.y = y; + + wire_ids.push_back(name); +} + +void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay, Loc loc) +{ + NPNR_ASSERT(pips.count(name) == 0); + PipInfo &pi = pips[name]; + pi.name = name; + pi.type = type; + pi.srcWire = srcWire; + pi.dstWire = dstWire; + pi.delay = delay; + pi.loc = loc; + + wire_info(srcWire).downhill.push_back(name); + wire_info(dstWire).uphill.push_back(name); + pip_ids.push_back(name); + + if (int(tilePipDimZ.size()) <= loc.x) + tilePipDimZ.resize(loc.x + 1); + + if (int(tilePipDimZ[loc.x].size()) <= loc.y) + tilePipDimZ[loc.x].resize(loc.y + 1); + + gridDimX = std::max(gridDimX, loc.x + 1); + gridDimY = std::max(gridDimY, loc.y + 1); + tilePipDimZ[loc.x][loc.y] = std::max(tilePipDimZ[loc.x][loc.y], loc.z + 1); +} + +void Arch::addBel(IdString name, IdString type, Loc loc, bool gb) +{ + NPNR_ASSERT(bels.count(name) == 0); + NPNR_ASSERT(bel_by_loc.count(loc) == 0); + BelInfo &bi = bels[name]; + bi.name = name; + bi.type = type; + bi.x = loc.x; + bi.y = loc.y; + bi.z = loc.z; + bi.gb = gb; + + bel_ids.push_back(name); + bel_by_loc[loc] = name; + + if (int(bels_by_tile.size()) <= loc.x) + bels_by_tile.resize(loc.x + 1); + + if (int(bels_by_tile[loc.x].size()) <= loc.y) + bels_by_tile[loc.x].resize(loc.y + 1); + + bels_by_tile[loc.x][loc.y].push_back(name); + + if (int(tileBelDimZ.size()) <= loc.x) + tileBelDimZ.resize(loc.x + 1); + + if (int(tileBelDimZ[loc.x].size()) <= loc.y) + tileBelDimZ[loc.x].resize(loc.y + 1); + + gridDimX = std::max(gridDimX, loc.x + 1); + gridDimY = std::max(gridDimY, loc.x + 1); + tileBelDimZ[loc.x][loc.y] = std::max(tileBelDimZ[loc.x][loc.y], loc.z + 1); +} + +void Arch::addBelInput(IdString bel, IdString name, IdString wire) +{ + NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); + PinInfo &pi = bel_info(bel).pins[name]; + pi.name = name; + pi.wire = wire; + pi.type = PORT_IN; + + wire_info(wire).downhill_bel_pins.push_back(BelPin{bel, name}); + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); +} + +void Arch::addBelOutput(IdString bel, IdString name, IdString wire) +{ + NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); + PinInfo &pi = bel_info(bel).pins[name]; + pi.name = name; + pi.wire = wire; + pi.type = PORT_OUT; + + wire_info(wire).uphill_bel_pin = BelPin{bel, name}; + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); +} + +void Arch::addBelInout(IdString bel, IdString name, IdString wire) +{ + NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); + PinInfo &pi = bel_info(bel).pins[name]; + pi.name = name; + pi.wire = wire; + pi.type = PORT_INOUT; + + wire_info(wire).downhill_bel_pins.push_back(BelPin{bel, name}); + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); +} + +void Arch::addGroupBel(IdString group, IdString bel) { groups[group].bels.push_back(bel); } + +void Arch::addGroupWire(IdString group, IdString wire) { groups[group].wires.push_back(wire); } + +void Arch::addGroupPip(IdString group, IdString pip) { groups[group].pips.push_back(pip); } + +void Arch::addGroupGroup(IdString group, IdString grp) { groups[group].groups.push_back(grp); } + +void Arch::addDecalGraphic(DecalId decal, const GraphicElement &graphic) +{ + decal_graphics[decal].push_back(graphic); + refreshUi(); +} + +void Arch::setWireDecal(WireId wire, DecalXY decalxy) +{ + wire_info(wire).decalxy = decalxy; + refreshUiWire(wire); +} + +void Arch::setPipDecal(PipId pip, DecalXY decalxy) +{ + pip_info(pip).decalxy = decalxy; + refreshUiPip(pip); +} + +void Arch::setBelDecal(BelId bel, DecalXY decalxy) +{ + bel_info(bel).decalxy = decalxy; + refreshUiBel(bel); +} + +void Arch::setGroupDecal(GroupId group, DecalXY decalxy) +{ + groups[group].decalxy = decalxy; + refreshUiGroup(group); +} + +void Arch::setWireAttr(IdString wire, IdString key, const std::string &value) { wire_info(wire).attrs[key] = value; } + +void Arch::setPipAttr(IdString pip, IdString key, const std::string &value) { pip_info(pip).attrs[key] = value; } + +void Arch::setBelAttr(IdString bel, IdString key, const std::string &value) { bel_info(bel).attrs[key] = value; } + +void Arch::setDelayScaling(double scale, double offset) +{ + args.delayScale = scale; + args.delayOffset = offset; +} + +void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; } + +void Arch::addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayInfo delay) +{ + if (get_or_default(cellTiming[cell].portClasses, fromPort, TMG_IGNORE) == TMG_IGNORE) + cellTiming[cell].portClasses[fromPort] = TMG_COMB_INPUT; + if (get_or_default(cellTiming[cell].portClasses, toPort, TMG_IGNORE) == TMG_IGNORE) + cellTiming[cell].portClasses[toPort] = TMG_COMB_OUTPUT; + cellTiming[cell].combDelays[CellDelayKey{fromPort, toPort}] = delay; +} + +void Arch::addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayInfo setup, DelayInfo hold) +{ + TimingClockingInfo ci; + ci.clock_port = clock; + ci.edge = RISING_EDGE; + ci.setup = setup; + ci.hold = hold; + cellTiming[cell].clockingInfo[port].push_back(ci); + cellTiming[cell].portClasses[port] = TMG_REGISTER_INPUT; +} + +void Arch::addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayInfo clktoq) +{ + TimingClockingInfo ci; + ci.clock_port = clock; + ci.edge = RISING_EDGE; + ci.clockToQ = clktoq; + cellTiming[cell].clockingInfo[port].push_back(ci); + cellTiming[cell].portClasses[port] = TMG_REGISTER_OUTPUT; +} + +// --------------------------------------------------------------- + +// TODO represent wires more intelligently. +IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString &wire) +{ + const std::string &wirename = wire.str(this); + char buf[32]; + if (wirename == "VCC" || wirename == "GND") { + return wire; + } + if (!isdigit(wirename[1]) || !isdigit(wirename[2]) || !isdigit(wirename[3])) { + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wirename.c_str()); + return id(buf); + } + char direction = wirename[0]; + int num = std::stoi(wirename.substr(1, 2)); + int segment = std::stoi(wirename.substr(3, 1)); + switch (direction) { + case 'N': + row += segment; + break; + case 'S': + row -= segment; + break; + case 'E': + col -= segment; + break; + case 'W': + col += segment; + break; + default: + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wirename.c_str()); + return id(buf); + break; + } + // wires wrap around the edges + // assumes 0-based indexes + if (row < 0) { + row = -1 - row; + direction = 'N'; + } else if (col < 0) { + col = -1 - col; + direction = 'W'; + } else if (row >= db->rows) { + row = 2 * db->rows - 1 - row; + direction = 'S'; + } else if (col >= db->cols) { + col = 2 * db->cols - 1 - col; + direction = 'E'; + } + snprintf(buf, 32, "%c%d0", direction, num); + wire = id(buf); + snprintf(buf, 32, "R%dC%d_%c%d", row + 1, col + 1, direction, num); + return id(buf); +} + +const PairPOD *pairLookup(const PairPOD *list, const size_t len, const int dest) +{ + for (size_t i = 0; i < len; i++) { + const PairPOD *pair = &list[i]; + if (pair->dest_id == dest) { + return pair; + } + } + return nullptr; +} + +bool aliasCompare(GlobalAliasPOD i, GlobalAliasPOD j) +{ + return (i.dest_row < j.dest_row) || (i.dest_row == j.dest_row && i.dest_col < j.dest_col) || + (i.dest_row == j.dest_row && i.dest_col == j.dest_col && i.dest_id < j.dest_id); +} +bool timingCompare(TimingPOD i, TimingPOD j) { return i.name_id < j.name_id; } + +template <class T, class C> const T *genericLookup(const T *first, int len, const T val, C compare) +{ + auto res = std::lower_bound(first, first + len, val, compare); + if (res - first != len && !compare(val, *res)) { + return res; + } else { + return nullptr; + } +} + +DelayInfo delayLookup(const TimingPOD *first, int len, IdString name) +{ + TimingPOD needle; + needle.name_id = name.index; + const TimingPOD *timing = genericLookup(first, len, needle, timingCompare); + DelayInfo info; + if (timing != nullptr) { + info.maxFall = std::max(timing->ff, timing->rf) / 1000; + info.minFall = std::min(timing->ff, timing->rf) / 1000; + info.maxRaise = std::max(timing->rr, timing->fr) / 1000; + info.minRaise = std::min(timing->rr, timing->fr) / 1000; + } else { + info.maxFall = 0; + info.minFall = 0; + info.maxRaise = 0; + info.minRaise = 0; + } + return info; +} + +DelayInfo Arch::getWireTypeDelay(IdString wire) +{ + IdString len; + IdString glbsrc; + switch (wire.index) { + case ID_X01: + case ID_X02: + case ID_X03: + case ID_X04: + case ID_X05: + case ID_X06: + case ID_X07: + case ID_X08: + len = id_X0; + break; + case ID_N100: + case ID_N130: + case ID_S100: + case ID_S130: + case ID_E100: + case ID_E130: + case ID_W100: + case ID_W130: + case ID_E110: + case ID_W110: + case ID_E120: + case ID_W120: + case ID_S110: + case ID_N110: + case ID_S120: + case ID_N120: + case ID_SN10: + case ID_SN20: + case ID_EW10: + case ID_EW20: + len = id_FX1; + break; + case ID_N200: + case ID_N210: + case ID_N220: + case ID_N230: + case ID_N240: + case ID_N250: + case ID_N260: + case ID_N270: + case ID_S200: + case ID_S210: + case ID_S220: + case ID_S230: + case ID_S240: + case ID_S250: + case ID_S260: + case ID_S270: + case ID_E200: + case ID_E210: + case ID_E220: + case ID_E230: + case ID_E240: + case ID_E250: + case ID_E260: + case ID_E270: + case ID_W200: + case ID_W210: + case ID_W220: + case ID_W230: + case ID_W240: + case ID_W250: + case ID_W260: + case ID_W270: + len = id_X2; + break; + case ID_N800: + case ID_N810: + case ID_N820: + case ID_N830: + case ID_S800: + case ID_S810: + case ID_S820: + case ID_S830: + case ID_E800: + case ID_E810: + case ID_E820: + case ID_E830: + case ID_W800: + case ID_W810: + case ID_W820: + case ID_W830: + len = id_X8; + break; + case ID_GT00: + case ID_GT10: + glbsrc = id_SPINE_TAP_PCLK; + break; + case ID_GBO0: + case ID_GBO1: + glbsrc = id_TAP_BRANCH_PCLK; + break; + case ID_GB00: + case ID_GB10: + case ID_GB20: + case ID_GB30: + case ID_GB40: + case ID_GB50: + case ID_GB60: + case ID_GB70: + glbsrc = id_BRANCH_PCLK; + break; + default: + if (wire.str(this).rfind("SPINE", 0) == 0) { + glbsrc = ID_CENT_SPINE_PCLK; + } else if (wire.str(this).rfind("UNK", 0) == 0) { + glbsrc = ID_PIO_CENT_PCLK; + } + break; + } + if (len != IdString()) { + return delayLookup(speed->wire.timings.get(), speed->wire.num_timings, len); + } else if (glbsrc != IdString()) { + return delayLookup(speed->glbsrc.timings.get(), speed->glbsrc.num_timings, glbsrc); + } else { + DelayInfo info; + info.maxFall = 0; + info.minFall = 0; + info.maxRaise = 0; + info.minRaise = 0; + return info; + } +} + +void Arch::read_cst(std::istream &in) { + std::regex iobre = std::regex("IO_LOC +\"([^\"]+)\" +([^ ;]+);"); + std::smatch match; + std::string line; + while (!in.eof()) { + std::getline(in, line); + if(!std::regex_match(line, match, iobre)) { + // empty line or comment + if(line.empty() || line.rfind("//", 0) == 0) { + continue; + } else { + log_warning("Invalid constraint: %s\n", line.c_str()); + continue; + } + } + //std::cout << match[1] << " " << match[2] << std::endl; + IdString net = id(match[1]); + IdString pinname = id(match[2]); + const PairPOD *belname = pairLookup(package->pins.get(), package->num_pins, pinname.index); + if ( belname == nullptr) + log_error("Pin %s not found\n", pinname.c_str(this)); + //BelId bel = getBelByName(belname->src_id); + // for (auto cell : sorted(cells)) { + // std::cout << cell.first.str(this) << std::endl; + // } + auto it = cells.find(net); + if (it == cells.end()) { + log_info("Cell %s not found\n", net.c_str(this)); + continue; + } + std::string bel = IdString(belname->src_id).str(this); + it->second->attrs[ID_BEL] = bel; + } +} + +Arch::Arch(ArchArgs args) : args(args) +{ + family = args.family; + device = args.device; + + // Load database + std::string chipdb = stringf("gowin/chipdb-%s.bin", family.c_str()); + auto db = reinterpret_cast<const DatabasePOD *>(get_chipdb(chipdb)); + if (db == nullptr) + log_error("Failed to load chipdb '%s'\n", chipdb.c_str()); + if (db->family.get() != family) { + log_error("Database is for family '%s' but provided device is family '%s'.\n", db->family.get(), + family.c_str()); + } + // setup id strings + for (size_t i = 0; i < db->num_ids; i++) { + IdString::initialize_add(this, db->id_strs[i].get(), uint32_t(i) + db->num_constids); + } + // setup timing info + speed = nullptr; + for (unsigned int i = 0; i < db->num_speeds; i++) { + const TimingClassPOD *tc = &db->speeds[i]; + // std::cout << IdString(tc->name_id).str(this) << std::endl; + if (IdString(tc->name_id) == id(args.speed)) { + speed = tc->groups.get(); + break; + } + } + if (speed == nullptr) { + log_error("Unsuported speed grade '%s'.\n", args.speed.c_str()); + } + const VariantPOD* variant = nullptr; + for (unsigned int i = 0; i < db->num_variants; i++) { + auto var = &db->variants[i]; + //std::cout << IdString(var->name_id).str(this) << std::endl; + if (IdString(var->name_id) == id(args.device)) { + variant = var; + break; + } + } + if (variant == nullptr) { + log_error("Unsuported device grade '%s'.\n", args.device.c_str()); + } + + package = nullptr; + for (unsigned int i = 0; i < variant->num_packages; i++) { + auto pkg = &variant->packages[i]; + // std::cout << IdString(pkg->name_id).str(this) << std::endl; + if (IdString(pkg->name_id) == id(args.package)) { + package = pkg; + break; + } + // for (int j=0; j < pkg->num_pins; j++) { + // auto pin = pkg->pins[j]; + // std::cout << IdString(pin.src_id).str(this) << " " << IdString(pin.dest_id).str(this) << std::endl; + // } + } + if (package == nullptr) { + log_error("Unsuported package '%s'.\n", args.package.c_str()); + } + // setup db + char buf[32]; + for (int i = 0; i < db->rows * db->cols; i++) { + int row = i / db->cols; + int col = i % db->cols; + const TilePOD *tile = db->grid[i].get(); + // setup wires + const PairPOD *pips[2] = {tile->pips.get(), tile->clock_pips.get()}; + unsigned int num_pips[2] = {tile->num_pips, tile->num_clock_pips}; + for (int p = 0; p < 2; p++) { + for (unsigned int j = 0; j < num_pips[p]; j++) { + const PairPOD pip = pips[p][j]; + int destrow = row; + int destcol = col; + IdString destid = pip.dest_id; + IdString gdestname = wireToGlobal(destrow, destcol, db, destid); + if (wires.count(gdestname) == 0) + addWire(gdestname, pip.dest_id, destcol, destrow); + int srcrow = row; + int srccol = col; + IdString srcid = pip.src_id; + IdString gsrcname = wireToGlobal(srcrow, srccol, db, srcid); + if (wires.count(gsrcname) == 0) + addWire(gsrcname, pip.src_id, srccol, srcrow); + } + } + for (unsigned int j = 0; j < tile->num_bels; j++) { + const BelsPOD *bel = &tile->bels[j]; + IdString belname; + IdString portname; + int z = 0; + bool dff = true; + switch (static_cast<ConstIds>(bel->type_id)) { + // fall through the ++ + case ID_LUT7: + z++; + dff = false; + case ID_LUT6: + z++; + dff = false; + case ID_LUT5: + z++; + case ID_LUT4: + z++; + case ID_LUT3: + z++; + case ID_LUT2: + z++; + case ID_LUT1: + z++; + case ID_LUT0: + // common LUT+DFF code + snprintf(buf, 32, "R%dC%d_SLICE%d", row + 1, col + 1, z); + belname = id(buf); + addBel(belname, id_SLICE, Loc(col, row, z), false); + snprintf(buf, 32, "R%dC%d_F%d", row + 1, col + 1, z); + addBelOutput(belname, id_F, id(buf)); + snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, z); + addBelInput(belname, id_A, id(buf)); + snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, z); + addBelInput(belname, id_B, id(buf)); + snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, z); + addBelInput(belname, id_C, id(buf)); + snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, z); + addBelInput(belname, id_D, id(buf)); + if (dff) { + snprintf(buf, 32, "R%dC%d_CLK%d", row + 1, col + 1, z / 2); + addBelInput(belname, id_CLK, id(buf)); + snprintf(buf, 32, "R%dC%d_LSR%d", row + 1, col + 1, z / 2); + addBelInput(belname, id_LSR, id(buf)); + snprintf(buf, 32, "R%dC%d_CE%d", row + 1, col + 1, z / 2); + addBelInput(belname, id_CE, id(buf)); + snprintf(buf, 32, "R%dC%d_Q%d", row + 1, col + 1, z); + addBelOutput(belname, id_Q, id(buf)); + } + break; + case ID_IOBJ: + z++; + case ID_IOBI: + z++; + case ID_IOBH: + z++; + case ID_IOBG: + z++; + case ID_IOBF: + z++; + case ID_IOBE: + z++; + case ID_IOBD: + z++; + case ID_IOBC: + z++; + case ID_IOBB: + z++; + case ID_IOBA: + snprintf(buf, 32, "R%dC%d_IOB%c", row + 1, col + 1, 'A' + z); + belname = id(buf); + addBel(belname, id_IOB, Loc(col, row, z), false); + portname = pairLookup(bel->ports.get(), bel->num_ports, ID_O)->src_id; + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_O, id(buf)); + portname = pairLookup(bel->ports.get(), bel->num_ports, ID_I)->src_id; + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_I, id(buf)); + portname = pairLookup(bel->ports.get(), bel->num_ports, ID_OE)->src_id; + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_OEN, id(buf)); + break; + + default: + break; + } + } + } + // setup pips + for (int i = 0; i < db->rows * db->cols; i++) { + int row = i / db->cols; + int col = i % db->cols; + const TilePOD *tile = db->grid[i].get(); + const PairPOD *pips[2] = {tile->pips.get(), tile->clock_pips.get()}; + unsigned int num_pips[2] = {tile->num_pips, tile->num_clock_pips}; + for (int p = 0; p < 2; p++) { + for (unsigned int j = 0; j < num_pips[p]; j++) { + const PairPOD pip = pips[p][j]; + int destrow = row; + int destcol = col; + IdString destid = pip.dest_id; + IdString gdestname = wireToGlobal(destrow, destcol, db, destid); + int srcrow = row; + int srccol = col; + IdString srcid = pip.src_id; + IdString gsrcname = wireToGlobal(srcrow, srccol, db, srcid); + + snprintf(buf, 32, "R%dC%d_%s_%s", row + 1, col + 1, IdString(pip.src_id).c_str(this), + IdString(pip.dest_id).c_str(this)); + IdString pipname = id(buf); + DelayInfo delay = getWireTypeDelay(pip.dest_id); + // local alias + auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, srcid.index); + // std::cout << "srcid " << srcid.str(this) << std::endl; + if (local_alias != nullptr) { + srcid = local_alias->src_id; + gsrcname = wireToGlobal(srcrow, srccol, db, srcid); + } + // global alias + srcid = pip.src_id; + GlobalAliasPOD alias; + alias.dest_col = srccol; + alias.dest_row = srcrow; + alias.dest_id = srcid.index; + auto alias_src = genericLookup(db->aliases.get(), db->num_aliases, alias, aliasCompare); + if (alias_src != nullptr) { + srccol = alias_src->src_col; + srcrow = alias_src->src_row; + srcid = alias_src->src_id; + gsrcname = wireToGlobal(srcrow, srccol, db, srcid); + // std::cout << buf << std::endl; + } + addPip(pipname, pip.dest_id, gsrcname, gdestname, delay, Loc(col, row, j)); + } + } + } + // Dummy for empty decals + decal_graphics[IdString()]; +} + +void IdString::initialize_arch(const BaseCtx *ctx) +{ +#define X(t) initialize_add(ctx, #t, ID_##t); +#include "constids.inc" +#undef X +} + +// --------------------------------------------------------------- + +BelId Arch::getBelByName(IdString name) const +{ + if (bels.count(name)) + return name; + return BelId(); +} + +IdString Arch::getBelName(BelId bel) const { return bel; } + +Loc Arch::getBelLocation(BelId bel) const +{ + auto &info = bels.at(bel); + return Loc(info.x, info.y, info.z); +} + +BelId Arch::getBelByLocation(Loc loc) const +{ + auto it = bel_by_loc.find(loc); + if (it != bel_by_loc.end()) + return it->second; + return BelId(); +} + +const std::vector<BelId> &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); } + +bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; } + +uint32_t Arch::getBelChecksum(BelId bel) const +{ + return bel.index; +} + +void Arch::bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) +{ + bels.at(bel).bound_cell = cell; + cell->bel = bel; + cell->belStrength = strength; + refreshUiBel(bel); +} + +void Arch::unbindBel(BelId bel) +{ + bels.at(bel).bound_cell->bel = BelId(); + bels.at(bel).bound_cell->belStrength = STRENGTH_NONE; + bels.at(bel).bound_cell = nullptr; + refreshUiBel(bel); +} + +bool Arch::checkBelAvail(BelId bel) const { return bels.at(bel).bound_cell == nullptr; } + +CellInfo *Arch::getBoundBelCell(BelId bel) const { return bels.at(bel).bound_cell; } + +CellInfo *Arch::getConflictingBelCell(BelId bel) const { return bels.at(bel).bound_cell; } + +const std::vector<BelId> &Arch::getBels() const { return bel_ids; } + +IdString Arch::getBelType(BelId bel) const { return bels.at(bel).type; } + +const std::map<IdString, std::string> &Arch::getBelAttrs(BelId bel) const { return bels.at(bel).attrs; } + +WireId Arch::getBelPinWire(BelId bel, IdString pin) const +{ + const auto &bdata = bels.at(bel); + if (!bdata.pins.count(pin)) + log_error("bel '%s' has no pin '%s'\n", bel.c_str(this), pin.c_str(this)); + return bdata.pins.at(pin).wire; +} + +PortType Arch::getBelPinType(BelId bel, IdString pin) const { return bels.at(bel).pins.at(pin).type; } + +std::vector<IdString> Arch::getBelPins(BelId bel) const +{ + std::vector<IdString> ret; + for (auto &it : bels.at(bel).pins) + ret.push_back(it.first); + return ret; +} + +// --------------------------------------------------------------- + +WireId Arch::getWireByName(IdString name) const +{ + if (wires.count(name)) + return name; + return WireId(); +} + +IdString Arch::getWireName(WireId wire) const { return wire; } + +IdString Arch::getWireType(WireId wire) const { return wires.at(wire).type; } + +const std::map<IdString, std::string> &Arch::getWireAttrs(WireId wire) const { return wires.at(wire).attrs; } + +uint32_t Arch::getWireChecksum(WireId wire) const +{ + // FIXME + return 0; +} + +void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength) +{ + wires.at(wire).bound_net = net; + net->wires[wire].pip = PipId(); + net->wires[wire].strength = strength; + refreshUiWire(wire); +} + +void Arch::unbindWire(WireId wire) +{ + auto &net_wires = wires.at(wire).bound_net->wires; + + auto pip = net_wires.at(wire).pip; + if (pip != PipId()) { + pips.at(pip).bound_net = nullptr; + refreshUiPip(pip); + } + + net_wires.erase(wire); + wires.at(wire).bound_net = nullptr; + refreshUiWire(wire); +} + +bool Arch::checkWireAvail(WireId wire) const { return wires.at(wire).bound_net == nullptr; } + +NetInfo *Arch::getBoundWireNet(WireId wire) const { return wires.at(wire).bound_net; } + +NetInfo *Arch::getConflictingWireNet(WireId wire) const { return wires.at(wire).bound_net; } + +const std::vector<BelPin> &Arch::getWireBelPins(WireId wire) const { return wires.at(wire).bel_pins; } + +const std::vector<WireId> &Arch::getWires() const { return wire_ids; } + +// --------------------------------------------------------------- + +PipId Arch::getPipByName(IdString name) const +{ + if (pips.count(name)) + return name; + return PipId(); +} + +IdString Arch::getPipName(PipId pip) const { return pip; } + +IdString Arch::getPipType(PipId pip) const { return pips.at(pip).type; } + +const std::map<IdString, std::string> &Arch::getPipAttrs(PipId pip) const { return pips.at(pip).attrs; } + +uint32_t Arch::getPipChecksum(PipId wire) const +{ + return wire.index; +} + +void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength) +{ + WireId wire = pips.at(pip).dstWire; + pips.at(pip).bound_net = net; + wires.at(wire).bound_net = net; + net->wires[wire].pip = pip; + net->wires[wire].strength = strength; + refreshUiPip(pip); + refreshUiWire(wire); +} + +void Arch::unbindPip(PipId pip) +{ + WireId wire = pips.at(pip).dstWire; + wires.at(wire).bound_net->wires.erase(wire); + pips.at(pip).bound_net = nullptr; + wires.at(wire).bound_net = nullptr; + refreshUiPip(pip); + refreshUiWire(wire); +} + +bool Arch::checkPipAvail(PipId pip) const { return pips.at(pip).bound_net == nullptr; } + +NetInfo *Arch::getBoundPipNet(PipId pip) const { return pips.at(pip).bound_net; } + +NetInfo *Arch::getConflictingPipNet(PipId pip) const { return pips.at(pip).bound_net; } + +WireId Arch::getConflictingPipWire(PipId pip) const { return pips.at(pip).bound_net ? pips.at(pip).dstWire : WireId(); } + +const std::vector<PipId> &Arch::getPips() const { return pip_ids; } + +Loc Arch::getPipLocation(PipId pip) const { return pips.at(pip).loc; } + +WireId Arch::getPipSrcWire(PipId pip) const { return pips.at(pip).srcWire; } + +WireId Arch::getPipDstWire(PipId pip) const { return pips.at(pip).dstWire; } + +DelayInfo Arch::getPipDelay(PipId pip) const { return pips.at(pip).delay; } + +const std::vector<PipId> &Arch::getPipsDownhill(WireId wire) const { return wires.at(wire).downhill; } + +const std::vector<PipId> &Arch::getPipsUphill(WireId wire) const { return wires.at(wire).uphill; } + +// --------------------------------------------------------------- + +GroupId Arch::getGroupByName(IdString name) const { return name; } + +IdString Arch::getGroupName(GroupId group) const { return group; } + +std::vector<GroupId> Arch::getGroups() const +{ + std::vector<GroupId> ret; + for (auto &it : groups) + ret.push_back(it.first); + return ret; +} + +const std::vector<BelId> &Arch::getGroupBels(GroupId group) const { return groups.at(group).bels; } + +const std::vector<WireId> &Arch::getGroupWires(GroupId group) const { return groups.at(group).wires; } + +const std::vector<PipId> &Arch::getGroupPips(GroupId group) const { return groups.at(group).pips; } + +const std::vector<GroupId> &Arch::getGroupGroups(GroupId group) const { return groups.at(group).groups; } + +// --------------------------------------------------------------- + +delay_t Arch::estimateDelay(WireId src, WireId dst) const +{ + const WireInfo &s = wires.at(src); + const WireInfo &d = wires.at(dst); + int dx = abs(s.x - d.x); + int dy = abs(s.y - d.y); + return (dx + dy) * args.delayScale + args.delayOffset; +} + +delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +{ + const auto &driver = net_info->driver; + auto driver_loc = getBelLocation(driver.cell->bel); + auto sink_loc = getBelLocation(sink.cell->bel); + + int dx = abs(sink_loc.x - driver_loc.x); + int dy = abs(sink_loc.y - driver_loc.y); + return (dx + dy) * args.delayScale + args.delayOffset; +} + +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } + +ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const +{ + ArcBounds bb; + + int src_x = wires.at(src).x; + int src_y = wires.at(src).y; + int dst_x = wires.at(dst).x; + int dst_y = wires.at(dst).y; + + bb.x0 = src_x; + bb.y0 = src_y; + bb.x1 = src_x; + bb.y1 = src_y; + + auto extend = [&](int x, int y) { + bb.x0 = std::min(bb.x0, x); + bb.x1 = std::max(bb.x1, x); + bb.y0 = std::min(bb.y0, y); + bb.y1 = std::max(bb.y1, y); + }; + extend(dst_x, dst_y); + return bb; +} + +// --------------------------------------------------------------- + +bool Arch::place() +{ + std::string placer = str_or_default(settings, id("placer"), defaultPlacer); + if (placer == "heap") { + bool have_iobuf_or_constr = false; + for (auto cell : sorted(cells)) { + CellInfo *ci = cell.second; + if (ci->type == id("IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) { + have_iobuf_or_constr = true; + break; + } + } + bool retVal; + if (!have_iobuf_or_constr) { + log_warning("Unable to use HeAP due to a lack of IO buffers or constrained cells as anchors; reverting to " + "SA.\n"); + retVal = placer1(getCtx(), Placer1Cfg(getCtx())); + } else { + PlacerHeapCfg cfg(getCtx()); + cfg.ioBufTypes.insert(id("IOB")); + cfg.beta = 0.5; + retVal = placer_heap(getCtx(), cfg); + } + getCtx()->settings[getCtx()->id("place")] = 1; + archInfoToAttributes(); + return retVal; + } else if (placer == "sa") { + bool retVal = placer1(getCtx(), Placer1Cfg(getCtx())); + getCtx()->settings[getCtx()->id("place")] = 1; + archInfoToAttributes(); + return retVal; + } else { + log_error("Gowin architecture does not support placer '%s'\n", placer.c_str()); + } +} + +bool Arch::route() +{ + std::string router = str_or_default(settings, id("router"), defaultRouter); + bool result; + if (router == "router1") { + result = router1(getCtx(), Router1Cfg(getCtx())); + } else if (router == "router2") { + router2(getCtx(), Router2Cfg(getCtx())); + result = true; + } else { + log_error("Gowin architecture does not support router '%s'\n", router.c_str()); + } + getCtx()->settings[getCtx()->id("route")] = 1; + archInfoToAttributes(); + return result; +} + +// --------------------------------------------------------------- + +const std::vector<GraphicElement> &Arch::getDecalGraphics(DecalId decal) const +{ + if (!decal_graphics.count(decal)) { + std::cerr << "No decal named " << decal.str(this) << std::endl; + log_error("No decal named %s!\n", decal.c_str(this)); + } + return decal_graphics.at(decal); +} + +DecalXY Arch::getBelDecal(BelId bel) const { return bels.at(bel).decalxy; } + +DecalXY Arch::getWireDecal(WireId wire) const { return wires.at(wire).decalxy; } + +DecalXY Arch::getPipDecal(PipId pip) const { return pips.at(pip).decalxy; } + +DecalXY Arch::getGroupDecal(GroupId group) const { return groups.at(group).decalxy; } + +// --------------------------------------------------------------- + +bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const +{ + if (!cellTiming.count(cell->name)) + return false; + const auto &tmg = cellTiming.at(cell->name); + auto fnd = tmg.combDelays.find(CellDelayKey{fromPort, toPort}); + if (fnd != tmg.combDelays.end()) { + delay = fnd->second; + return true; + } else { + return false; + } +} + +// Get the port class, also setting clockPort if applicable +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const +{ + if (!cellTiming.count(cell->name)) + return TMG_IGNORE; + const auto &tmg = cellTiming.at(cell->name); + if (tmg.clockingInfo.count(port)) + clockInfoCount = int(tmg.clockingInfo.at(port).size()); + else + clockInfoCount = 0; + return get_or_default(tmg.portClasses, port, TMG_IGNORE); +} + +TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const +{ + NPNR_ASSERT(cellTiming.count(cell->name)); + const auto &tmg = cellTiming.at(cell->name); + NPNR_ASSERT(tmg.clockingInfo.count(port)); + return tmg.clockingInfo.at(port).at(index); +} + +bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const +{ + std::vector<const CellInfo *> cells; + cells.push_back(cell); + Loc loc = getBelLocation(bel); + for (auto tbel : getBelsByTile(loc.x, loc.y)) { + if (tbel == bel) + continue; + CellInfo *bound = getBoundBelCell(tbel); + if (bound != nullptr) + cells.push_back(bound); + } + return cellsCompatible(cells.data(), int(cells.size())); +} + +bool Arch::isBelLocationValid(BelId bel) const +{ + std::vector<const CellInfo *> cells; + Loc loc = getBelLocation(bel); + for (auto tbel : getBelsByTile(loc.x, loc.y)) { + CellInfo *bound = getBoundBelCell(tbel); + if (bound != nullptr) + cells.push_back(bound); + } + return cellsCompatible(cells.data(), int(cells.size())); +} + +#ifdef WITH_HEAP +const std::string Arch::defaultPlacer = "heap"; +#else +const std::string Arch::defaultPlacer = "sa"; +#endif + +const std::vector<std::string> Arch::availablePlacers = {"sa", +#ifdef WITH_HEAP + "heap" +#endif +}; + +const std::string Arch::defaultRouter = "router1"; +const std::vector<std::string> Arch::availableRouters = {"router1", "router2"}; + +void Arch::assignArchInfo() +{ + for (auto &cell : getCtx()->cells) { + IdString cname = cell.first; + CellInfo *ci = cell.second.get(); + if (ci->type == id("SLICE")) { + ci->is_slice = true; + ci->ff_used = ci->params.at(id_FF_USED).as_bool(); + ci->slice_clk = get_net_or_empty(ci, id("CLK")); + ci->slice_ce = get_net_or_empty(ci, id("CE")); + ci->slice_lsr = get_net_or_empty(ci, id("LSR")); + + // add timing paths + addCellTimingClock(cname, id_CLK); + IdString ports[4] = {id_A, id_B, id_C, id_D}; + for (int i = 0; i < 4; i++) { + DelayInfo setup = delayLookup(speed->dff.timings.get(), speed->dff.num_timings, id_clksetpos); + DelayInfo hold = delayLookup(speed->dff.timings.get(), speed->dff.num_timings, id_clkholdpos); + // DelayInfo setup = getDelayFromNS(0.1); + // DelayInfo hold = getDelayFromNS(0.1); + addCellTimingSetupHold(cname, ports[i], id_CLK, setup, hold); + } + DelayInfo clkout = delayLookup(speed->dff.timings.get(), speed->dff.num_timings, id_clk_qpos); + // DelayInfo clkout = getDelayFromNS(0.1); + addCellTimingClockToOut(cname, id_Q, id_CLK, clkout); + IdString port_delay[4] = {id_a_f, id_b_f, id_c_f, id_d_f}; + for (int i = 0; i < 4; i++) { + DelayInfo delay = delayLookup(speed->lut.timings.get(), speed->lut.num_timings, port_delay[i]); + // DelayInfo delay = getDelayFromNS(0.1); + addCellTimingDelay(cname, ports[i], id_F, delay); + } + + } else { + ci->is_slice = false; + } + } +} + +bool Arch::cellsCompatible(const CellInfo **cells, int count) const +{ + const NetInfo *clk[4] = {nullptr, nullptr, nullptr, nullptr}; + const NetInfo *ce[4] = {nullptr, nullptr, nullptr, nullptr}; + const NetInfo *lsr[4] = {nullptr, nullptr, nullptr, nullptr}; + for (int i = 0; i < count; i++) { + const CellInfo *ci = cells[i]; + if (ci->is_slice && ci->slice_clk != nullptr) { + Loc loc = getBelLocation(ci->bel); + int cls = loc.z / 2; + if (loc.z >= 6 && ci->ff_used) // top slice have no ff + return false; + if (clk[cls] == nullptr) + clk[cls] = ci->slice_clk; + else if (clk[cls] != ci->slice_clk) + return false; + if (ce[cls] == nullptr) + ce[cls] = ci->slice_ce; + else if (ce[cls] != ci->slice_ce) + return false; + if (lsr[cls] == nullptr) + lsr[cls] = ci->slice_lsr; + else if (lsr[cls] != ci->slice_lsr) + return false; + } + } + return true; +} + +NEXTPNR_NAMESPACE_END |