Merge branch 'master' into redist_slack
This commit is contained in:
commit
9382938661
2
.gitignore
vendored
2
.gitignore
vendored
@ -25,3 +25,5 @@ build/
|
|||||||
/Testing/*
|
/Testing/*
|
||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
install_manifest.txt
|
install_manifest.txt
|
||||||
|
/bbasm
|
||||||
|
/ImportExecutables.cmake
|
||||||
|
@ -167,6 +167,8 @@ if(MINGW)
|
|||||||
add_definitions("-Wa,-mbig-obj")
|
add_definitions("-Wa,-mbig-obj")
|
||||||
endif(MINGW)
|
endif(MINGW)
|
||||||
|
|
||||||
|
include(bba/bba.cmake)
|
||||||
|
|
||||||
foreach (family ${ARCH})
|
foreach (family ${ARCH})
|
||||||
message(STATUS "Configuring architecture : ${family}")
|
message(STATUS "Configuring architecture : ${family}")
|
||||||
string(TOUPPER ${family} ufamily)
|
string(TOUPPER ${family} ufamily)
|
||||||
|
12
bba/bba.cmake
Normal file
12
bba/bba.cmake
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
IF(CMAKE_CROSSCOMPILING)
|
||||||
|
SET(IMPORT_EXECUTABLES "IMPORTFILE-NOTFOUND" CACHE FILEPATH "Point it to the export file from a native build")
|
||||||
|
INCLUDE(${IMPORT_EXECUTABLES})
|
||||||
|
ENDIF(CMAKE_CROSSCOMPILING)
|
||||||
|
|
||||||
|
IF(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
ADD_EXECUTABLE(bbasm bba/main.cc)
|
||||||
|
ENDIF(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
|
||||||
|
IF(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
EXPORT(TARGETS bbasm FILE ${CMAKE_BINARY_DIR}/ImportExecutables.cmake )
|
||||||
|
ENDIF(NOT CMAKE_CROSSCOMPILING)
|
15
bba/main.cc
Normal file
15
bba/main.cc
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char buffer[512];
|
||||||
|
int i, j;
|
||||||
|
while (1) {
|
||||||
|
i = read(0, buffer, 512);
|
||||||
|
if (i == 0) break;
|
||||||
|
assert(i > 0);
|
||||||
|
j = write(1, buffer, i);
|
||||||
|
assert(i == j);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
@ -17,8 +17,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nextpnr.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define dbg(...) log(__VA_ARGS__)
|
#define dbg(...) log(__VA_ARGS__)
|
||||||
@ -84,8 +84,7 @@ void archcheck_locs(const Context *ctx)
|
|||||||
|
|
||||||
log_info("Checking all locations..\n");
|
log_info("Checking all locations..\n");
|
||||||
for (int x = 0; x < ctx->getGridDimX(); x++)
|
for (int x = 0; x < ctx->getGridDimX(); x++)
|
||||||
for (int y = 0; y < ctx->getGridDimY(); y++)
|
for (int y = 0; y < ctx->getGridDimY(); y++) {
|
||||||
{
|
|
||||||
dbg("> %d %d\n", x, y);
|
dbg("> %d %d\n", x, y);
|
||||||
std::unordered_set<int> usedz;
|
std::unordered_set<int> usedz;
|
||||||
|
|
||||||
|
@ -147,6 +147,7 @@ struct GraphicElement
|
|||||||
{
|
{
|
||||||
G_NONE,
|
G_NONE,
|
||||||
G_LINE,
|
G_LINE,
|
||||||
|
G_ARROW,
|
||||||
G_BOX,
|
G_BOX,
|
||||||
G_CIRCLE,
|
G_CIRCLE,
|
||||||
G_LABEL
|
G_LABEL
|
||||||
@ -272,6 +273,16 @@ struct CellInfo : ArchCellInfo
|
|||||||
|
|
||||||
// cell_port -> bel_pin
|
// cell_port -> bel_pin
|
||||||
std::unordered_map<IdString, IdString> pins;
|
std::unordered_map<IdString, IdString> pins;
|
||||||
|
|
||||||
|
// placement constraints
|
||||||
|
CellInfo *constr_parent;
|
||||||
|
std::vector<CellInfo*> constr_children;
|
||||||
|
const int UNCONSTR = INT_MIN;
|
||||||
|
int constr_x = UNCONSTR; // this.x - parent.x
|
||||||
|
int constr_y = UNCONSTR; // this.y - parent.y
|
||||||
|
int constr_z = UNCONSTR; // this.z - parent.z
|
||||||
|
bool constr_abs_z = false; // parent.z := 0
|
||||||
|
// parent.[xyz] := 0 when (constr_parent == nullptr)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DeterministicRNG
|
struct DeterministicRNG
|
||||||
|
@ -28,19 +28,20 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns)
|
wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns)
|
||||||
{
|
{
|
||||||
wirelen_t wirelength = 0;
|
wirelen_t wirelength = 0;
|
||||||
int driver_x, driver_y;
|
Loc driver_loc;
|
||||||
bool driver_gb;
|
bool driver_gb;
|
||||||
CellInfo *driver_cell = net->driver.cell;
|
CellInfo *driver_cell = net->driver.cell;
|
||||||
if (!driver_cell)
|
if (!driver_cell)
|
||||||
return 0;
|
return 0;
|
||||||
if (driver_cell->bel == BelId())
|
if (driver_cell->bel == BelId())
|
||||||
return 0;
|
return 0;
|
||||||
ctx->estimatePosition(driver_cell->bel, driver_x, driver_y, driver_gb);
|
driver_gb = ctx->getBelGlobalBuf(driver_cell->bel);
|
||||||
|
driver_loc = ctx->getBelLocation(driver_cell->bel);
|
||||||
WireId drv_wire = ctx->getBelPinWire(driver_cell->bel, ctx->portPinFromId(net->driver.port));
|
WireId drv_wire = ctx->getBelPinWire(driver_cell->bel, ctx->portPinFromId(net->driver.port));
|
||||||
if (driver_gb)
|
if (driver_gb)
|
||||||
return 0;
|
return 0;
|
||||||
float worst_slack = 1000;
|
float worst_slack = 1000;
|
||||||
int xmin = driver_x, xmax = driver_x, ymin = driver_y, ymax = driver_y;
|
int xmin = driver_loc.x, xmax = driver_loc.x, ymin = driver_loc.y, ymax = driver_loc.y;
|
||||||
for (auto load : net->users) {
|
for (auto load : net->users) {
|
||||||
if (load.cell == nullptr)
|
if (load.cell == nullptr)
|
||||||
continue;
|
continue;
|
||||||
@ -56,15 +57,14 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
|
|||||||
worst_slack = std::min(slack, worst_slack);
|
worst_slack = std::min(slack, worst_slack);
|
||||||
}
|
}
|
||||||
|
|
||||||
int load_x, load_y;
|
if (ctx->getBelGlobalBuf(load_cell->bel))
|
||||||
bool load_gb;
|
|
||||||
ctx->estimatePosition(load_cell->bel, load_x, load_y, load_gb);
|
|
||||||
if (load_gb)
|
|
||||||
continue;
|
continue;
|
||||||
xmin = std::min(xmin, load_x);
|
Loc load_loc = ctx->getBelLocation(load_cell->bel);
|
||||||
ymin = std::min(ymin, load_y);
|
|
||||||
xmax = std::max(xmax, load_x);
|
xmin = std::min(xmin, load_loc.x);
|
||||||
ymax = std::max(ymax, load_y);
|
ymin = std::min(ymin, load_loc.y);
|
||||||
|
xmax = std::max(xmax, load_loc.x);
|
||||||
|
ymax = std::max(ymax, load_loc.y);
|
||||||
}
|
}
|
||||||
if (ctx->timing_driven && type == MetricType::COST) {
|
if (ctx->timing_driven && type == MetricType::COST) {
|
||||||
wirelength = wirelen_t((((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-worst_slack / 5)))));
|
wirelength = wirelen_t((((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-worst_slack / 5)))));
|
||||||
|
@ -50,9 +50,7 @@ class SAPlacer
|
|||||||
{
|
{
|
||||||
int num_bel_types = 0;
|
int num_bel_types = 0;
|
||||||
for (auto bel : ctx->getBels()) {
|
for (auto bel : ctx->getBels()) {
|
||||||
int x, y;
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
bool gb;
|
|
||||||
ctx->estimatePosition(bel, x, y, gb);
|
|
||||||
BelType type = ctx->getBelType(bel);
|
BelType type = ctx->getBelType(bel);
|
||||||
int type_idx;
|
int type_idx;
|
||||||
if (bel_types.find(type) == bel_types.end()) {
|
if (bel_types.find(type) == bel_types.end()) {
|
||||||
@ -63,13 +61,13 @@ class SAPlacer
|
|||||||
}
|
}
|
||||||
if (int(fast_bels.size()) < type_idx + 1)
|
if (int(fast_bels.size()) < type_idx + 1)
|
||||||
fast_bels.resize(type_idx + 1);
|
fast_bels.resize(type_idx + 1);
|
||||||
if (int(fast_bels.at(type_idx).size()) < (x + 1))
|
if (int(fast_bels.at(type_idx).size()) < (loc.x + 1))
|
||||||
fast_bels.at(type_idx).resize(x + 1);
|
fast_bels.at(type_idx).resize(loc.x + 1);
|
||||||
if (int(fast_bels.at(type_idx).at(x).size()) < (y + 1))
|
if (int(fast_bels.at(type_idx).at(loc.x).size()) < (loc.y + 1))
|
||||||
fast_bels.at(type_idx).at(x).resize(y + 1);
|
fast_bels.at(type_idx).at(loc.x).resize(loc.y + 1);
|
||||||
max_x = std::max(max_x, x);
|
max_x = std::max(max_x, loc.x);
|
||||||
max_y = std::max(max_y, y);
|
max_y = std::max(max_y, loc.y);
|
||||||
fast_bels.at(type_idx).at(x).at(y).push_back(bel);
|
fast_bels.at(type_idx).at(loc.x).at(loc.y).push_back(bel);
|
||||||
}
|
}
|
||||||
diameter = std::max(max_x, max_y) + 1;
|
diameter = std::max(max_x, max_y) + 1;
|
||||||
}
|
}
|
||||||
@ -411,12 +409,10 @@ class SAPlacer
|
|||||||
BelId random_bel_for_cell(CellInfo *cell)
|
BelId random_bel_for_cell(CellInfo *cell)
|
||||||
{
|
{
|
||||||
BelType targetType = ctx->belTypeFromId(cell->type);
|
BelType targetType = ctx->belTypeFromId(cell->type);
|
||||||
int x, y;
|
Loc curr_loc = ctx->getBelLocation(cell->bel);
|
||||||
bool gb;
|
|
||||||
ctx->estimatePosition(cell->bel, x, y, gb);
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int nx = ctx->rng(2 * diameter + 1) + std::max(x - diameter, 0);
|
int nx = ctx->rng(2 * diameter + 1) + std::max(curr_loc.x - diameter, 0);
|
||||||
int ny = ctx->rng(2 * diameter + 1) + std::max(y - diameter, 0);
|
int ny = ctx->rng(2 * diameter + 1) + std::max(curr_loc.y - diameter, 0);
|
||||||
int beltype_idx = bel_types.at(targetType);
|
int beltype_idx = bel_types.at(targetType);
|
||||||
if (nx >= int(fast_bels.at(beltype_idx).size()))
|
if (nx >= int(fast_bels.at(beltype_idx).size()))
|
||||||
continue;
|
continue;
|
||||||
|
@ -94,6 +94,9 @@ void assign_budget(Context *ctx)
|
|||||||
IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
|
IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
|
||||||
if (clock_domain != IdString()) {
|
if (clock_domain != IdString()) {
|
||||||
delay_t slack = delay_t(1.0e12 / ctx->target_freq); // TODO: clock constraints
|
delay_t slack = delay_t(1.0e12 / ctx->target_freq); // TODO: clock constraints
|
||||||
|
delay_t clkToQ;
|
||||||
|
if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ))
|
||||||
|
slack -= clkToQ;
|
||||||
if (port.second.net)
|
if (port.second.net)
|
||||||
follow_net(ctx, port.second.net, 0, slack, updates);
|
follow_net(ctx, port.second.net, 0, slack, updates);
|
||||||
}
|
}
|
||||||
|
46
ecp5/arch.cc
46
ecp5/arch.cc
@ -192,17 +192,20 @@ BelId Arch::getBelByName(IdString name) const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
BelRange Arch::getBelsAtSameTile(BelId bel) const
|
BelRange Arch::getBelsByTile(int x, int y) const
|
||||||
{
|
{
|
||||||
BelRange br;
|
BelRange br;
|
||||||
NPNR_ASSERT(bel != BelId());
|
|
||||||
br.b.cursor_tile = bel.location.y * chip_info->width + bel.location.x;
|
br.b.cursor_tile = y * chip_info->width + x;
|
||||||
br.e.cursor_tile = bel.location.y * chip_info->width + bel.location.x;
|
br.e.cursor_tile = y * chip_info->width + x;
|
||||||
br.b.cursor_index = 0;
|
br.b.cursor_index = 0;
|
||||||
br.e.cursor_index = locInfo(bel)->num_bels - 1;
|
br.e.cursor_index = chip_info->locations[chip_info->location_type[br.b.cursor_tile]].num_bels - 1;
|
||||||
br.b.chip = chip_info;
|
br.b.chip = chip_info;
|
||||||
br.e.chip = chip_info;
|
br.e.chip = chip_info;
|
||||||
++br.e;
|
if (br.e.cursor_index == -1)
|
||||||
|
++br.e.cursor_index;
|
||||||
|
else
|
||||||
|
++br.e;
|
||||||
return br;
|
return br;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,6 +281,7 @@ PipId Arch::getPipByName(IdString name) const
|
|||||||
Location loc;
|
Location loc;
|
||||||
std::string basename;
|
std::string basename;
|
||||||
std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this));
|
std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this));
|
||||||
|
ret.location = loc;
|
||||||
const LocationTypePOD *loci = locInfo(ret);
|
const LocationTypePOD *loci = locInfo(ret);
|
||||||
for (int i = 0; i < loci->num_pips; i++) {
|
for (int i = 0; i < loci->num_pips; i++) {
|
||||||
PipId curr;
|
PipId curr;
|
||||||
@ -285,6 +289,8 @@ PipId Arch::getPipByName(IdString name) const
|
|||||||
curr.index = i;
|
curr.index = i;
|
||||||
pip_by_name[getPipName(curr)] = curr;
|
pip_by_name[getPipName(curr)] = curr;
|
||||||
}
|
}
|
||||||
|
if (pip_by_name.find(name) == pip_by_name.end())
|
||||||
|
NPNR_ASSERT_FALSE_STR("no pip named " + name.str(this));
|
||||||
return pip_by_name[name];
|
return pip_by_name[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,36 +405,8 @@ BelId Arch::getBelByLocation(Loc loc) const
|
|||||||
return BelId();
|
return BelId();
|
||||||
}
|
}
|
||||||
|
|
||||||
BelRange Arch::getBelsByTile(int x, int y) const
|
|
||||||
{
|
|
||||||
BelRange br;
|
|
||||||
|
|
||||||
int num_bels = 0;
|
|
||||||
|
|
||||||
if (x < chip_info->width && y < chip_info->height) {
|
|
||||||
const LocationTypePOD &locI = chip_info->locations[chip_info->location_type[y * chip_info->width + x]];
|
|
||||||
num_bels = locI.num_bels;
|
|
||||||
}
|
|
||||||
|
|
||||||
br.b.cursor_tile = y * chip_info->width + x;
|
|
||||||
br.e.cursor_tile = y * chip_info->width + x;
|
|
||||||
br.b.cursor_index = 0;
|
|
||||||
br.e.cursor_index = num_bels - 1;
|
|
||||||
br.b.chip = chip_info;
|
|
||||||
br.e.chip = chip_info;
|
|
||||||
++br.e;
|
|
||||||
return br;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
|
|
||||||
{
|
|
||||||
x = bel.location.x;
|
|
||||||
y = bel.location.y;
|
|
||||||
gb = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||||
{
|
{
|
||||||
return 200 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
|
return 200 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
|
||||||
|
@ -482,8 +482,6 @@ struct Arch : BaseCtx
|
|||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
BelRange getBelsAtSameTile(BelId bel) const;
|
|
||||||
|
|
||||||
BelType getBelType(BelId bel) const
|
BelType getBelType(BelId bel) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
NPNR_ASSERT(bel != BelId());
|
||||||
@ -752,7 +750,6 @@ struct Arch : BaseCtx
|
|||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
|
|
||||||
delay_t estimateDelay(WireId src, WireId dst) const;
|
delay_t estimateDelay(WireId src, WireId dst) const;
|
||||||
delay_t getDelayEpsilon() const { return 20; }
|
delay_t getDelayEpsilon() const { return 20; }
|
||||||
delay_t getRipupDelayPenalty() const { return 200; }
|
delay_t getRipupDelayPenalty() const { return 200; }
|
||||||
|
@ -66,7 +66,8 @@ bool Arch::isBelLocationValid(BelId bel) const
|
|||||||
{
|
{
|
||||||
if (getBelType(bel) == TYPE_TRELLIS_SLICE) {
|
if (getBelType(bel) == TYPE_TRELLIS_SLICE) {
|
||||||
std::vector<const CellInfo *> bel_cells;
|
std::vector<const CellInfo *> bel_cells;
|
||||||
for (auto bel_other : getBelsAtSameTile(bel)) {
|
Loc bel_loc = getBelLocation(bel);
|
||||||
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
IdString cell_other = getBoundBelCell(bel_other);
|
IdString cell_other = getBoundBelCell(bel_other);
|
||||||
if (cell_other != IdString()) {
|
if (cell_other != IdString()) {
|
||||||
const CellInfo *ci_other = cells.at(cell_other).get();
|
const CellInfo *ci_other = cells.at(cell_other).get();
|
||||||
@ -89,8 +90,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
|||||||
NPNR_ASSERT(getBelType(bel) == TYPE_TRELLIS_SLICE);
|
NPNR_ASSERT(getBelType(bel) == TYPE_TRELLIS_SLICE);
|
||||||
|
|
||||||
std::vector<const CellInfo *> bel_cells;
|
std::vector<const CellInfo *> bel_cells;
|
||||||
|
Loc bel_loc = getBelLocation(bel);
|
||||||
for (auto bel_other : getBelsAtSameTile(bel)) {
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
IdString cell_other = getBoundBelCell(bel_other);
|
IdString cell_other = getBoundBelCell(bel_other);
|
||||||
if (cell_other != IdString() && bel_other != bel) {
|
if (cell_other != IdString() && bel_other != bel) {
|
||||||
const CellInfo *ci_other = cells.at(cell_other).get();
|
const CellInfo *ci_other = cells.at(cell_other).get();
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* nextpnr -- Next Generation Place and Route
|
* nextpnr -- Next Generation Place and Route
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
* Copyright (C) 2018 David Shah <dave@ds0.me>
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -20,13 +20,132 @@
|
|||||||
|
|
||||||
#ifndef NO_PYTHON
|
#ifndef NO_PYTHON
|
||||||
|
|
||||||
|
#include "arch_pybindings.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "pybindings.h"
|
#include "pybindings.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
void arch_wrap_python() {}
|
void arch_wrap_python()
|
||||||
|
{
|
||||||
|
using namespace PythonConversion;
|
||||||
|
class_<ArchArgs>("ArchArgs").def_readwrite("type", &ArchArgs::type);
|
||||||
|
|
||||||
|
class_<BelId>("BelId").def_readwrite("index", &BelId::index);
|
||||||
|
|
||||||
|
class_<WireId>("WireId").def_readwrite("index", &WireId::index);
|
||||||
|
|
||||||
|
class_<PipId>("PipId").def_readwrite("index", &PipId::index);
|
||||||
|
|
||||||
|
class_<BelPin>("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
|
||||||
|
|
||||||
|
enum_<PortPin>("PortPin")
|
||||||
|
#define X(t) .value("PIN_" #t, PIN_##t)
|
||||||
|
|
||||||
|
#include "portpins.inc"
|
||||||
|
;
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
|
||||||
|
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
|
||||||
|
.def("checksum", &Context::checksum)
|
||||||
|
.def("pack", &Context::pack)
|
||||||
|
.def("place", &Context::place)
|
||||||
|
.def("route", &Context::route);
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<BelType>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum");
|
||||||
|
fn_wrapper_3a_v<Context, decltype(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>,
|
||||||
|
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel");
|
||||||
|
fn_wrapper_1a_v<Context, decltype(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap(
|
||||||
|
ctx_cls, "unbindBel");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBoundBelCell), &Context::getBoundBelCell, conv_to_str<IdString>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
|
||||||
|
conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
|
||||||
|
"getBels");
|
||||||
|
|
||||||
|
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
|
||||||
|
conv_from_str<BelId>, conv_from_str<PortPin>>::def_wrap(ctx_cls, "getBelPinWire");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getWireBelPins), &Context::getWireBelPins, wrap_context<BelPinRange>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireBelPins");
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum");
|
||||||
|
fn_wrapper_3a_v<Context, decltype(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>,
|
||||||
|
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire");
|
||||||
|
fn_wrapper_1a_v<Context, decltype(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap(
|
||||||
|
ctx_cls, "unbindWire");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBoundWireNet), &Context::getBoundWireNet, conv_to_str<IdString>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getConflictingWireNet), &Context::getConflictingWireNet,
|
||||||
|
conv_to_str<IdString>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet");
|
||||||
|
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap(
|
||||||
|
ctx_cls, "getWires");
|
||||||
|
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap(
|
||||||
|
ctx_cls, "getPips");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum");
|
||||||
|
fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>,
|
||||||
|
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip");
|
||||||
|
fn_wrapper_1a_v<Context, decltype(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap(
|
||||||
|
ctx_cls, "unbindPip");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBoundPipNet), &Context::getBoundPipNet, conv_to_str<IdString>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getConflictingPipNet), &Context::getConflictingPipNet,
|
||||||
|
conv_to_str<IdString>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet");
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipsDownhill), &Context::getPipsDownhill, wrap_context<PipRange>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill, wrap_context<PipRange>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getWireAliases), &Context::getWireAliases, wrap_context<PipRange>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases");
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayInfo>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay");
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPackagePinBel), &Context::getPackagePinBel, conv_to_str<BelId>,
|
||||||
|
pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBelPackagePin), &Context::getBelPackagePin, pass_through<std::string>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin");
|
||||||
|
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
|
||||||
|
ctx_cls, "getChipName");
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
|
||||||
|
"archId");
|
||||||
|
|
||||||
|
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||||
|
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||||
|
|
||||||
|
readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
|
||||||
|
"cells");
|
||||||
|
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
|
||||||
|
"nets");
|
||||||
|
WRAP_RANGE(Bel, conv_to_str<BelId>);
|
||||||
|
WRAP_RANGE(Wire, conv_to_str<WireId>);
|
||||||
|
WRAP_RANGE(AllPip, conv_to_str<PipId>);
|
||||||
|
WRAP_RANGE(Pip, conv_to_str<PipId>);
|
||||||
|
|
||||||
|
WRAP_MAP_UPTR(CellMap, "IdCellMap");
|
||||||
|
WRAP_MAP_UPTR(NetMap, "IdNetMap");
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
#endif
|
#endif // NO_PYTHON
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
|
|
||||||
|
#include "io.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@ -182,12 +183,47 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Find bank voltages
|
||||||
|
std::unordered_map<int, IOVoltage> bankVcc;
|
||||||
|
std::unordered_map<int, bool> bankLvds;
|
||||||
|
|
||||||
// Set all bankref tiles to 3.3V (TODO)
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
if (ci->bel != BelId() && ci->type == ctx->id("TRELLIS_IO")) {
|
||||||
|
int bank = ctx->getPioBelBank(ci->bel);
|
||||||
|
std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT");
|
||||||
|
std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33");
|
||||||
|
|
||||||
|
if (dir != "INPUT") {
|
||||||
|
IOVoltage vcc = get_vccio(ioType_from_str(iotype));
|
||||||
|
if (bankVcc.find(bank) != bankVcc.end()) {
|
||||||
|
// TODO: strong and weak constraints
|
||||||
|
if (bankVcc[bank] != vcc) {
|
||||||
|
log_error("Error processing '%s': incompatible IO voltages %s and %s on bank %d.",
|
||||||
|
cell.first.c_str(ctx), iovoltage_to_str(bankVcc[bank]).c_str(),
|
||||||
|
iovoltage_to_str(vcc).c_str(), bank);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bankVcc[bank] = vcc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iotype == "LVDS")
|
||||||
|
bankLvds[bank] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all bankref tiles to appropriate VccIO
|
||||||
for (const auto &tile : empty_chip.tiles) {
|
for (const auto &tile : empty_chip.tiles) {
|
||||||
std::string type = tile.second->info.type;
|
std::string type = tile.second->info.type;
|
||||||
if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") {
|
if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") {
|
||||||
cc.tiles[tile.first].add_enum("BANK.VCCIO", "3V3");
|
int bank = std::stoi(type.substr(7));
|
||||||
|
if (bankVcc.find(bank) != bankVcc.end())
|
||||||
|
cc.tiles[tile.first].add_enum("BANK.VCCIO", iovoltage_to_str(bankVcc[bank]));
|
||||||
|
if (bankLvds[bank]) {
|
||||||
|
cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON");
|
||||||
|
cc.tiles[tile.first].add_enum("BANK.LVDSO", "ON");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,6 +271,20 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
std::string pic_tile = get_pic_tile(ctx, empty_chip, bel);
|
std::string pic_tile = get_pic_tile(ctx, empty_chip, bel);
|
||||||
cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
|
cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
|
||||||
cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
|
cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
|
||||||
|
if (is_differential(ioType_from_str(iotype))) {
|
||||||
|
// Explicitly disable other pair
|
||||||
|
std::string other;
|
||||||
|
if (pio == "PIOA")
|
||||||
|
other = "PIOB";
|
||||||
|
else if (pio == "PIOC")
|
||||||
|
other = "PIOD";
|
||||||
|
else
|
||||||
|
log_error("cannot place differential IO at location %s\n", pio.c_str());
|
||||||
|
//cc.tiles[pio_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
|
||||||
|
//cc.tiles[pic_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
|
||||||
|
cc.tiles[pio_tile].add_enum(other + ".PULLMODE", "NONE");
|
||||||
|
cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE");
|
||||||
|
}
|
||||||
if (dir != "INPUT" &&
|
if (dir != "INPUT" &&
|
||||||
(ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr)) {
|
(ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr)) {
|
||||||
// Tie tristate low if unconnected for outputs or bidir
|
// Tie tristate low if unconnected for outputs or bidir
|
||||||
@ -247,7 +297,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get();
|
std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get();
|
||||||
cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0");
|
cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0");
|
||||||
}
|
}
|
||||||
if (dir == "INPUT") {
|
if (dir == "INPUT" && !is_differential(ioType_from_str(iotype))) {
|
||||||
cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON");
|
cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,6 +63,7 @@ int main(int argc, char *argv[])
|
|||||||
#ifndef NO_GUI
|
#ifndef NO_GUI
|
||||||
options.add_options()("gui", "start gui");
|
options.add_options()("gui", "start gui");
|
||||||
#endif
|
#endif
|
||||||
|
options.add_options()("test", "check architecture database integrity");
|
||||||
|
|
||||||
options.add_options()("25k", "set device type to LFE5U-25F");
|
options.add_options()("25k", "set device type to LFE5U-25F");
|
||||||
options.add_options()("45k", "set device type to LFE5U-45F");
|
options.add_options()("45k", "set device type to LFE5U-45F");
|
||||||
@ -148,6 +149,9 @@ int main(int argc, char *argv[])
|
|||||||
if (vm.count("no-tmdriv"))
|
if (vm.count("no-tmdriv"))
|
||||||
ctx->timing_driven = false;
|
ctx->timing_driven = false;
|
||||||
|
|
||||||
|
if (vm.count("test"))
|
||||||
|
ctx->archcheck();
|
||||||
|
|
||||||
#ifndef NO_GUI
|
#ifndef NO_GUI
|
||||||
if (vm.count("gui")) {
|
if (vm.count("gui")) {
|
||||||
Application a(argc, argv);
|
Application a(argc, argv);
|
||||||
|
@ -77,18 +77,18 @@ void Arch::addBel(IdString name, IdString type, Loc loc, bool gb)
|
|||||||
bel_ids.push_back(name);
|
bel_ids.push_back(name);
|
||||||
bel_by_loc[loc] = name;
|
bel_by_loc[loc] = name;
|
||||||
|
|
||||||
if (bels_by_tile.size() <= loc.x)
|
if (int(bels_by_tile.size()) <= loc.x)
|
||||||
bels_by_tile.resize(loc.x + 1);
|
bels_by_tile.resize(loc.x + 1);
|
||||||
|
|
||||||
if (bels_by_tile[loc.x].size() <= loc.y)
|
if (int(bels_by_tile[loc.x].size()) <= loc.y)
|
||||||
bels_by_tile[loc.x].resize(loc.y + 1);
|
bels_by_tile[loc.x].resize(loc.y + 1);
|
||||||
|
|
||||||
bels_by_tile[loc.x][loc.y].push_back(name);
|
bels_by_tile[loc.x][loc.y].push_back(name);
|
||||||
|
|
||||||
if (tileDimZ.size() <= loc.x)
|
if (int(tileDimZ.size()) <= loc.x)
|
||||||
tileDimZ.resize(loc.x + 1);
|
tileDimZ.resize(loc.x + 1);
|
||||||
|
|
||||||
if (tileDimZ[loc.x].size() <= loc.y)
|
if (int(tileDimZ[loc.x].size()) <= loc.y)
|
||||||
tileDimZ[loc.x].resize(loc.y + 1);
|
tileDimZ[loc.x].resize(loc.y + 1);
|
||||||
|
|
||||||
gridDimX = std::max(gridDimX, loc.x + 1);
|
gridDimX = std::max(gridDimX, loc.x + 1);
|
||||||
@ -193,6 +193,30 @@ BelId Arch::getBelByName(IdString name) const
|
|||||||
|
|
||||||
IdString Arch::getBelName(BelId bel) const { return bel; }
|
IdString Arch::getBelName(BelId bel) const { return bel; }
|
||||||
|
|
||||||
|
Loc Arch::getBelLocation(BelId bel) const
|
||||||
|
{
|
||||||
|
auto &info = bels.at(bel);
|
||||||
|
return Loc(info.x, info.y, info.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
BelId Arch::getBelByLocation(Loc loc) const
|
||||||
|
{
|
||||||
|
auto it = bel_by_loc.find(loc);
|
||||||
|
if (it != bel_by_loc.end())
|
||||||
|
return it->second;
|
||||||
|
return BelId();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<BelId> &Arch::getBelsByTile(int x, int y) const
|
||||||
|
{
|
||||||
|
return bels_by_tile.at(x).at(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Arch::getBelGlobalBuf(BelId bel) const
|
||||||
|
{
|
||||||
|
return bels.at(bel).gb;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t Arch::getBelChecksum(BelId bel) const
|
uint32_t Arch::getBelChecksum(BelId bel) const
|
||||||
{
|
{
|
||||||
// FIXME
|
// FIXME
|
||||||
@ -234,6 +258,7 @@ std::vector<PortPin> Arch::getBelPins(BelId bel) const
|
|||||||
std::vector<PortPin> ret;
|
std::vector<PortPin> ret;
|
||||||
for (auto &it : bels.at(bel).pins)
|
for (auto &it : bels.at(bel).pins)
|
||||||
ret.push_back(it.first);
|
ret.push_back(it.first);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
@ -368,13 +393,6 @@ const std::vector<GroupId> &Arch::getGroupGroups(GroupId group) const { return g
|
|||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
|
|
||||||
{
|
|
||||||
x = bels.at(bel).x;
|
|
||||||
y = bels.at(bel).y;
|
|
||||||
gb = bels.at(bel).gb;
|
|
||||||
}
|
|
||||||
|
|
||||||
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||||
{
|
{
|
||||||
const WireInfo &s = wires.at(src);
|
const WireInfo &s = wires.at(src);
|
||||||
|
@ -141,7 +141,7 @@ struct Arch : BaseCtx
|
|||||||
IdString getBelName(BelId bel) const;
|
IdString getBelName(BelId bel) const;
|
||||||
Loc getBelLocation(BelId bel) const;
|
Loc getBelLocation(BelId bel) const;
|
||||||
BelId getBelByLocation(Loc loc) const;
|
BelId getBelByLocation(Loc loc) const;
|
||||||
std::vector<BelId> getBelsByTile(int x, int y) const;
|
const std::vector<BelId> &getBelsByTile(int x, int y) const;
|
||||||
bool getBelGlobalBuf(BelId bel) const;
|
bool getBelGlobalBuf(BelId bel) const;
|
||||||
uint32_t getBelChecksum(BelId bel) const;
|
uint32_t getBelChecksum(BelId bel) const;
|
||||||
void bindBel(BelId bel, IdString cell, PlaceStrength strength);
|
void bindBel(BelId bel, IdString cell, PlaceStrength strength);
|
||||||
@ -191,7 +191,6 @@ struct Arch : BaseCtx
|
|||||||
const std::vector<PipId> &getGroupPips(GroupId group) const;
|
const std::vector<PipId> &getGroupPips(GroupId group) const;
|
||||||
const std::vector<GroupId> &getGroupGroups(GroupId group) const;
|
const std::vector<GroupId> &getGroupGroups(GroupId group) const;
|
||||||
|
|
||||||
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
|
|
||||||
delay_t estimateDelay(WireId src, WireId dst) const;
|
delay_t estimateDelay(WireId src, WireId dst) const;
|
||||||
delay_t getDelayEpsilon() const { return 0.01; }
|
delay_t getDelayEpsilon() const { return 0.01; }
|
||||||
delay_t getRipupDelayPenalty() const { return 1.0; }
|
delay_t getRipupDelayPenalty() const { return 1.0; }
|
||||||
|
@ -20,12 +20,22 @@
|
|||||||
|
|
||||||
#ifndef NO_PYTHON
|
#ifndef NO_PYTHON
|
||||||
|
|
||||||
|
#include "arch_pybindings.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "pybindings.h"
|
#include "pybindings.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
void arch_wrap_python() { class_<ArchArgs>("ArchArgs"); }
|
void arch_wrap_python()
|
||||||
|
{
|
||||||
|
using namespace PythonConversion;
|
||||||
|
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
|
||||||
|
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
|
||||||
|
.def("checksum", &Context::checksum)
|
||||||
|
.def("pack", &Context::pack)
|
||||||
|
.def("place", &Context::place)
|
||||||
|
.def("route", &Context::route);
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
@ -312,6 +312,7 @@ void DesignWidget::newContext(Context *ctx)
|
|||||||
QMap<QString, QTreeWidgetItem *> pip_items;
|
QMap<QString, QTreeWidgetItem *> pip_items;
|
||||||
pip_root->setText(0, "Pips");
|
pip_root->setText(0, "Pips");
|
||||||
treeWidget->insertTopLevelItem(0, pip_root);
|
treeWidget->insertTopLevelItem(0, pip_root);
|
||||||
|
#ifndef ARCH_ECP5
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
for (auto pip : ctx->getPips()) {
|
for (auto pip : ctx->getPips()) {
|
||||||
auto id = ctx->getPipName(pip);
|
auto id = ctx->getPipName(pip);
|
||||||
@ -338,6 +339,7 @@ void DesignWidget::newContext(Context *ctx)
|
|||||||
for (auto pip : nameToItem[2].toStdMap()) {
|
for (auto pip : nameToItem[2].toStdMap()) {
|
||||||
pip_root->addChild(pip.second);
|
pip_root->addChild(pip.second);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
nets_root = new QTreeWidgetItem(treeWidget);
|
nets_root = new QTreeWidgetItem(treeWidget);
|
||||||
nets_root->setText(0, "Nets");
|
nets_root->setText(0, "Nets");
|
||||||
|
@ -246,7 +246,7 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent)
|
|||||||
{
|
{
|
||||||
colors_.background = QColor("#000000");
|
colors_.background = QColor("#000000");
|
||||||
colors_.grid = QColor("#333");
|
colors_.grid = QColor("#333");
|
||||||
colors_.frame = QColor("#d0d0d0");
|
colors_.frame = QColor("#808080");
|
||||||
colors_.hidden = QColor("#606060");
|
colors_.hidden = QColor("#606060");
|
||||||
colors_.inactive = QColor("#303030");
|
colors_.inactive = QColor("#303030");
|
||||||
colors_.active = QColor("#f0f0f0");
|
colors_.active = QColor("#f0f0f0");
|
||||||
@ -327,7 +327,7 @@ void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal)
|
|||||||
line.build(out);
|
line.build(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.type == GraphicElement::G_LINE) {
|
if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) {
|
||||||
PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2)
|
PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2)
|
||||||
.build(out);
|
.build(out);
|
||||||
}
|
}
|
||||||
@ -360,7 +360,7 @@ void FPGAViewWidget::drawDecal(LineShaderData out[], const DecalXY &decal)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.type == GraphicElement::G_LINE) {
|
if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) {
|
||||||
auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2,
|
auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2,
|
||||||
offsetY + scale * el.y2);
|
offsetY + scale * el.y2);
|
||||||
switch (el.style) {
|
switch (el.style) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* nextpnr -- Next Generation Place and Route
|
* nextpnr -- Next Generation Place and Route
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
* 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
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -291,23 +292,6 @@ BelRange Arch::getBelsByTile(int x, int y) const
|
|||||||
return br;
|
return br;
|
||||||
}
|
}
|
||||||
|
|
||||||
BelRange Arch::getBelsAtSameTile(BelId bel) const
|
|
||||||
{
|
|
||||||
BelRange br;
|
|
||||||
NPNR_ASSERT(bel != BelId());
|
|
||||||
int x = chip_info->bel_data[bel.index].x;
|
|
||||||
int y = chip_info->bel_data[bel.index].y;
|
|
||||||
int start = bel.index, end = bel.index;
|
|
||||||
while (start >= 0 && chip_info->bel_data[start].x == x && chip_info->bel_data[start].y == y)
|
|
||||||
start--;
|
|
||||||
start++;
|
|
||||||
br.b.cursor = start;
|
|
||||||
while (end < chip_info->num_bels && chip_info->bel_data[end].x == x && chip_info->bel_data[end].y == y)
|
|
||||||
end++;
|
|
||||||
br.e.cursor = end;
|
|
||||||
return br;
|
|
||||||
}
|
|
||||||
|
|
||||||
PortType Arch::getBelPinType(BelId bel, PortPin pin) const
|
PortType Arch::getBelPinType(BelId bel, PortPin pin) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
NPNR_ASSERT(bel != BelId());
|
||||||
@ -331,11 +315,12 @@ WireId Arch::getBelPinWire(BelId bel, PortPin pin) const
|
|||||||
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
|
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();
|
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
|
||||||
|
|
||||||
for (int i = 0; i < num_bel_wires; i++)
|
for (int i = 0; i < num_bel_wires; i++) {
|
||||||
if (bel_wires[i].port == pin) {
|
if (bel_wires[i].port == pin) {
|
||||||
ret.index = bel_wires[i].wire_index;
|
ret.index = bel_wires[i].wire_index;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -482,14 +467,6 @@ std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
|
|
||||||
{
|
|
||||||
NPNR_ASSERT(bel != BelId());
|
|
||||||
x = chip_info->bel_data[bel.index].x;
|
|
||||||
y = chip_info->bel_data[bel.index].y;
|
|
||||||
gb = chip_info->bel_data[bel.index].type == TYPE_SB_GB;
|
|
||||||
}
|
|
||||||
|
|
||||||
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(src != WireId());
|
NPNR_ASSERT(src != WireId());
|
||||||
@ -739,6 +716,14 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
|
|||||||
} else if (fromPort == id("I2") && toPort == id("COUT")) {
|
} else if (fromPort == id("I2") && toPort == id("COUT")) {
|
||||||
delay = 230;
|
delay = 230;
|
||||||
return true;
|
return true;
|
||||||
|
} else if (fromPort == id("CLK") && toPort == id("O")) {
|
||||||
|
delay = 540;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (cell->type == id("ICESTORM_RAM")) {
|
||||||
|
if (fromPort == id("RCLK")) {
|
||||||
|
delay = 2140;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -749,6 +734,11 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const
|
|||||||
if (cell->type == id("ICESTORM_LC") && bool_or_default(cell->params, id("DFF_ENABLE"))) {
|
if (cell->type == id("ICESTORM_LC") && bool_or_default(cell->params, id("DFF_ENABLE"))) {
|
||||||
if (port != id("LO") && port != id("CIN") && port != id("COUT"))
|
if (port != id("LO") && port != id("CIN") && port != id("COUT"))
|
||||||
return id("CLK");
|
return id("CLK");
|
||||||
|
} else if (cell->type == id("ICESTORM_RAM")) {
|
||||||
|
if (port.str(this)[0] == 'R')
|
||||||
|
return id("RCLK");
|
||||||
|
else
|
||||||
|
return id("WCLK");
|
||||||
}
|
}
|
||||||
return IdString();
|
return IdString();
|
||||||
}
|
}
|
||||||
@ -757,6 +747,8 @@ bool Arch::isClockPort(const CellInfo *cell, IdString port) const
|
|||||||
{
|
{
|
||||||
if (cell->type == id("ICESTORM_LC") && port == id("CLK"))
|
if (cell->type == id("ICESTORM_LC") && port == id("CLK"))
|
||||||
return true;
|
return true;
|
||||||
|
if (cell->type == id("ICESTORM_RAM") && (port == id("RCLK") || (port == id("WCLK"))))
|
||||||
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,10 +83,6 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD {
|
|||||||
int32_t num_uphill, num_downhill;
|
int32_t num_uphill, num_downhill;
|
||||||
RelPtr<int32_t> pips_uphill, pips_downhill;
|
RelPtr<int32_t> pips_uphill, pips_downhill;
|
||||||
|
|
||||||
int32_t num_bels_downhill;
|
|
||||||
BelPortPOD bel_uphill;
|
|
||||||
RelPtr<BelPortPOD> bels_downhill;
|
|
||||||
|
|
||||||
int32_t num_bel_pins;
|
int32_t num_bel_pins;
|
||||||
RelPtr<BelPortPOD> bel_pins;
|
RelPtr<BelPortPOD> bel_pins;
|
||||||
|
|
||||||
@ -453,8 +449,6 @@ struct Arch : BaseCtx
|
|||||||
|
|
||||||
bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == TYPE_SB_GB; }
|
bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == TYPE_SB_GB; }
|
||||||
|
|
||||||
BelRange getBelsAtSameTile(BelId bel) const NPNR_DEPRECATED;
|
|
||||||
|
|
||||||
BelType getBelType(BelId bel) const
|
BelType getBelType(BelId bel) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
NPNR_ASSERT(bel != BelId());
|
||||||
@ -685,7 +679,6 @@ struct Arch : BaseCtx
|
|||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const NPNR_DEPRECATED;
|
|
||||||
delay_t estimateDelay(WireId src, WireId dst) const;
|
delay_t estimateDelay(WireId src, WireId dst) const;
|
||||||
delay_t getDelayEpsilon() const { return 20; }
|
delay_t getDelayEpsilon() const { return 20; }
|
||||||
delay_t getRipupDelayPenalty() const { return 200; }
|
delay_t getRipupDelayPenalty() const { return 200; }
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -71,7 +72,8 @@ bool Arch::isBelLocationValid(BelId bel) const
|
|||||||
{
|
{
|
||||||
if (getBelType(bel) == TYPE_ICESTORM_LC) {
|
if (getBelType(bel) == TYPE_ICESTORM_LC) {
|
||||||
std::vector<const CellInfo *> bel_cells;
|
std::vector<const CellInfo *> bel_cells;
|
||||||
for (auto bel_other : getBelsAtSameTile(bel)) {
|
Loc bel_loc = getBelLocation(bel);
|
||||||
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
IdString cell_other = getBoundBelCell(bel_other);
|
IdString cell_other = getBoundBelCell(bel_other);
|
||||||
if (cell_other != IdString()) {
|
if (cell_other != IdString()) {
|
||||||
const CellInfo *ci_other = cells.at(cell_other).get();
|
const CellInfo *ci_other = cells.at(cell_other).get();
|
||||||
@ -94,8 +96,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
|||||||
NPNR_ASSERT(getBelType(bel) == TYPE_ICESTORM_LC);
|
NPNR_ASSERT(getBelType(bel) == TYPE_ICESTORM_LC);
|
||||||
|
|
||||||
std::vector<const CellInfo *> bel_cells;
|
std::vector<const CellInfo *> bel_cells;
|
||||||
|
Loc bel_loc = getBelLocation(bel);
|
||||||
for (auto bel_other : getBelsAtSameTile(bel)) {
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
IdString cell_other = getBoundBelCell(bel_other);
|
IdString cell_other = getBoundBelCell(bel_other);
|
||||||
if (cell_other != IdString() && bel_other != bel) {
|
if (cell_other != IdString() && bel_other != bel) {
|
||||||
const CellInfo *ci_other = cells.at(cell_other).get();
|
const CellInfo *ci_other = cells.at(cell_other).get();
|
||||||
@ -106,6 +108,32 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
|||||||
bel_cells.push_back(cell);
|
bel_cells.push_back(cell);
|
||||||
return logicCellsCompatible(bel_cells);
|
return logicCellsCompatible(bel_cells);
|
||||||
} else if (cell->type == id_sb_io) {
|
} else if (cell->type == id_sb_io) {
|
||||||
|
// Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to.
|
||||||
|
|
||||||
|
// Find shared PLL by looking for driving bel siblings from D_IN_0
|
||||||
|
// that are a PLL clock output.
|
||||||
|
auto wire = getBelPinWire(bel, PIN_D_IN_0);
|
||||||
|
PortPin pll_bel_pin;
|
||||||
|
BelId pll_bel;
|
||||||
|
for (auto pin : getWireBelPins(wire)) {
|
||||||
|
if (pin.pin == PIN_PLLOUT_A || pin.pin == PIN_PLLOUT_B) {
|
||||||
|
pll_bel = pin.bel;
|
||||||
|
pll_bel_pin = pin.pin;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Is there a PLL that shares this IO buffer?
|
||||||
|
if (pll_bel.index != -1) {
|
||||||
|
// Is a PLL placed in this PLL bel?
|
||||||
|
if (!checkBelAvail(pll_bel)) {
|
||||||
|
// Is the shared port driving a net?
|
||||||
|
auto pll_cell = getBoundBelCell(pll_bel);
|
||||||
|
auto pi = cells.at(pll_cell)->ports[portPinToId(pll_bel_pin)];
|
||||||
|
if (pi.net != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return getBelPackagePin(bel) != "";
|
return getBelPackagePin(bel) != "";
|
||||||
} else if (cell->type == id_sb_gb) {
|
} else if (cell->type == id_sb_gb) {
|
||||||
NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr);
|
NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr);
|
||||||
|
@ -79,8 +79,6 @@ void arch_wrap_python()
|
|||||||
conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
|
conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
|
||||||
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
|
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
|
||||||
"getBels");
|
"getBels");
|
||||||
fn_wrapper_1a<Context, decltype(&Context::getBelsAtSameTile), &Context::getBelsAtSameTile, wrap_context<BelRange>,
|
|
||||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelsAtSameTile");
|
|
||||||
|
|
||||||
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
|
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
|
||||||
conv_from_str<BelId>, conv_from_str<PortPin>>::def_wrap(ctx_cls, "getBelPinWire");
|
conv_from_str<BelId>, conv_from_str<PortPin>>::def_wrap(ctx_cls, "getBelPinWire");
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -74,14 +75,26 @@ void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cf
|
|||||||
for (int i = 0; i < cfg.num_bits; i++) {
|
for (int i = 0; i < cfg.num_bits; i++) {
|
||||||
int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
|
int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
|
||||||
if (cbit && !value)
|
if (cbit && !value)
|
||||||
log_error("clearing already set config bit %s", name.c_str());
|
log_error("clearing already set config bit %s\n", name.c_str());
|
||||||
cbit = value;
|
cbit = value;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
|
int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
|
||||||
cbit = value;
|
cbit = value;
|
||||||
if (cbit && !value)
|
if (cbit && !value)
|
||||||
log_error("clearing already set config bit %s[%d]", name.c_str(), index);
|
log_error("clearing already set config bit %s[%d]\n", name.c_str(), index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled.
|
||||||
|
// On {HX,LP}1K devices these bits are active low, so we need to invert them.
|
||||||
|
void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg,
|
||||||
|
const std::string &name, bool value)
|
||||||
|
{
|
||||||
|
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
||||||
|
set_config(ti, tile_cfg, name, !value);
|
||||||
|
} else {
|
||||||
|
set_config(ti, tile_cfg, name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +130,7 @@ static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
|
|||||||
typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
|
typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
|
||||||
|
|
||||||
static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
|
static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
|
||||||
bool value)
|
bool value, std::string prefix)
|
||||||
{
|
{
|
||||||
const ChipInfoPOD *chip = ctx->chip_info;
|
const ChipInfoPOD *chip = ctx->chip_info;
|
||||||
|
|
||||||
@ -125,7 +138,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi
|
|||||||
const auto &cbit = cell_cbits.entries[i];
|
const auto &cbit = cell_cbits.entries[i];
|
||||||
if (cbit.entry_name.get() == name) {
|
if (cbit.entry_name.get() == name) {
|
||||||
const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
|
const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
|
||||||
set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value);
|
set_config(ti, config.at(cbit.y).at(cbit.x), prefix + cbit.cbit_name.get(), value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +146,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi
|
|||||||
}
|
}
|
||||||
|
|
||||||
void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
|
void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
|
||||||
const std::vector<std::pair<std::string, int>> ¶ms, bool string_style)
|
const std::vector<std::pair<std::string, int>> ¶ms, bool string_style, std::string prefix)
|
||||||
{
|
{
|
||||||
const ChipInfoPOD *chip = ctx->chip_info;
|
const ChipInfoPOD *chip = ctx->chip_info;
|
||||||
const auto &bc = get_ec_config(chip, cell->bel);
|
const auto &bc = get_ec_config(chip, cell->bel);
|
||||||
@ -163,10 +176,10 @@ void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *ce
|
|||||||
|
|
||||||
value.resize(p.second);
|
value.resize(p.second);
|
||||||
if (p.second == 1) {
|
if (p.second == 1) {
|
||||||
set_ec_cbit(config, ctx, bc, p.first, value.at(0));
|
set_ec_cbit(config, ctx, bc, p.first, value.at(0), prefix);
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < p.second; i++) {
|
for (int i = 0; i < p.second; i++) {
|
||||||
set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i));
|
set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,8 +271,13 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_set<Loc> sb_io_used_by_pll;
|
||||||
|
std::unordered_set<Loc> sb_io_used_by_io;
|
||||||
|
|
||||||
// Set logic cell config
|
// Set logic cell config
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
|
||||||
BelId bel = cell.second.get()->bel;
|
BelId bel = cell.second.get()->bel;
|
||||||
if (bel == BelId()) {
|
if (bel == BelId()) {
|
||||||
std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
|
std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
|
||||||
@ -304,6 +322,7 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
} else if (cell.second->type == ctx->id("SB_IO")) {
|
} else if (cell.second->type == ctx->id("SB_IO")) {
|
||||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||||
int x = beli.x, y = beli.y, z = beli.z;
|
int x = beli.x, y = beli.y, z = beli.z;
|
||||||
|
sb_io_used_by_io.insert(Loc(x, y, z));
|
||||||
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
||||||
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
|
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
|
||||||
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
|
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
|
||||||
@ -405,7 +424,70 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
{"MODE_8x8", 1},
|
{"MODE_8x8", 1},
|
||||||
{"A_SIGNED", 1},
|
{"A_SIGNED", 1},
|
||||||
{"B_SIGNED", 1}};
|
{"B_SIGNED", 1}};
|
||||||
configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false);
|
configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig."));
|
||||||
|
} else if (cell.second->type == ctx->id("ICESTORM_PLL")) {
|
||||||
|
const std::vector<std::pair<std::string, int>> pll_params = {{"DELAY_ADJMODE_FB", 1},
|
||||||
|
{"DELAY_ADJMODE_REL", 1},
|
||||||
|
{"DIVF", 7},
|
||||||
|
{"DIVQ", 3},
|
||||||
|
{"DIVR", 4},
|
||||||
|
{"FDA_FEEDBACK", 4},
|
||||||
|
{"FDA_RELATIVE", 4},
|
||||||
|
{"FEEDBACK_PATH", 3},
|
||||||
|
{"FILTER_RANGE", 3},
|
||||||
|
{"PLLOUT_SELECT_A", 2},
|
||||||
|
{"PLLOUT_SELECT_B", 2},
|
||||||
|
{"PLLTYPE", 3},
|
||||||
|
{"SHIFTREG_DIV_MODE", 1},
|
||||||
|
{"TEST_MODE", 1}};
|
||||||
|
configure_extra_cell(config, ctx, cell.second.get(), pll_params, false, std::string("PLL."));
|
||||||
|
|
||||||
|
// Configure the SB_IOs that the clock outputs are going through.
|
||||||
|
for (auto &port : cell.second->ports) {
|
||||||
|
// If this port is not a PLLOUT port, ignore it.
|
||||||
|
if (port.second.name != ctx->id("PLLOUT_A") && port.second.name != ctx->id("PLLOUT_B"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If the output is not driving any net, ignore it.
|
||||||
|
if (port.second.net == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get IO Bel that this PLL port goes through by finding sibling
|
||||||
|
// Bel driving the same wire via PIN_D_IN_0.
|
||||||
|
auto wire = ctx->getBelPinWire(cell.second->bel, ctx->portPinFromId(port.second.name));
|
||||||
|
BelId io_bel;
|
||||||
|
for (auto pin : ctx->getWireBelPins(wire)) {
|
||||||
|
if (pin.pin == PIN_D_IN_0) {
|
||||||
|
io_bel = pin.bel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NPNR_ASSERT(io_bel.index != -1);
|
||||||
|
auto io_bel_loc = ctx->getBelLocation(io_bel);
|
||||||
|
|
||||||
|
// Check that this SB_IO is either unused or just used as an output.
|
||||||
|
if (sb_io_used_by_io.count(io_bel_loc)) {
|
||||||
|
log_error("SB_IO '%s' already in use, cannot route PLL through\n", ctx->getBelName(bel).c_str(ctx));
|
||||||
|
}
|
||||||
|
sb_io_used_by_pll.insert(io_bel_loc);
|
||||||
|
|
||||||
|
// Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html)
|
||||||
|
auto ieren = get_ieren(bi, io_bel_loc.x, io_bel_loc.y, io_bel_loc.z);
|
||||||
|
int iex, iey, iez;
|
||||||
|
std::tie(iex, iey, iez) = ieren;
|
||||||
|
NPNR_ASSERT(iez != -1);
|
||||||
|
|
||||||
|
// Write config.
|
||||||
|
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
||||||
|
// Enable input buffer and disable pull-up resistor in block
|
||||||
|
// (this is used by the PLL).
|
||||||
|
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
|
||||||
|
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
|
||||||
|
// PINTYPE[0] passes the PLL through to the fabric.
|
||||||
|
set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x),
|
||||||
|
"IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
NPNR_ASSERT(false);
|
NPNR_ASSERT(false);
|
||||||
}
|
}
|
||||||
@ -416,14 +498,16 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
||||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||||
int x = beli.x, y = beli.y, z = beli.z;
|
int x = beli.x, y = beli.y, z = beli.z;
|
||||||
|
if (sb_io_used_by_pll.count(Loc(x, y, z))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto ieren = get_ieren(bi, x, y, z);
|
auto ieren = get_ieren(bi, x, y, z);
|
||||||
int iex, iey, iez;
|
int iex, iey, iez;
|
||||||
std::tie(iex, iey, iez) = ieren;
|
std::tie(iex, iey, iez) = ieren;
|
||||||
if (iez != -1) {
|
if (iez != -1) {
|
||||||
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
|
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) {
|
} else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) {
|
||||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -207,6 +208,40 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
|
|||||||
add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT);
|
add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT);
|
||||||
add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT);
|
add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT);
|
||||||
|
|
||||||
|
} else if (type == ctx->id("ICESTORM_PLL")) {
|
||||||
|
new_cell->params[ctx->id("DELAY_ADJMODE_FB")] = "0";
|
||||||
|
new_cell->params[ctx->id("DELAY_ADJMODE_REL")] = "0";
|
||||||
|
|
||||||
|
new_cell->params[ctx->id("DIVF")] = "0";
|
||||||
|
new_cell->params[ctx->id("DIVQ")] = "0";
|
||||||
|
new_cell->params[ctx->id("DIVR")] = "0";
|
||||||
|
|
||||||
|
new_cell->params[ctx->id("FDA_FEEDBACK")] = "0";
|
||||||
|
new_cell->params[ctx->id("FDA_RELATIVE")] = "0";
|
||||||
|
new_cell->params[ctx->id("FEEDBACK_PATH")] = "0";
|
||||||
|
new_cell->params[ctx->id("FILTER_RANGE")] = "0";
|
||||||
|
|
||||||
|
new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0";
|
||||||
|
new_cell->params[ctx->id("PLLOUT_SELECT_B")] = "0";
|
||||||
|
|
||||||
|
new_cell->params[ctx->id("PLLTYPE")] = "0";
|
||||||
|
new_cell->params[ctx->id("SHIFTREG_DIVMODE")] = "0";
|
||||||
|
new_cell->params[ctx->id("TEST_MODE")] = "0";
|
||||||
|
|
||||||
|
add_port(ctx, new_cell.get(), "BYPASS", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "DYNAMICDELAY", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "EXTFEEDBACK", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "LATCHINPUTVALUE", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "REFERENCECLK", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "RESETB", PORT_IN);
|
||||||
|
|
||||||
|
add_port(ctx, new_cell.get(), "SCLK", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "SDI", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "SDI", PORT_OUT);
|
||||||
|
|
||||||
|
add_port(ctx, new_cell.get(), "LOCK", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "PLLOUT_A", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "PLLOUT_B", PORT_OUT);
|
||||||
} else {
|
} else {
|
||||||
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
|
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
|
||||||
}
|
}
|
||||||
@ -312,6 +347,21 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
if (cell->type == ctx->id("SB_PLL40_PAD"))
|
||||||
|
return 2;
|
||||||
|
if (cell->type == ctx->id("SB_PLL40_2_PAD"))
|
||||||
|
return 4;
|
||||||
|
if (cell->type == ctx->id("SB_PLL40_2F_PAD"))
|
||||||
|
return 5;
|
||||||
|
if (cell->type == ctx->id("SB_PLL40_CORE"))
|
||||||
|
return 3;
|
||||||
|
if (cell->type == ctx->id("SB_PLL40_2F_CORE"))
|
||||||
|
return 7;
|
||||||
|
NPNR_ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
|
bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
|
||||||
{
|
{
|
||||||
if (port.cell == nullptr)
|
if (port.cell == nullptr)
|
||||||
|
@ -71,6 +71,21 @@ inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell-
|
|||||||
|
|
||||||
inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
|
inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
|
||||||
|
|
||||||
|
inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
|
||||||
|
cell->type == ctx->id("SB_PLL40_2F_PAD") || cell->type == ctx->id("SB_PLL40_CORE") ||
|
||||||
|
cell->type == ctx->id("SB_PLL40_2F_CORE");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
|
||||||
|
cell->type == ctx->id("SB_PLL40_2F_PAD");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
|
||||||
|
|
||||||
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
|
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
|
||||||
// as needed. Set no_dff if a DFF is not being used, so that the output
|
// as needed. Set no_dff if a DFF is not being used, so that the output
|
||||||
// can be reconnected
|
// can be reconnected
|
||||||
|
@ -41,8 +41,6 @@ extra_cells = dict()
|
|||||||
extra_cell_config = dict()
|
extra_cell_config = dict()
|
||||||
packages = list()
|
packages = list()
|
||||||
|
|
||||||
wire_uphill_belport = dict()
|
|
||||||
wire_downhill_belports = dict()
|
|
||||||
wire_belports = dict()
|
wire_belports = dict()
|
||||||
|
|
||||||
wire_names = dict()
|
wire_names = dict()
|
||||||
@ -184,6 +182,8 @@ def wire_type(name):
|
|||||||
wt = "LOCAL"
|
wt = "LOCAL"
|
||||||
elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"):
|
elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"):
|
||||||
wt = "LOCAL"
|
wt = "LOCAL"
|
||||||
|
elif name in ("PLLOUT_A", "PLLOUT_B"):
|
||||||
|
wt = "LOCAL"
|
||||||
|
|
||||||
if wt is None:
|
if wt is None:
|
||||||
print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr)
|
print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr)
|
||||||
@ -451,17 +451,12 @@ for i in range(8):
|
|||||||
add_wire(0, 0, "padin_%d" % i)
|
add_wire(0, 0, "padin_%d" % i)
|
||||||
|
|
||||||
def add_bel_input(bel, wire, port):
|
def add_bel_input(bel, wire, port):
|
||||||
if wire not in wire_downhill_belports:
|
|
||||||
wire_downhill_belports[wire] = set()
|
|
||||||
wire_downhill_belports[wire].add((bel, port))
|
|
||||||
if wire not in wire_belports:
|
if wire not in wire_belports:
|
||||||
wire_belports[wire] = set()
|
wire_belports[wire] = set()
|
||||||
wire_belports[wire].add((bel, port))
|
wire_belports[wire].add((bel, port))
|
||||||
bel_wires[bel].append((wire, port, 0))
|
bel_wires[bel].append((wire, port, 0))
|
||||||
|
|
||||||
def add_bel_output(bel, wire, port):
|
def add_bel_output(bel, wire, port):
|
||||||
assert wire not in wire_uphill_belport
|
|
||||||
wire_uphill_belport[wire] = (bel, port)
|
|
||||||
if wire not in wire_belports:
|
if wire not in wire_belports:
|
||||||
wire_belports[wire] = set()
|
wire_belports[wire] = set()
|
||||||
wire_belports[wire].add((bel, port))
|
wire_belports[wire].add((bel, port))
|
||||||
@ -591,6 +586,9 @@ def is_ec_output(ec_entry):
|
|||||||
if "glb_netwk_" in wirename: return True
|
if "glb_netwk_" in wirename: return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def is_ec_pll_clock_output(ec, ec_entry):
|
||||||
|
return ec[0] == 'PLL' and ec_entry[0] in ('PLLOUT_A', 'PLLOUT_B')
|
||||||
|
|
||||||
def add_bel_ec(ec):
|
def add_bel_ec(ec):
|
||||||
ectype, x, y, z = ec
|
ectype, x, y, z = ec
|
||||||
bel = len(bel_name)
|
bel = len(bel_name)
|
||||||
@ -605,6 +603,10 @@ def add_bel_ec(ec):
|
|||||||
add_bel_output(bel, wire_names[entry[1]], entry[0])
|
add_bel_output(bel, wire_names[entry[1]], entry[0])
|
||||||
else:
|
else:
|
||||||
add_bel_input(bel, wire_names[entry[1]], entry[0])
|
add_bel_input(bel, wire_names[entry[1]], entry[0])
|
||||||
|
elif is_ec_pll_clock_output(ec, entry):
|
||||||
|
x, y, z = entry[1]
|
||||||
|
z = 'io_{}/D_IN_0'.format(z)
|
||||||
|
add_bel_output(bel, wire_names[(x, y, z)], entry[0])
|
||||||
else:
|
else:
|
||||||
extra_cell_config[bel].append(entry)
|
extra_cell_config[bel].append(entry)
|
||||||
|
|
||||||
@ -662,7 +664,7 @@ for tile_xy, tile_type in sorted(tiles.items()):
|
|||||||
add_bel_ec(ec)
|
add_bel_ec(ec)
|
||||||
|
|
||||||
for ec in sorted(extra_cells.keys()):
|
for ec in sorted(extra_cells.keys()):
|
||||||
if ec[1] == 0 and ec[2] == 0:
|
if ec[1] in (0, dev_width - 1) and ec[2] in (0, dev_height - 1):
|
||||||
add_bel_ec(ec)
|
add_bel_ec(ec)
|
||||||
|
|
||||||
class BinaryBlobAssembler:
|
class BinaryBlobAssembler:
|
||||||
@ -1001,15 +1003,6 @@ for wire in range(num_wires):
|
|||||||
num_downhill = 0
|
num_downhill = 0
|
||||||
list_downhill = None
|
list_downhill = None
|
||||||
|
|
||||||
if wire in wire_downhill_belports:
|
|
||||||
num_bels_downhill = len(wire_downhill_belports[wire])
|
|
||||||
bba.l("wire%d_downbels" % wire, "BelPortPOD")
|
|
||||||
for belport in sorted(wire_downhill_belports[wire]):
|
|
||||||
bba.u32(belport[0], "bel_index")
|
|
||||||
bba.u32(portpins[belport[1]], "port")
|
|
||||||
else:
|
|
||||||
num_bels_downhill = 0
|
|
||||||
|
|
||||||
if wire in wire_belports:
|
if wire in wire_belports:
|
||||||
num_bel_pins = len(wire_belports[wire])
|
num_bel_pins = len(wire_belports[wire])
|
||||||
bba.l("wire%d_bels" % wire, "BelPortPOD")
|
bba.l("wire%d_bels" % wire, "BelPortPOD")
|
||||||
@ -1028,19 +1021,9 @@ for wire in range(num_wires):
|
|||||||
info["num_downhill"] = num_downhill
|
info["num_downhill"] = num_downhill
|
||||||
info["list_downhill"] = list_downhill
|
info["list_downhill"] = list_downhill
|
||||||
|
|
||||||
info["num_bels_downhill"] = num_bels_downhill
|
|
||||||
info["list_bels_downhill"] = ("wire%d_downbels" % wire) if num_bels_downhill > 0 else None
|
|
||||||
|
|
||||||
info["num_bel_pins"] = num_bel_pins
|
info["num_bel_pins"] = num_bel_pins
|
||||||
info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None
|
info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None
|
||||||
|
|
||||||
if wire in wire_uphill_belport:
|
|
||||||
info["uphill_bel"] = wire_uphill_belport[wire][0]
|
|
||||||
info["uphill_pin"] = portpins[wire_uphill_belport[wire][1]]
|
|
||||||
else:
|
|
||||||
info["uphill_bel"] = -1
|
|
||||||
info["uphill_pin"] = 0
|
|
||||||
|
|
||||||
avg_x, avg_y = 0, 0
|
avg_x, avg_y = 0, 0
|
||||||
if wire in wire_xy:
|
if wire in wire_xy:
|
||||||
for x, y in wire_xy[wire]:
|
for x, y in wire_xy[wire]:
|
||||||
@ -1115,10 +1098,6 @@ for wire, info in enumerate(wireinfo):
|
|||||||
bba.u32(info["num_downhill"], "num_downhill")
|
bba.u32(info["num_downhill"], "num_downhill")
|
||||||
bba.r(info["list_uphill"], "pips_uphill")
|
bba.r(info["list_uphill"], "pips_uphill")
|
||||||
bba.r(info["list_downhill"], "pips_downhill")
|
bba.r(info["list_downhill"], "pips_downhill")
|
||||||
bba.u32(info["num_bels_downhill"], "num_bels_downhill")
|
|
||||||
bba.u32(info["uphill_bel"], "bel_uphill.bel_index")
|
|
||||||
bba.u32(info["uphill_pin"], "bel_uphill.port")
|
|
||||||
bba.r(info["list_bels_downhill"], "bels_downhill")
|
|
||||||
bba.u32(info["num_bel_pins"], "num_bel_pins")
|
bba.u32(info["num_bel_pins"], "num_bel_pins")
|
||||||
bba.r(info["list_bel_pins"], "bel_pins")
|
bba.r(info["list_bel_pins"], "bel_pins")
|
||||||
bba.u32(len(wire_segments[wire]), "num_segments")
|
bba.u32(len(wire_segments[wire]), "num_segments")
|
||||||
|
@ -19,13 +19,18 @@ if (MSVC)
|
|||||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC)
|
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC)
|
||||||
foreach (dev ${devices})
|
foreach (dev ${devices})
|
||||||
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
|
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
|
||||||
|
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
|
||||||
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin)
|
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin)
|
||||||
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
|
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
|
||||||
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
|
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
|
||||||
add_custom_command(OUTPUT ${DEV_CC_DB}
|
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
|
||||||
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB}
|
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}
|
||||||
DEPENDS ${DEV_TXT_DB} ${DB_PY}
|
DEPENDS ${DEV_TXT_DB} ${DB_PY}
|
||||||
)
|
)
|
||||||
|
add_custom_command(OUTPUT ${DEV_CC_DB}
|
||||||
|
COMMAND bbasm < ${DEV_CC_BBA_DB} > ${DEV_CC_DB}
|
||||||
|
DEPENDS bbasm ${DEV_CC_BBA_DB}
|
||||||
|
)
|
||||||
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
|
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
|
||||||
set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
foreach (target ${family_targets})
|
foreach (target ${family_targets})
|
||||||
@ -36,14 +41,20 @@ else()
|
|||||||
target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w)
|
target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w)
|
||||||
foreach (dev ${devices})
|
foreach (dev ${devices})
|
||||||
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
|
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
|
||||||
|
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
|
||||||
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc)
|
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc)
|
||||||
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
|
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
|
||||||
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
|
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
|
||||||
add_custom_command(OUTPUT ${DEV_CC_DB}
|
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
|
||||||
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB}.new
|
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new
|
||||||
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
|
COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
|
||||||
DEPENDS ${DEV_TXT_DB} ${DB_PY}
|
DEPENDS ${DEV_TXT_DB} ${DB_PY}
|
||||||
)
|
)
|
||||||
|
add_custom_command(OUTPUT ${DEV_CC_DB}
|
||||||
|
COMMAND bbasm < ${DEV_CC_BBA_DB} > ${DEV_CC_DB}.new
|
||||||
|
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
|
||||||
|
DEPENDS bbasm ${DEV_CC_BBA_DB}
|
||||||
|
)
|
||||||
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
|
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
|
||||||
foreach (target ${family_targets})
|
foreach (target ${family_targets})
|
||||||
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ice40_chipdb>)
|
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ice40_chipdb>)
|
||||||
|
@ -647,7 +647,7 @@ void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, fl
|
|||||||
float ty = 0.5 * (y1 + y2);
|
float ty = 0.5 * (y1 + y2);
|
||||||
|
|
||||||
GraphicElement el;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::G_LINE;
|
el.type = GraphicElement::G_ARROW;
|
||||||
el.style = style;
|
el.style = style;
|
||||||
|
|
||||||
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
|
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
|
||||||
|
@ -464,7 +464,11 @@ enum GfxTileWireId
|
|||||||
TILE_WIRE_SP12_H_R_23,
|
TILE_WIRE_SP12_H_R_23,
|
||||||
|
|
||||||
TILE_WIRE_SP12_H_L_22,
|
TILE_WIRE_SP12_H_L_22,
|
||||||
TILE_WIRE_SP12_H_L_23
|
TILE_WIRE_SP12_H_L_23,
|
||||||
|
|
||||||
|
TILE_WIRE_PLLIN,
|
||||||
|
TILE_WIRE_PLLOUT_A,
|
||||||
|
TILE_WIRE_PLLOUT_B
|
||||||
};
|
};
|
||||||
|
|
||||||
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
|
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
|
||||||
|
172
ice40/pack.cc
172
ice40/pack.cc
@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -311,6 +312,10 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne
|
|||||||
(user.port != ctx->id("CLK") &&
|
(user.port != ctx->id("CLK") &&
|
||||||
((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) {
|
((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) {
|
||||||
uc->ports[user.port].net = nullptr;
|
uc->ports[user.port].net = nullptr;
|
||||||
|
} else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") &&
|
||||||
|
user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") && user.port != ctx->id("RCLKE") &&
|
||||||
|
user.port != ctx->id("WCLKE")) {
|
||||||
|
uc->ports[user.port].net = nullptr;
|
||||||
} else {
|
} else {
|
||||||
uc->ports[user.port].net = constnet;
|
uc->ports[user.port].net = constnet;
|
||||||
constnet->users.push_back(user);
|
constnet->users.push_back(user);
|
||||||
@ -536,6 +541,56 @@ static void promote_globals(Context *ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spliceLUT adds a pass-through LUT LC between the given cell's output port
|
||||||
|
// and either all users or only non_LUT users.
|
||||||
|
static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
|
||||||
|
{
|
||||||
|
auto port = ci->ports[portId];
|
||||||
|
|
||||||
|
NPNR_ASSERT(port.net != nullptr);
|
||||||
|
|
||||||
|
// Create pass-through LUT.
|
||||||
|
std::unique_ptr<CellInfo> pt =
|
||||||
|
create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc");
|
||||||
|
pt->params[ctx->id("LUT_INIT")] = "255"; // output is always I3
|
||||||
|
|
||||||
|
// Create LUT output net.
|
||||||
|
std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||||
|
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net");
|
||||||
|
out_net->driver.cell = pt.get();
|
||||||
|
out_net->driver.port = ctx->id("O");
|
||||||
|
pt->ports.at(ctx->id("O")).net = out_net.get();
|
||||||
|
|
||||||
|
// New users of the original cell's port
|
||||||
|
std::vector<PortRef> new_users;
|
||||||
|
for (const auto &user : port.net->users) {
|
||||||
|
if (onlyNonLUTs && user.cell->type == ctx->id("ICESTORM_LC")) {
|
||||||
|
new_users.push_back(user);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Rewrite pointer into net in user.
|
||||||
|
user.cell->ports[user.port].net = out_net.get();
|
||||||
|
// Add user to net.
|
||||||
|
PortRef pr;
|
||||||
|
pr.cell = user.cell;
|
||||||
|
pr.port = user.port;
|
||||||
|
out_net->users.push_back(pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add LUT to new users.
|
||||||
|
PortRef pr;
|
||||||
|
pr.cell = pt.get();
|
||||||
|
pr.port = ctx->id("I3");
|
||||||
|
new_users.push_back(pr);
|
||||||
|
pt->ports.at(ctx->id("I3")).net = port.net;
|
||||||
|
|
||||||
|
// Replace users of the original net.
|
||||||
|
port.net->users = new_users;
|
||||||
|
|
||||||
|
ctx->nets[out_net->name] = std::move(out_net);
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
|
||||||
// Pack special functions
|
// Pack special functions
|
||||||
static void pack_special(Context *ctx)
|
static void pack_special(Context *ctx)
|
||||||
{
|
{
|
||||||
@ -590,6 +645,123 @@ static void pack_special(Context *ctx)
|
|||||||
}
|
}
|
||||||
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
||||||
}
|
}
|
||||||
|
new_cells.push_back(std::move(packed));
|
||||||
|
} else if (is_sb_pll40(ctx, ci)) {
|
||||||
|
std::unique_ptr<CellInfo> packed =
|
||||||
|
create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL");
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
|
||||||
|
if (is_sb_pll40_pad(ctx, ci)) {
|
||||||
|
// TODO(q3k): Implement these after checking their behaviour on
|
||||||
|
// a board with exposed 'clock pads'.
|
||||||
|
log_error("SB_PLL40_*_PAD cells are not supported yet.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto attr : ci->attrs)
|
||||||
|
packed->attrs[attr.first] = attr.second;
|
||||||
|
for (auto param : ci->params)
|
||||||
|
packed->params[param.first] = param.second;
|
||||||
|
|
||||||
|
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
|
||||||
|
packed->params[ctx->id("FEEDBACK_PATH")] =
|
||||||
|
feedback_path == "DELAY"
|
||||||
|
? "0"
|
||||||
|
: feedback_path == "SIMPLE" ? "1"
|
||||||
|
: feedback_path == "PHASE_AND_DELAY"
|
||||||
|
? "2"
|
||||||
|
: feedback_path == "EXTERNAL" ? "6" : feedback_path;
|
||||||
|
packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
|
||||||
|
|
||||||
|
for (auto port : ci->ports) {
|
||||||
|
PortInfo &pi = port.second;
|
||||||
|
std::string newname = pi.name.str(ctx);
|
||||||
|
size_t bpos = newname.find('[');
|
||||||
|
if (bpos != std::string::npos) {
|
||||||
|
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
|
||||||
|
}
|
||||||
|
if (pi.name == ctx->id("PLLOUTCOREA"))
|
||||||
|
newname = "PLLOUT_A";
|
||||||
|
if (pi.name == ctx->id("PLLOUTCOREB"))
|
||||||
|
newname = "PLLOUT_B";
|
||||||
|
if (pi.name == ctx->id("PLLOUTCORE"))
|
||||||
|
newname = "PLLOUT_A";
|
||||||
|
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If PLL is not constrained already, do that - we need this
|
||||||
|
// information to then constrain the LOCK LUT.
|
||||||
|
BelId pll_bel;
|
||||||
|
bool constrained = false;
|
||||||
|
if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) {
|
||||||
|
for (auto bel : ctx->getBels()) {
|
||||||
|
if (ctx->getBelType(bel) != TYPE_ICESTORM_PLL)
|
||||||
|
continue;
|
||||||
|
log_info(" constrained '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx));
|
||||||
|
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
|
||||||
|
pll_bel = bel;
|
||||||
|
constrained = true;
|
||||||
|
}
|
||||||
|
if (!constrained) {
|
||||||
|
log_error(" could not constrain '%s' to any PLL Bel\n", packed->name.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The LOCK signal on iCE40 PLLs goes through the neigh_op_bnl_1 wire.
|
||||||
|
// In practice, this means the LOCK signal can only directly reach LUT
|
||||||
|
// inputs.
|
||||||
|
// If we have a net connected to LOCK, make sure it only drives LUTs.
|
||||||
|
auto port = packed->ports[ctx->id("LOCK")];
|
||||||
|
if (port.net != nullptr) {
|
||||||
|
bool found_lut = false;
|
||||||
|
bool all_luts = true;
|
||||||
|
unsigned int lut_count = 0;
|
||||||
|
for (const auto &user : port.net->users) {
|
||||||
|
NPNR_ASSERT(user.cell != nullptr);
|
||||||
|
if (user.cell->type == ctx->id("ICESTORM_LC")) {
|
||||||
|
found_lut = true;
|
||||||
|
lut_count++;
|
||||||
|
} else {
|
||||||
|
all_luts = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_lut && all_luts) {
|
||||||
|
// Every user is a LUT, carry on now.
|
||||||
|
} else if (found_lut && !all_luts && lut_count < 8) {
|
||||||
|
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
|
||||||
|
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
|
||||||
|
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
|
||||||
|
new_cells.push_back(std::move(pt));
|
||||||
|
} else {
|
||||||
|
// Strategy: create a pass-through LUT, move every user behind it.
|
||||||
|
log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx));
|
||||||
|
auto pt = spliceLUT(ctx, packed.get(), port.name, false);
|
||||||
|
new_cells.push_back(std::move(pt));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find wire that will be driven by this port.
|
||||||
|
const auto pll_out_wire = ctx->getBelPinWire(pll_bel, ctx->portPinFromId(port.name));
|
||||||
|
NPNR_ASSERT(pll_out_wire.index != -1);
|
||||||
|
|
||||||
|
// Now, constrain all LUTs on the output of the signal to be at
|
||||||
|
// the correct Bel relative to the PLL Bel.
|
||||||
|
int x = ctx->chip_info->wire_data[pll_out_wire.index].x;
|
||||||
|
int y = ctx->chip_info->wire_data[pll_out_wire.index].y;
|
||||||
|
int z = 0;
|
||||||
|
for (const auto &user : port.net->users) {
|
||||||
|
NPNR_ASSERT(user.cell != nullptr);
|
||||||
|
NPNR_ASSERT(user.cell->type == ctx->id("ICESTORM_LC"));
|
||||||
|
|
||||||
|
// TODO(q3k): handle when the Bel might be already the
|
||||||
|
// target of another constraint.
|
||||||
|
NPNR_ASSERT(z < 8);
|
||||||
|
auto target_bel = ctx->getBelByLocation(Loc(x, y, z++));
|
||||||
|
auto target_bel_name = ctx->getBelName(target_bel).str(ctx);
|
||||||
|
user.cell->attrs[ctx->id("BEL")] = target_bel_name;
|
||||||
|
log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
new_cells.push_back(std::move(packed));
|
new_cells.push_back(std::move(packed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,11 +75,9 @@ static void get_chain_midpoint(const Context *ctx, const CellChain &chain, float
|
|||||||
for (auto cell : chain.cells) {
|
for (auto cell : chain.cells) {
|
||||||
if (cell->bel == BelId())
|
if (cell->bel == BelId())
|
||||||
continue;
|
continue;
|
||||||
int bel_x, bel_y;
|
Loc bel_loc = ctx->getBelLocation(cell->bel);
|
||||||
bool bel_gb;
|
total_x += bel_loc.x;
|
||||||
ctx->estimatePosition(cell->bel, bel_x, bel_y, bel_gb);
|
total_y += bel_loc.y;
|
||||||
total_x += bel_x;
|
|
||||||
total_y += bel_y;
|
|
||||||
N++;
|
N++;
|
||||||
}
|
}
|
||||||
NPNR_ASSERT(N > 0);
|
NPNR_ASSERT(N > 0);
|
||||||
|
@ -118,6 +118,8 @@ X(DYNAMICDELAY_5)
|
|||||||
X(DYNAMICDELAY_6)
|
X(DYNAMICDELAY_6)
|
||||||
X(DYNAMICDELAY_7)
|
X(DYNAMICDELAY_7)
|
||||||
X(LOCK)
|
X(LOCK)
|
||||||
|
X(PLLOUT_A)
|
||||||
|
X(PLLOUT_B)
|
||||||
X(BYPASS)
|
X(BYPASS)
|
||||||
X(RESETB)
|
X(RESETB)
|
||||||
X(LATCHINPUTVALUE)
|
X(LATCHINPUTVALUE)
|
||||||
|
Loading…
Reference in New Issue
Block a user