
This involves very few changes, all typical to WASM ports: * WASM doesn't currently support threads or atomics so those are disabled. * WASM doesn't currently support exceptions so the exception machinery is stubbed out. * WASM doesn't (and can't) have mmap(), so an emulation library is used. That library currently doesn't support MAP_SHARED flags, so MAP_PRIVATE is used instead. There is also an update to bring ECP5 bbasm CMake rules to parity with iCE40 ones, since although it is possible to embed chipdb into nextpnr on WASM, a 200 MB WASM file has very few practical uses. The README is not updated and there is no included toolchain file because at the moment it's not possible to build nextpnr with upstream boost and wasi-libc. Boost requires a patch (merged, will be available in boost 1.74.0), wasi-libc requires a few unmerged patches.
1298 lines
42 KiB
C++
1298 lines
42 KiB
C++
/*
|
|
* nextpnr -- Next Generation Place and Route
|
|
*
|
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
|
* Copyright (C) 2018 Serge Bazanski <q3k@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 <algorithm>
|
|
#include <boost/iostreams/device/mapped_file.hpp>
|
|
#include <cmath>
|
|
#include "cells.h"
|
|
#include "gfx.h"
|
|
#include "log.h"
|
|
#include "nextpnr.h"
|
|
#include "placer1.h"
|
|
#include "placer_heap.h"
|
|
#include "router1.h"
|
|
#include "router2.h"
|
|
#include "timing_opt.h"
|
|
#include "util.h"
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void IdString::initialize_arch(const BaseCtx *ctx)
|
|
{
|
|
#define X(t) initialize_add(ctx, #t, ID_##t);
|
|
#include "constids.inc"
|
|
#undef X
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); }
|
|
|
|
#if defined(_MSC_VER)
|
|
void load_chipdb();
|
|
#endif
|
|
|
|
#if defined(EXTERNAL_CHIPDB_ROOT)
|
|
const char *chipdb_blob_384 = nullptr;
|
|
const char *chipdb_blob_1k = nullptr;
|
|
const char *chipdb_blob_5k = nullptr;
|
|
const char *chipdb_blob_u4k = nullptr;
|
|
const char *chipdb_blob_8k = nullptr;
|
|
|
|
boost::iostreams::mapped_file blob_files[5];
|
|
|
|
const char *mmap_file(int index, const char *filename)
|
|
{
|
|
try {
|
|
blob_files[index].open(filename, boost::iostreams::mapped_file::priv);
|
|
if (!blob_files[index].is_open())
|
|
log_error("Unable to read chipdb %s\n", filename);
|
|
return (const char *)blob_files[index].data();
|
|
} catch (...) {
|
|
log_error("Unable to read chipdb %s\n", filename);
|
|
}
|
|
}
|
|
|
|
void load_chipdb()
|
|
{
|
|
chipdb_blob_384 = mmap_file(0, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-384.bin");
|
|
chipdb_blob_1k = mmap_file(1, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-1k.bin");
|
|
chipdb_blob_5k = mmap_file(2, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-5k.bin");
|
|
chipdb_blob_u4k = mmap_file(3, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-u4k.bin");
|
|
chipdb_blob_8k = mmap_file(4, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-8k.bin");
|
|
}
|
|
#endif
|
|
Arch::Arch(ArchArgs args) : args(args)
|
|
{
|
|
#if defined(_MSC_VER) || defined(EXTERNAL_CHIPDB_ROOT)
|
|
load_chipdb();
|
|
#endif
|
|
|
|
#ifdef ICE40_HX1K_ONLY
|
|
if (args.type == ArchArgs::HX1K) {
|
|
fast_part = true;
|
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k));
|
|
} else {
|
|
log_error("Unsupported iCE40 chip type.\n");
|
|
}
|
|
#else
|
|
if (args.type == ArchArgs::LP384) {
|
|
fast_part = false;
|
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_384));
|
|
} else if (args.type == ArchArgs::LP1K || args.type == ArchArgs::HX1K) {
|
|
fast_part = args.type == ArchArgs::HX1K;
|
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k));
|
|
} else if (args.type == ArchArgs::UP5K) {
|
|
fast_part = false;
|
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_5k));
|
|
} else if (args.type == ArchArgs::U4K) {
|
|
fast_part = false;
|
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_u4k));
|
|
} else if (args.type == ArchArgs::LP8K || args.type == ArchArgs::HX8K) {
|
|
fast_part = args.type == ArchArgs::HX8K;
|
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_8k));
|
|
} else {
|
|
log_error("Unsupported iCE40 chip type.\n");
|
|
}
|
|
#endif
|
|
|
|
package_info = nullptr;
|
|
for (int i = 0; i < chip_info->num_packages; i++) {
|
|
if (chip_info->packages_data[i].name.get() == args.package) {
|
|
package_info = &(chip_info->packages_data[i]);
|
|
break;
|
|
}
|
|
}
|
|
if (package_info == nullptr)
|
|
log_error("Unsupported package '%s'.\n", args.package.c_str());
|
|
|
|
bel_carry.resize(chip_info->num_bels);
|
|
bel_to_cell.resize(chip_info->num_bels);
|
|
wire_to_net.resize(chip_info->num_wires);
|
|
pip_to_net.resize(chip_info->num_pips);
|
|
switches_locked.resize(chip_info->num_switches);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
std::string Arch::getChipName() const
|
|
{
|
|
#ifdef ICE40_HX1K_ONLY
|
|
if (args.type == ArchArgs::HX1K) {
|
|
return "Lattice LP1K";
|
|
} else {
|
|
log_error("Unsupported iCE40 chip type.\n");
|
|
}
|
|
#else
|
|
if (args.type == ArchArgs::LP384) {
|
|
return "Lattice LP384";
|
|
} else if (args.type == ArchArgs::LP1K) {
|
|
return "Lattice LP1K";
|
|
} else if (args.type == ArchArgs::HX1K) {
|
|
return "Lattice HX1K";
|
|
} else if (args.type == ArchArgs::UP5K) {
|
|
return "Lattice UP5K";
|
|
} else if (args.type == ArchArgs::U4K) {
|
|
return "Lattice U4K";
|
|
} else if (args.type == ArchArgs::LP8K) {
|
|
return "Lattice LP8K";
|
|
} else if (args.type == ArchArgs::HX8K) {
|
|
return "Lattice HX8K";
|
|
} else {
|
|
log_error("Unknown chip\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
IdString Arch::archArgsToId(ArchArgs args) const
|
|
{
|
|
if (args.type == ArchArgs::LP384)
|
|
return id("lp384");
|
|
if (args.type == ArchArgs::LP1K)
|
|
return id("lp1k");
|
|
if (args.type == ArchArgs::HX1K)
|
|
return id("hx1k");
|
|
if (args.type == ArchArgs::UP5K)
|
|
return id("up5k");
|
|
if (args.type == ArchArgs::U4K)
|
|
return id("u4k");
|
|
if (args.type == ArchArgs::LP8K)
|
|
return id("lp8k");
|
|
if (args.type == ArchArgs::HX8K)
|
|
return id("hx8k");
|
|
return IdString();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
BelId Arch::getBelByName(IdString name) const
|
|
{
|
|
BelId ret;
|
|
|
|
if (bel_by_name.empty()) {
|
|
for (int i = 0; i < chip_info->num_bels; i++)
|
|
bel_by_name[id(chip_info->bel_data[i].name.get())] = i;
|
|
}
|
|
|
|
auto it = bel_by_name.find(name);
|
|
if (it != bel_by_name.end())
|
|
ret.index = it->second;
|
|
|
|
return ret;
|
|
}
|
|
|
|
BelId Arch::getBelByLocation(Loc loc) const
|
|
{
|
|
BelId bel;
|
|
|
|
if (bel_by_loc.empty()) {
|
|
for (int i = 0; i < chip_info->num_bels; i++) {
|
|
BelId b;
|
|
b.index = i;
|
|
bel_by_loc[getBelLocation(b)] = i;
|
|
}
|
|
}
|
|
|
|
auto it = bel_by_loc.find(loc);
|
|
if (it != bel_by_loc.end())
|
|
bel.index = it->second;
|
|
|
|
return bel;
|
|
}
|
|
|
|
BelRange Arch::getBelsByTile(int x, int y) const
|
|
{
|
|
// In iCE40 chipdb bels at the same tile are consecutive and dense z ordinates
|
|
// are used
|
|
BelRange br;
|
|
|
|
br.b.cursor = Arch::getBelByLocation(Loc(x, y, 0)).index;
|
|
br.e.cursor = br.b.cursor;
|
|
|
|
if (br.e.cursor != -1) {
|
|
while (br.e.cursor < chip_info->num_bels && chip_info->bel_data[br.e.cursor].x == x &&
|
|
chip_info->bel_data[br.e.cursor].y == y)
|
|
br.e.cursor++;
|
|
}
|
|
|
|
return br;
|
|
}
|
|
|
|
PortType Arch::getBelPinType(BelId bel, IdString pin) const
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
|
|
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
|
|
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
|
|
|
|
if (num_bel_wires < 7) {
|
|
for (int i = 0; i < num_bel_wires; i++) {
|
|
if (bel_wires[i].port == pin.index)
|
|
return PortType(bel_wires[i].type);
|
|
}
|
|
} else {
|
|
int b = 0, e = num_bel_wires - 1;
|
|
while (b <= e) {
|
|
int i = (b + e) / 2;
|
|
if (bel_wires[i].port == pin.index)
|
|
return PortType(bel_wires[i].type);
|
|
if (bel_wires[i].port > pin.index)
|
|
e = i - 1;
|
|
else
|
|
b = i + 1;
|
|
}
|
|
}
|
|
|
|
return PORT_INOUT;
|
|
}
|
|
|
|
std::vector<std::pair<IdString, std::string>> Arch::getBelAttrs(BelId bel) const
|
|
{
|
|
std::vector<std::pair<IdString, std::string>> ret;
|
|
|
|
ret.push_back(std::make_pair(id("INDEX"), stringf("%d", bel.index)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
WireId Arch::getBelPinWire(BelId bel, IdString pin) const
|
|
{
|
|
WireId ret;
|
|
|
|
NPNR_ASSERT(bel != BelId());
|
|
|
|
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
|
|
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
|
|
|
|
if (num_bel_wires < 7) {
|
|
for (int i = 0; i < num_bel_wires; i++) {
|
|
if (bel_wires[i].port == pin.index) {
|
|
ret.index = bel_wires[i].wire_index;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
int b = 0, e = num_bel_wires - 1;
|
|
while (b <= e) {
|
|
int i = (b + e) / 2;
|
|
if (bel_wires[i].port == pin.index) {
|
|
ret.index = bel_wires[i].wire_index;
|
|
break;
|
|
}
|
|
if (bel_wires[i].port > pin.index)
|
|
e = i - 1;
|
|
else
|
|
b = i + 1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<IdString> Arch::getBelPins(BelId bel) const
|
|
{
|
|
std::vector<IdString> ret;
|
|
|
|
NPNR_ASSERT(bel != BelId());
|
|
|
|
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
|
|
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
|
|
|
|
for (int i = 0; i < num_bel_wires; i++)
|
|
ret.push_back(IdString(bel_wires[i].port));
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool Arch::isBelLocked(BelId bel) const
|
|
{
|
|
const BelConfigPOD *bel_config = nullptr;
|
|
for (int i = 0; i < chip_info->num_belcfgs; i++) {
|
|
if (chip_info->bel_config[i].bel_index == bel.index) {
|
|
bel_config = &chip_info->bel_config[i];
|
|
break;
|
|
}
|
|
}
|
|
NPNR_ASSERT(bel_config != nullptr);
|
|
for (int i = 0; i < bel_config->num_entries; i++) {
|
|
if (strcmp("LOCKED", bel_config->entries[i].cbit_name.get()))
|
|
continue;
|
|
if ("LOCKED_" + archArgs().package == bel_config->entries[i].entry_name.get())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
WireId Arch::getWireByName(IdString name) const
|
|
{
|
|
WireId ret;
|
|
|
|
if (wire_by_name.empty()) {
|
|
for (int i = 0; i < chip_info->num_wires; i++)
|
|
wire_by_name[id(chip_info->wire_data[i].name.get())] = i;
|
|
}
|
|
|
|
auto it = wire_by_name.find(name);
|
|
if (it != wire_by_name.end())
|
|
ret.index = it->second;
|
|
|
|
return ret;
|
|
}
|
|
|
|
IdString Arch::getWireType(WireId wire) const
|
|
{
|
|
NPNR_ASSERT(wire != WireId());
|
|
switch (chip_info->wire_data[wire.index].type) {
|
|
case WireInfoPOD::WIRE_TYPE_NONE:
|
|
return IdString();
|
|
case WireInfoPOD::WIRE_TYPE_GLB2LOCAL:
|
|
return id("GLB2LOCAL");
|
|
case WireInfoPOD::WIRE_TYPE_GLB_NETWK:
|
|
return id("GLB_NETWK");
|
|
case WireInfoPOD::WIRE_TYPE_LOCAL:
|
|
return id("LOCAL");
|
|
case WireInfoPOD::WIRE_TYPE_LUTFF_IN:
|
|
return id("LUTFF_IN");
|
|
case WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT:
|
|
return id("LUTFF_IN_LUT");
|
|
case WireInfoPOD::WIRE_TYPE_LUTFF_LOUT:
|
|
return id("LUTFF_LOUT");
|
|
case WireInfoPOD::WIRE_TYPE_LUTFF_OUT:
|
|
return id("LUTFF_OUT");
|
|
case WireInfoPOD::WIRE_TYPE_LUTFF_COUT:
|
|
return id("LUTFF_COUT");
|
|
case WireInfoPOD::WIRE_TYPE_LUTFF_GLOBAL:
|
|
return id("LUTFF_GLOBAL");
|
|
case WireInfoPOD::WIRE_TYPE_CARRY_IN_MUX:
|
|
return id("CARRY_IN_MUX");
|
|
case WireInfoPOD::WIRE_TYPE_SP4_V:
|
|
return id("SP4_V");
|
|
case WireInfoPOD::WIRE_TYPE_SP4_H:
|
|
return id("SP4_H");
|
|
case WireInfoPOD::WIRE_TYPE_SP12_V:
|
|
return id("SP12_V");
|
|
case WireInfoPOD::WIRE_TYPE_SP12_H:
|
|
return id("SP12_H");
|
|
}
|
|
return IdString();
|
|
}
|
|
|
|
std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
|
|
{
|
|
std::vector<std::pair<IdString, std::string>> ret;
|
|
auto &wi = chip_info->wire_data[wire.index];
|
|
|
|
ret.push_back(std::make_pair(id("INDEX"), stringf("%d", wire.index)));
|
|
|
|
ret.push_back(std::make_pair(id("GRID_X"), stringf("%d", wi.x)));
|
|
ret.push_back(std::make_pair(id("GRID_Y"), stringf("%d", wi.y)));
|
|
ret.push_back(std::make_pair(id("GRID_Z"), stringf("%d", wi.z)));
|
|
|
|
#if 0
|
|
for (int i = 0; i < wi.num_segments; i++) {
|
|
auto &si = wi.segments[i];
|
|
ret.push_back(std::make_pair(id(stringf("segment[%d]", i)),
|
|
stringf("X%d/Y%d/%s", si.x, si.y, chip_info->tile_wire_names[si.index].get())));
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
PipId Arch::getPipByName(IdString name) const
|
|
{
|
|
PipId ret;
|
|
|
|
if (pip_by_name.empty()) {
|
|
for (int i = 0; i < chip_info->num_pips; i++) {
|
|
PipId pip;
|
|
pip.index = i;
|
|
pip_by_name[getPipName(pip)] = i;
|
|
}
|
|
}
|
|
|
|
auto it = pip_by_name.find(name);
|
|
if (it != pip_by_name.end())
|
|
ret.index = it->second;
|
|
|
|
return ret;
|
|
}
|
|
|
|
IdString Arch::getPipName(PipId pip) const
|
|
{
|
|
NPNR_ASSERT(pip != PipId());
|
|
|
|
#if 1
|
|
int x = chip_info->pip_data[pip.index].x;
|
|
int y = chip_info->pip_data[pip.index].y;
|
|
|
|
std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get();
|
|
std::replace(src_name.begin(), src_name.end(), '/', '.');
|
|
|
|
std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get();
|
|
std::replace(dst_name.begin(), dst_name.end(), '/', '.');
|
|
|
|
return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name);
|
|
#else
|
|
return id(chip_info->pip_data[pip.index].name.get());
|
|
#endif
|
|
}
|
|
|
|
IdString Arch::getPipType(PipId pip) const { return IdString(); }
|
|
|
|
std::vector<std::pair<IdString, std::string>> Arch::getPipAttrs(PipId pip) const
|
|
{
|
|
std::vector<std::pair<IdString, std::string>> ret;
|
|
|
|
ret.push_back(std::make_pair(id("INDEX"), stringf("%d", pip.index)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
BelId Arch::getPackagePinBel(const std::string &pin) const
|
|
{
|
|
for (int i = 0; i < package_info->num_pins; i++) {
|
|
if (package_info->pins[i].name.get() == pin) {
|
|
BelId id;
|
|
id.index = package_info->pins[i].bel_index;
|
|
return id;
|
|
}
|
|
}
|
|
return BelId();
|
|
}
|
|
|
|
std::string Arch::getBelPackagePin(BelId bel) const
|
|
{
|
|
for (int i = 0; i < package_info->num_pins; i++) {
|
|
if (package_info->pins[i].bel_index == bel.index) {
|
|
return std::string(package_info->pins[i].name.get());
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
GroupId Arch::getGroupByName(IdString name) const
|
|
{
|
|
for (auto g : getGroups())
|
|
if (getGroupName(g) == name)
|
|
return g;
|
|
return GroupId();
|
|
}
|
|
|
|
IdString Arch::getGroupName(GroupId group) const
|
|
{
|
|
std::string suffix;
|
|
|
|
switch (group.type) {
|
|
case GroupId::TYPE_FRAME:
|
|
suffix = "tile";
|
|
break;
|
|
case GroupId::TYPE_MAIN_SW:
|
|
suffix = "main_sw";
|
|
break;
|
|
case GroupId::TYPE_LOCAL_SW:
|
|
suffix = "local_sw";
|
|
break;
|
|
case GroupId::TYPE_LC0_SW:
|
|
suffix = "lc0_sw";
|
|
break;
|
|
case GroupId::TYPE_LC1_SW:
|
|
suffix = "lc1_sw";
|
|
break;
|
|
case GroupId::TYPE_LC2_SW:
|
|
suffix = "lc2_sw";
|
|
break;
|
|
case GroupId::TYPE_LC3_SW:
|
|
suffix = "lc3_sw";
|
|
break;
|
|
case GroupId::TYPE_LC4_SW:
|
|
suffix = "lc4_sw";
|
|
break;
|
|
case GroupId::TYPE_LC5_SW:
|
|
suffix = "lc5_sw";
|
|
break;
|
|
case GroupId::TYPE_LC6_SW:
|
|
suffix = "lc6_sw";
|
|
break;
|
|
case GroupId::TYPE_LC7_SW:
|
|
suffix = "lc7_sw";
|
|
break;
|
|
default:
|
|
return IdString();
|
|
}
|
|
|
|
return id("X" + std::to_string(group.x) + "/Y" + std::to_string(group.y) + "/" + suffix);
|
|
}
|
|
|
|
std::vector<GroupId> Arch::getGroups() const
|
|
{
|
|
std::vector<GroupId> ret;
|
|
|
|
for (int y = 0; y < chip_info->height; y++) {
|
|
for (int x = 0; x < chip_info->width; x++) {
|
|
TileType type = chip_info->tile_grid[y * chip_info->width + x];
|
|
if (type == TILE_NONE)
|
|
continue;
|
|
|
|
GroupId group;
|
|
group.type = GroupId::TYPE_FRAME;
|
|
group.x = x;
|
|
group.y = y;
|
|
// ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_MAIN_SW;
|
|
ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_LOCAL_SW;
|
|
ret.push_back(group);
|
|
|
|
if (type == TILE_LOGIC) {
|
|
group.type = GroupId::TYPE_LC0_SW;
|
|
ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_LC1_SW;
|
|
ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_LC2_SW;
|
|
ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_LC3_SW;
|
|
ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_LC4_SW;
|
|
ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_LC5_SW;
|
|
ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_LC6_SW;
|
|
ret.push_back(group);
|
|
|
|
group.type = GroupId::TYPE_LC7_SW;
|
|
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;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const
|
|
{
|
|
const auto &driver = net_info->driver;
|
|
if (driver.port == id_COUT) {
|
|
NPNR_ASSERT(sink.port == id_CIN || sink.port == id_I3);
|
|
NPNR_ASSERT(driver.cell->constr_abs_z);
|
|
bool cin = sink.port == id_CIN;
|
|
bool same_y = driver.cell->constr_z < 7;
|
|
if (cin && same_y)
|
|
budget = 0;
|
|
else {
|
|
switch (args.type) {
|
|
#ifndef ICE40_HX1K_ONLY
|
|
case ArchArgs::HX8K:
|
|
#endif
|
|
case ArchArgs::HX1K:
|
|
budget = cin ? 190 : (same_y ? 260 : 560);
|
|
break;
|
|
#ifndef ICE40_HX1K_ONLY
|
|
case ArchArgs::LP384:
|
|
case ArchArgs::LP1K:
|
|
case ArchArgs::LP8K:
|
|
budget = cin ? 290 : (same_y ? 380 : 670);
|
|
break;
|
|
case ArchArgs::UP5K:
|
|
case ArchArgs::U4K:
|
|
budget = cin ? 560 : (same_y ? 660 : 1220);
|
|
break;
|
|
#endif
|
|
default:
|
|
log_error("Unsupported iCE40 chip type.\n");
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool Arch::place()
|
|
{
|
|
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
|
|
if (placer == "heap") {
|
|
PlacerHeapCfg cfg(getCtx());
|
|
cfg.ioBufTypes.insert(id_SB_IO);
|
|
if (!placer_heap(getCtx(), cfg))
|
|
return false;
|
|
} else if (placer == "sa") {
|
|
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
|
|
return false;
|
|
} else {
|
|
log_error("iCE40 architecture does not support placer '%s'\n", placer.c_str());
|
|
}
|
|
bool retVal = true;
|
|
if (bool_or_default(settings, id("opt_timing"), false)) {
|
|
TimingOptCfg tocfg(getCtx());
|
|
tocfg.cellTypes.insert(id_ICESTORM_LC);
|
|
retVal = timing_opt(getCtx(), tocfg);
|
|
}
|
|
getCtx()->settings[getCtx()->id("place")] = 1;
|
|
archInfoToAttributes();
|
|
return retVal;
|
|
}
|
|
|
|
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("iCE40 architecture does not support router '%s'\n", router.c_str());
|
|
}
|
|
getCtx()->settings[getCtx()->id("route")] = 1;
|
|
archInfoToAttributes();
|
|
return result;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
DecalXY Arch::getBelDecal(BelId bel) const
|
|
{
|
|
DecalXY decalxy;
|
|
decalxy.decal.type = DecalId::TYPE_BEL;
|
|
decalxy.decal.index = bel.index;
|
|
decalxy.decal.active = bel_to_cell.at(bel.index) != nullptr;
|
|
return decalxy;
|
|
}
|
|
|
|
DecalXY Arch::getWireDecal(WireId wire) const
|
|
{
|
|
DecalXY decalxy;
|
|
decalxy.decal.type = DecalId::TYPE_WIRE;
|
|
decalxy.decal.index = wire.index;
|
|
decalxy.decal.active = wire_to_net.at(wire.index) != nullptr;
|
|
return decalxy;
|
|
}
|
|
|
|
DecalXY Arch::getPipDecal(PipId pip) const
|
|
{
|
|
DecalXY decalxy;
|
|
decalxy.decal.type = DecalId::TYPE_PIP;
|
|
decalxy.decal.index = pip.index;
|
|
decalxy.decal.active = pip_to_net.at(pip.index) != nullptr;
|
|
return decalxy;
|
|
};
|
|
|
|
DecalXY Arch::getGroupDecal(GroupId group) const
|
|
{
|
|
DecalXY decalxy;
|
|
decalxy.decal.type = DecalId::TYPE_GROUP;
|
|
decalxy.decal.index = (group.type << 16) | (group.x << 8) | (group.y);
|
|
decalxy.decal.active = true;
|
|
return decalxy;
|
|
};
|
|
|
|
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|
{
|
|
std::vector<GraphicElement> ret;
|
|
|
|
if (decal.type == DecalId::TYPE_GROUP) {
|
|
int type = (decal.index >> 16) & 255;
|
|
int x = (decal.index >> 8) & 255;
|
|
int y = decal.index & 255;
|
|
|
|
if (type == GroupId::TYPE_FRAME) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_LINE;
|
|
el.style = GraphicElement::STYLE_FRAME;
|
|
|
|
el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.01, el.y2 = y + 0.01;
|
|
ret.push_back(el);
|
|
el.x1 = x + 0.01, el.x2 = x + 0.01, el.y1 = y + 0.01, el.y2 = y + 0.02;
|
|
ret.push_back(el);
|
|
|
|
el.x1 = x + 0.99, el.x2 = x + 0.98, el.y1 = y + 0.01, el.y2 = y + 0.01;
|
|
ret.push_back(el);
|
|
el.x1 = x + 0.99, el.x2 = x + 0.99, el.y1 = y + 0.01, el.y2 = y + 0.02;
|
|
ret.push_back(el);
|
|
|
|
el.x1 = x + 0.99, el.x2 = x + 0.98, el.y1 = y + 0.99, el.y2 = y + 0.99;
|
|
ret.push_back(el);
|
|
el.x1 = x + 0.99, el.x2 = x + 0.99, el.y1 = y + 0.99, el.y2 = y + 0.98;
|
|
ret.push_back(el);
|
|
|
|
el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.99, el.y2 = y + 0.99;
|
|
ret.push_back(el);
|
|
el.x1 = x + 0.01, el.x2 = x + 0.01, el.y1 = y + 0.99, el.y2 = y + 0.98;
|
|
ret.push_back(el);
|
|
}
|
|
|
|
if (type == GroupId::TYPE_MAIN_SW) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
el.style = GraphicElement::STYLE_FRAME;
|
|
|
|
el.x1 = x + main_swbox_x1;
|
|
el.x2 = x + main_swbox_x2;
|
|
el.y1 = y + main_swbox_y1;
|
|
el.y2 = y + main_swbox_y2;
|
|
ret.push_back(el);
|
|
}
|
|
|
|
if (type == GroupId::TYPE_LOCAL_SW) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
el.style = GraphicElement::STYLE_FRAME;
|
|
|
|
el.x1 = x + local_swbox_x1;
|
|
el.x2 = x + local_swbox_x2;
|
|
el.y1 = y + local_swbox_y1;
|
|
el.y2 = y + local_swbox_y2;
|
|
ret.push_back(el);
|
|
}
|
|
|
|
if (GroupId::TYPE_LC0_SW <= type && type <= GroupId::TYPE_LC7_SW) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
el.style = GraphicElement::STYLE_FRAME;
|
|
|
|
el.x1 = x + lut_swbox_x1;
|
|
el.x2 = x + lut_swbox_x2;
|
|
el.y1 = y + logic_cell_y1 + logic_cell_pitch * (type - GroupId::TYPE_LC0_SW);
|
|
el.y2 = y + logic_cell_y2 + logic_cell_pitch * (type - GroupId::TYPE_LC0_SW);
|
|
ret.push_back(el);
|
|
}
|
|
}
|
|
|
|
if (decal.type == DecalId::TYPE_WIRE) {
|
|
int n = chip_info->wire_data[decal.index].num_segments;
|
|
const WireSegmentPOD *p = chip_info->wire_data[decal.index].segments.get();
|
|
|
|
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
gfxTileWire(ret, p[i].x, p[i].y, chip_info->width, chip_info->height, GfxTileWireId(p[i].index), style);
|
|
|
|
#if 0
|
|
if (ret.empty()) {
|
|
WireId wire;
|
|
wire.index = decal.index;
|
|
log_warning("No gfx decal for wire %s (%d).\n", getWireName(wire).c_str(getCtx()), decal.index);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (decal.type == DecalId::TYPE_PIP) {
|
|
const PipInfoPOD &p = chip_info->pip_data[decal.index];
|
|
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_HIDDEN;
|
|
gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style);
|
|
|
|
#if 0
|
|
if (ret.empty()) {
|
|
PipId pip;
|
|
pip.index = decal.index;
|
|
log_warning("No gfx decal for pip %s (%d).\n", getPipName(pip).c_str(getCtx()), decal.index);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (decal.type == DecalId::TYPE_BEL) {
|
|
BelId bel;
|
|
bel.index = decal.index;
|
|
|
|
auto bel_type = getBelType(bel);
|
|
|
|
if (bel_type == id_ICESTORM_LC) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
|
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
|
|
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
|
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
|
|
(chip_info->bel_data[bel.index].z) * logic_cell_pitch;
|
|
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
|
|
(chip_info->bel_data[bel.index].z) * logic_cell_pitch;
|
|
ret.push_back(el);
|
|
}
|
|
|
|
if (bel_type == id_SB_IO) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
|
el.x1 = chip_info->bel_data[bel.index].x + lut_swbox_x1;
|
|
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
|
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
|
|
(4 * chip_info->bel_data[bel.index].z) * logic_cell_pitch;
|
|
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
|
|
(4 * chip_info->bel_data[bel.index].z + 3) * logic_cell_pitch;
|
|
ret.push_back(el);
|
|
}
|
|
|
|
if (bel_type == id_ICESTORM_RAM) {
|
|
for (int i = 0; i < 2; i++) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
|
el.x1 = chip_info->bel_data[bel.index].x + lut_swbox_x1;
|
|
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
|
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i;
|
|
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + i + 7 * logic_cell_pitch;
|
|
ret.push_back(el);
|
|
}
|
|
}
|
|
|
|
if (bel_type == id_SB_GB) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
|
el.x1 = chip_info->bel_data[bel.index].x + local_swbox_x1 + 0.05;
|
|
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2 - 0.05;
|
|
el.y1 = chip_info->bel_data[bel.index].y + main_swbox_y2 - 0.05;
|
|
el.y2 = chip_info->bel_data[bel.index].y + main_swbox_y2 - 0.10;
|
|
ret.push_back(el);
|
|
}
|
|
|
|
if (bel_type == id_ICESTORM_PLL || bel_type == id_SB_WARMBOOT) {
|
|
GraphicElement el;
|
|
el.type = GraphicElement::TYPE_BOX;
|
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
|
el.x1 = chip_info->bel_data[bel.index].x + local_swbox_x1 + 0.05;
|
|
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2 - 0.05;
|
|
el.y1 = chip_info->bel_data[bel.index].y + main_swbox_y2;
|
|
el.y2 = chip_info->bel_data[bel.index].y + main_swbox_y2 + 0.05;
|
|
ret.push_back(el);
|
|
}
|
|
|
|
#if 0
|
|
if (ret.empty()) {
|
|
BelId bel;
|
|
bel.index = decal.index;
|
|
log_warning("No gfx decal for bel %s (%d).\n", getBelName(bel).c_str(getCtx()), decal.index);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
|
|
{
|
|
if (cell->type == id_ICESTORM_LC && cell->lcInfo.dffEnable) {
|
|
if (toPort == id_O)
|
|
return false;
|
|
} else if (cell->type == id_ICESTORM_RAM || cell->type == id_ICESTORM_SPRAM) {
|
|
return false;
|
|
}
|
|
return getCellDelayInternal(cell, fromPort, toPort, delay);
|
|
}
|
|
|
|
bool Arch::getCellDelayInternal(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
|
|
{
|
|
for (int i = 0; i < chip_info->num_timing_cells; i++) {
|
|
const auto &tc = chip_info->cell_timing[i];
|
|
if (tc.type == cell->type.index) {
|
|
for (int j = 0; j < tc.num_paths; j++) {
|
|
const auto &path = tc.path_delays[j];
|
|
if (path.from_port == fromPort.index && path.to_port == toPort.index) {
|
|
if (fast_part)
|
|
delay.delay = path.fast_delay;
|
|
else
|
|
delay.delay = path.slow_delay;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Get the port class, also setting clockPort to associated clock if applicable
|
|
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
|
|
{
|
|
clockInfoCount = 0;
|
|
if (cell->type == id_ICESTORM_LC) {
|
|
if (port == id_CLK)
|
|
return TMG_CLOCK_INPUT;
|
|
if (port == id_CIN)
|
|
return TMG_COMB_INPUT;
|
|
if (port == id_COUT || port == id_LO)
|
|
return TMG_COMB_OUTPUT;
|
|
if (port == id_O) {
|
|
// LCs with no inputs are constant drivers
|
|
if (cell->lcInfo.inputCount == 0)
|
|
return TMG_IGNORE;
|
|
if (cell->lcInfo.dffEnable) {
|
|
clockInfoCount = 1;
|
|
return TMG_REGISTER_OUTPUT;
|
|
} else
|
|
return TMG_COMB_OUTPUT;
|
|
} else {
|
|
if (cell->lcInfo.dffEnable) {
|
|
clockInfoCount = 1;
|
|
return TMG_REGISTER_INPUT;
|
|
} else
|
|
return TMG_COMB_INPUT;
|
|
}
|
|
} else if (cell->type == id_ICESTORM_RAM) {
|
|
|
|
if (port == id_RCLK || port == id_WCLK)
|
|
return TMG_CLOCK_INPUT;
|
|
|
|
clockInfoCount = 1;
|
|
|
|
if (cell->ports.at(port).type == PORT_OUT)
|
|
return TMG_REGISTER_OUTPUT;
|
|
else
|
|
return TMG_REGISTER_INPUT;
|
|
} else if (cell->type == id_ICESTORM_DSP || cell->type == id_ICESTORM_SPRAM) {
|
|
if (port == id_CLK || port == id_CLOCK)
|
|
return TMG_CLOCK_INPUT;
|
|
else {
|
|
clockInfoCount = 1;
|
|
if (cell->ports.at(port).type == PORT_OUT)
|
|
return TMG_REGISTER_OUTPUT;
|
|
else
|
|
return TMG_REGISTER_INPUT;
|
|
}
|
|
} else if (cell->type == id_SB_IO) {
|
|
if (port == id_INPUT_CLK || port == id_OUTPUT_CLK)
|
|
return TMG_CLOCK_INPUT;
|
|
if (port == id_CLOCK_ENABLE) {
|
|
clockInfoCount = 2;
|
|
return TMG_REGISTER_INPUT;
|
|
}
|
|
if ((port == id_D_IN_0 && !(cell->ioInfo.pintype & 0x1)) || port == id_D_IN_1) {
|
|
clockInfoCount = 1;
|
|
return TMG_REGISTER_OUTPUT;
|
|
} else if (port == id_D_IN_0) {
|
|
return TMG_STARTPOINT;
|
|
}
|
|
if (port == id_D_OUT_0 || port == id_D_OUT_1) {
|
|
if ((cell->ioInfo.pintype & 0xC) == 0x8) {
|
|
return TMG_ENDPOINT;
|
|
} else {
|
|
clockInfoCount = 1;
|
|
return TMG_REGISTER_INPUT;
|
|
}
|
|
}
|
|
if (port == id_OUTPUT_ENABLE) {
|
|
if ((cell->ioInfo.pintype & 0x30) == 0x30) {
|
|
return TMG_REGISTER_INPUT;
|
|
} else {
|
|
return TMG_ENDPOINT;
|
|
}
|
|
}
|
|
|
|
return TMG_IGNORE;
|
|
} else if (cell->type == id_ICESTORM_PLL) {
|
|
if (port == id_PLLOUT_A || port == id_PLLOUT_B || port == id_PLLOUT_A_GLOBAL || port == id_PLLOUT_B_GLOBAL)
|
|
return TMG_GEN_CLOCK;
|
|
return TMG_IGNORE;
|
|
} else if (cell->type == id_ICESTORM_LFOSC) {
|
|
if (port == id_CLKLF)
|
|
return TMG_GEN_CLOCK;
|
|
return TMG_IGNORE;
|
|
} else if (cell->type == id_ICESTORM_HFOSC) {
|
|
if (port == id_CLKHF)
|
|
return TMG_GEN_CLOCK;
|
|
return TMG_IGNORE;
|
|
} else if (cell->type == id_SB_GB) {
|
|
if (port == id_GLOBAL_BUFFER_OUTPUT)
|
|
return cell->gbInfo.forPadIn ? TMG_GEN_CLOCK : TMG_COMB_OUTPUT;
|
|
return TMG_COMB_INPUT;
|
|
} else if (cell->type == id_SB_WARMBOOT) {
|
|
return TMG_ENDPOINT;
|
|
} else if (cell->type == id_SB_LED_DRV_CUR) {
|
|
if (port == id_LEDPU)
|
|
return TMG_IGNORE;
|
|
return TMG_ENDPOINT;
|
|
} else if (cell->type == id_SB_RGB_DRV) {
|
|
if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2 || port == id_RGBPU)
|
|
return TMG_IGNORE;
|
|
return TMG_ENDPOINT;
|
|
} else if (cell->type == id_SB_RGBA_DRV) {
|
|
if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2)
|
|
return TMG_IGNORE;
|
|
return TMG_ENDPOINT;
|
|
} else if (cell->type == id_SB_LEDDA_IP) {
|
|
if (port == id_CLK || port == id_CLOCK)
|
|
return TMG_CLOCK_INPUT;
|
|
return TMG_IGNORE;
|
|
} else if (cell->type == id_SB_I2C || cell->type == id_SB_SPI) {
|
|
if (port == this->id("SBCLKI"))
|
|
return TMG_CLOCK_INPUT;
|
|
|
|
clockInfoCount = 1;
|
|
|
|
if (cell->ports.at(port).type == PORT_OUT)
|
|
return TMG_REGISTER_OUTPUT;
|
|
else
|
|
return TMG_REGISTER_INPUT;
|
|
}
|
|
log_error("cell type '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this), cell->name.c_str(this));
|
|
}
|
|
|
|
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
|
|
{
|
|
TimingClockingInfo info;
|
|
if (cell->type == id_ICESTORM_LC) {
|
|
info.clock_port = id_CLK;
|
|
info.edge = cell->lcInfo.negClk ? FALLING_EDGE : RISING_EDGE;
|
|
if (port == id_O) {
|
|
bool has_clktoq = getCellDelayInternal(cell, id_CLK, id_O, info.clockToQ);
|
|
NPNR_ASSERT(has_clktoq);
|
|
} else {
|
|
if (port == id_I0 || port == id_I1 || port == id_I2 || port == id_I3) {
|
|
DelayInfo dlut;
|
|
bool has_ld = getCellDelayInternal(cell, port, id_O, dlut);
|
|
NPNR_ASSERT(has_ld);
|
|
if (args.type == ArchArgs::LP1K || args.type == ArchArgs::LP8K || args.type == ArchArgs::LP384) {
|
|
info.setup.delay = 30 + dlut.delay;
|
|
} else if (args.type == ArchArgs::UP5K || args.type == ArchArgs::U4K) { // XXX verify u4k
|
|
info.setup.delay = dlut.delay - 50;
|
|
} else {
|
|
info.setup.delay = 20 + dlut.delay;
|
|
}
|
|
} else {
|
|
info.setup.delay = 100;
|
|
}
|
|
info.hold.delay = 0;
|
|
}
|
|
} else if (cell->type == id_ICESTORM_RAM) {
|
|
if (port.str(this)[0] == 'R') {
|
|
info.clock_port = id_RCLK;
|
|
info.edge = bool_or_default(cell->params, id("NEG_CLK_R")) ? FALLING_EDGE : RISING_EDGE;
|
|
} else {
|
|
info.clock_port = id_WCLK;
|
|
info.edge = bool_or_default(cell->params, id("NEG_CLK_W")) ? FALLING_EDGE : RISING_EDGE;
|
|
}
|
|
if (cell->ports.at(port).type == PORT_OUT) {
|
|
bool has_clktoq = getCellDelayInternal(cell, info.clock_port, port, info.clockToQ);
|
|
NPNR_ASSERT(has_clktoq);
|
|
} else {
|
|
info.setup.delay = 100;
|
|
info.hold.delay = 0;
|
|
}
|
|
} else if (cell->type == id_SB_IO) {
|
|
delay_t io_setup = 80, io_clktoq = 140;
|
|
if (args.type == ArchArgs::LP1K || args.type == ArchArgs::LP8K || args.type == ArchArgs::LP384) {
|
|
io_setup = 115;
|
|
io_clktoq = 210;
|
|
} else if (args.type == ArchArgs::UP5K || args.type == ArchArgs::U4K) {
|
|
io_setup = 205;
|
|
io_clktoq = 1005;
|
|
}
|
|
if (port == id_CLOCK_ENABLE) {
|
|
info.clock_port = (index == 1) ? id_OUTPUT_CLK : id_INPUT_CLK;
|
|
info.edge = cell->ioInfo.negtrig ? FALLING_EDGE : RISING_EDGE;
|
|
info.setup.delay = io_setup;
|
|
info.hold.delay = 0;
|
|
} else if (port == id_D_OUT_0 || port == id_OUTPUT_ENABLE) {
|
|
info.clock_port = id_OUTPUT_CLK;
|
|
info.edge = cell->ioInfo.negtrig ? FALLING_EDGE : RISING_EDGE;
|
|
info.setup.delay = io_setup;
|
|
info.hold.delay = 0;
|
|
} else if (port == id_D_OUT_1) {
|
|
info.clock_port = id_OUTPUT_CLK;
|
|
info.edge = cell->ioInfo.negtrig ? RISING_EDGE : FALLING_EDGE;
|
|
info.setup.delay = io_setup;
|
|
info.hold.delay = 0;
|
|
} else if (port == id_D_IN_0) {
|
|
info.clock_port = id_INPUT_CLK;
|
|
info.edge = cell->ioInfo.negtrig ? FALLING_EDGE : RISING_EDGE;
|
|
info.clockToQ.delay = io_clktoq;
|
|
} else if (port == id_D_IN_1) {
|
|
info.clock_port = id_INPUT_CLK;
|
|
info.edge = cell->ioInfo.negtrig ? RISING_EDGE : FALLING_EDGE;
|
|
info.clockToQ.delay = io_clktoq;
|
|
} else {
|
|
NPNR_ASSERT_FALSE("no clock data for IO cell port");
|
|
}
|
|
} else if (cell->type == id_ICESTORM_DSP || cell->type == id_ICESTORM_SPRAM) {
|
|
info.clock_port = cell->type == id_ICESTORM_SPRAM ? id_CLOCK : id_CLK;
|
|
info.edge = RISING_EDGE;
|
|
if (cell->ports.at(port).type == PORT_OUT) {
|
|
bool has_clktoq = getCellDelayInternal(cell, info.clock_port, port, info.clockToQ);
|
|
if (!has_clktoq)
|
|
info.clockToQ.delay = 100;
|
|
} else {
|
|
info.setup.delay = 100;
|
|
info.hold.delay = 0;
|
|
}
|
|
} else if (cell->type == id_SB_I2C || cell->type == id_SB_SPI) {
|
|
info.clock_port = this->id("SBCLKI");
|
|
info.edge = RISING_EDGE;
|
|
if (cell->ports.at(port).type == PORT_OUT) {
|
|
/* Dummy number */
|
|
info.clockToQ.delay = 1500;
|
|
} else {
|
|
/* Dummy number */
|
|
info.setup.delay = 1500;
|
|
info.hold.delay = 0;
|
|
}
|
|
} else {
|
|
NPNR_ASSERT_FALSE("unhandled cell type in getPortClockingInfo");
|
|
}
|
|
return info;
|
|
}
|
|
|
|
bool Arch::isGlobalNet(const NetInfo *net) const
|
|
{
|
|
if (net == nullptr)
|
|
return false;
|
|
return net->driver.cell != nullptr && net->driver.port == id_GLOBAL_BUFFER_OUTPUT;
|
|
}
|
|
|
|
// Assign arch arg info
|
|
void Arch::assignArchInfo()
|
|
{
|
|
for (auto &net : getCtx()->nets) {
|
|
NetInfo *ni = net.second.get();
|
|
if (isGlobalNet(ni))
|
|
ni->is_global = true;
|
|
ni->is_enable = false;
|
|
ni->is_reset = false;
|
|
for (auto usr : ni->users) {
|
|
if (is_enable_port(this, usr))
|
|
ni->is_enable = true;
|
|
if (is_reset_port(this, usr))
|
|
ni->is_reset = true;
|
|
}
|
|
}
|
|
for (auto &cell : getCtx()->cells) {
|
|
CellInfo *ci = cell.second.get();
|
|
assignCellInfo(ci);
|
|
}
|
|
}
|
|
|
|
void Arch::assignCellInfo(CellInfo *cell)
|
|
{
|
|
if (cell->type == id_ICESTORM_LC) {
|
|
cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE);
|
|
cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE);
|
|
cell->lcInfo.negClk = bool_or_default(cell->params, id_NEG_CLK);
|
|
cell->lcInfo.clk = get_net_or_empty(cell, id_CLK);
|
|
cell->lcInfo.cen = get_net_or_empty(cell, id_CEN);
|
|
cell->lcInfo.sr = get_net_or_empty(cell, id_SR);
|
|
cell->lcInfo.inputCount = 0;
|
|
if (get_net_or_empty(cell, id_I0))
|
|
cell->lcInfo.inputCount++;
|
|
if (get_net_or_empty(cell, id_I1))
|
|
cell->lcInfo.inputCount++;
|
|
if (get_net_or_empty(cell, id_I2))
|
|
cell->lcInfo.inputCount++;
|
|
if (get_net_or_empty(cell, id_I3))
|
|
cell->lcInfo.inputCount++;
|
|
} else if (cell->type == id_SB_IO) {
|
|
cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT";
|
|
cell->ioInfo.global = bool_or_default(cell->attrs, this->id("GLOBAL"));
|
|
cell->ioInfo.pintype = int_or_default(cell->params, this->id("PIN_TYPE"));
|
|
cell->ioInfo.negtrig = bool_or_default(cell->params, this->id("NEG_TRIGGER"));
|
|
|
|
} else if (cell->type == id_SB_GB) {
|
|
cell->gbInfo.forPadIn = bool_or_default(cell->attrs, this->id("FOR_PAD_IN"));
|
|
}
|
|
}
|
|
|
|
ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
|
|
{
|
|
ArcBounds bb;
|
|
|
|
int src_x = chip_info->wire_data[src.index].x;
|
|
int src_y = chip_info->wire_data[src.index].y;
|
|
int dst_x = chip_info->wire_data[dst.index].x;
|
|
int dst_y = chip_info->wire_data[dst.index].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;
|
|
}
|
|
|
|
#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"};
|
|
|
|
NEXTPNR_NAMESPACE_END
|