2020-06-13 05:09:46 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
2021-01-07 00:22:17 +08:00
|
|
|
* Copyright (C) 2021 Lofty <dan.ravensloft@gmail.com>
|
2020-06-13 05:09:46 +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.
|
|
|
|
*/
|
2021-01-07 00:22:17 +08:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
2021-05-01 21:55:33 +08:00
|
|
|
#include "log.h"
|
2021-01-07 00:22:17 +08:00
|
|
|
#include "nextpnr.h"
|
|
|
|
|
2021-05-08 20:38:17 +08:00
|
|
|
#include "placer1.h"
|
|
|
|
#include "placer_heap.h"
|
|
|
|
#include "router1.h"
|
|
|
|
#include "router2.h"
|
|
|
|
#include "timing.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
#include "cyclonev.h"
|
2021-01-07 00:22:17 +08:00
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
2021-01-07 02:07:59 +08:00
|
|
|
using namespace mistral;
|
2021-01-07 00:22:17 +08:00
|
|
|
|
2021-05-01 03:52:52 +08:00
|
|
|
void IdString::initialize_arch(const BaseCtx *ctx)
|
|
|
|
{
|
|
|
|
#define X(t) initialize_add(ctx, #t, ID_##t);
|
|
|
|
|
|
|
|
#include "constids.inc"
|
|
|
|
|
|
|
|
#undef X
|
|
|
|
}
|
|
|
|
|
2022-02-03 23:28:46 +08:00
|
|
|
CycloneV::rnode_t Arch::find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi,
|
|
|
|
int pi) const
|
2022-01-19 15:42:29 +08:00
|
|
|
{
|
|
|
|
auto pn1 = CycloneV::pnode(bt, x, y, port, bi, pi);
|
|
|
|
auto rn1 = cyclonev->pnode_to_rnode(pn1);
|
2022-02-03 23:28:46 +08:00
|
|
|
if (rn1)
|
2022-01-19 15:42:29 +08:00
|
|
|
return rn1;
|
|
|
|
|
2022-02-03 23:28:46 +08:00
|
|
|
if (bt == CycloneV::GPIO) {
|
2022-01-19 15:42:29 +08:00
|
|
|
auto pn2 = cyclonev->p2p_to(pn1);
|
2022-02-03 23:28:46 +08:00
|
|
|
if (!pn2) {
|
2022-01-19 15:42:29 +08:00
|
|
|
auto pnv = cyclonev->p2p_from(pn1);
|
2022-02-03 23:28:46 +08:00
|
|
|
if (!pnv.empty())
|
2022-01-19 15:42:29 +08:00
|
|
|
pn2 = pnv[0];
|
|
|
|
}
|
|
|
|
auto pn3 = cyclonev->hmc_get_bypass(pn2);
|
|
|
|
auto rn2 = cyclonev->pnode_to_rnode(pn3);
|
|
|
|
return rn2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
WireId Arch::get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi) const
|
|
|
|
{
|
|
|
|
auto rn = find_rnode(bt, x, y, port, bi, pi);
|
2022-02-03 23:28:46 +08:00
|
|
|
if (rn)
|
2022-01-19 15:42:29 +08:00
|
|
|
return WireId(rn);
|
2022-02-03 23:28:46 +08:00
|
|
|
|
2022-01-19 15:42:29 +08:00
|
|
|
log_error("Trying to connect unknown node %s\n", CycloneV::pn2s(CycloneV::pnode(bt, x, y, port, bi, pi)).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Arch::has_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi) const
|
|
|
|
{
|
|
|
|
return find_rnode(bt, x, y, port, bi, pi) != 0;
|
|
|
|
}
|
|
|
|
|
2021-01-07 00:22:17 +08:00
|
|
|
Arch::Arch(ArchArgs args)
|
|
|
|
{
|
|
|
|
this->args = args;
|
2021-06-05 02:25:34 +08:00
|
|
|
this->cyclonev = mistral::CycloneV::get_model(args.device);
|
2021-01-07 00:22:17 +08:00
|
|
|
NPNR_ASSERT(this->cyclonev != nullptr);
|
2021-02-04 22:28:39 +08:00
|
|
|
|
2021-05-01 21:55:33 +08:00
|
|
|
// Setup fast identifier maps
|
|
|
|
for (int i = 0; i < 1024; i++) {
|
2022-08-10 17:57:17 +08:00
|
|
|
IdString int_id = idf("%d", i);
|
2021-05-01 21:55:33 +08:00
|
|
|
int2id.push_back(int_id);
|
|
|
|
id2int[int_id] = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int t = int(CycloneV::NONE); t <= int(CycloneV::DCMUX); t++) {
|
|
|
|
IdString rnode_id = id(CycloneV::rnode_type_names[t]);
|
|
|
|
rn_t2id.push_back(rnode_id);
|
|
|
|
id2rn_t[rnode_id] = CycloneV::rnode_type_t(t);
|
|
|
|
}
|
|
|
|
|
2021-05-01 22:25:43 +08:00
|
|
|
log_info("Initialising bels...\n");
|
2021-05-03 22:12:24 +08:00
|
|
|
bels_by_tile.resize(cyclonev->get_tile_sx() * cyclonev->get_tile_sy());
|
2021-10-08 22:21:21 +08:00
|
|
|
|
|
|
|
for (auto lab_pos : cyclonev->lab_get_pos())
|
|
|
|
create_lab(CycloneV::pos2x(lab_pos), CycloneV::pos2y(lab_pos), /*is_mlab=*/false);
|
|
|
|
|
|
|
|
for (auto mlab_pos : cyclonev->mlab_get_pos())
|
|
|
|
create_lab(CycloneV::pos2x(mlab_pos), CycloneV::pos2y(mlab_pos), /*is_mlab=*/true);
|
2021-05-01 21:55:33 +08:00
|
|
|
|
2021-05-09 23:51:28 +08:00
|
|
|
for (auto gpio_pos : cyclonev->gpio_get_pos())
|
2021-05-09 20:16:22 +08:00
|
|
|
create_gpio(CycloneV::pos2x(gpio_pos), CycloneV::pos2y(gpio_pos));
|
2021-05-09 23:51:28 +08:00
|
|
|
|
|
|
|
for (auto cmuxh_pos : cyclonev->cmuxh_get_pos())
|
|
|
|
create_clkbuf(CycloneV::pos2x(cmuxh_pos), CycloneV::pos2y(cmuxh_pos));
|
2021-05-09 20:16:22 +08:00
|
|
|
|
2021-10-17 20:22:44 +08:00
|
|
|
create_control(CycloneV::pos2x(cyclonev->ctrl_get_pos()[0]), CycloneV::pos2y(cyclonev->ctrl_get_pos()[0]));
|
|
|
|
|
2021-10-14 23:04:32 +08:00
|
|
|
auto hps_pos = cyclonev->hps_get_pos();
|
|
|
|
if (!hps_pos.empty()) {
|
2021-10-20 04:36:25 +08:00
|
|
|
create_hps_mpu_general_purpose(CycloneV::pos2x(hps_pos[CycloneV::I_HPS_MPU_GENERAL_PURPOSE]),
|
|
|
|
CycloneV::pos2y(hps_pos[CycloneV::I_HPS_MPU_GENERAL_PURPOSE]));
|
2021-10-14 23:04:32 +08:00
|
|
|
}
|
|
|
|
|
2021-12-22 01:10:45 +08:00
|
|
|
for (auto m10k_pos : cyclonev->m10k_get_pos())
|
|
|
|
create_m10k(CycloneV::pos2x(m10k_pos), CycloneV::pos2y(m10k_pos));
|
|
|
|
|
2021-05-01 22:25:43 +08:00
|
|
|
// This import takes about 5s, perhaps long term we can speed it up, e.g. defer to Mistral more...
|
|
|
|
log_info("Initialising routing graph...\n");
|
|
|
|
int pip_count = 0;
|
2021-10-20 04:19:18 +08:00
|
|
|
for (const auto &rnode : cyclonev->rnodes()) {
|
2021-10-20 04:36:25 +08:00
|
|
|
WireId dst_wire(rnode.id());
|
|
|
|
for (const auto &src : rnode.sources()) {
|
|
|
|
WireId src_wire(src);
|
|
|
|
wires[dst_wire].wires_uphill.push_back(src_wire);
|
|
|
|
wires[src_wire].wires_downhill.push_back(dst_wire);
|
|
|
|
++pip_count;
|
|
|
|
}
|
2021-05-01 22:25:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
log_info(" imported %d wires and %d pips\n", int(wires.size()), pip_count);
|
|
|
|
|
2021-05-01 21:55:33 +08:00
|
|
|
BaseArch::init_cell_types();
|
|
|
|
BaseArch::init_bel_buckets();
|
2021-01-07 00:22:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int Arch::getTileBelDimZ(int x, int y) const
|
|
|
|
{
|
2021-05-03 22:12:24 +08:00
|
|
|
// This seems like a reasonable upper bound
|
|
|
|
return 256;
|
2021-01-07 00:22:17 +08:00
|
|
|
}
|
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
BelId Arch::getBelByName(IdStringList name) const
|
2021-01-07 00:22:17 +08:00
|
|
|
{
|
|
|
|
BelId bel;
|
2021-05-01 21:55:33 +08:00
|
|
|
NPNR_ASSERT(name.size() == 4);
|
|
|
|
int x = id2int.at(name[1]);
|
|
|
|
int y = id2int.at(name[2]);
|
|
|
|
int z = id2int.at(name[3]);
|
2021-01-07 00:22:17 +08:00
|
|
|
|
|
|
|
bel.pos = CycloneV::xy2pos(x, y);
|
2021-05-03 22:12:24 +08:00
|
|
|
bel.z = z;
|
|
|
|
|
|
|
|
NPNR_ASSERT(name[0] == getBelType(bel));
|
2021-01-07 00:22:17 +08:00
|
|
|
|
|
|
|
return bel;
|
|
|
|
}
|
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
IdStringList Arch::getBelName(BelId bel) const
|
2021-01-07 00:22:17 +08:00
|
|
|
{
|
|
|
|
int x = CycloneV::pos2x(bel.pos);
|
|
|
|
int y = CycloneV::pos2y(bel.pos);
|
2021-01-07 02:07:59 +08:00
|
|
|
int z = bel.z & 0xFF;
|
2021-01-07 00:22:17 +08:00
|
|
|
|
2021-05-01 21:55:33 +08:00
|
|
|
std::array<IdString, 4> ids{
|
2021-05-03 22:12:24 +08:00
|
|
|
getBelType(bel),
|
2021-05-01 21:55:33 +08:00
|
|
|
int2id.at(x),
|
|
|
|
int2id.at(y),
|
|
|
|
int2id.at(z),
|
|
|
|
};
|
|
|
|
|
|
|
|
return IdStringList(ids);
|
|
|
|
}
|
2021-01-07 00:22:17 +08:00
|
|
|
|
2022-12-07 17:27:58 +08:00
|
|
|
bool Arch::isBelLocationValid(BelId bel, bool explain_invalid) const
|
2021-05-06 20:49:26 +08:00
|
|
|
{
|
|
|
|
auto &data = bel_data(bel);
|
2021-10-04 02:33:07 +08:00
|
|
|
if (data.type.in(id_MISTRAL_COMB, id_MISTRAL_MCOMB)) {
|
|
|
|
return is_alm_legal(data.lab_data.lab, data.lab_data.alm) && check_lab_input_count(data.lab_data.lab) &&
|
|
|
|
check_mlab_groups(data.lab_data.lab);
|
2021-05-06 20:49:26 +08:00
|
|
|
} else if (data.type == id_MISTRAL_FF) {
|
2021-05-15 01:26:51 +08:00
|
|
|
return is_alm_legal(data.lab_data.lab, data.lab_data.alm) && check_lab_input_count(data.lab_data.lab) &&
|
2021-10-04 02:33:07 +08:00
|
|
|
is_lab_ctrlset_legal(data.lab_data.lab) && check_mlab_groups(data.lab_data.lab);
|
2021-05-06 20:49:26 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-05-15 01:26:51 +08:00
|
|
|
void Arch::update_bel(BelId bel)
|
|
|
|
{
|
|
|
|
auto &data = bel_data(bel);
|
2021-10-04 02:33:07 +08:00
|
|
|
if (data.type.in(id_MISTRAL_COMB, id_MISTRAL_MCOMB, id_MISTRAL_FF)) {
|
2021-05-15 01:26:51 +08:00
|
|
|
update_alm_input_count(data.lab_data.lab, data.lab_data.alm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-01 21:55:33 +08:00
|
|
|
WireId Arch::getWireByName(IdStringList name) const
|
|
|
|
{
|
|
|
|
// non-mistral wires
|
|
|
|
auto found_npnr = npnr_wirebyname.find(name);
|
|
|
|
if (found_npnr != npnr_wirebyname.end())
|
|
|
|
return found_npnr->second;
|
|
|
|
// mistral wires
|
|
|
|
NPNR_ASSERT(name.size() == 4);
|
|
|
|
CycloneV::rnode_type_t ty = id2rn_t.at(name[0]);
|
|
|
|
int x = id2int.at(name[1]);
|
|
|
|
int y = id2int.at(name[2]);
|
|
|
|
int z = id2int.at(name[3]);
|
|
|
|
return WireId(CycloneV::rnode(ty, x, y, z));
|
|
|
|
}
|
|
|
|
|
|
|
|
IdStringList Arch::getWireName(WireId wire) const
|
|
|
|
{
|
|
|
|
if (wire.is_nextpnr_created()) {
|
|
|
|
// non-mistral wires
|
|
|
|
std::array<IdString, 4> ids{
|
|
|
|
id_WIRE,
|
|
|
|
int2id.at(CycloneV::rn2x(wire.node)),
|
|
|
|
int2id.at(CycloneV::rn2y(wire.node)),
|
|
|
|
wires.at(wire).name_override,
|
|
|
|
};
|
|
|
|
return IdStringList(ids);
|
|
|
|
} else {
|
|
|
|
std::array<IdString, 4> ids{
|
|
|
|
rn_t2id.at(CycloneV::rn2t(wire.node)),
|
|
|
|
int2id.at(CycloneV::rn2x(wire.node)),
|
|
|
|
int2id.at(CycloneV::rn2y(wire.node)),
|
|
|
|
int2id.at(CycloneV::rn2z(wire.node)),
|
|
|
|
};
|
|
|
|
return IdStringList(ids);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PipId Arch::getPipByName(IdStringList name) const
|
|
|
|
{
|
|
|
|
WireId src = getWireByName(name.slice(0, 4));
|
|
|
|
WireId dst = getWireByName(name.slice(4, 8));
|
|
|
|
NPNR_ASSERT(src != WireId());
|
|
|
|
NPNR_ASSERT(dst != WireId());
|
|
|
|
return PipId(src.node, dst.node);
|
|
|
|
}
|
|
|
|
|
|
|
|
IdStringList Arch::getPipName(PipId pip) const
|
|
|
|
{
|
|
|
|
return IdStringList::concat(getWireName(getPipSrcWire(pip)), getWireName(getPipDstWire(pip)));
|
2021-02-03 08:12:14 +08:00
|
|
|
}
|
2021-01-07 00:22:17 +08:00
|
|
|
|
2021-02-04 10:29:59 +08:00
|
|
|
std::vector<BelId> Arch::getBelsByTile(int x, int y) const
|
|
|
|
{
|
|
|
|
// This should probably be redesigned, but it's a hack.
|
2021-05-03 22:12:24 +08:00
|
|
|
std::vector<BelId> bels;
|
|
|
|
if (x >= 0 && x < cyclonev->get_tile_sx() && y >= 0 && y < cyclonev->get_tile_sy()) {
|
|
|
|
for (size_t i = 0; i < bels_by_tile.at(pos2idx(x, y)).size(); i++)
|
|
|
|
bels.push_back(BelId(CycloneV::xy2pos(x, y), i));
|
2021-02-04 10:29:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return bels;
|
|
|
|
}
|
|
|
|
|
2021-05-03 22:12:24 +08:00
|
|
|
IdString Arch::getBelType(BelId bel) const { return bel_data(bel).type; }
|
2021-02-04 10:29:59 +08:00
|
|
|
|
2021-05-03 22:12:24 +08:00
|
|
|
std::vector<IdString> Arch::getBelPins(BelId bel) const
|
|
|
|
{
|
|
|
|
std::vector<IdString> pins;
|
|
|
|
for (auto &p : bel_data(bel).pins)
|
|
|
|
pins.push_back(p.first);
|
|
|
|
return pins;
|
2021-02-04 10:29:59 +08:00
|
|
|
}
|
2021-01-07 00:22:17 +08:00
|
|
|
|
2021-05-05 04:23:11 +08:00
|
|
|
bool Arch::isValidBelForCellType(IdString cell_type, BelId bel) const
|
|
|
|
{
|
2021-05-04 03:37:59 +08:00
|
|
|
// Any combinational cell type can - theoretically - be placed at a combinational ALM bel
|
|
|
|
// The precise legality mechanics will be dealt with in isBelLocationValid.
|
|
|
|
IdString bel_type = getBelType(bel);
|
|
|
|
if (bel_type == id_MISTRAL_COMB)
|
|
|
|
return is_comb_cell(cell_type);
|
2021-10-04 02:33:07 +08:00
|
|
|
else if (bel_type == id_MISTRAL_MCOMB)
|
|
|
|
return is_comb_cell(cell_type) || (cell_type == id_MISTRAL_MLAB);
|
2021-05-06 20:49:26 +08:00
|
|
|
else if (bel_type == id_MISTRAL_IO)
|
|
|
|
return is_io_cell(cell_type);
|
2021-05-16 04:28:48 +08:00
|
|
|
else if (bel_type == id_MISTRAL_CLKENA)
|
|
|
|
return is_clkbuf_cell(cell_type);
|
2021-05-04 03:37:59 +08:00
|
|
|
else
|
|
|
|
return bel_type == cell_type;
|
|
|
|
}
|
|
|
|
|
2021-05-05 04:23:11 +08:00
|
|
|
BelBucketId Arch::getBelBucketForCellType(IdString cell_type) const
|
|
|
|
{
|
2021-10-04 02:33:07 +08:00
|
|
|
if (is_comb_cell(cell_type) || cell_type == id_MISTRAL_MLAB)
|
2021-05-04 03:37:59 +08:00
|
|
|
return id_MISTRAL_COMB;
|
2021-05-06 20:49:26 +08:00
|
|
|
else if (is_io_cell(cell_type))
|
|
|
|
return id_MISTRAL_IO;
|
2021-05-16 04:28:48 +08:00
|
|
|
else if (is_clkbuf_cell(cell_type))
|
|
|
|
return id_MISTRAL_CLKENA;
|
2021-05-04 03:37:59 +08:00
|
|
|
else
|
|
|
|
return cell_type;
|
|
|
|
}
|
|
|
|
|
2021-10-04 02:33:07 +08:00
|
|
|
BelBucketId Arch::getBelBucketForBel(BelId bel) const
|
|
|
|
{
|
|
|
|
IdString bel_type = getBelType(bel);
|
|
|
|
if (bel_type == id_MISTRAL_MCOMB)
|
|
|
|
return id_MISTRAL_COMB;
|
|
|
|
else
|
|
|
|
return bel_type;
|
|
|
|
}
|
|
|
|
|
2021-05-09 20:16:22 +08:00
|
|
|
BelId Arch::bel_by_block_idx(int x, int y, IdString type, int block_index) const
|
|
|
|
{
|
|
|
|
auto &bels = bels_by_tile.at(pos2idx(x, y));
|
|
|
|
for (size_t i = 0; i < bels.size(); i++) {
|
|
|
|
auto &bel_data = bels.at(i);
|
|
|
|
if (bel_data.type == type && bel_data.block_index == block_index)
|
|
|
|
return BelId(CycloneV::xy2pos(x, y), i);
|
|
|
|
}
|
|
|
|
return BelId();
|
|
|
|
}
|
|
|
|
|
2021-05-03 04:41:09 +08:00
|
|
|
BelId Arch::add_bel(int x, int y, IdString name, IdString type)
|
2021-05-03 03:40:27 +08:00
|
|
|
{
|
2021-05-03 22:12:24 +08:00
|
|
|
auto &bels = bels_by_tile.at(pos2idx(x, y));
|
|
|
|
BelId id = BelId(CycloneV::xy2pos(x, y), bels.size());
|
|
|
|
all_bels.push_back(id);
|
|
|
|
bels.emplace_back();
|
|
|
|
auto &bel = bels.back();
|
2021-05-03 03:40:27 +08:00
|
|
|
bel.name = name;
|
|
|
|
bel.type = type;
|
2021-05-03 22:12:24 +08:00
|
|
|
// TODO: buckets (for example LABs and MLABs in the same bucket)
|
2021-05-03 04:41:09 +08:00
|
|
|
bel.bucket = type;
|
2021-05-03 03:40:27 +08:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
WireId Arch::add_wire(int x, int y, IdString name, uint64_t flags)
|
|
|
|
{
|
|
|
|
std::array<IdString, 4> ids{
|
|
|
|
id_WIRE,
|
|
|
|
int2id.at(x),
|
|
|
|
int2id.at(y),
|
|
|
|
name,
|
|
|
|
};
|
|
|
|
IdStringList full_name(ids);
|
|
|
|
auto existing = npnr_wirebyname.find(full_name);
|
|
|
|
if (existing != npnr_wirebyname.end()) {
|
|
|
|
// Already exists, don't create anything
|
|
|
|
return existing->second;
|
|
|
|
} else {
|
|
|
|
// Determine a unique ID for the wire
|
|
|
|
int z = 0;
|
|
|
|
WireId id;
|
|
|
|
while (wires.count(id = WireId(CycloneV::rnode(CycloneV::rnode_type_t((z >> 10) + 128), x, y, (z & 0x3FF)))))
|
|
|
|
z++;
|
|
|
|
wires[id].name_override = name;
|
|
|
|
wires[id].flags = flags;
|
2021-05-03 22:19:52 +08:00
|
|
|
npnr_wirebyname[full_name] = id;
|
2021-05-03 03:40:27 +08:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-14 05:47:29 +08:00
|
|
|
void Arch::reserve_route(WireId src, WireId dst)
|
|
|
|
{
|
|
|
|
auto &dst_data = wires.at(dst);
|
|
|
|
int idx = -1;
|
|
|
|
|
|
|
|
for (int i = 0; i < int(dst_data.wires_uphill.size()); i++) {
|
|
|
|
if (dst_data.wires_uphill.at(i) == src) {
|
|
|
|
idx = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NPNR_ASSERT(idx != -1);
|
|
|
|
|
|
|
|
dst_data.flags = WireInfo::RESERVED_ROUTE | unsigned(idx);
|
|
|
|
}
|
|
|
|
|
2021-05-09 23:27:57 +08:00
|
|
|
bool Arch::wires_connected(WireId src, WireId dst) const
|
|
|
|
{
|
|
|
|
PipId pip(src.node, dst.node);
|
|
|
|
return getBoundPipNet(pip) != nullptr;
|
|
|
|
}
|
|
|
|
|
2021-05-03 03:40:27 +08:00
|
|
|
PipId Arch::add_pip(WireId src, WireId dst)
|
|
|
|
{
|
|
|
|
wires[src].wires_downhill.push_back(dst);
|
|
|
|
wires[dst].wires_uphill.push_back(src);
|
|
|
|
return PipId(src.node, dst.node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arch::add_bel_pin(BelId bel, IdString pin, PortType dir, WireId wire)
|
|
|
|
{
|
2021-05-03 22:12:24 +08:00
|
|
|
auto &b = bel_data(bel);
|
2021-05-03 22:19:52 +08:00
|
|
|
NPNR_ASSERT(!b.pins.count(pin));
|
2021-05-03 22:12:24 +08:00
|
|
|
b.pins[pin].dir = dir;
|
|
|
|
b.pins[pin].wire = wire;
|
2021-05-03 03:40:27 +08:00
|
|
|
|
|
|
|
BelPin bel_pin;
|
|
|
|
bel_pin.bel = bel;
|
|
|
|
bel_pin.pin = pin;
|
|
|
|
wires[wire].bel_pins.push_back(bel_pin);
|
|
|
|
}
|
|
|
|
|
2021-05-08 04:11:34 +08:00
|
|
|
void Arch::assign_default_pinmap(CellInfo *cell)
|
|
|
|
{
|
2021-12-22 21:54:30 +08:00
|
|
|
if (cell->type == id_MISTRAL_M10K)
|
|
|
|
return; // M10Ks always have a custom pinmap
|
2021-05-08 04:11:34 +08:00
|
|
|
for (auto &port : cell->ports) {
|
|
|
|
auto &pinmap = cell->pin_data[port.first].bel_pins;
|
|
|
|
if (!pinmap.empty())
|
|
|
|
continue; // already mapped
|
|
|
|
if (is_comb_cell(cell->type) && comb_pinmap.count(port.first))
|
|
|
|
pinmap.push_back(comb_pinmap.at(port.first)); // default comb mapping for placer purposes
|
|
|
|
else
|
|
|
|
pinmap.push_back(port.first); // default: assume bel pin named the same as cell pin
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-08 20:38:17 +08:00
|
|
|
void Arch::assignArchInfo()
|
|
|
|
{
|
2021-06-01 23:51:18 +08:00
|
|
|
for (auto &cell : cells) {
|
|
|
|
CellInfo *ci = cell.second.get();
|
2021-10-04 02:33:07 +08:00
|
|
|
if (is_comb_cell(ci->type) || ci->type == id_MISTRAL_MLAB)
|
2021-05-08 20:38:17 +08:00
|
|
|
assign_comb_info(ci);
|
|
|
|
else if (ci->type == id_MISTRAL_FF)
|
|
|
|
assign_ff_info(ci);
|
|
|
|
assign_default_pinmap(ci);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-07 17:00:53 +08:00
|
|
|
BoundingBox Arch::getRouteBoundingBox(WireId src, WireId dst) const
|
2021-05-14 06:51:47 +08:00
|
|
|
{
|
2022-12-07 17:00:53 +08:00
|
|
|
BoundingBox bounds;
|
2021-05-14 06:51:47 +08:00
|
|
|
int src_x = CycloneV::rn2x(src.node);
|
|
|
|
int src_y = CycloneV::rn2y(src.node);
|
|
|
|
int dst_x = CycloneV::rn2x(dst.node);
|
|
|
|
int dst_y = CycloneV::rn2y(dst.node);
|
|
|
|
bounds.x0 = std::min(src_x, dst_x);
|
|
|
|
bounds.y0 = std::min(src_y, dst_y);
|
|
|
|
bounds.x1 = std::max(src_x, dst_x);
|
|
|
|
bounds.y1 = std::max(src_y, dst_y);
|
|
|
|
return bounds;
|
|
|
|
}
|
|
|
|
|
2021-05-08 20:38:17 +08:00
|
|
|
bool Arch::place()
|
|
|
|
{
|
2022-02-17 01:09:54 +08:00
|
|
|
std::string placer = str_or_default(settings, id_placer, defaultPlacer);
|
2021-05-08 20:38:17 +08:00
|
|
|
|
|
|
|
if (placer == "heap") {
|
|
|
|
PlacerHeapCfg cfg(getCtx());
|
|
|
|
cfg.ioBufTypes.insert(id_MISTRAL_IO);
|
|
|
|
cfg.ioBufTypes.insert(id_MISTRAL_IB);
|
|
|
|
cfg.ioBufTypes.insert(id_MISTRAL_OB);
|
|
|
|
cfg.cellGroups.emplace_back();
|
|
|
|
cfg.cellGroups.back().insert({id_MISTRAL_COMB});
|
|
|
|
cfg.cellGroups.back().insert({id_MISTRAL_FF});
|
|
|
|
|
2023-05-16 23:39:44 +08:00
|
|
|
// The Cyclone V is asymmetrical enough that it's somewhat beneficial to prefer connecting things horizontally.
|
|
|
|
cfg.hpwl_scale_x = 1;
|
|
|
|
cfg.hpwl_scale_y = 2;
|
|
|
|
|
2021-05-08 20:38:17 +08:00
|
|
|
cfg.beta = 0.5; // TODO: find a good value of beta for sensible ALM spreading
|
|
|
|
cfg.criticalityExponent = 7;
|
|
|
|
if (!placer_heap(getCtx(), cfg))
|
|
|
|
return false;
|
|
|
|
} else if (placer == "sa") {
|
|
|
|
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
log_error("Mistral architecture does not support placer '%s'\n", placer.c_str());
|
|
|
|
}
|
|
|
|
|
2022-02-17 01:09:54 +08:00
|
|
|
getCtx()->attrs[id_step] = std::string("place");
|
2021-05-08 20:38:17 +08:00
|
|
|
archInfoToAttributes();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Arch::route()
|
|
|
|
{
|
|
|
|
lab_pre_route();
|
|
|
|
|
2023-05-16 22:38:16 +08:00
|
|
|
route_globals();
|
|
|
|
|
2022-02-17 01:09:54 +08:00
|
|
|
std::string router = str_or_default(settings, id_router, defaultRouter);
|
2021-05-08 20:38:17 +08:00
|
|
|
bool result;
|
|
|
|
if (router == "router1") {
|
|
|
|
result = router1(getCtx(), Router1Cfg(getCtx()));
|
|
|
|
} else if (router == "router2") {
|
|
|
|
router2(getCtx(), Router2Cfg(getCtx()));
|
|
|
|
result = true;
|
|
|
|
} else {
|
|
|
|
log_error("Mistral architecture does not support router '%s'\n", router.c_str());
|
|
|
|
}
|
2022-02-17 01:09:54 +08:00
|
|
|
getCtx()->attrs[id_step] = std::string("route");
|
2021-05-08 20:38:17 +08:00
|
|
|
archInfoToAttributes();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-05-01 20:40:45 +08:00
|
|
|
const std::string Arch::defaultPlacer = "heap";
|
2023-03-17 16:27:36 +08:00
|
|
|
|
|
|
|
const std::vector<std::string> Arch::availablePlacers = {"sa", "heap"};
|
2021-05-01 20:40:45 +08:00
|
|
|
|
2021-05-15 20:58:03 +08:00
|
|
|
const std::string Arch::defaultRouter = "router2";
|
2021-05-01 20:40:45 +08:00
|
|
|
const std::vector<std::string> Arch::availableRouters = {"router1", "router2"};
|
|
|
|
|
2021-05-15 21:51:12 +08:00
|
|
|
NEXTPNR_NAMESPACE_END
|