Merge branch 'master' into redist_slack

This commit is contained in:
Eddie Hung 2018-07-24 22:20:10 -07:00
commit 9382938661
34 changed files with 730 additions and 186 deletions

2
.gitignore vendored
View File

@ -25,3 +25,5 @@ build/
/Testing/* /Testing/*
CTestTestfile.cmake CTestTestfile.cmake
install_manifest.txt install_manifest.txt
/bbasm
/ImportExecutables.cmake

View File

@ -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
View 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
View 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;
}

View File

@ -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;

View File

@ -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

View File

@ -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)))));

View File

@ -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;

View File

@ -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);
} }

View File

@ -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));

View File

@ -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; }

View File

@ -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();

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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; }

View File

@ -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

View File

@ -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");

View File

@ -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) {

View File

@ -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;
} }

View File

@ -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; }

View File

@ -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);

View File

@ -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");

View File

@ -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>> &params, bool string_style) const std::vector<std::pair<std::string, int>> &params, 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];

View File

@ -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)

View File

@ -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

View File

@ -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")

View File

@ -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>)

View File

@ -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) {

View File

@ -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);

View File

@ -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));
} }
} }

View File

@ -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);

View File

@ -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)