2018-07-06 18:15:07 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
2021-06-09 20:09:08 +08:00
|
|
|
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
|
|
|
* Copyright (C) 2018 gatecat <gatecat@ds0.me>
|
2018-07-06 18:15:07 +08:00
|
|
|
*
|
|
|
|
* 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 <algorithm>
|
2019-02-09 20:34:57 +08:00
|
|
|
#include <boost/iostreams/device/mapped_file.hpp>
|
2018-10-16 20:30:23 +08:00
|
|
|
#include <boost/range/adaptor/reversed.hpp>
|
2018-07-06 18:15:07 +08:00
|
|
|
#include <cmath>
|
2018-07-06 20:02:37 +08:00
|
|
|
#include <cstring>
|
2020-06-27 19:20:16 +08:00
|
|
|
#include "embed.h"
|
2018-07-31 20:39:37 +08:00
|
|
|
#include "gfx.h"
|
2018-09-30 01:37:17 +08:00
|
|
|
#include "globals.h"
|
2018-07-06 18:15:07 +08:00
|
|
|
#include "log.h"
|
|
|
|
#include "nextpnr.h"
|
2018-07-12 00:15:08 +08:00
|
|
|
#include "placer1.h"
|
2019-01-26 21:22:44 +08:00
|
|
|
#include "placer_heap.h"
|
2018-07-12 00:04:09 +08:00
|
|
|
#include "router1.h"
|
2019-11-16 19:04:39 +08:00
|
|
|
#include "router2.h"
|
2018-09-30 01:29:23 +08:00
|
|
|
#include "timing.h"
|
2018-09-30 01:37:17 +08:00
|
|
|
#include "util.h"
|
2018-07-06 20:02:37 +08:00
|
|
|
|
2018-07-06 18:15:07 +08:00
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void IdString::initialize_arch(const BaseCtx *ctx)
|
|
|
|
{
|
2018-08-09 01:08:43 +08:00
|
|
|
#define X(t) initialize_add(ctx, #t, ID_##t);
|
2018-08-19 23:59:36 +08:00
|
|
|
|
2018-08-09 01:08:43 +08:00
|
|
|
#include "constids.inc"
|
2018-08-19 23:59:36 +08:00
|
|
|
|
2018-07-06 18:15:07 +08:00
|
|
|
#undef X
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2020-06-27 19:20:16 +08:00
|
|
|
static const ChipInfoPOD *get_chip_info(ArchArgs::ArchArgsTypes chip)
|
|
|
|
{
|
2020-06-25 23:11:47 +08:00
|
|
|
std::string chipdb;
|
2020-06-27 19:20:16 +08:00
|
|
|
if (chip == ArchArgs::LFE5U_12F || chip == ArchArgs::LFE5U_25F || chip == ArchArgs::LFE5UM_25F ||
|
|
|
|
chip == ArchArgs::LFE5UM5G_25F) {
|
2020-06-25 23:11:47 +08:00
|
|
|
chipdb = "ecp5/chipdb-25k.bin";
|
2020-06-27 19:20:16 +08:00
|
|
|
} else if (chip == ArchArgs::LFE5U_45F || chip == ArchArgs::LFE5UM_45F || chip == ArchArgs::LFE5UM5G_45F) {
|
2020-06-25 23:11:47 +08:00
|
|
|
chipdb = "ecp5/chipdb-45k.bin";
|
2020-06-27 19:20:16 +08:00
|
|
|
} else if (chip == ArchArgs::LFE5U_85F || chip == ArchArgs::LFE5UM_85F || chip == ArchArgs::LFE5UM5G_85F) {
|
2020-06-25 23:11:47 +08:00
|
|
|
chipdb = "ecp5/chipdb-85k.bin";
|
|
|
|
} else {
|
|
|
|
log_error("Unknown chip\n");
|
|
|
|
}
|
2019-02-09 20:34:57 +08:00
|
|
|
|
2020-06-25 23:11:47 +08:00
|
|
|
auto ptr = reinterpret_cast<const RelPtr<ChipInfoPOD> *>(get_chipdb(chipdb));
|
|
|
|
if (ptr == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
return ptr->get();
|
|
|
|
}
|
2019-02-09 20:34:57 +08:00
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
bool Arch::is_available(ArchArgs::ArchArgsTypes chip) { return get_chip_info(chip) != nullptr; }
|
2019-02-09 20:34:57 +08:00
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
std::vector<std::string> Arch::get_supported_packages(ArchArgs::ArchArgsTypes chip)
|
2019-02-09 20:34:57 +08:00
|
|
|
{
|
2020-06-25 23:11:47 +08:00
|
|
|
const ChipInfoPOD *chip_info = get_chip_info(chip);
|
|
|
|
std::vector<std::string> packages;
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &pkg : chip_info->package_info)
|
|
|
|
packages.push_back(pkg.name.get());
|
2020-06-25 23:11:47 +08:00
|
|
|
return packages;
|
2019-02-09 20:34:57 +08:00
|
|
|
}
|
2020-06-25 23:11:47 +08:00
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
2018-07-08 17:15:30 +08:00
|
|
|
|
2018-07-06 18:15:07 +08:00
|
|
|
Arch::Arch(ArchArgs args) : args(args)
|
|
|
|
{
|
2020-06-25 23:11:47 +08:00
|
|
|
chip_info = get_chip_info(args.type);
|
|
|
|
if (chip_info == nullptr)
|
2018-07-08 17:15:30 +08:00
|
|
|
log_error("Unsupported ECP5 chip type.\n");
|
2019-10-27 03:38:28 +08:00
|
|
|
if (chip_info->const_id_count != DB_CONST_ID_COUNT)
|
|
|
|
log_error("Chip database 'bba' and nextpnr code are out of sync; please rebuild (or contact distribution "
|
|
|
|
"maintainer)!\n");
|
2020-06-25 23:11:47 +08:00
|
|
|
|
2018-07-18 22:01:53 +08:00
|
|
|
package_info = nullptr;
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &pkg : chip_info->package_info) {
|
|
|
|
if (args.package == pkg.name.get()) {
|
|
|
|
package_info = &pkg;
|
2018-07-18 22:01:53 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-11-16 01:24:16 +08:00
|
|
|
speed_grade = &(chip_info->speed_grades[args.speed]);
|
2018-07-18 22:01:53 +08:00
|
|
|
if (!package_info)
|
|
|
|
log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
|
2018-08-19 02:04:32 +08:00
|
|
|
|
2022-03-31 18:17:57 +08:00
|
|
|
tile_status.resize(chip_info->num_tiles);
|
|
|
|
for (int i = 0; i < chip_info->num_tiles; i++) {
|
|
|
|
auto &ts = tile_status.at(i);
|
|
|
|
auto &tile_data = chip_info->tile_info[i];
|
|
|
|
ts.boundcells.resize(chip_info->locations[chip_info->location_type[i]].bel_data.size(), nullptr);
|
|
|
|
for (auto &name : tile_data.tile_names) {
|
|
|
|
if (strcmp(chip_info->tiletype_names[name.type_idx].get(), "PLC2") == 0) {
|
|
|
|
// Is a logic tile
|
|
|
|
ts.lts = new LogicTileStatus();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-29 10:52:08 +08:00
|
|
|
|
2021-02-04 20:07:11 +08:00
|
|
|
BaseArch::init_cell_types();
|
|
|
|
BaseArch::init_bel_buckets();
|
2021-01-29 20:12:12 +08:00
|
|
|
|
|
|
|
for (int i = 0; i < chip_info->width; i++)
|
2022-08-10 17:57:17 +08:00
|
|
|
x_ids.push_back(idf("X%d", i));
|
2021-01-29 20:12:12 +08:00
|
|
|
for (int i = 0; i < chip_info->height; i++)
|
2022-08-10 17:57:17 +08:00
|
|
|
y_ids.push_back(idf("Y%d", i));
|
2021-01-29 21:30:56 +08:00
|
|
|
|
|
|
|
for (int i = 0; i < chip_info->width; i++) {
|
2022-08-10 17:57:17 +08:00
|
|
|
IdString x_id = idf("X%d", i);
|
2021-01-29 21:30:56 +08:00
|
|
|
x_ids.push_back(x_id);
|
|
|
|
id_to_x[x_id] = i;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < chip_info->height; i++) {
|
2022-08-10 17:57:17 +08:00
|
|
|
IdString y_id = idf("Y%d", i);
|
2021-01-29 21:30:56 +08:00
|
|
|
y_ids.push_back(y_id);
|
|
|
|
id_to_y[y_id] = i;
|
|
|
|
}
|
2021-12-10 15:46:15 +08:00
|
|
|
|
|
|
|
wire_tile_vecidx.resize(chip_info->num_tiles, -1);
|
|
|
|
int n_wires = 0;
|
|
|
|
for (auto e : getWires()) {
|
|
|
|
if (e.index == 0) {
|
|
|
|
wire_tile_vecidx.at(e.location.y * chip_info->width + e.location.x) = n_wires;
|
|
|
|
}
|
|
|
|
n_wires++;
|
|
|
|
}
|
|
|
|
wire2net.resize(n_wires, nullptr);
|
|
|
|
wire_fanout.resize(n_wires, 0);
|
|
|
|
|
|
|
|
pip_tile_vecidx.resize(chip_info->num_tiles, -1);
|
|
|
|
int n_pips = 0;
|
|
|
|
for (auto e : getPips()) {
|
|
|
|
if (e.index == 0) {
|
|
|
|
pip_tile_vecidx.at(e.location.y * chip_info->width + e.location.x) = n_pips;
|
|
|
|
}
|
|
|
|
n_pips++;
|
|
|
|
}
|
|
|
|
pip2net.resize(n_pips, nullptr);
|
2021-12-14 00:04:45 +08:00
|
|
|
|
|
|
|
lutperm_allowed.resize(chip_info->width * chip_info->height * 4);
|
2018-07-06 18:15:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2018-07-31 23:01:38 +08:00
|
|
|
std::string Arch::getChipName() const
|
2018-07-06 18:15:07 +08:00
|
|
|
{
|
2020-03-13 19:22:11 +08:00
|
|
|
if (args.type == ArchArgs::LFE5U_12F) {
|
|
|
|
return "LFE5U-12F";
|
|
|
|
} else if (args.type == ArchArgs::LFE5U_25F) {
|
2018-07-09 18:02:31 +08:00
|
|
|
return "LFE5U-25F";
|
2018-07-06 20:02:37 +08:00
|
|
|
} else if (args.type == ArchArgs::LFE5U_45F) {
|
2018-07-09 18:02:31 +08:00
|
|
|
return "LFE5U-45F";
|
2018-07-06 20:02:37 +08:00
|
|
|
} else if (args.type == ArchArgs::LFE5U_85F) {
|
2018-07-09 18:02:31 +08:00
|
|
|
return "LFE5U-85F";
|
2018-10-16 21:37:24 +08:00
|
|
|
} else if (args.type == ArchArgs::LFE5UM_25F) {
|
|
|
|
return "LFE5UM-25F";
|
|
|
|
} else if (args.type == ArchArgs::LFE5UM_45F) {
|
|
|
|
return "LFE5UM-45F";
|
|
|
|
} else if (args.type == ArchArgs::LFE5UM_85F) {
|
|
|
|
return "LFE5UM-85F";
|
|
|
|
} else if (args.type == ArchArgs::LFE5UM5G_25F) {
|
|
|
|
return "LFE5UM5G-25F";
|
|
|
|
} else if (args.type == ArchArgs::LFE5UM5G_45F) {
|
|
|
|
return "LFE5UM5G-45F";
|
|
|
|
} else if (args.type == ArchArgs::LFE5UM5G_85F) {
|
|
|
|
return "LFE5UM5G-85F";
|
2018-07-06 18:15:07 +08:00
|
|
|
} else {
|
|
|
|
log_error("Unknown chip\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
std::string Arch::get_full_chip_name() const
|
2019-08-27 21:36:20 +08:00
|
|
|
{
|
|
|
|
std::string name = getChipName();
|
|
|
|
name += "-";
|
|
|
|
switch (args.speed) {
|
|
|
|
case ArchArgs::SPEED_6:
|
|
|
|
name += "6";
|
|
|
|
break;
|
|
|
|
case ArchArgs::SPEED_7:
|
|
|
|
name += "7";
|
|
|
|
break;
|
|
|
|
case ArchArgs::SPEED_8:
|
|
|
|
case ArchArgs::SPEED_8_5G:
|
|
|
|
name += "8";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
name += args.package;
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2018-07-06 18:15:07 +08:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
IdString Arch::archArgsToId(ArchArgs args) const
|
|
|
|
{
|
2020-03-13 19:22:11 +08:00
|
|
|
if (args.type == ArchArgs::LFE5U_12F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5u_12f;
|
2018-07-06 20:02:37 +08:00
|
|
|
if (args.type == ArchArgs::LFE5U_25F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5u_25f;
|
2018-07-06 20:02:37 +08:00
|
|
|
if (args.type == ArchArgs::LFE5U_45F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5u_45f;
|
2018-07-06 20:02:37 +08:00
|
|
|
if (args.type == ArchArgs::LFE5U_85F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5u_85f;
|
2018-10-16 21:37:24 +08:00
|
|
|
if (args.type == ArchArgs::LFE5UM_25F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5um_25f;
|
2018-10-16 21:37:24 +08:00
|
|
|
if (args.type == ArchArgs::LFE5UM_45F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5um_45f;
|
2018-10-16 21:37:24 +08:00
|
|
|
if (args.type == ArchArgs::LFE5UM_85F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5um_85f;
|
2018-10-16 21:37:24 +08:00
|
|
|
if (args.type == ArchArgs::LFE5UM5G_25F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5um5g_25f;
|
2018-10-16 21:37:24 +08:00
|
|
|
if (args.type == ArchArgs::LFE5UM5G_45F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5um5g_45f;
|
2018-10-16 21:37:24 +08:00
|
|
|
if (args.type == ArchArgs::LFE5UM5G_85F)
|
2022-02-17 01:09:54 +08:00
|
|
|
return id_lfe5um5g_85f;
|
2018-07-06 18:15:07 +08:00
|
|
|
return IdString();
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2021-01-29 20:12:12 +08:00
|
|
|
BelId Arch::getBelByName(IdStringList name) const
|
2018-07-06 18:15:07 +08:00
|
|
|
{
|
2021-01-29 21:30:56 +08:00
|
|
|
if (name.size() != 3)
|
|
|
|
return BelId();
|
2018-07-06 18:15:07 +08:00
|
|
|
BelId ret;
|
2018-07-06 20:02:37 +08:00
|
|
|
Location loc;
|
2021-01-29 21:30:56 +08:00
|
|
|
loc.x = id_to_x.at(name[0]);
|
|
|
|
loc.y = id_to_y.at(name[1]);
|
2018-07-06 20:02:37 +08:00
|
|
|
ret.location = loc;
|
2021-02-03 18:33:06 +08:00
|
|
|
const LocationTypePOD *loci = loc_info(ret);
|
2021-02-08 19:24:00 +08:00
|
|
|
for (int i = 0; i < loci->bel_data.ssize(); i++) {
|
2021-01-29 21:30:56 +08:00
|
|
|
if (std::strcmp(loci->bel_data[i].name.get(), name[2].c_str(this)) == 0) {
|
2018-07-06 20:02:37 +08:00
|
|
|
ret.index = i;
|
2021-01-29 21:30:56 +08:00
|
|
|
return ret;
|
2018-07-06 20:02:37 +08:00
|
|
|
}
|
|
|
|
}
|
2021-01-29 21:30:56 +08:00
|
|
|
return BelId();
|
2018-07-06 18:15:07 +08:00
|
|
|
}
|
|
|
|
|
2018-07-24 22:09:29 +08:00
|
|
|
BelRange Arch::getBelsByTile(int x, int y) const
|
2018-07-06 18:15:07 +08:00
|
|
|
{
|
|
|
|
BelRange br;
|
2018-07-24 22:09:29 +08:00
|
|
|
|
|
|
|
br.b.cursor_tile = y * chip_info->width + x;
|
|
|
|
br.e.cursor_tile = y * chip_info->width + x;
|
2018-07-06 20:02:37 +08:00
|
|
|
br.b.cursor_index = 0;
|
2021-02-08 19:24:00 +08:00
|
|
|
br.e.cursor_index = chip_info->locations[chip_info->location_type[br.b.cursor_tile]].bel_data.ssize() - 1;
|
2018-07-17 22:18:06 +08:00
|
|
|
br.b.chip = chip_info;
|
|
|
|
br.e.chip = chip_info;
|
2018-07-24 22:38:35 +08:00
|
|
|
if (br.e.cursor_index == -1)
|
|
|
|
++br.e.cursor_index;
|
|
|
|
else
|
|
|
|
++br.e;
|
2018-07-06 18:15:07 +08:00
|
|
|
return br;
|
|
|
|
}
|
|
|
|
|
2018-08-09 01:08:43 +08:00
|
|
|
WireId Arch::getBelPinWire(BelId bel, IdString pin) const
|
2018-07-06 18:15:07 +08:00
|
|
|
{
|
|
|
|
WireId ret;
|
|
|
|
|
|
|
|
NPNR_ASSERT(bel != BelId());
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
for (auto &bw : loc_info(bel)->bel_data[bel.index].bel_wires)
|
2021-01-28 03:39:19 +08:00
|
|
|
if (bw.port == pin.index) {
|
|
|
|
ret.location = bel.location + bw.rel_wire_loc;
|
|
|
|
ret.index = bw.wire_index;
|
2018-07-06 18:15:07 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-08-09 01:08:43 +08:00
|
|
|
PortType Arch::getBelPinType(BelId bel, IdString pin) const
|
2018-07-22 23:07:38 +08:00
|
|
|
{
|
|
|
|
NPNR_ASSERT(bel != BelId());
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
for (auto &bw : loc_info(bel)->bel_data[bel.index].bel_wires)
|
2021-01-28 03:39:19 +08:00
|
|
|
if (bw.port == pin.index)
|
|
|
|
return PortType(bw.type);
|
2018-07-22 23:07:38 +08:00
|
|
|
|
|
|
|
return PORT_INOUT;
|
|
|
|
}
|
|
|
|
|
2018-07-06 18:15:07 +08:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2021-01-29 21:30:56 +08:00
|
|
|
WireId Arch::getWireByName(IdStringList name) const
|
2018-07-06 18:15:07 +08:00
|
|
|
{
|
2021-01-29 21:30:56 +08:00
|
|
|
if (name.size() != 3)
|
|
|
|
return WireId();
|
2018-07-06 18:15:07 +08:00
|
|
|
WireId ret;
|
2018-07-06 20:02:37 +08:00
|
|
|
Location loc;
|
2021-01-29 21:30:56 +08:00
|
|
|
loc.x = id_to_x.at(name[0]);
|
|
|
|
loc.y = id_to_y.at(name[1]);
|
2018-07-06 20:02:37 +08:00
|
|
|
ret.location = loc;
|
2021-02-03 18:33:06 +08:00
|
|
|
const LocationTypePOD *loci = loc_info(ret);
|
2021-02-08 19:24:00 +08:00
|
|
|
for (int i = 0; i < loci->wire_data.ssize(); i++) {
|
2021-01-29 21:30:56 +08:00
|
|
|
if (std::strcmp(loci->wire_data[i].name.get(), name[2].c_str(this)) == 0) {
|
2018-07-06 20:02:37 +08:00
|
|
|
ret.index = i;
|
2021-01-29 21:30:56 +08:00
|
|
|
return ret;
|
2018-07-06 20:02:37 +08:00
|
|
|
}
|
|
|
|
}
|
2021-01-29 21:30:56 +08:00
|
|
|
return WireId();
|
2018-07-06 18:15:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2021-01-29 21:30:56 +08:00
|
|
|
PipId Arch::getPipByName(IdStringList name) const
|
2018-07-06 18:15:07 +08:00
|
|
|
{
|
2021-01-29 21:30:56 +08:00
|
|
|
if (name.size() != 3)
|
|
|
|
return PipId();
|
2018-07-06 18:15:07 +08:00
|
|
|
auto it = pip_by_name.find(name);
|
|
|
|
if (it != pip_by_name.end())
|
2018-07-06 20:02:37 +08:00
|
|
|
return it->second;
|
2018-07-06 18:15:07 +08:00
|
|
|
|
2018-07-06 20:02:37 +08:00
|
|
|
PipId ret;
|
|
|
|
Location loc;
|
|
|
|
std::string basename;
|
2021-01-29 21:30:56 +08:00
|
|
|
loc.x = id_to_x.at(name[0]);
|
|
|
|
loc.y = id_to_y.at(name[1]);
|
2018-07-24 22:38:35 +08:00
|
|
|
ret.location = loc;
|
2021-02-03 18:33:06 +08:00
|
|
|
const LocationTypePOD *loci = loc_info(ret);
|
2021-02-08 19:24:00 +08:00
|
|
|
for (int i = 0; i < loci->pip_data.ssize(); i++) {
|
2018-07-06 20:02:37 +08:00
|
|
|
PipId curr;
|
|
|
|
curr.location = loc;
|
|
|
|
curr.index = i;
|
2018-07-15 01:50:23 +08:00
|
|
|
pip_by_name[getPipName(curr)] = curr;
|
2018-07-06 20:02:37 +08:00
|
|
|
}
|
2018-07-24 22:38:35 +08:00
|
|
|
if (pip_by_name.find(name) == pip_by_name.end())
|
2021-01-29 21:30:56 +08:00
|
|
|
NPNR_ASSERT_FALSE_STR("no pip named " + name.str(getCtx()));
|
2018-07-06 20:02:37 +08:00
|
|
|
return pip_by_name[name];
|
2018-07-06 18:15:07 +08:00
|
|
|
}
|
|
|
|
|
2021-01-29 21:30:56 +08:00
|
|
|
IdStringList Arch::getPipName(PipId pip) const
|
2018-07-06 18:15:07 +08:00
|
|
|
{
|
|
|
|
NPNR_ASSERT(pip != PipId());
|
|
|
|
|
2021-01-29 21:30:56 +08:00
|
|
|
// TODO: can we improve how pip names are stored/built?
|
2021-02-03 18:33:06 +08:00
|
|
|
auto &pip_data = loc_info(pip)->pip_data[pip.index];
|
2021-01-29 21:30:56 +08:00
|
|
|
WireId src = getPipSrcWire(pip), dst = getPipDstWire(pip);
|
|
|
|
std::string pip_name = stringf("%d_%d_%s->%d_%d_%s", pip_data.rel_src_loc.x, pip_data.rel_src_loc.y,
|
2021-02-03 18:33:06 +08:00
|
|
|
get_wire_basename(src).c_str(this), pip_data.rel_dst_loc.x, pip_data.rel_dst_loc.y,
|
|
|
|
get_wire_basename(dst).c_str(this));
|
2018-07-06 18:15:07 +08:00
|
|
|
|
2021-01-29 21:30:56 +08:00
|
|
|
std::array<IdString, 3> ids{x_ids.at(pip.location.x), y_ids.at(pip.location.y), id(pip_name)};
|
|
|
|
return IdStringList(ids);
|
2018-07-06 18:15:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
BelId Arch::get_package_pin_bel(const std::string &pin) const
|
2018-07-18 22:01:53 +08:00
|
|
|
{
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &ppin : package_info->pin_data) {
|
|
|
|
if (ppin.name.get() == pin) {
|
2018-07-18 22:01:53 +08:00
|
|
|
BelId bel;
|
2021-01-28 03:39:19 +08:00
|
|
|
bel.location = ppin.abs_loc;
|
|
|
|
bel.index = ppin.bel_index;
|
2018-07-18 22:01:53 +08:00
|
|
|
return bel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return BelId();
|
|
|
|
}
|
2018-07-06 18:15:07 +08:00
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
std::string Arch::get_bel_package_pin(BelId bel) const
|
2018-07-18 22:01:53 +08:00
|
|
|
{
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &ppin : package_info->pin_data) {
|
|
|
|
if (Location(ppin.abs_loc) == bel.location && ppin.bel_index == bel.index) {
|
|
|
|
return ppin.name.get();
|
2018-07-18 22:01:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
2018-07-22 23:07:38 +08:00
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
int Arch::get_pio_bel_bank(BelId bel) const
|
2018-07-24 01:15:59 +08:00
|
|
|
{
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &pio : chip_info->pio_info) {
|
|
|
|
if (Location(pio.abs_loc) == bel.location && pio.bel_index == bel.index) {
|
|
|
|
return pio.bank;
|
2018-07-24 01:15:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
NPNR_ASSERT_FALSE("failed to find PIO");
|
|
|
|
}
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
std::string Arch::get_pio_function_name(BelId bel) const
|
2018-07-24 01:15:59 +08:00
|
|
|
{
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &pio : chip_info->pio_info) {
|
|
|
|
if (Location(pio.abs_loc) == bel.location && pio.bel_index == bel.index) {
|
|
|
|
const char *func = pio.function_name.get();
|
2018-07-24 01:15:59 +08:00
|
|
|
if (func == nullptr)
|
|
|
|
return "";
|
|
|
|
else
|
|
|
|
return func;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NPNR_ASSERT_FALSE("failed to find PIO");
|
|
|
|
}
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
BelId Arch::get_pio_by_function_name(const std::string &name) const
|
2018-07-24 01:15:59 +08:00
|
|
|
{
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &pio : chip_info->pio_info) {
|
|
|
|
const char *func = pio.function_name.get();
|
2018-07-24 01:15:59 +08:00
|
|
|
if (func != nullptr && func == name) {
|
|
|
|
BelId bel;
|
2021-01-28 03:39:19 +08:00
|
|
|
bel.location = pio.abs_loc;
|
|
|
|
bel.index = pio.bel_index;
|
2018-07-24 01:15:59 +08:00
|
|
|
return bel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return BelId();
|
|
|
|
}
|
|
|
|
|
2018-08-09 01:08:43 +08:00
|
|
|
std::vector<IdString> Arch::getBelPins(BelId bel) const
|
2018-07-22 23:07:38 +08:00
|
|
|
{
|
2018-08-09 01:08:43 +08:00
|
|
|
std::vector<IdString> ret;
|
2018-07-22 23:07:38 +08:00
|
|
|
NPNR_ASSERT(bel != BelId());
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
for (auto &bw : loc_info(bel)->bel_data[bel.index].bel_wires) {
|
2018-08-09 01:08:43 +08:00
|
|
|
IdString id;
|
2021-01-28 03:39:19 +08:00
|
|
|
id.index = bw.port;
|
2018-08-09 01:08:43 +08:00
|
|
|
ret.push_back(id);
|
|
|
|
}
|
2018-07-22 23:07:38 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-23 18:45:31 +08:00
|
|
|
BelId Arch::getBelByLocation(Loc loc) const
|
|
|
|
{
|
2018-07-23 16:53:07 +08:00
|
|
|
if (loc.x >= chip_info->width || loc.y >= chip_info->height)
|
|
|
|
return BelId();
|
|
|
|
const LocationTypePOD &locI = chip_info->locations[chip_info->location_type[loc.y * chip_info->width + loc.x]];
|
2021-02-08 19:24:00 +08:00
|
|
|
for (int i = 0; i < locI.bel_data.ssize(); i++) {
|
2018-07-23 16:53:07 +08:00
|
|
|
if (locI.bel_data[i].z == loc.z) {
|
|
|
|
BelId bi;
|
|
|
|
bi.location.x = loc.x;
|
|
|
|
bi.location.y = loc.y;
|
|
|
|
bi.index = i;
|
|
|
|
return bi;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return BelId();
|
|
|
|
}
|
|
|
|
|
2018-07-06 18:15:07 +08:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2018-07-08 20:24:32 +08:00
|
|
|
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
|
|
|
{
|
2021-02-03 18:33:06 +08:00
|
|
|
int num_uh = loc_info(dst)->wire_data[dst.index].pips_uphill.size();
|
2019-02-08 03:19:15 +08:00
|
|
|
if (num_uh < 6) {
|
|
|
|
for (auto uh : getPipsUphill(dst)) {
|
|
|
|
if (getPipSrcWire(uh) == src)
|
|
|
|
return getPipDelay(uh).maxDelay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 19:04:13 +08:00
|
|
|
auto est_location = [&](WireId w) -> std::pair<int, int> {
|
2021-02-03 18:33:06 +08:00
|
|
|
const auto &wire = loc_info(w)->wire_data[w.index];
|
2019-08-27 20:14:41 +08:00
|
|
|
if (w == gsrclk_wire) {
|
|
|
|
auto phys_wire = getPipSrcWire(*(getPipsUphill(w).begin()));
|
|
|
|
return std::make_pair(int(phys_wire.location.x), int(phys_wire.location.y));
|
2021-01-28 03:39:19 +08:00
|
|
|
} else if (wire.bel_pins.size() > 0) {
|
2019-02-25 19:04:13 +08:00
|
|
|
return std::make_pair(w.location.x + wire.bel_pins[0].rel_bel_loc.x,
|
|
|
|
w.location.y + wire.bel_pins[0].rel_bel_loc.y);
|
2021-01-28 03:39:19 +08:00
|
|
|
} else if (wire.pips_downhill.size() > 0) {
|
2019-02-25 19:04:13 +08:00
|
|
|
return std::make_pair(w.location.x + wire.pips_downhill[0].rel_loc.x,
|
|
|
|
w.location.y + wire.pips_downhill[0].rel_loc.y);
|
2021-01-28 03:39:19 +08:00
|
|
|
} else if (wire.pips_uphill.size() > 0) {
|
2019-02-25 19:04:13 +08:00
|
|
|
return std::make_pair(w.location.x + wire.pips_uphill[0].rel_loc.x,
|
|
|
|
w.location.y + wire.pips_uphill[0].rel_loc.y);
|
2019-02-12 18:56:17 +08:00
|
|
|
} else {
|
2019-02-25 19:04:13 +08:00
|
|
|
return std::make_pair(int(w.location.x), int(w.location.y));
|
2019-02-12 18:56:17 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-10-25 16:37:13 +08:00
|
|
|
auto src_loc = est_location(src);
|
|
|
|
std::pair<int, int> dst_loc;
|
|
|
|
if (wire_loc_overrides.count(dst)) {
|
|
|
|
dst_loc = wire_loc_overrides.at(dst);
|
|
|
|
} else {
|
|
|
|
dst_loc = est_location(dst);
|
|
|
|
}
|
2019-02-12 18:56:17 +08:00
|
|
|
|
2019-02-08 03:19:15 +08:00
|
|
|
int dx = abs(src_loc.first - dst_loc.first), dy = abs(src_loc.second - dst_loc.second);
|
2019-02-25 19:56:10 +08:00
|
|
|
|
2019-06-21 17:55:23 +08:00
|
|
|
return (120 - 22 * args.speed) *
|
2019-02-25 19:07:21 +08:00
|
|
|
(6 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
|
2018-07-08 20:24:32 +08:00
|
|
|
}
|
2018-07-06 18:15:07 +08:00
|
|
|
|
2022-12-07 17:00:53 +08:00
|
|
|
BoundingBox Arch::getRouteBoundingBox(WireId src, WireId dst) const
|
2019-11-16 19:04:39 +08:00
|
|
|
{
|
2022-12-07 17:00:53 +08:00
|
|
|
BoundingBox bb;
|
2020-01-16 20:00:52 +08:00
|
|
|
|
|
|
|
bb.x0 = src.location.x;
|
|
|
|
bb.y0 = src.location.y;
|
|
|
|
bb.x1 = src.location.x;
|
|
|
|
bb.y1 = src.location.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);
|
|
|
|
};
|
|
|
|
|
2019-11-16 19:04:39 +08:00
|
|
|
auto est_location = [&](WireId w) -> std::pair<int, int> {
|
2021-02-03 18:33:06 +08:00
|
|
|
const auto &wire = loc_info(w)->wire_data[w.index];
|
2019-11-16 19:04:39 +08:00
|
|
|
if (w == gsrclk_wire) {
|
|
|
|
auto phys_wire = getPipSrcWire(*(getPipsUphill(w).begin()));
|
|
|
|
return std::make_pair(int(phys_wire.location.x), int(phys_wire.location.y));
|
2021-01-28 03:39:19 +08:00
|
|
|
} else if (wire.bel_pins.size() > 0) {
|
2019-11-16 19:04:39 +08:00
|
|
|
return std::make_pair(w.location.x + wire.bel_pins[0].rel_bel_loc.x,
|
|
|
|
w.location.y + wire.bel_pins[0].rel_bel_loc.y);
|
2021-01-28 03:39:19 +08:00
|
|
|
} else if (wire.pips_downhill.size() > 0) {
|
2019-11-16 19:04:39 +08:00
|
|
|
return std::make_pair(w.location.x + wire.pips_downhill[0].rel_loc.x,
|
|
|
|
w.location.y + wire.pips_downhill[0].rel_loc.y);
|
2021-01-28 03:39:19 +08:00
|
|
|
} else if (wire.pips_uphill.size() > 0) {
|
2019-11-16 19:04:39 +08:00
|
|
|
return std::make_pair(w.location.x + wire.pips_uphill[0].rel_loc.x,
|
|
|
|
w.location.y + wire.pips_uphill[0].rel_loc.y);
|
|
|
|
} else {
|
|
|
|
return std::make_pair(int(w.location.x), int(w.location.y));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto src_loc = est_location(src);
|
2020-01-16 20:00:52 +08:00
|
|
|
extend(src_loc.first, src_loc.second);
|
|
|
|
if (wire_loc_overrides.count(src)) {
|
|
|
|
extend(wire_loc_overrides.at(src).first, wire_loc_overrides.at(src).second);
|
|
|
|
}
|
2019-11-16 19:04:39 +08:00
|
|
|
std::pair<int, int> dst_loc;
|
2020-01-16 20:00:52 +08:00
|
|
|
extend(dst.location.x, dst.location.y);
|
2019-11-16 19:04:39 +08:00
|
|
|
if (wire_loc_overrides.count(dst)) {
|
|
|
|
dst_loc = wire_loc_overrides.at(dst);
|
|
|
|
} else {
|
|
|
|
dst_loc = est_location(dst);
|
|
|
|
}
|
2020-01-16 20:00:52 +08:00
|
|
|
extend(dst_loc.first, dst_loc.second);
|
2019-11-16 19:04:39 +08:00
|
|
|
return bb;
|
|
|
|
}
|
|
|
|
|
2021-12-20 00:41:34 +08:00
|
|
|
delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const
|
2018-07-30 21:35:40 +08:00
|
|
|
{
|
2022-08-11 01:58:22 +08:00
|
|
|
if ((src_pin == id_FCO && dst_pin == id_FCI) || dst_pin.in(id_FXA, id_FXB) || (src_pin == id_F && dst_pin == id_DI))
|
2019-01-08 18:52:03 +08:00
|
|
|
return 0;
|
2021-12-20 00:41:34 +08:00
|
|
|
auto driver_loc = getBelLocation(src_bel);
|
|
|
|
auto sink_loc = getBelLocation(dst_bel);
|
2019-02-25 19:04:13 +08:00
|
|
|
// Encourage use of direct interconnect
|
2022-03-31 18:17:57 +08:00
|
|
|
// exact LUT input doesn't matter as they can be permuted by the router...
|
2019-02-25 19:04:13 +08:00
|
|
|
if (driver_loc.x == sink_loc.x && driver_loc.y == sink_loc.y) {
|
2022-03-31 18:17:57 +08:00
|
|
|
if (dst_pin.in(id_A, id_B, id_C, id_D) && src_pin == id_Q) {
|
|
|
|
int lut = (sink_loc.z >> lc_idx_shift), ff = (driver_loc.z >> lc_idx_shift);
|
|
|
|
if (lut == ff)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (dst_pin.in(id_A, id_B, id_C, id_D) && src_pin == id_F) {
|
|
|
|
int l0 = (driver_loc.z >> lc_idx_shift);
|
|
|
|
if (l0 != 1 && l0 != 6)
|
|
|
|
return 0;
|
|
|
|
}
|
2019-02-25 19:04:13 +08:00
|
|
|
}
|
|
|
|
|
2019-02-08 03:19:15 +08:00
|
|
|
int dx = abs(driver_loc.x - sink_loc.x), dy = abs(driver_loc.y - sink_loc.y);
|
2019-02-25 19:56:10 +08:00
|
|
|
|
2019-06-21 17:55:23 +08:00
|
|
|
return (120 - 22 * args.speed) *
|
2022-04-20 18:29:08 +08:00
|
|
|
(3 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
|
2018-07-30 21:35:40 +08:00
|
|
|
}
|
|
|
|
|
2019-02-25 18:54:24 +08:00
|
|
|
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const
|
|
|
|
{
|
2019-01-08 21:06:02 +08:00
|
|
|
if (net_info->driver.port == id_FCO && sink.port == id_FCI) {
|
|
|
|
budget = 0;
|
|
|
|
return true;
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (sink.port.in(id_FXA, id_FXB)) {
|
2019-01-08 21:06:02 +08:00
|
|
|
budget = 0;
|
2019-02-08 03:19:15 +08:00
|
|
|
return true;
|
2019-01-08 21:06:02 +08:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-07-27 12:37:19 +08:00
|
|
|
|
2019-06-21 17:55:23 +08:00
|
|
|
delay_t Arch::getRipupDelayPenalty() const { return 400; }
|
|
|
|
|
2018-07-06 18:15:07 +08:00
|
|
|
// -----------------------------------------------------------------------
|
2018-07-12 00:04:09 +08:00
|
|
|
|
2019-02-25 19:56:10 +08:00
|
|
|
bool Arch::place()
|
|
|
|
{
|
2022-02-17 01:09:54 +08:00
|
|
|
std::string placer = str_or_default(settings, id_placer, defaultPlacer);
|
2019-03-24 19:10:20 +08:00
|
|
|
|
|
|
|
if (placer == "heap") {
|
2019-02-25 20:48:01 +08:00
|
|
|
PlacerHeapCfg cfg(getCtx());
|
2019-06-21 17:20:46 +08:00
|
|
|
cfg.criticalityExponent = 4;
|
2019-02-25 20:48:01 +08:00
|
|
|
cfg.ioBufTypes.insert(id_TRELLIS_IO);
|
2021-03-31 09:20:09 +08:00
|
|
|
|
|
|
|
cfg.cellGroups.emplace_back();
|
|
|
|
cfg.cellGroups.back().insert(id_MULT18X18D);
|
|
|
|
cfg.cellGroups.back().insert(id_ALU54B);
|
|
|
|
|
2022-03-31 18:17:57 +08:00
|
|
|
cfg.cellGroups.emplace_back();
|
|
|
|
cfg.cellGroups.back().insert(id_TRELLIS_COMB);
|
|
|
|
cfg.cellGroups.back().insert(id_TRELLIS_FF);
|
|
|
|
cfg.cellGroups.back().insert(id_TRELLIS_RAMW);
|
|
|
|
cfg.placeAllAtOnce = true;
|
|
|
|
|
|
|
|
cfg.beta = 0.75;
|
|
|
|
|
2019-02-25 20:48:01 +08:00
|
|
|
if (!placer_heap(getCtx(), cfg))
|
2019-02-25 19:56:10 +08:00
|
|
|
return false;
|
2019-03-24 19:10:20 +08:00
|
|
|
} else if (placer == "sa") {
|
|
|
|
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str());
|
2019-02-25 19:56:10 +08:00
|
|
|
}
|
2019-06-22 23:57:00 +08:00
|
|
|
|
|
|
|
// In out-of-context mode, create a locked macro
|
|
|
|
if (bool_or_default(settings, id("arch.ooc")))
|
|
|
|
for (auto &cell : cells)
|
|
|
|
cell.second->belStrength = STRENGTH_LOCKED;
|
|
|
|
|
Add remapping of DSP clk/ce/rst signals in a block.
Each DSP block contains two slices, and each slice contains multiple
MULT18X18D and ALU54B units. Each unit configures each register to use
any of CLK0/1/2/3, CE0/1/2/3, and RST0/1/2/3 ports, and the ports are
connected per unit (so for example, two MULTs in the same block could
connect their CLK0s to different external signals). However, the
hardware only has one actual port per block, so it's required that
all CLK0 signals within a block are the same.
Because the packer is in general allowed to combine two unrelated units
into one block, it may end up combining units that use different signals
for the same port, which would eventually have caused a router failure.
This commit adds validity checks which ensure only unique signals are
used per block, and adds remapping so that conflicting signals are
automatically reassigned when possible and required.
2022-11-09 10:47:00 +08:00
|
|
|
// Once placement is complete, DSP slices sharing a block may need
|
|
|
|
// CLK/CE/RST ports remapped to avoid conflicting assignments.
|
|
|
|
remap_dsp_blocks();
|
|
|
|
|
2022-02-17 01:09:54 +08:00
|
|
|
getCtx()->settings[id_place] = 1;
|
2019-06-22 23:57:00 +08:00
|
|
|
|
2019-06-07 17:48:15 +08:00
|
|
|
archInfoToAttributes();
|
2019-02-25 19:56:10 +08:00
|
|
|
return true;
|
|
|
|
}
|
2018-07-12 00:15:08 +08:00
|
|
|
|
2018-09-30 01:37:17 +08:00
|
|
|
bool Arch::route()
|
|
|
|
{
|
2022-02-17 01:09:54 +08:00
|
|
|
std::string router = str_or_default(settings, id_router, defaultRouter);
|
2020-02-03 19:54:38 +08:00
|
|
|
|
2021-12-14 00:04:45 +08:00
|
|
|
disable_router_lutperm = getCtx()->setting<bool>("arch.disable_router_lutperm", false);
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
setup_wire_locations();
|
2018-09-29 23:49:29 +08:00
|
|
|
route_ecp5_globals(getCtx());
|
2019-06-07 18:55:20 +08:00
|
|
|
assignArchInfo();
|
2018-09-30 01:29:23 +08:00
|
|
|
assign_budget(getCtx(), true);
|
2020-02-03 19:54:38 +08:00
|
|
|
|
|
|
|
bool result;
|
|
|
|
if (router == "router1") {
|
|
|
|
result = router1(getCtx(), Router1Cfg(getCtx()));
|
|
|
|
} else if (router == "router2") {
|
|
|
|
router2(getCtx(), Router2Cfg(getCtx()));
|
2020-02-03 21:46:05 +08:00
|
|
|
result = true;
|
2020-02-03 19:54:38 +08:00
|
|
|
} else {
|
|
|
|
log_error("ECP5 architecture does not support router '%s'\n", router.c_str());
|
|
|
|
}
|
|
|
|
|
2022-02-17 01:09:54 +08:00
|
|
|
getCtx()->settings[id_route] = 1;
|
2019-06-07 17:48:15 +08:00
|
|
|
archInfoToAttributes();
|
2018-11-16 20:59:27 +08:00
|
|
|
return result;
|
2018-09-29 23:49:29 +08:00
|
|
|
}
|
2018-07-12 00:04:09 +08:00
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
2018-07-06 18:15:07 +08:00
|
|
|
|
2018-07-31 20:39:37 +08:00
|
|
|
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
2018-07-06 18:15:07 +08:00
|
|
|
{
|
|
|
|
std::vector<GraphicElement> ret;
|
2018-07-31 20:39:37 +08:00
|
|
|
|
2019-10-06 17:26:56 +08:00
|
|
|
if (decal.type == DecalId::TYPE_GROUP) {
|
|
|
|
int type = decal.z;
|
|
|
|
int x = decal.location.x;
|
2019-11-10 22:24:06 +08:00
|
|
|
int y = decal.location.y;
|
2019-10-06 17:26:56 +08:00
|
|
|
|
|
|
|
if (type == GroupId::TYPE_SWITCHBOX) {
|
|
|
|
GraphicElement el;
|
|
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
|
|
el.style = GraphicElement::STYLE_FRAME;
|
|
|
|
|
|
|
|
el.x1 = x + switchbox_x1;
|
|
|
|
el.x2 = x + switchbox_x2;
|
|
|
|
el.y1 = y + switchbox_y1;
|
|
|
|
el.y2 = y + switchbox_y2;
|
|
|
|
ret.push_back(el);
|
|
|
|
}
|
2019-12-08 00:41:22 +08:00
|
|
|
} else if (decal.type == DecalId::TYPE_WIRE) {
|
2019-10-06 23:59:44 +08:00
|
|
|
WireId wire;
|
|
|
|
wire.index = decal.z;
|
|
|
|
wire.location = decal.location;
|
|
|
|
auto wire_type = getWireType(wire);
|
|
|
|
int x = decal.location.x;
|
2019-11-10 22:24:06 +08:00
|
|
|
int y = decal.location.y;
|
2019-10-20 17:12:26 +08:00
|
|
|
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
2021-02-03 18:33:06 +08:00
|
|
|
GfxTileWireId tilewire = GfxTileWireId(loc_info(wire)->wire_data[wire.index].tile_wire);
|
2019-10-25 15:28:08 +08:00
|
|
|
gfxTileWire(ret, x, y, chip_info->width, chip_info->height, wire_type, tilewire, style);
|
2019-12-08 00:41:22 +08:00
|
|
|
} else if (decal.type == DecalId::TYPE_PIP) {
|
2019-11-09 20:12:20 +08:00
|
|
|
PipId pip;
|
|
|
|
pip.index = decal.z;
|
|
|
|
pip.location = decal.location;
|
|
|
|
WireId src_wire = getPipSrcWire(pip);
|
|
|
|
WireId dst_wire = getPipDstWire(pip);
|
|
|
|
int x = decal.location.x;
|
2019-11-10 22:24:06 +08:00
|
|
|
int y = decal.location.y;
|
2021-02-03 18:33:06 +08:00
|
|
|
GfxTileWireId src_id = GfxTileWireId(loc_info(src_wire)->wire_data[src_wire.index].tile_wire);
|
|
|
|
GfxTileWireId dst_id = GfxTileWireId(loc_info(dst_wire)->wire_data[dst_wire.index].tile_wire);
|
2019-11-09 20:12:20 +08:00
|
|
|
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_HIDDEN;
|
2019-12-08 16:33:06 +08:00
|
|
|
gfxTilePip(ret, x, y, chip_info->width, chip_info->height, src_wire, getWireType(src_wire), src_id, dst_wire,
|
|
|
|
getWireType(dst_wire), dst_id, style);
|
2019-12-08 00:41:22 +08:00
|
|
|
} else if (decal.type == DecalId::TYPE_BEL) {
|
2018-07-31 20:39:37 +08:00
|
|
|
BelId bel;
|
|
|
|
bel.index = decal.z;
|
|
|
|
bel.location = decal.location;
|
|
|
|
auto bel_type = getBelType(bel);
|
2019-10-06 17:26:56 +08:00
|
|
|
int x = decal.location.x;
|
2019-11-10 22:24:06 +08:00
|
|
|
int y = decal.location.y;
|
2021-02-03 18:33:06 +08:00
|
|
|
int z = loc_info(bel)->bel_data[bel.index].z;
|
2019-12-15 16:21:58 +08:00
|
|
|
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
|
|
|
gfxTileBel(ret, x, y, z, chip_info->width, chip_info->height, bel_type, style);
|
2018-07-31 20:39:37 +08:00
|
|
|
}
|
|
|
|
|
2018-07-06 18:15:07 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-31 20:39:37 +08:00
|
|
|
DecalXY Arch::getBelDecal(BelId bel) const
|
|
|
|
{
|
|
|
|
DecalXY decalxy;
|
|
|
|
decalxy.decal.type = DecalId::TYPE_BEL;
|
|
|
|
decalxy.decal.location = bel.location;
|
|
|
|
decalxy.decal.z = bel.index;
|
2022-03-31 18:17:57 +08:00
|
|
|
decalxy.decal.active = getBoundBelCell(bel) != nullptr;
|
2018-07-31 20:39:37 +08:00
|
|
|
return decalxy;
|
|
|
|
}
|
2018-07-06 18:15:07 +08:00
|
|
|
|
2019-10-06 23:59:44 +08:00
|
|
|
DecalXY Arch::getWireDecal(WireId wire) const
|
|
|
|
{
|
|
|
|
DecalXY decalxy;
|
|
|
|
decalxy.decal.type = DecalId::TYPE_WIRE;
|
|
|
|
decalxy.decal.location = wire.location;
|
|
|
|
decalxy.decal.z = wire.index;
|
2019-10-12 16:35:52 +08:00
|
|
|
decalxy.decal.active = getBoundWireNet(wire) != nullptr;
|
2019-12-08 16:33:06 +08:00
|
|
|
return decalxy;
|
2019-10-06 23:59:44 +08:00
|
|
|
}
|
2018-07-06 18:15:07 +08:00
|
|
|
|
2019-11-09 20:12:20 +08:00
|
|
|
DecalXY Arch::getPipDecal(PipId pip) const
|
|
|
|
{
|
|
|
|
DecalXY decalxy;
|
|
|
|
decalxy.decal.type = DecalId::TYPE_PIP;
|
|
|
|
decalxy.decal.location = pip.location;
|
|
|
|
decalxy.decal.z = pip.index;
|
|
|
|
decalxy.decal.active = getBoundPipNet(pip) != nullptr;
|
2019-12-08 16:33:06 +08:00
|
|
|
return decalxy;
|
2019-11-09 20:12:20 +08:00
|
|
|
};
|
2018-07-06 18:15:07 +08:00
|
|
|
|
2019-10-06 17:26:56 +08:00
|
|
|
DecalXY Arch::getGroupDecal(GroupId group) const
|
|
|
|
{
|
|
|
|
DecalXY decalxy;
|
|
|
|
decalxy.decal.type = DecalId::TYPE_GROUP;
|
|
|
|
decalxy.decal.location = group.location;
|
|
|
|
decalxy.decal.z = group.type;
|
|
|
|
decalxy.decal.active = true;
|
|
|
|
return decalxy;
|
|
|
|
}
|
2018-07-12 23:22:29 +08:00
|
|
|
|
2018-07-06 20:02:37 +08:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2021-02-19 18:39:57 +08:00
|
|
|
bool Arch::get_delay_from_tmg_db(IdString tctype, IdString from, IdString to, DelayQuad &delay) const
|
2018-07-08 17:15:30 +08:00
|
|
|
{
|
2019-02-27 19:57:36 +08:00
|
|
|
auto fnd_dk = celldelay_cache.find({tctype, from, to});
|
|
|
|
if (fnd_dk != celldelay_cache.end()) {
|
|
|
|
delay = fnd_dk->second.second;
|
|
|
|
return fnd_dk->second.first;
|
|
|
|
}
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &tc : speed_grade->cell_timings) {
|
2018-11-16 01:24:16 +08:00
|
|
|
if (tc.cell_type == tctype.index) {
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &dly : tc.prop_delays) {
|
2018-11-16 01:24:16 +08:00
|
|
|
if (dly.from_port == from.index && dly.to_port == to.index) {
|
2021-02-19 18:39:57 +08:00
|
|
|
delay = DelayQuad(dly.min_delay, dly.max_delay);
|
2019-02-27 19:57:36 +08:00
|
|
|
celldelay_cache[{tctype, from, to}] = std::make_pair(true, delay);
|
2018-11-16 01:24:16 +08:00
|
|
|
return true;
|
|
|
|
}
|
2018-08-19 23:59:36 +08:00
|
|
|
}
|
2021-02-19 18:39:57 +08:00
|
|
|
celldelay_cache[{tctype, from, to}] = std::make_pair(false, DelayQuad());
|
2018-11-16 01:24:16 +08:00
|
|
|
return false;
|
2018-08-19 23:59:36 +08:00
|
|
|
}
|
2018-11-16 01:24:16 +08:00
|
|
|
}
|
|
|
|
NPNR_ASSERT_FALSE("failed to find timing cell in db");
|
|
|
|
}
|
2018-08-19 23:59:36 +08:00
|
|
|
|
2021-02-19 18:39:57 +08:00
|
|
|
void Arch::get_setuphold_from_tmg_db(IdString tctype, IdString clock, IdString port, DelayPair &setup,
|
|
|
|
DelayPair &hold) const
|
2018-11-16 01:24:16 +08:00
|
|
|
{
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &tc : speed_grade->cell_timings) {
|
2018-11-16 01:24:16 +08:00
|
|
|
if (tc.cell_type == tctype.index) {
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &sh : tc.setup_holds) {
|
2018-11-16 01:24:16 +08:00
|
|
|
if (sh.clock_port == clock.index && sh.sig_port == port.index) {
|
|
|
|
setup.max_delay = sh.max_setup;
|
|
|
|
setup.min_delay = sh.min_setup;
|
|
|
|
hold.max_delay = sh.max_hold;
|
|
|
|
hold.min_delay = sh.min_hold;
|
|
|
|
return;
|
|
|
|
}
|
2018-08-19 23:59:36 +08:00
|
|
|
}
|
|
|
|
}
|
2018-11-16 01:24:16 +08:00
|
|
|
}
|
|
|
|
NPNR_ASSERT_FALSE("failed to find timing cell in db");
|
|
|
|
}
|
2018-08-19 23:59:36 +08:00
|
|
|
|
2021-02-19 18:39:57 +08:00
|
|
|
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const
|
2018-11-16 01:24:16 +08:00
|
|
|
{
|
|
|
|
// Data for -8 grade
|
2022-03-31 18:17:57 +08:00
|
|
|
if (cell->type == id_TRELLIS_COMB) {
|
|
|
|
bool has_carry = cell->combInfo.flags & ArchCellInfo::COMB_CARRY;
|
|
|
|
IdString tmg_type = has_carry ? (((cell->constr_z >> Arch::lc_idx_shift) % 2) ? id_TRELLIS_COMB_CARRY1
|
|
|
|
: id_TRELLIS_COMB_CARRY0)
|
|
|
|
: id_TRELLIS_COMB;
|
2022-08-11 01:58:22 +08:00
|
|
|
if (fromPort.in(id_A, id_B, id_C, id_D, id_M, id_F1, id_FXA, id_FXB, id_FCI))
|
2022-03-31 18:17:57 +08:00
|
|
|
return get_delay_from_tmg_db(tmg_type, fromPort, toPort, delay);
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
} else if (cell->type == id_TRELLIS_FF) {
|
|
|
|
return false;
|
|
|
|
} else if (cell->type == id_TRELLIS_RAMW) {
|
2018-08-20 00:12:03 +08:00
|
|
|
if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) ||
|
|
|
|
(fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) ||
|
|
|
|
(fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) ||
|
|
|
|
(fromPort == id_D0 && toPort == id_WADO0) || (fromPort == id_D1 && toPort == id_WDO2)) {
|
2021-02-19 18:39:57 +08:00
|
|
|
delay = DelayQuad(0);
|
2018-08-19 23:59:36 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2018-09-30 01:29:23 +08:00
|
|
|
} else if (cell->type == id_DCCA) {
|
|
|
|
if (fromPort == id_CLKI && toPort == id_CLKO) {
|
2021-02-19 18:39:57 +08:00
|
|
|
delay = DelayQuad(0);
|
2018-09-30 01:29:23 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2021-07-06 18:45:27 +08:00
|
|
|
} else if (cell->type == id_DCSC) {
|
2022-08-11 01:58:22 +08:00
|
|
|
if ((fromPort.in(id_CLK0, id_CLK1)) && toPort == id_DCSOUT) {
|
2021-07-06 18:45:27 +08:00
|
|
|
delay = DelayQuad(0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2018-10-16 20:30:23 +08:00
|
|
|
} else if (cell->type == id_DP16KD) {
|
|
|
|
return false;
|
2019-07-08 22:09:54 +08:00
|
|
|
} else if (cell->type == id_MULT18X18D) {
|
2020-04-30 23:09:22 +08:00
|
|
|
if (cell->multInfo.is_clocked)
|
|
|
|
return false;
|
2019-07-08 22:09:54 +08:00
|
|
|
std::string fn = fromPort.str(this), tn = toPort.str(this);
|
|
|
|
if (fn.size() > 1 && (fn.front() == 'A' || fn.front() == 'B') && std::isdigit(fn.at(1))) {
|
|
|
|
if (tn.size() > 1 && tn.front() == 'P' && std::isdigit(tn.at(1)))
|
2021-02-03 18:33:06 +08:00
|
|
|
return get_delay_from_tmg_db(cell->multInfo.timing_id, id(std::string("") + fn.front()), id_P, delay);
|
2019-07-08 22:09:54 +08:00
|
|
|
}
|
|
|
|
return false;
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (cell->type.in(id_IOLOGIC, id_SIOLOGIC)) {
|
2018-12-15 00:40:38 +08:00
|
|
|
return false;
|
2018-08-19 23:59:36 +08:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-08 17:15:30 +08:00
|
|
|
}
|
|
|
|
|
2018-11-04 22:51:48 +08:00
|
|
|
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
|
2018-08-08 20:37:59 +08:00
|
|
|
{
|
2018-10-22 00:15:34 +08:00
|
|
|
auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; };
|
2018-11-04 22:51:48 +08:00
|
|
|
clockInfoCount = 0;
|
2022-03-31 18:17:57 +08:00
|
|
|
if (cell->type == id_TRELLIS_COMB) {
|
|
|
|
if (port == id_WCK)
|
2018-08-19 23:59:36 +08:00
|
|
|
return TMG_CLOCK_INPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_A, id_B, id_C, id_D, id_FCI, id_FXA, id_FXB, id_F1))
|
2018-08-19 23:59:36 +08:00
|
|
|
return TMG_COMB_INPUT;
|
2022-03-31 18:17:57 +08:00
|
|
|
if (port == id_F && disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) &&
|
2018-10-22 00:15:34 +08:00
|
|
|
disconnected(id_FCI))
|
|
|
|
return TMG_IGNORE; // LUT with no inputs is a constant
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_F, id_FCO, id_OFX))
|
2018-08-19 23:59:36 +08:00
|
|
|
return TMG_COMB_OUTPUT;
|
2022-03-31 18:17:57 +08:00
|
|
|
if (port == id_M)
|
|
|
|
return TMG_COMB_INPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_WD, id_WAD0, id_WAD1, id_WAD2, id_WAD3, id_WRE)) {
|
2018-11-04 22:51:48 +08:00
|
|
|
clockInfoCount = 1;
|
2018-08-19 23:59:36 +08:00
|
|
|
return TMG_REGISTER_INPUT;
|
|
|
|
}
|
2022-03-31 18:17:57 +08:00
|
|
|
return TMG_IGNORE;
|
|
|
|
} else if (cell->type == id_TRELLIS_FF) {
|
|
|
|
bool using_m = (cell->ffInfo.flags & ArchCellInfo::FF_M_USED);
|
|
|
|
if (port == id_CLK)
|
|
|
|
return TMG_CLOCK_INPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port == id_DI || (using_m && (port == id_M)) || port.in(id_CE, id_LSR)) {
|
2022-03-31 18:17:57 +08:00
|
|
|
clockInfoCount = 1;
|
|
|
|
return TMG_REGISTER_INPUT;
|
|
|
|
}
|
|
|
|
if (port == id_Q) {
|
2018-11-04 22:51:48 +08:00
|
|
|
clockInfoCount = 1;
|
2018-08-19 23:59:36 +08:00
|
|
|
return TMG_REGISTER_OUTPUT;
|
|
|
|
}
|
2022-03-31 18:17:57 +08:00
|
|
|
return TMG_IGNORE;
|
|
|
|
} else if (cell->type == id_TRELLIS_RAMW) {
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_A0, id_A1, id_B0, id_B1, id_C0, id_C1, id_D0, id_D1))
|
2022-03-31 18:17:57 +08:00
|
|
|
return TMG_COMB_INPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_WDO0, id_WDO1, id_WDO2, id_WDO3, id_WADO0, id_WADO1, id_WADO2, id_WADO3))
|
2018-08-19 23:59:36 +08:00
|
|
|
return TMG_COMB_OUTPUT;
|
2022-03-31 18:17:57 +08:00
|
|
|
return TMG_IGNORE;
|
2018-08-19 23:59:36 +08:00
|
|
|
} else if (cell->type == id_TRELLIS_IO) {
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_T, id_I))
|
2018-08-19 23:59:36 +08:00
|
|
|
return TMG_ENDPOINT;
|
|
|
|
if (port == id_O)
|
|
|
|
return TMG_STARTPOINT;
|
|
|
|
return TMG_IGNORE;
|
2018-09-30 00:36:08 +08:00
|
|
|
} else if (cell->type == id_DCCA) {
|
2018-09-30 01:29:23 +08:00
|
|
|
if (port == id_CLKI)
|
|
|
|
return TMG_COMB_INPUT;
|
|
|
|
if (port == id_CLKO)
|
|
|
|
return TMG_COMB_OUTPUT;
|
2018-09-30 00:36:08 +08:00
|
|
|
return TMG_IGNORE;
|
2021-07-06 18:45:27 +08:00
|
|
|
} else if (cell->type == id_DCSC) {
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_CLK0, id_CLK1))
|
2021-07-06 18:45:27 +08:00
|
|
|
return TMG_COMB_INPUT;
|
|
|
|
if (port == id_DCSOUT)
|
|
|
|
return TMG_COMB_OUTPUT;
|
|
|
|
return TMG_IGNORE;
|
2018-10-05 18:35:37 +08:00
|
|
|
} else if (cell->type == id_DP16KD) {
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_CLKA, id_CLKB))
|
2018-10-16 20:30:23 +08:00
|
|
|
return TMG_CLOCK_INPUT;
|
|
|
|
std::string port_name = port.str(this);
|
|
|
|
for (auto c : boost::adaptors::reverse(port_name)) {
|
|
|
|
if (std::isdigit(c))
|
|
|
|
continue;
|
2018-11-04 22:51:48 +08:00
|
|
|
if (c == 'A' || c == 'B')
|
|
|
|
clockInfoCount = 1;
|
2018-10-16 20:30:23 +08:00
|
|
|
else
|
|
|
|
NPNR_ASSERT_FALSE_STR("bad ram port");
|
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
|
|
|
|
}
|
|
|
|
NPNR_ASSERT_FALSE_STR("no timing type for RAM port '" + port.str(this) + "'");
|
2018-10-22 03:03:49 +08:00
|
|
|
} else if (cell->type == id_MULT18X18D) {
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_CLK0, id_CLK1, id_CLK2, id_CLK3))
|
2019-07-08 22:09:54 +08:00
|
|
|
return TMG_CLOCK_INPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_CE0, id_CE1, id_CE2, id_CE3, id_RST0, id_RST1, id_RST2, id_RST3, id_SIGNEDA, id_SIGNEDB)) {
|
2020-05-01 15:17:29 +08:00
|
|
|
if (cell->multInfo.is_clocked) {
|
|
|
|
clockInfoCount = 1;
|
|
|
|
return TMG_REGISTER_INPUT;
|
|
|
|
} else {
|
|
|
|
return TMG_COMB_INPUT;
|
|
|
|
}
|
|
|
|
}
|
2019-07-08 22:09:54 +08:00
|
|
|
std::string pname = port.str(this);
|
|
|
|
if (pname.size() > 1) {
|
2020-05-01 15:17:29 +08:00
|
|
|
if ((pname.front() == 'A' || pname.front() == 'B') && std::isdigit(pname.at(1))) {
|
|
|
|
if (cell->multInfo.is_clocked) {
|
|
|
|
clockInfoCount = 1;
|
2020-04-30 01:57:04 +08:00
|
|
|
return TMG_REGISTER_INPUT;
|
2020-05-01 15:17:29 +08:00
|
|
|
} else {
|
|
|
|
return TMG_COMB_INPUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((pname.front() == 'P') && std::isdigit(pname.at(1))) {
|
|
|
|
if (cell->multInfo.is_clocked) {
|
|
|
|
clockInfoCount = 1;
|
|
|
|
return TMG_REGISTER_OUTPUT;
|
|
|
|
} else {
|
|
|
|
return TMG_COMB_OUTPUT;
|
|
|
|
}
|
|
|
|
}
|
2019-07-08 22:09:54 +08:00
|
|
|
}
|
|
|
|
return TMG_IGNORE;
|
2018-10-22 03:03:49 +08:00
|
|
|
} else if (cell->type == id_ALU54B) {
|
|
|
|
return TMG_IGNORE; // FIXME
|
2018-11-01 03:52:41 +08:00
|
|
|
} else if (cell->type == id_EHXPLLL) {
|
|
|
|
return TMG_IGNORE;
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (cell->type.in(id_DCUA, id_EXTREFB, id_PCSCLKDIV)) {
|
|
|
|
if (port.in(id_CH0_FF_TXI_CLK, id_CH0_FF_RXI_CLK, id_CH1_FF_TXI_CLK, id_CH1_FF_RXI_CLK))
|
2018-11-11 18:46:07 +08:00
|
|
|
return TMG_CLOCK_INPUT;
|
|
|
|
std::string prefix = port.str(this).substr(0, 9);
|
|
|
|
if (prefix == "CH0_FF_TX" || prefix == "CH0_FF_RX" || prefix == "CH1_FF_TX" || prefix == "CH1_FF_RX") {
|
|
|
|
clockInfoCount = 1;
|
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
|
|
|
|
}
|
|
|
|
return TMG_IGNORE;
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (cell->type.in(id_IOLOGIC, id_SIOLOGIC)) {
|
|
|
|
if (port.in(id_CLK, id_ECLK)) {
|
2018-12-15 00:40:38 +08:00
|
|
|
return TMG_CLOCK_INPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (port.in(id_IOLDO, id_IOLDOI, id_IOLDOD, id_IOLTO, id_PADDI, id_DQSR90, id_DQSW, id_DQSW270)) {
|
2018-12-15 00:40:38 +08:00
|
|
|
return TMG_IGNORE;
|
|
|
|
} else {
|
|
|
|
clockInfoCount = 1;
|
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
|
|
|
|
}
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (cell->type.in(id_DTR, id_USRMCLK, id_SEDGA, id_GSR, id_JTAGG)) {
|
2019-01-22 03:03:12 +08:00
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_STARTPOINT : TMG_ENDPOINT;
|
|
|
|
} else if (cell->type == id_OSCG) {
|
|
|
|
if (port == id_OSC)
|
|
|
|
return TMG_GEN_CLOCK;
|
|
|
|
else
|
|
|
|
return TMG_IGNORE;
|
|
|
|
} else if (cell->type == id_CLKDIVF) {
|
|
|
|
if (port == id_CLKI)
|
|
|
|
return TMG_CLOCK_INPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
else if (port.in(id_RST, id_ALIGNWD))
|
2019-01-22 03:03:12 +08:00
|
|
|
return TMG_ENDPOINT;
|
|
|
|
else if (port == id_CDIVX)
|
|
|
|
return TMG_GEN_CLOCK;
|
|
|
|
else
|
|
|
|
NPNR_ASSERT_FALSE("bad clkdiv port");
|
2019-02-11 19:31:56 +08:00
|
|
|
} else if (cell->type == id_DQSBUFM) {
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_READ0, id_READ1)) {
|
2019-02-11 19:31:56 +08:00
|
|
|
clockInfoCount = 1;
|
|
|
|
return TMG_REGISTER_INPUT;
|
|
|
|
} else if (port == id_DATAVALID) {
|
|
|
|
clockInfoCount = 1;
|
|
|
|
return TMG_REGISTER_OUTPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (port.in(id_SCLK, id_ECLK, id_DQSI)) {
|
2019-02-11 19:31:56 +08:00
|
|
|
return TMG_CLOCK_INPUT;
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (port.in(id_DQSR90, id_DQSW, id_DQSW270)) {
|
2019-02-11 19:31:56 +08:00
|
|
|
return TMG_GEN_CLOCK;
|
|
|
|
}
|
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_STARTPOINT : TMG_ENDPOINT;
|
|
|
|
} else if (cell->type == id_DDRDLL) {
|
|
|
|
if (port == id_CLK)
|
|
|
|
return TMG_CLOCK_INPUT;
|
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_STARTPOINT : TMG_ENDPOINT;
|
2019-02-12 02:59:28 +08:00
|
|
|
} else if (cell->type == id_TRELLIS_ECLKBUF) {
|
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_COMB_OUTPUT : TMG_COMB_INPUT;
|
2019-02-13 19:23:12 +08:00
|
|
|
} else if (cell->type == id_ECLKSYNCB) {
|
|
|
|
if (cell->ports.at(port).name == id_STOP)
|
|
|
|
return TMG_ENDPOINT;
|
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_COMB_OUTPUT : TMG_COMB_INPUT;
|
2019-10-09 17:55:10 +08:00
|
|
|
} else if (cell->type == id_ECLKBRIDGECS) {
|
|
|
|
if (cell->ports.at(port).name == id_SEL)
|
|
|
|
return TMG_ENDPOINT;
|
|
|
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_COMB_OUTPUT : TMG_COMB_INPUT;
|
2018-08-19 23:59:36 +08:00
|
|
|
} else {
|
2018-11-30 03:26:23 +08:00
|
|
|
log_error("cell type '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this),
|
|
|
|
cell->name.c_str(this));
|
2018-08-19 23:59:36 +08:00
|
|
|
}
|
2018-08-08 20:37:59 +08:00
|
|
|
}
|
2018-07-08 17:15:30 +08:00
|
|
|
|
2018-11-04 22:51:48 +08:00
|
|
|
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
|
|
|
|
{
|
|
|
|
TimingClockingInfo info;
|
2021-02-19 18:39:57 +08:00
|
|
|
info.setup = DelayPair(0);
|
|
|
|
info.hold = DelayPair(0);
|
|
|
|
info.clockToQ = DelayQuad(0);
|
2022-03-31 18:17:57 +08:00
|
|
|
if (cell->type == id_TRELLIS_COMB) {
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_WD, id_WAD0, id_WAD1, id_WAD2, id_WAD3, id_WRE)) {
|
2022-03-31 18:17:57 +08:00
|
|
|
if (port == id_WD)
|
|
|
|
port = id_WD0;
|
|
|
|
info.edge = (cell->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV) ? FALLING_EDGE : RISING_EDGE;
|
2018-11-04 22:51:48 +08:00
|
|
|
info.clock_port = id_WCK;
|
2021-02-03 18:33:06 +08:00
|
|
|
get_setuphold_from_tmg_db(id_SDPRAME, id_WCK, port, info.setup, info.hold);
|
2022-03-31 18:17:57 +08:00
|
|
|
}
|
|
|
|
} else if (cell->type == id_TRELLIS_FF) {
|
|
|
|
bool using_m = (cell->ffInfo.flags & ArchCellInfo::FF_M_USED);
|
2022-08-11 01:58:22 +08:00
|
|
|
if (port.in(id_DI, id_CE, id_LSR) || (using_m && port == id_M)) {
|
2022-03-31 18:17:57 +08:00
|
|
|
if (port == id_DI)
|
|
|
|
port = id_DI0;
|
|
|
|
if (port == id_M)
|
|
|
|
port = id_M0;
|
|
|
|
info.edge = (cell->ffInfo.flags & ArchCellInfo::FF_CLKINV) ? FALLING_EDGE : RISING_EDGE;
|
2018-11-04 22:51:48 +08:00
|
|
|
info.clock_port = id_CLK;
|
2021-02-03 18:33:06 +08:00
|
|
|
get_setuphold_from_tmg_db(id_SLOGICB, id_CLK, port, info.setup, info.hold);
|
2018-11-04 22:51:48 +08:00
|
|
|
} else {
|
2022-03-31 18:17:57 +08:00
|
|
|
NPNR_ASSERT(port == id_Q);
|
|
|
|
port = id_Q0;
|
|
|
|
info.edge = (cell->ffInfo.flags & ArchCellInfo::FF_CLKINV) ? FALLING_EDGE : RISING_EDGE;
|
2018-11-04 22:51:48 +08:00
|
|
|
info.clock_port = id_CLK;
|
2021-02-03 18:33:06 +08:00
|
|
|
bool is_path = get_delay_from_tmg_db(id_SLOGICB, id_CLK, port, info.clockToQ);
|
2018-11-16 01:24:16 +08:00
|
|
|
NPNR_ASSERT(is_path);
|
2018-11-04 22:51:48 +08:00
|
|
|
}
|
|
|
|
} else if (cell->type == id_DP16KD) {
|
2018-11-04 23:11:01 +08:00
|
|
|
std::string port_name = port.str(this);
|
2019-10-01 17:36:22 +08:00
|
|
|
IdString half_clock;
|
2018-11-04 23:11:01 +08:00
|
|
|
for (auto c : boost::adaptors::reverse(port_name)) {
|
2018-11-04 22:51:48 +08:00
|
|
|
if (std::isdigit(c))
|
|
|
|
continue;
|
2018-11-04 23:11:01 +08:00
|
|
|
if (c == 'A') {
|
2019-10-01 17:36:22 +08:00
|
|
|
half_clock = id_CLKA;
|
2018-11-04 23:11:01 +08:00
|
|
|
break;
|
|
|
|
} else if (c == 'B') {
|
2019-10-01 17:36:22 +08:00
|
|
|
half_clock = id_CLKB;
|
2018-11-04 23:11:01 +08:00
|
|
|
break;
|
|
|
|
} else
|
|
|
|
NPNR_ASSERT_FALSE_STR("bad ram port " + port.str(this));
|
2018-11-04 22:51:48 +08:00
|
|
|
}
|
2019-10-01 17:36:22 +08:00
|
|
|
if (cell->ramInfo.is_pdp) {
|
|
|
|
bool is_output = cell->ports.at(port).type == PORT_OUT;
|
|
|
|
// In PDP mode, all read signals are in CLKB domain and write signals in CLKA domain
|
2022-08-11 01:58:22 +08:00
|
|
|
if (is_output || port.in(id_OCEB, id_CEB, id_ADB5, id_ADB6, id_ADB7, id_ADB8, id_ADB9, id_ADB10, id_ADB11,
|
|
|
|
id_ADB12, id_ADB13))
|
2019-10-01 17:36:22 +08:00
|
|
|
info.clock_port = id_CLKB;
|
|
|
|
else
|
|
|
|
info.clock_port = id_CLKA;
|
|
|
|
} else {
|
|
|
|
info.clock_port = half_clock;
|
|
|
|
}
|
2022-02-17 01:09:54 +08:00
|
|
|
info.edge = (str_or_default(cell->params, info.clock_port == id_CLKB ? id_CLKBMUX : id_CLKAMUX, "CLK") == "INV")
|
2018-11-04 22:51:48 +08:00
|
|
|
? FALLING_EDGE
|
|
|
|
: RISING_EDGE;
|
|
|
|
if (cell->ports.at(port).type == PORT_OUT) {
|
2021-02-03 18:33:06 +08:00
|
|
|
bool is_path = get_delay_from_tmg_db(cell->ramInfo.regmode_timing_id, half_clock, port, info.clockToQ);
|
2018-11-16 01:24:16 +08:00
|
|
|
NPNR_ASSERT(is_path);
|
2018-11-04 22:51:48 +08:00
|
|
|
} else {
|
2021-02-03 18:33:06 +08:00
|
|
|
get_setuphold_from_tmg_db(cell->ramInfo.regmode_timing_id, half_clock, port, info.setup, info.hold);
|
2018-11-04 22:51:48 +08:00
|
|
|
}
|
2018-11-11 18:46:07 +08:00
|
|
|
} else if (cell->type == id_DCUA) {
|
|
|
|
std::string prefix = port.str(this).substr(0, 9);
|
|
|
|
info.edge = RISING_EDGE;
|
|
|
|
if (prefix == "CH0_FF_TX")
|
|
|
|
info.clock_port = id_CH0_FF_TXI_CLK;
|
|
|
|
else if (prefix == "CH0_FF_RX")
|
|
|
|
info.clock_port = id_CH0_FF_RXI_CLK;
|
|
|
|
else if (prefix == "CH1_FF_TX")
|
|
|
|
info.clock_port = id_CH1_FF_TXI_CLK;
|
|
|
|
else if (prefix == "CH1_FF_RX")
|
|
|
|
info.clock_port = id_CH1_FF_RXI_CLK;
|
|
|
|
if (cell->ports.at(port).type == PORT_OUT) {
|
2021-02-19 18:39:57 +08:00
|
|
|
info.clockToQ = DelayQuad(getDelayFromNS(0.7));
|
2018-11-11 18:46:07 +08:00
|
|
|
} else {
|
2021-02-19 18:39:57 +08:00
|
|
|
info.setup = DelayPair(getDelayFromNS(1));
|
|
|
|
info.hold = DelayPair(getDelayFromNS(0));
|
2018-11-11 18:46:07 +08:00
|
|
|
}
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (cell->type.in(id_IOLOGIC, id_SIOLOGIC)) {
|
2018-12-15 00:40:38 +08:00
|
|
|
info.clock_port = id_CLK;
|
2021-06-10 20:10:29 +08:00
|
|
|
info.edge = RISING_EDGE;
|
2018-12-15 00:40:38 +08:00
|
|
|
if (cell->ports.at(port).type == PORT_OUT) {
|
2021-02-19 18:39:57 +08:00
|
|
|
info.clockToQ = DelayQuad(getDelayFromNS(0.5));
|
2018-12-15 00:40:38 +08:00
|
|
|
} else {
|
2021-02-19 18:39:57 +08:00
|
|
|
info.setup = DelayPair(getDelayFromNS(0.1));
|
|
|
|
info.hold = DelayPair(getDelayFromNS(0));
|
2018-12-15 00:40:38 +08:00
|
|
|
}
|
2019-02-11 19:31:56 +08:00
|
|
|
} else if (cell->type == id_DQSBUFM) {
|
|
|
|
info.clock_port = id_SCLK;
|
2021-06-10 20:10:29 +08:00
|
|
|
info.edge = RISING_EDGE;
|
2019-02-11 19:31:56 +08:00
|
|
|
if (port == id_DATAVALID) {
|
2021-02-19 18:39:57 +08:00
|
|
|
info.clockToQ = DelayQuad(getDelayFromNS(0.2));
|
2022-08-11 01:58:22 +08:00
|
|
|
} else if (port.in(id_READ0, id_READ1)) {
|
2021-02-19 18:39:57 +08:00
|
|
|
info.setup = DelayPair(getDelayFromNS(0.5));
|
|
|
|
info.hold = DelayPair(getDelayFromNS(-0.4));
|
2019-02-11 19:31:56 +08:00
|
|
|
} else {
|
|
|
|
NPNR_ASSERT_FALSE("unknown DQSBUFM register port");
|
|
|
|
}
|
2020-04-30 01:57:04 +08:00
|
|
|
} else if (cell->type == id_MULT18X18D) {
|
|
|
|
std::string port_name = port.str(this);
|
|
|
|
// To keep the timing DB small, like signals (e.g. P[35:0] have been
|
|
|
|
// grouped. To look up the timing, we therefore need to map this port
|
|
|
|
// to the enclosing port group.
|
|
|
|
auto has_prefix = [](std::string base, std::string prefix) {
|
|
|
|
return base.compare(0, prefix.size(), prefix) == 0;
|
|
|
|
};
|
|
|
|
IdString port_group;
|
|
|
|
if (has_prefix(port_name, "A")) {
|
|
|
|
port_group = id_A;
|
|
|
|
} else if (has_prefix(port_name, "B")) {
|
|
|
|
port_group = id_B;
|
|
|
|
} else if (has_prefix(port_name, "P")) {
|
|
|
|
port_group = id_P;
|
|
|
|
} else if (has_prefix(port_name, "CE")) {
|
|
|
|
port_group = id_CE0;
|
|
|
|
} else if (has_prefix(port_name, "RST")) {
|
|
|
|
port_group = id_RST0;
|
|
|
|
} else if (has_prefix(port_name, "SIGNED")) {
|
|
|
|
// Both SIGNEDA and SIGNEDB exist in the DB, so can directly use these here
|
|
|
|
port_group = port;
|
|
|
|
} else {
|
|
|
|
NPNR_ASSERT_FALSE("Unknown MULT18X18D register port");
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this port is clocked at all, it must be clocked from CLK0
|
2020-04-30 02:39:52 +08:00
|
|
|
IdString clock_id = id_CLK0;
|
2020-05-01 15:17:29 +08:00
|
|
|
info.clock_port = clock_id;
|
|
|
|
info.edge = RISING_EDGE;
|
2020-04-30 01:57:04 +08:00
|
|
|
if (cell->ports.at(port).type == PORT_OUT) {
|
2021-02-03 18:33:06 +08:00
|
|
|
bool is_path = get_delay_from_tmg_db(cell->multInfo.timing_id, clock_id, port_group, info.clockToQ);
|
2020-04-30 01:57:04 +08:00
|
|
|
NPNR_ASSERT(is_path);
|
|
|
|
} else {
|
2021-02-03 18:33:06 +08:00
|
|
|
get_setuphold_from_tmg_db(cell->multInfo.timing_id, clock_id, port_group, info.setup, info.hold);
|
2020-04-30 01:57:04 +08:00
|
|
|
}
|
2018-11-04 22:51:48 +08:00
|
|
|
}
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
std::vector<std::pair<std::string, std::string>> Arch::get_tiles_at_loc(int row, int col)
|
2018-08-01 21:58:38 +08:00
|
|
|
{
|
|
|
|
std::vector<std::pair<std::string, std::string>> ret;
|
|
|
|
auto &tileloc = chip_info->tile_info[row * chip_info->width + col];
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &tn : tileloc.tile_names) {
|
|
|
|
ret.push_back(std::make_pair(tn.name.get(), chip_info->tiletype_names[tn.type_idx].get()));
|
2018-08-01 21:58:38 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
GlobalInfoPOD Arch::global_info_at_loc(Location loc)
|
2018-07-26 19:05:15 +08:00
|
|
|
{
|
|
|
|
int locidx = loc.y * chip_info->width + loc.x;
|
|
|
|
return chip_info->location_glbinfo[locidx];
|
|
|
|
}
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
bool Arch::get_pio_dqs_group(BelId pio, bool &dqsright, int &dqsrow)
|
2019-02-12 01:56:19 +08:00
|
|
|
{
|
2021-01-28 03:39:19 +08:00
|
|
|
for (auto &ppio : chip_info->pio_info) {
|
|
|
|
if (Location(ppio.abs_loc) == pio.location && ppio.bel_index == pio.index) {
|
|
|
|
int dqs = ppio.dqsgroup;
|
2019-02-12 01:56:19 +08:00
|
|
|
if (dqs == -1)
|
|
|
|
return false;
|
|
|
|
else {
|
|
|
|
dqsright = (dqs & 2048) != 0;
|
|
|
|
dqsrow = dqs & 0x1FF;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NPNR_ASSERT_FALSE("failed to find PIO");
|
|
|
|
}
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
BelId Arch::get_dqsbuf(bool dqsright, int dqsrow)
|
2019-02-12 01:56:19 +08:00
|
|
|
{
|
|
|
|
BelId bel;
|
|
|
|
bel.location.y = dqsrow;
|
|
|
|
bel.location.x = (dqsright ? (chip_info->width - 1) : 0);
|
2021-02-08 19:24:00 +08:00
|
|
|
for (int i = 0; i < loc_info(bel)->bel_data.ssize(); i++) {
|
2021-02-03 18:33:06 +08:00
|
|
|
auto &bd = loc_info(bel)->bel_data[i];
|
2019-02-12 01:56:19 +08:00
|
|
|
if (bd.type == id_DQSBUFM.index) {
|
|
|
|
bel.index = i;
|
|
|
|
return bel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NPNR_ASSERT_FALSE("failed to find DQSBUF");
|
|
|
|
}
|
|
|
|
|
2021-02-03 18:33:06 +08:00
|
|
|
WireId Arch::get_bank_eclk(int bank, int eclk)
|
2019-02-12 01:56:19 +08:00
|
|
|
{
|
2021-02-03 18:33:06 +08:00
|
|
|
return get_wire_by_loc_basename(Location(0, 0), "G_BANK" + std::to_string(bank) + "ECLK" + std::to_string(eclk));
|
2019-02-12 01:56:19 +08:00
|
|
|
}
|
|
|
|
|
2019-03-24 19:10:20 +08:00
|
|
|
#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
|
|
|
|
};
|
|
|
|
|
2020-02-03 19:54:38 +08:00
|
|
|
const std::string Arch::defaultRouter = "router1";
|
|
|
|
const std::vector<std::string> Arch::availableRouters = {"router1", "router2"};
|
|
|
|
|
2019-10-06 17:26:56 +08:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2021-01-29 21:30:56 +08:00
|
|
|
GroupId Arch::getGroupByName(IdStringList name) const
|
2019-10-06 17:26:56 +08:00
|
|
|
{
|
|
|
|
for (auto g : getGroups())
|
|
|
|
if (getGroupName(g) == name)
|
|
|
|
return g;
|
|
|
|
return GroupId();
|
|
|
|
}
|
|
|
|
|
2021-01-29 21:30:56 +08:00
|
|
|
IdStringList Arch::getGroupName(GroupId group) const
|
2019-10-06 17:26:56 +08:00
|
|
|
{
|
|
|
|
std::string suffix;
|
|
|
|
|
|
|
|
switch (group.type) {
|
|
|
|
case GroupId::TYPE_SWITCHBOX:
|
|
|
|
suffix = "switchbox";
|
|
|
|
break;
|
|
|
|
default:
|
2021-01-29 21:30:56 +08:00
|
|
|
return IdStringList();
|
2019-10-06 17:26:56 +08:00
|
|
|
}
|
|
|
|
|
2021-01-29 21:30:56 +08:00
|
|
|
std::array<IdString, 3> ids{x_ids.at(group.location.x), y_ids.at(group.location.y), id(suffix)};
|
|
|
|
return IdStringList(ids);
|
2019-10-06 17:26:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<GroupId> Arch::getGroups() const
|
|
|
|
{
|
|
|
|
std::vector<GroupId> ret;
|
|
|
|
|
2019-12-08 16:33:06 +08:00
|
|
|
for (int y = 1; y < chip_info->height - 1; y++) {
|
|
|
|
for (int x = 1; x < chip_info->width - 1; x++) {
|
2019-10-06 17:26:56 +08:00
|
|
|
GroupId group;
|
|
|
|
group.type = GroupId::TYPE_SWITCHBOX;
|
|
|
|
group.location.x = x;
|
2019-12-08 16:33:06 +08:00
|
|
|
group.location.y = y;
|
2019-10-06 17:26:56 +08:00
|
|
|
ret.push_back(group);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<BelId> Arch::getGroupBels(GroupId group) const
|
|
|
|
{
|
|
|
|
std::vector<BelId> ret;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<WireId> Arch::getGroupWires(GroupId group) const
|
|
|
|
{
|
|
|
|
std::vector<WireId> ret;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<PipId> Arch::getGroupPips(GroupId group) const
|
|
|
|
{
|
|
|
|
std::vector<PipId> ret;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
|
|
|
|
{
|
|
|
|
std::vector<GroupId> ret;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2019-10-06 23:59:44 +08:00
|
|
|
std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
|
|
|
|
{
|
|
|
|
std::vector<std::pair<IdString, std::string>> ret;
|
2021-02-03 18:33:06 +08:00
|
|
|
auto &wi = loc_info(wire)->wire_data[wire.index];
|
2019-10-06 23:59:44 +08:00
|
|
|
|
2022-02-17 01:09:54 +08:00
|
|
|
ret.push_back(std::make_pair(id_TILE_WIRE_ID, stringf("%d", wi.tile_wire)));
|
2019-10-06 23:59:44 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2018-07-06 18:15:07 +08:00
|
|
|
NEXTPNR_NAMESPACE_END
|