aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/nextpnr.h24
-rw-r--r--common/router1.cc19
-rw-r--r--common/timing.cc5
-rw-r--r--generic/arch.cc28
-rw-r--r--generic/arch.h13
-rw-r--r--gui/designwidget.cc14
-rw-r--r--ice40/arch.cc53
-rw-r--r--ice40/arch.h22
-rw-r--r--ice40/arch_place.cc14
-rw-r--r--ice40/archdefs.h3
-rw-r--r--ice40/bitstream.cc1168
-rw-r--r--ice40/bitstream.h1
-rw-r--r--ice40/main.cc24
13 files changed, 816 insertions, 572 deletions
diff --git a/common/nextpnr.h b/common/nextpnr.h
index bc64adb5..40fd3d13 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -27,6 +27,8 @@
#include <unordered_set>
#include <vector>
+#include <boost/functional/hash.hpp>
+
#ifndef NEXTPNR_H
#define NEXTPNR_H
@@ -158,8 +160,30 @@ struct GraphicElement
std::string text;
};
+struct Loc
+{
+ int x = -1, y = -1, z = -1;
+
+ bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); }
+ bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); }
+};
+
NEXTPNR_NAMESPACE_END
+namespace std {
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX Loc>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Loc &obj) const noexcept
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, hash<int>()(obj.x));
+ boost::hash_combine(seed, hash<int>()(obj.y));
+ boost::hash_combine(seed, hash<int>()(obj.z));
+ return seed;
+ }
+};
+} // namespace std
+
#include "archdefs.h"
NEXTPNR_NAMESPACE_BEGIN
diff --git a/common/router1.cc b/common/router1.cc
index 94c7070e..50022169 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -402,6 +402,21 @@ struct Router
NEXTPNR_NAMESPACE_BEGIN
+static void prioritise_nets(Context *ctx, std::vector<IdString> &netsArray)
+{
+ std::unordered_map<IdString, delay_t> netScores;
+ for (auto net_name : netsArray) {
+ delay_t score = std::numeric_limits<delay_t>::max();
+ for (const auto &sink : ctx->nets.at(net_name)->users) {
+ if (sink.budget < score)
+ score = sink.budget;
+ }
+ netScores[net_name] = score;
+ }
+ std::sort(netsArray.begin(), netsArray.end(),
+ [&netScores](IdString a, IdString b) { return netScores[a] < netScores[b]; });
+}
+
bool router1(Context *ctx)
{
try {
@@ -508,7 +523,7 @@ bool router1(Context *ctx)
bool printNets = ctx->verbose && (netsQueue.size() < 10);
std::vector<IdString> netsArray(netsQueue.begin(), netsQueue.end());
- ctx->sorted_shuffle(netsArray);
+ prioritise_nets(ctx, netsArray);
netsQueue.clear();
for (auto net_name : netsArray) {
@@ -560,7 +575,7 @@ bool router1(Context *ctx)
int ripCnt = 0;
std::vector<IdString> ripupArray(ripupQueue.begin(), ripupQueue.end());
- ctx->sorted_shuffle(ripupArray);
+ prioritise_nets(ctx, ripupArray);
for (auto net_name : ripupArray) {
if (printNets)
diff --git a/common/timing.cc b/common/timing.cc
index 479dba26..5f744621 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -217,6 +217,11 @@ void update_budget(Context *ctx)
if (it == updates.end()) continue;
user.budget = delays.at(pi) + it->second;
+ // HACK HACK HACK
+ if (net.second->driver.port == ctx->id("COUT"))
+ user.budget = 0;
+ // HACK HACK HACK
+
// Post-update check
// if (user.budget < 0)
// log_warning("port %s.%s, connected to net '%s', has negative "
diff --git a/generic/arch.cc b/generic/arch.cc
index 390830aa..b7ec847e 100644
--- a/generic/arch.cc
+++ b/generic/arch.cc
@@ -29,8 +29,8 @@ void Arch::addWire(IdString name, int x, int y)
NPNR_ASSERT(wires.count(name) == 0);
WireInfo &wi = wires[name];
wi.name = name;
- wi.grid_x = x;
- wi.grid_y = y;
+ wi.x = x;
+ wi.y = y;
wire_ids.push_back(name);
}
@@ -62,18 +62,28 @@ void Arch::addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo
pip_ids.push_back(name);
}
-void Arch::addBel(IdString name, IdString type, int x, int y, bool gb)
+void Arch::addBel(IdString name, IdString type, int x, int y, int z, bool gb)
{
+ Loc loc;
+ loc.x = x;
+ loc.y = y;
+ loc.z = z;
+
NPNR_ASSERT(bels.count(name) == 0);
+ NPNR_ASSERT(bel_by_loc.count(loc) == 0);
BelInfo &bi = bels[name];
bi.name = name;
bi.type = type;
- bi.grid_x = x;
- bi.grid_y = y;
+ bi.x = x;
+ bi.y = y;
+ bi.z = z;
bi.gb = gb;
bel_ids.push_back(name);
bel_ids_by_type[type].push_back(name);
+
+ bel_by_loc[loc] = name;
+ bels_by_tile[x][y].push_back(name);
}
void Arch::addBelInput(IdString bel, IdString name, IdString wire)
@@ -348,8 +358,8 @@ 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).grid_x;
- y = bels.at(bel).grid_y;
+ x = bels.at(bel).x;
+ y = bels.at(bel).y;
gb = bels.at(bel).gb;
}
@@ -357,8 +367,8 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
const WireInfo &s = wires.at(src);
const WireInfo &d = wires.at(dst);
- int dx = abs(s.grid_x - d.grid_x);
- int dy = abs(s.grid_y - d.grid_y);
+ int dx = abs(s.x - d.x);
+ int dy = abs(s.y - d.y);
return (dx + dy) * grid_distance_to_delay;
}
diff --git a/generic/arch.h b/generic/arch.h
index 5d7ac540..ea4bb565 100644
--- a/generic/arch.h
+++ b/generic/arch.h
@@ -44,7 +44,7 @@ struct WireInfo
BelPin uphill_bel_pin;
std::vector<BelPin> downhill_bel_pins;
DecalXY decalxy;
- int grid_x, grid_y;
+ int x, y;
};
struct PinInfo
@@ -59,7 +59,7 @@ struct BelInfo
IdString name, type, bound_cell;
std::unordered_map<IdString, PinInfo> pins;
DecalXY decalxy;
- int grid_x, grid_y;
+ int x, y, z;
bool gb;
};
@@ -85,6 +85,9 @@ struct Arch : BaseCtx
std::vector<IdString> bel_ids, wire_ids, pip_ids;
std::unordered_map<IdString, std::vector<IdString>> bel_ids_by_type;
+ std::unordered_map<Loc, BelId> bel_by_loc;
+ std::unordered_map<int, std::unordered_map<int, std::vector<BelId>>> bels_by_tile;
+
std::unordered_map<DecalId, std::vector<GraphicElement>> decal_graphics;
DecalXY frame_decalxy;
@@ -94,7 +97,7 @@ struct Arch : BaseCtx
void addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay);
void addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay);
- void addBel(IdString name, IdString type, int x, int y, bool gb);
+ void addBel(IdString name, IdString type, int x, int y, int z, bool gb);
void addBelInput(IdString bel, IdString name, IdString wire);
void addBelOutput(IdString bel, IdString name, IdString wire);
void addBelInout(IdString bel, IdString name, IdString wire);
@@ -129,6 +132,10 @@ struct Arch : BaseCtx
BelId getBelByName(IdString name) const;
IdString getBelName(BelId bel) const;
+ Loc getBelLocation(BelId bel) const;
+ BelId getBelByLocation(Loc loc) const;
+ std::vector<BelId> getBelsByTile(int x, int y) const;
+ bool getBelGlobalBuf(BelId bel) const;
uint32_t getBelChecksum(BelId bel) const;
void bindBel(BelId bel, IdString cell, PlaceStrength strength);
void unbindBel(BelId bel);
diff --git a/gui/designwidget.cc b/gui/designwidget.cc
index 4123bf30..f7ae82f5 100644
--- a/gui/designwidget.cc
+++ b/gui/designwidget.cc
@@ -230,6 +230,9 @@ void DesignWidget::addToHistory(QTreeWidgetItem *item)
void DesignWidget::newContext(Context *ctx)
{
treeWidget->clear();
+ // reset pointers since they are not valid after clear
+ nets_root = nullptr;
+ cells_root = nullptr;
history_ignore = false;
history_index = -1;
history.clear();
@@ -334,16 +337,7 @@ void DesignWidget::newContext(Context *ctx)
for (auto pip : nameToItem[2].toStdMap()) {
pip_root->addChild(pip.second);
}
-
- // Add nets to tree
- nets_root = new QTreeWidgetItem(treeWidget);
- nets_root->setText(0, "Nets");
- treeWidget->insertTopLevelItem(0, nets_root);
-
- // Add cells to tree
- cells_root = new QTreeWidgetItem(treeWidget);
- cells_root->setText(0, "Cells");
- treeWidget->insertTopLevelItem(0, cells_root);
+ updateTree();
}
void DesignWidget::updateTree()
diff --git a/ice40/arch.cc b/ice40/arch.cc
index 1aa02294..e9a7d2b6 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -25,7 +25,7 @@
#include "placer1.h"
#include "router1.h"
#include "util.h"
-
+#include "cells.h"
NEXTPNR_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
@@ -255,11 +255,52 @@ BelId Arch::getBelByName(IdString name) const
return ret;
}
+BelId Arch::getBelByLocation(Loc loc) const
+{
+ BelId bel;
+
+ if (bel_by_loc.empty()) {
+ for (int i = 0; i < chip_info->num_bels; i++) {
+ BelId b;
+ b.index = i;
+ bel_by_loc[getBelLocation(b)] = i;
+ }
+ }
+
+ auto it = bel_by_loc.find(loc);
+ if (it != bel_by_loc.end())
+ bel.index = it->second;
+
+ return bel;
+}
+
+BelRange Arch::getBelsByTile(int x, int y) const
+{
+ // In iCE40 chipdb bels at the same tile are consecutive and dense z ordinates are used
+ BelRange br;
+
+ Loc loc;
+ loc.x = x;
+ loc.y = y;
+ loc.z = 0;
+
+ br.b.cursor = Arch::getBelByLocation(loc).index;
+ br.e.cursor = br.b.cursor;
+
+ if (br.e.cursor != -1) {
+ while (br.e.cursor < chip_info->num_bels &&
+ chip_info->bel_data[br.e.cursor].x == x &&
+ chip_info->bel_data[br.e.cursor].y == y)
+ br.e.cursor++;
+ }
+
+ return br;
+}
+
BelRange Arch::getBelsAtSameTile(BelId bel) const
{
BelRange br;
NPNR_ASSERT(bel != BelId());
- // This requires Bels at the same tile are consecutive
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;
@@ -704,6 +745,14 @@ void Arch::assignArchInfo()
NetInfo *ni = net.second.get();
if (isGlobalNet(ni))
ni->is_global = true;
+ ni->is_enable = false;
+ ni->is_reset = false;
+ for (auto usr : ni->users) {
+ if (is_enable_port(this, usr))
+ ni->is_enable = true;
+ if (is_reset_port(this, usr))
+ ni->is_reset = true;
+ }
}
for (auto &cell : getCtx()->cells) {
CellInfo *ci = cell.second.get();
diff --git a/ice40/arch.h b/ice40/arch.h
index 3b6d23dc..beba2ccf 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -350,6 +350,7 @@ struct Arch : BaseCtx
mutable std::unordered_map<IdString, int> bel_by_name;
mutable std::unordered_map<IdString, int> wire_by_name;
mutable std::unordered_map<IdString, int> pip_by_name;
+ mutable std::unordered_map<Loc, int> bel_by_loc;
std::vector<IdString> bel_to_cell;
std::vector<IdString> wire_to_net;
@@ -440,7 +441,24 @@ struct Arch : BaseCtx
return range;
}
- BelRange getBelsAtSameTile(BelId bel) const;
+ Loc getBelLocation(BelId bel) const
+ {
+ Loc loc;
+ loc.x = chip_info->bel_data[bel.index].x;
+ loc.y = chip_info->bel_data[bel.index].y;
+ loc.z = chip_info->bel_data[bel.index].z;
+ return loc;
+ }
+
+ BelId getBelByLocation(Loc loc) const;
+ BelRange getBelsByTile(int x, int y) const;
+
+ 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
{
@@ -671,7 +689,7 @@ struct Arch : BaseCtx
// -------------------------------------------------
- void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
+ void estimatePosition(BelId bel, int &x, int &y, bool &gb) const NPNR_DEPRECATED;
delay_t estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; }
diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc
index 1f2943a0..116ab7d3 100644
--- a/ice40/arch_place.cc
+++ b/ice40/arch_place.cc
@@ -108,21 +108,15 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
} else if (cell->type == id_sb_io) {
return getBelPackagePin(bel) != "";
} else if (cell->type == id_sb_gb) {
- bool is_reset = false, is_cen = false;
NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr);
- for (auto user : cell->ports.at(id_glb_buf_out).net->users) {
- if (is_reset_port(this, user))
- is_reset = true;
- if (is_enable_port(this, user))
- is_cen = true;
- }
+ const NetInfo *net = cell->ports.at(id_glb_buf_out).net;
IdString glb_net = getWireName(getWireBelPin(bel, PIN_GLOBAL_BUFFER_OUTPUT));
int glb_id = std::stoi(std::string("") + glb_net.str(this).back());
- if (is_reset && is_cen)
+ if (net->is_reset && net->is_enable)
return false;
- else if (is_reset)
+ else if (net->is_reset)
return (glb_id % 2) == 0;
- else if (is_cen)
+ else if (net->is_enable)
return (glb_id % 2) == 1;
else
return true;
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index 55e2c2fb..dec6f702 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -21,8 +21,6 @@
#error Include "archdefs.h" via "nextpnr.h" only.
#endif
-#include <boost/functional/hash.hpp>
-
NEXTPNR_NAMESPACE_BEGIN
typedef int delay_t;
@@ -153,6 +151,7 @@ struct DecalId
struct ArchNetInfo
{
bool is_global = false;
+ bool is_reset = false, is_enable = false;
};
struct NetInfo;
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index c12fa90e..924868b5 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -1,529 +1,639 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
- * Copyright (C) 2018 David Shah <david@symbioticeda.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-#include "bitstream.h"
-#include <cctype>
-#include <vector>
-#include "log.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-inline TileType tile_at(const Context *ctx, int x, int y)
-{
- return ctx->chip_info->tile_grid[y * ctx->chip_info->width + x];
-}
-
-const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &name)
-{
- for (int i = 0; i < tile.num_config_entries; i++) {
- if (std::string(tile.entries[i].name.get()) == name) {
- return tile.entries[i];
- }
- }
- NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
-}
-
-std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
-{
- for (int i = 0; i < bi.num_ierens; i++) {
- auto ie = bi.ierens[i];
- if (ie.iox == x && ie.ioy == y && ie.ioz == z) {
- return std::make_tuple(ie.ierx, ie.iery, ie.ierz);
- }
- }
- // No pin at this location
- return std::make_tuple(-1, -1, -1);
-};
-
-void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, bool value,
- int index = -1)
-{
- const ConfigEntryPOD &cfg = find_config(ti, name);
- if (index == -1) {
- for (int i = 0; i < cfg.num_bits; i++) {
- int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
- if (cbit && !value)
- log_error("clearing already set config bit %s", name.c_str());
- cbit = value;
- }
- } else {
- int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
- cbit = value;
- if (cbit && !value)
- log_error("clearing already set config bit %s[%d]", name.c_str(), index);
- }
-}
-
-int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0)
-{
- auto found = cell->params.find(param);
- if (found != cell->params.end())
- return std::stoi(found->second);
- else
- return defval;
-}
-
-std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std::string defval = "")
-{
- auto found = cell->params.find(param);
- if (found != cell->params.end())
- return found->second;
- else
- return defval;
-}
-
-char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
-
-static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
-{
- for (int i = 0; i < chip->num_belcfgs; i++) {
- if (chip->bel_config[i].bel_index == bel.index)
- return chip->bel_config[i];
- }
- NPNR_ASSERT_FALSE("failed to find bel config");
-}
-
-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,
- bool value)
-{
- const ChipInfoPOD *chip = ctx->chip_info;
-
- for (int i = 0; i < cell_cbits.num_entries; i++) {
- const auto &cbit = cell_cbits.entries[i];
- if (cbit.entry_name.get() == name) {
- 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);
- return;
- }
- }
- NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
-}
-
-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 ChipInfoPOD *chip = ctx->chip_info;
- const auto &bc = get_ec_config(chip, cell->bel);
- for (auto p : params) {
- std::vector<bool> value;
- if (string_style) {
- // Lattice's weird string style params, not sure if
- // prefixes other than 0b should be supported, only 0b features in docs
- std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
- assert(raw.substr(0, 2) == "0b");
- raw = raw.substr(2);
- value.resize(raw.length());
- for (int i = 0; i < (int)raw.length(); i++) {
- if (raw[i] == '1') {
- value[(raw.length() - 1) - i] = 1;
- } else {
- assert(raw[i] == '0');
- value[(raw.length() - 1) - i] = 0;
- }
- }
- } else {
- int ival = get_param_or_def(cell, ctx->id(p.first), 0);
-
- for (int i = 0; i < p.second; i++)
- value.push_back((ival >> i) & 0x1);
- }
-
- value.resize(p.second);
- if (p.second == 1) {
- set_ec_cbit(config, ctx, bc, p.first, value.at(0));
- } else {
- for (int i = 0; i < p.second; i++) {
- set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i));
- }
- }
- }
-}
-
-void write_asc(const Context *ctx, std::ostream &out)
-{
- // [y][x][row][col]
- const ChipInfoPOD &ci = *ctx->chip_info;
- const BitstreamInfoPOD &bi = *ci.bits_info;
- chipconfig_t config;
- config.resize(ci.height);
- for (int y = 0; y < ci.height; y++) {
- config.at(y).resize(ci.width);
- for (int x = 0; x < ci.width; x++) {
- TileType tile = tile_at(ctx, x, y);
- int rows = bi.tiles_nonrouting[tile].rows;
- int cols = bi.tiles_nonrouting[tile].cols;
- config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
- }
- }
- out << ".comment from next-pnr" << std::endl;
-
- switch (ctx->args.type) {
- case ArchArgs::LP384:
- out << ".device 384" << std::endl;
- break;
- case ArchArgs::HX1K:
- case ArchArgs::LP1K:
- out << ".device 1k" << std::endl;
- break;
- case ArchArgs::HX8K:
- case ArchArgs::LP8K:
- out << ".device 8k" << std::endl;
- break;
- case ArchArgs::UP5K:
- out << ".device 5k" << std::endl;
- break;
- default:
- NPNR_ASSERT_FALSE("unsupported device type");
- }
- // Set pips
- for (auto pip : ctx->getPips()) {
- if (ctx->pip_to_net[pip.index] != IdString()) {
- const PipInfoPOD &pi = ci.pip_data[pip.index];
- const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
- for (int i = 0; i < swi.num_bits; i++) {
- bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
- int8_t &cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
- if (bool(cbit) != 0)
- NPNR_ASSERT(false);
- cbit = val;
- }
- }
- }
- // Set logic cell config
- for (auto &cell : ctx->cells) {
- BelId bel = cell.second.get()->bel;
- if (bel == BelId()) {
- std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
- continue;
- }
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- if (cell.second->type == ctx->id("ICESTORM_LC")) {
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
- unsigned lut_init = get_param_or_def(cell.second.get(), ctx->id("LUT_INIT"));
- bool neg_clk = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK"));
- bool dff_enable = get_param_or_def(cell.second.get(), ctx->id("DFF_ENABLE"));
- bool async_sr = get_param_or_def(cell.second.get(), ctx->id("ASYNC_SR"));
- bool set_noreset = get_param_or_def(cell.second.get(), ctx->id("SET_NORESET"));
- bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
- std::vector<bool> lc(20, false);
- // From arachne-pnr
- static std::vector<int> lut_perm = {
- 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
- };
- for (int i = 0; i < 16; i++) {
- if ((lut_init >> i) & 0x1)
- lc.at(lut_perm.at(i)) = true;
- }
- lc.at(8) = carry_enable;
- lc.at(9) = dff_enable;
- lc.at(18) = set_noreset;
- lc.at(19) = async_sr;
-
- for (int i = 0; i < 20; i++)
- set_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), lc.at(i), i);
- if (dff_enable)
- set_config(ti, config.at(y).at(x), "NegClk", neg_clk);
-
- bool carry_const = get_param_or_def(cell.second.get(), ctx->id("CIN_CONST"));
- bool carry_set = get_param_or_def(cell.second.get(), ctx->id("CIN_SET"));
- if (carry_const) {
- if (!ctx->force)
- NPNR_ASSERT(z == 0);
- set_config(ti, config.at(y).at(x), "CarryInSet", carry_set);
- }
- } else if (cell.second->type == ctx->id("SB_IO")) {
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
- 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 pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
- for (int i = 0; i < 6; i++) {
- bool val = (pin_type >> i) & 0x01;
- set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
- }
- set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
- auto ieren = get_ieren(bi, x, y, z);
- int iex, iey, iez;
- std::tie(iex, iey, iez) = ieren;
- NPNR_ASSERT(iez != -1);
-
- bool input_en = false;
- if ((ctx->wire_to_net[ctx->getWireBelPin(bel, PIN_D_IN_0).index] != IdString()) ||
- (ctx->wire_to_net[ctx->getWireBelPin(bel, PIN_D_IN_1).index] != IdString())) {
- input_en = true;
- }
-
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
- set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
- } else {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), input_en);
- set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
- }
-
- if (ctx->args.type == ArchArgs::UP5K) {
- if (iez == 0) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
- } else if (iez == 1) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
- }
- }
- } else if (cell.second->type == ctx->id("SB_GB")) {
- // no cell config bits
- } else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y;
- const TileInfoPOD &ti_ramt = bi.tiles_nonrouting[TILE_RAMT];
- const TileInfoPOD &ti_ramb = bi.tiles_nonrouting[TILE_RAMB];
- if (!(ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
- set_config(ti_ramb, config.at(y).at(x), "RamConfig.PowerUp", true);
- }
- bool negclk_r = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_R"));
- bool negclk_w = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_W"));
- int write_mode = get_param_or_def(cell.second.get(), ctx->id("WRITE_MODE"));
- int read_mode = get_param_or_def(cell.second.get(), ctx->id("READ_MODE"));
- set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w);
- set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r);
-
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_0", write_mode & 0x1);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
- } else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
- // No config needed
- } else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- NPNR_ASSERT(ctx->args.type == ArchArgs::UP5K);
- if (x == 0 && y == 0) {
- const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
- if (z == 1) {
- set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_0", true);
- } else if (z == 2) {
- set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_1", true);
- } else {
- NPNR_ASSERT(false);
- }
- } else if (x == 25 && y == 0) {
- const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
- if (z == 3) {
- set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_0", true);
- } else if (z == 4) {
- set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_1", true);
- } else {
- NPNR_ASSERT(false);
- }
- }
- } else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
- const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
- {"A_REG", 1},
- {"B_REG", 1},
- {"D_REG", 1},
- {"TOP_8x8_MULT_REG", 1},
- {"BOT_8x8_MULT_REG", 1},
- {"PIPELINE_16x16_MULT_REG1", 1},
- {"PIPELINE_16x16_MULT_REG2", 1},
- {"TOPOUTPUT_SELECT", 2},
- {"TOPADDSUB_LOWERINPUT", 2},
- {"TOPADDSUB_UPPERINPUT", 1},
- {"TOPADDSUB_CARRYSELECT", 2},
- {"BOTOUTPUT_SELECT", 2},
- {"BOTADDSUB_LOWERINPUT", 2},
- {"BOTADDSUB_UPPERINPUT", 1},
- {"BOTADDSUB_CARRYSELECT", 2},
- {"MODE_8x8", 1},
- {"A_SIGNED", 1},
- {"B_SIGNED", 1}};
- configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false);
- } else {
- NPNR_ASSERT(false);
- }
- }
- // Set config bits in unused IO and RAM
- for (auto bel : ctx->getBels()) {
- if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_SB_IO) {
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- auto ieren = get_ieren(bi, x, y, z);
- int iex, iey, iez;
- std::tie(iex, iey, iez) = ieren;
- if (iez != -1) {
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
- 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) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y;
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_RAMB];
- if ((ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
- set_config(ti, config.at(y).at(x), "RamConfig.PowerUp", true);
- }
- }
- }
-
- // Set other config bits
- for (int y = 0; y < ci.height; y++) {
- for (int x = 0; x < ci.width; x++) {
- TileType tile = tile_at(ctx, x, y);
- const TileInfoPOD &ti = bi.tiles_nonrouting[tile];
-
- // set all ColBufCtrl bits (FIXME)
- bool setColBufCtrl = true;
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- if (tile == TILE_RAMB || tile == TILE_RAMT) {
- setColBufCtrl = (y == 3 || y == 5 || y == 11 || y == 13);
- } else {
- setColBufCtrl = (y == 4 || y == 5 || y == 12 || y == 13);
- }
- } else if (ctx->args.type == ArchArgs::LP8K || ctx->args.type == ArchArgs::HX8K) {
- setColBufCtrl = (y == 8 || y == 9 || y == 24 || y == 25);
- } else if (ctx->args.type == ArchArgs::UP5K) {
- setColBufCtrl = (y == 4 || y == 5 || y == 14 || y == 15 || y == 26 || y == 27);
- }
- if (setColBufCtrl) {
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_0", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_1", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_2", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_3", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_4", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_5", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_6", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_7", true);
- }
-
- // Weird UltraPlus bits
- if (tile == TILE_DSP0 || tile == TILE_DSP1 || tile == TILE_DSP2 || tile == TILE_DSP3 ||
- tile == TILE_IPCON) {
- if (ctx->args.type == ArchArgs::UP5K && x == 25 && y == 14) {
- // Mystery bits not set in this one tile
- } else {
- for (int lc_idx = 0; lc_idx < 8; lc_idx++) {
- static const std::vector<int> ip_dsp_lut_perm = {
- 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
- };
- for (int i = 0; i < 16; i++)
- set_config(ti, config.at(y).at(x), "LC_" + std::to_string(lc_idx), ((i % 8) >= 4),
- ip_dsp_lut_perm.at(i));
- if (tile == TILE_IPCON)
- set_config(ti, config.at(y).at(x),
- "Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
- else
- set_config(ti, config.at(y).at(x),
- "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
- std::to_string(lc_idx) + "_inmux02_5",
- true);
- }
- }
- }
- }
- }
-
- // Write config out
- for (int y = 0; y < ci.height; y++) {
- for (int x = 0; x < ci.width; x++) {
- TileType tile = tile_at(ctx, x, y);
- if (tile == TILE_NONE)
- continue;
- switch (tile) {
- case TILE_LOGIC:
- out << ".logic_tile";
- break;
- case TILE_IO:
- out << ".io_tile";
- break;
- case TILE_RAMB:
- out << ".ramb_tile";
- break;
- case TILE_RAMT:
- out << ".ramt_tile";
- break;
- case TILE_DSP0:
- out << ".dsp0_tile";
- break;
- case TILE_DSP1:
- out << ".dsp1_tile";
- break;
- case TILE_DSP2:
- out << ".dsp2_tile";
- break;
- case TILE_DSP3:
- out << ".dsp3_tile";
- break;
- case TILE_IPCON:
- out << ".ipcon_tile";
- break;
- default:
- NPNR_ASSERT(false);
- }
- out << " " << x << " " << y << std::endl;
- for (auto row : config.at(y).at(x)) {
- for (auto col : row) {
- if (col == 1)
- out << "1";
- else
- out << "0";
- }
- out << std::endl;
- }
- out << std::endl;
- }
- }
-
- // Write RAM init data
- for (auto &cell : ctx->cells) {
- if (cell.second->bel != BelId()) {
- if (cell.second->type == ctx->id("ICESTORM_RAM")) {
- const BelInfoPOD &beli = ci.bel_data[cell.second->bel.index];
- int x = beli.x, y = beli.y;
- out << ".ram_data " << x << " " << y << std::endl;
- for (int w = 0; w < 16; w++) {
- std::vector<bool> bits(256);
- std::string init =
- get_param_str_or_def(cell.second.get(), ctx->id(std::string("INIT_") + get_hexdigit(w)));
- NPNR_ASSERT(init != "");
- for (size_t i = 0; i < init.size(); i++) {
- bool val = (init.at((init.size() - 1) - i) == '1');
- bits.at(i) = val;
- }
- for (int i = bits.size() - 4; i >= 0; i -= 4) {
- int c = bits.at(i) + (bits.at(i + 1) << 1) + (bits.at(i + 2) << 2) + (bits.at(i + 3) << 3);
- out << char(std::tolower(get_hexdigit(c)));
- }
- out << std::endl;
- }
- out << std::endl;
- }
- }
- }
-
- // Write symbols
- // const bool write_symbols = 1;
- for (auto wire : ctx->getWires()) {
- IdString net = ctx->getBoundWireNet(wire);
- if (net != IdString())
- out << ".sym " << wire.index << " " << net.str(ctx) << std::endl;
- }
-}
-
-NEXTPNR_NAMESPACE_END
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+#include "bitstream.h"
+#include <cctype>
+#include <vector>
+#include "log.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+inline TileType tile_at(const Context *ctx, int x, int y)
+{
+ return ctx->chip_info->tile_grid[y * ctx->chip_info->width + x];
+}
+
+const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &name)
+{
+ for (int i = 0; i < tile.num_config_entries; i++) {
+ if (std::string(tile.entries[i].name.get()) == name) {
+ return tile.entries[i];
+ }
+ }
+ NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
+}
+
+std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
+{
+ for (int i = 0; i < bi.num_ierens; i++) {
+ auto ie = bi.ierens[i];
+ if (ie.iox == x && ie.ioy == y && ie.ioz == z) {
+ return std::make_tuple(ie.ierx, ie.iery, ie.ierz);
+ }
+ }
+ // No pin at this location
+ return std::make_tuple(-1, -1, -1);
+};
+
+void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, bool value,
+ int index = -1)
+{
+ const ConfigEntryPOD &cfg = find_config(ti, name);
+ if (index == -1) {
+ for (int i = 0; i < cfg.num_bits; i++) {
+ int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
+ if (cbit && !value)
+ log_error("clearing already set config bit %s", name.c_str());
+ cbit = value;
+ }
+ } else {
+ int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
+ cbit = value;
+ if (cbit && !value)
+ log_error("clearing already set config bit %s[%d]", name.c_str(), index);
+ }
+}
+
+int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0)
+{
+ auto found = cell->params.find(param);
+ if (found != cell->params.end())
+ return std::stoi(found->second);
+ else
+ return defval;
+}
+
+std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std::string defval = "")
+{
+ auto found = cell->params.find(param);
+ if (found != cell->params.end())
+ return found->second;
+ else
+ return defval;
+}
+
+char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
+
+static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
+{
+ for (int i = 0; i < chip->num_belcfgs; i++) {
+ if (chip->bel_config[i].bel_index == bel.index)
+ return chip->bel_config[i];
+ }
+ NPNR_ASSERT_FALSE("failed to find bel config");
+}
+
+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,
+ bool value)
+{
+ const ChipInfoPOD *chip = ctx->chip_info;
+
+ for (int i = 0; i < cell_cbits.num_entries; i++) {
+ const auto &cbit = cell_cbits.entries[i];
+ if (cbit.entry_name.get() == name) {
+ 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);
+ return;
+ }
+ }
+ NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
+}
+
+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 ChipInfoPOD *chip = ctx->chip_info;
+ const auto &bc = get_ec_config(chip, cell->bel);
+ for (auto p : params) {
+ std::vector<bool> value;
+ if (string_style) {
+ // Lattice's weird string style params, not sure if
+ // prefixes other than 0b should be supported, only 0b features in docs
+ std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
+ assert(raw.substr(0, 2) == "0b");
+ raw = raw.substr(2);
+ value.resize(raw.length());
+ for (int i = 0; i < (int)raw.length(); i++) {
+ if (raw[i] == '1') {
+ value[(raw.length() - 1) - i] = 1;
+ } else {
+ assert(raw[i] == '0');
+ value[(raw.length() - 1) - i] = 0;
+ }
+ }
+ } else {
+ int ival = get_param_or_def(cell, ctx->id(p.first), 0);
+
+ for (int i = 0; i < p.second; i++)
+ value.push_back((ival >> i) & 0x1);
+ }
+
+ value.resize(p.second);
+ if (p.second == 1) {
+ set_ec_cbit(config, ctx, bc, p.first, value.at(0));
+ } else {
+ for (int i = 0; i < p.second; i++) {
+ set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i));
+ }
+ }
+ }
+}
+
+std::string tagTileType(TileType &tile)
+{
+ if (tile == TILE_NONE)
+ return "";
+ switch (tile) {
+ case TILE_LOGIC:
+ return ".logic_tile";
+ break;
+ case TILE_IO:
+ return ".io_tile";
+ break;
+ case TILE_RAMB:
+ return ".ramb_tile";
+ break;
+ case TILE_RAMT:
+ return ".ramt_tile";
+ break;
+ case TILE_DSP0:
+ return ".dsp0_tile";
+ break;
+ case TILE_DSP1:
+ return ".dsp1_tile";
+ break;
+ case TILE_DSP2:
+ return ".dsp2_tile";
+ break;
+ case TILE_DSP3:
+ return ".dsp3_tile";
+ break;
+ case TILE_IPCON:
+ return ".ipcon_tile";
+ break;
+ default:
+ NPNR_ASSERT(false);
+ }
+}
+void write_asc(const Context *ctx, std::ostream &out)
+{
+ // [y][x][row][col]
+ const ChipInfoPOD &ci = *ctx->chip_info;
+ const BitstreamInfoPOD &bi = *ci.bits_info;
+ chipconfig_t config;
+ config.resize(ci.height);
+ for (int y = 0; y < ci.height; y++) {
+ config.at(y).resize(ci.width);
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ int rows = bi.tiles_nonrouting[tile].rows;
+ int cols = bi.tiles_nonrouting[tile].cols;
+ config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
+ }
+ }
+ out << ".comment from next-pnr" << std::endl;
+
+ switch (ctx->args.type) {
+ case ArchArgs::LP384:
+ out << ".device 384" << std::endl;
+ break;
+ case ArchArgs::HX1K:
+ case ArchArgs::LP1K:
+ out << ".device 1k" << std::endl;
+ break;
+ case ArchArgs::HX8K:
+ case ArchArgs::LP8K:
+ out << ".device 8k" << std::endl;
+ break;
+ case ArchArgs::UP5K:
+ out << ".device 5k" << std::endl;
+ break;
+ default:
+ NPNR_ASSERT_FALSE("unsupported device type\n");
+ }
+ // Set pips
+ for (auto pip : ctx->getPips()) {
+ if (ctx->pip_to_net[pip.index] != IdString()) {
+ const PipInfoPOD &pi = ci.pip_data[pip.index];
+ const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
+ for (int i = 0; i < swi.num_bits; i++) {
+ bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
+ int8_t &cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
+ if (bool(cbit) != 0)
+ NPNR_ASSERT(false);
+ cbit = val;
+ }
+ }
+ }
+ // Set logic cell config
+ for (auto &cell : ctx->cells) {
+ BelId bel = cell.second.get()->bel;
+ if (bel == BelId()) {
+ std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
+ continue;
+ }
+ if (cell.second->type == ctx->id("ICESTORM_LC")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
+ unsigned lut_init = get_param_or_def(cell.second.get(), ctx->id("LUT_INIT"));
+ bool neg_clk = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK"));
+ bool dff_enable = get_param_or_def(cell.second.get(), ctx->id("DFF_ENABLE"));
+ bool async_sr = get_param_or_def(cell.second.get(), ctx->id("ASYNC_SR"));
+ bool set_noreset = get_param_or_def(cell.second.get(), ctx->id("SET_NORESET"));
+ bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
+ std::vector<bool> lc(20, false);
+ // From arachne-pnr
+ static std::vector<int> lut_perm = {
+ 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
+ };
+ for (int i = 0; i < 16; i++) {
+ if ((lut_init >> i) & 0x1)
+ lc.at(lut_perm.at(i)) = true;
+ }
+ lc.at(8) = carry_enable;
+ lc.at(9) = dff_enable;
+ lc.at(18) = set_noreset;
+ lc.at(19) = async_sr;
+
+ for (int i = 0; i < 20; i++)
+ set_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), lc.at(i), i);
+ if (dff_enable)
+ set_config(ti, config.at(y).at(x), "NegClk", neg_clk);
+
+ bool carry_const = get_param_or_def(cell.second.get(), ctx->id("CIN_CONST"));
+ bool carry_set = get_param_or_def(cell.second.get(), ctx->id("CIN_SET"));
+ if (carry_const) {
+ if (!ctx->force)
+ NPNR_ASSERT(z == 0);
+ set_config(ti, config.at(y).at(x), "CarryInSet", carry_set);
+ }
+ } else if (cell.second->type == ctx->id("SB_IO")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
+ 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 pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
+ for (int i = 0; i < 6; i++) {
+ bool val = (pin_type >> i) & 0x01;
+ set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
+ }
+ set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
+ auto ieren = get_ieren(bi, x, y, z);
+ int iex, iey, iez;
+ std::tie(iex, iey, iez) = ieren;
+ NPNR_ASSERT(iez != -1);
+
+ bool input_en = false;
+ if ((ctx->wire_to_net[ctx->getWireBelPin(bel, PIN_D_IN_0).index] != IdString()) ||
+ (ctx->wire_to_net[ctx->getWireBelPin(bel, PIN_D_IN_1).index] != IdString())) {
+ input_en = true;
+ }
+
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
+ } else {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), input_en);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
+ }
+
+ if (ctx->args.type == ArchArgs::UP5K) {
+ if (iez == 0) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
+ } else if (iez == 1) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
+ }
+ }
+ } else if (cell.second->type == ctx->id("SB_GB")) {
+ // no cell config bits
+ } else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y;
+ const TileInfoPOD &ti_ramt = bi.tiles_nonrouting[TILE_RAMT];
+ const TileInfoPOD &ti_ramb = bi.tiles_nonrouting[TILE_RAMB];
+ if (!(ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
+ set_config(ti_ramb, config.at(y).at(x), "RamConfig.PowerUp", true);
+ }
+ bool negclk_r = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_R"));
+ bool negclk_w = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_W"));
+ int write_mode = get_param_or_def(cell.second.get(), ctx->id("WRITE_MODE"));
+ int read_mode = get_param_or_def(cell.second.get(), ctx->id("READ_MODE"));
+ set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w);
+ set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r);
+
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_0", write_mode & 0x1);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
+ } else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
+ // No config needed
+ } else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ NPNR_ASSERT(ctx->args.type == ArchArgs::UP5K);
+ if (x == 0 && y == 0) {
+ const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
+ if (z == 1) {
+ set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_0", true);
+ } else if (z == 2) {
+ set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_1", true);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ } else if (x == 25 && y == 0) {
+ const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
+ if (z == 3) {
+ set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_0", true);
+ } else if (z == 4) {
+ set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_1", true);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ }
+ } else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
+ const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
+ {"A_REG", 1},
+ {"B_REG", 1},
+ {"D_REG", 1},
+ {"TOP_8x8_MULT_REG", 1},
+ {"BOT_8x8_MULT_REG", 1},
+ {"PIPELINE_16x16_MULT_REG1", 1},
+ {"PIPELINE_16x16_MULT_REG2", 1},
+ {"TOPOUTPUT_SELECT", 2},
+ {"TOPADDSUB_LOWERINPUT", 2},
+ {"TOPADDSUB_UPPERINPUT", 1},
+ {"TOPADDSUB_CARRYSELECT", 2},
+ {"BOTOUTPUT_SELECT", 2},
+ {"BOTADDSUB_LOWERINPUT", 2},
+ {"BOTADDSUB_UPPERINPUT", 1},
+ {"BOTADDSUB_CARRYSELECT", 2},
+ {"MODE_8x8", 1},
+ {"A_SIGNED", 1},
+ {"B_SIGNED", 1}};
+ configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ }
+ // Set config bits in unused IO and RAM
+ for (auto bel : ctx->getBels()) {
+ if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_SB_IO) {
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ auto ieren = get_ieren(bi, x, y, z);
+ int iex, iey, iez;
+ std::tie(iex, iey, iez) = ieren;
+ if (iez != -1) {
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
+ 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) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y;
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_RAMB];
+ if ((ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
+ set_config(ti, config.at(y).at(x), "RamConfig.PowerUp", true);
+ }
+ }
+ }
+
+ // Set other config bits
+ for (int y = 0; y < ci.height; y++) {
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ const TileInfoPOD &ti = bi.tiles_nonrouting[tile];
+
+ // set all ColBufCtrl bits (FIXME)
+ bool setColBufCtrl = true;
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ if (tile == TILE_RAMB || tile == TILE_RAMT) {
+ setColBufCtrl = (y == 3 || y == 5 || y == 11 || y == 13);
+ } else {
+ setColBufCtrl = (y == 4 || y == 5 || y == 12 || y == 13);
+ }
+ } else if (ctx->args.type == ArchArgs::LP8K || ctx->args.type == ArchArgs::HX8K) {
+ setColBufCtrl = (y == 8 || y == 9 || y == 24 || y == 25);
+ } else if (ctx->args.type == ArchArgs::UP5K) {
+ setColBufCtrl = (y == 4 || y == 5 || y == 14 || y == 15 || y == 26 || y == 27);
+ }
+ if (setColBufCtrl) {
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_0", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_1", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_2", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_3", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_4", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_5", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_6", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_7", true);
+ }
+
+ // Weird UltraPlus bits
+ if (tile == TILE_DSP0 || tile == TILE_DSP1 || tile == TILE_DSP2 || tile == TILE_DSP3 ||
+ tile == TILE_IPCON) {
+ if (ctx->args.type == ArchArgs::UP5K && x == 25 && y == 14) {
+ // Mystery bits not set in this one tile
+ } else {
+ for (int lc_idx = 0; lc_idx < 8; lc_idx++) {
+ static const std::vector<int> ip_dsp_lut_perm = {
+ 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
+ };
+ for (int i = 0; i < 16; i++)
+ set_config(ti, config.at(y).at(x), "LC_" + std::to_string(lc_idx), ((i % 8) >= 4),
+ ip_dsp_lut_perm.at(i));
+ if (tile == TILE_IPCON)
+ set_config(ti, config.at(y).at(x),
+ "Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
+ else
+ set_config(ti, config.at(y).at(x),
+ "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
+ std::to_string(lc_idx) + "_inmux02_5",
+ true);
+ }
+ }
+ }
+ }
+ }
+
+ // Write config out
+ for (int y = 0; y < ci.height; y++) {
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ if (tile == TILE_NONE)
+ continue;
+ out << tagTileType(tile);
+ out << " " << x << " " << y << std::endl;
+ for (auto row : config.at(y).at(x)) {
+ for (auto col : row) {
+ if (col == 1)
+ out << "1";
+ else
+ out << "0";
+ }
+ out << std::endl;
+ }
+ out << std::endl;
+ }
+ }
+
+ // Write RAM init data
+ for (auto &cell : ctx->cells) {
+ if (cell.second->bel != BelId()) {
+ if (cell.second->type == ctx->id("ICESTORM_RAM")) {
+ const BelInfoPOD &beli = ci.bel_data[cell.second->bel.index];
+ int x = beli.x, y = beli.y;
+ out << ".ram_data " << x << " " << y << std::endl;
+ for (int w = 0; w < 16; w++) {
+ std::vector<bool> bits(256);
+ std::string init =
+ get_param_str_or_def(cell.second.get(), ctx->id(std::string("INIT_") + get_hexdigit(w)));
+ NPNR_ASSERT(init != "");
+ for (size_t i = 0; i < init.size(); i++) {
+ bool val = (init.at((init.size() - 1) - i) == '1');
+ bits.at(i) = val;
+ }
+ for (int i = bits.size() - 4; i >= 0; i -= 4) {
+ int c = bits.at(i) + (bits.at(i + 1) << 1) + (bits.at(i + 2) << 2) + (bits.at(i + 3) << 3);
+ out << char(std::tolower(get_hexdigit(c)));
+ }
+ out << std::endl;
+ }
+ out << std::endl;
+ }
+ }
+ }
+
+ // Write symbols
+ // const bool write_symbols = 1;
+ for (auto wire : ctx->getWires()) {
+ IdString net = ctx->getBoundWireNet(wire);
+ if (net != IdString())
+ out << ".sym " << wire.index << " " << net.str(ctx) << std::endl;
+ }
+}
+
+void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
+{
+ constexpr size_t line_buf_size = 65536;
+ char buffer[line_buf_size];
+ int tile_x = -1, tile_y = -1, line_nr = -1;
+
+ while (1) {
+ in.getline(buffer, line_buf_size);
+ if (buffer[0] == '.') {
+ line_nr = -1;
+ const char *tok = strtok(buffer, " \t\r\n");
+
+ if (!strcmp(tok, ".device")) {
+ std::string config_device = strtok(nullptr, " \t\r\n");
+ std::string expected;
+ switch (ctx->args.type) {
+ case ArchArgs::LP384:
+ expected = "384";
+ break;
+ case ArchArgs::HX1K:
+ case ArchArgs::LP1K:
+ expected = "1k";
+ break;
+ case ArchArgs::HX8K:
+ case ArchArgs::LP8K:
+ expected = "8k";
+ break;
+ case ArchArgs::UP5K:
+ expected = "5k";
+ break;
+ default:
+ log_error("unsupported device type\n");
+ }
+ if (expected != config_device)
+ log_error("device type does not match\n");
+ } else if (!strcmp(tok, ".io_tile") || !strcmp(tok, ".logic_tile") || !strcmp(tok, ".ramb_tile") ||
+ !strcmp(tok, ".ramt_tile") || !strcmp(tok, ".ipcon_tile") || !strcmp(tok, ".dsp0_tile") ||
+ !strcmp(tok, ".dsp1_tile") || !strcmp(tok, ".dsp2_tile") || !strcmp(tok, ".dsp3_tile")) {
+ line_nr = 0;
+ tile_x = atoi(strtok(nullptr, " \t\r\n"));
+ tile_y = atoi(strtok(nullptr, " \t\r\n"));
+
+ TileType tile = tile_at(ctx, tile_x, tile_y);
+ if (tok != tagTileType(tile))
+ log_error("Wrong tile type for specified position\n");
+
+ } else if (!strcmp(tok, ".extra_bit")) {
+ /*
+ int b = atoi(strtok(nullptr, " \t\r\n"));
+ int x = atoi(strtok(nullptr, " \t\r\n"));
+ int y = atoi(strtok(nullptr, " \t\r\n"));
+ std::tuple<int, int, int> key(b, x, y);
+ extra_bits.insert(key);
+ */
+ } else if (!strcmp(tok, ".sym")) {
+ int wireIndex = atoi(strtok(nullptr, " \t\r\n"));
+ const char *name = strtok(nullptr, " \t\r\n");
+
+ std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
+ IdString netName = ctx->id(name);
+ created_net->name = netName;
+ ctx->nets[netName] = std::move(created_net);
+
+ WireId wire;
+ wire.index = wireIndex;
+ ctx->bindWire(wire, netName, STRENGTH_WEAK);
+ }
+ } else if (line_nr >= 0 && strlen(buffer) > 0) {
+ if (line_nr > int(config.at(tile_y).at(tile_x).size() - 1))
+ log_error("Invalid data in input asc file");
+ for (int i = 0; buffer[i] == '0' || buffer[i] == '1'; i++)
+ config.at(tile_y).at(tile_x).at(line_nr).at(i) = (buffer[i] == '1') ? 1 : 0;
+ line_nr++;
+ }
+ if (in.eof())
+ break;
+ }
+}
+
+bool read_asc(Context *ctx, std::istream &in)
+{
+ try {
+ // [y][x][row][col]
+ const ChipInfoPOD &ci = *ctx->chip_info;
+ const BitstreamInfoPOD &bi = *ci.bits_info;
+ chipconfig_t config;
+ config.resize(ci.height);
+ for (int y = 0; y < ci.height; y++) {
+ config.at(y).resize(ci.width);
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ int rows = bi.tiles_nonrouting[tile].rows;
+ int cols = bi.tiles_nonrouting[tile].cols;
+ config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
+ }
+ }
+ read_config(ctx, in, config);
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
+NEXTPNR_NAMESPACE_END
diff --git a/ice40/bitstream.h b/ice40/bitstream.h
index 2b6cda1d..41a2ae68 100644
--- a/ice40/bitstream.h
+++ b/ice40/bitstream.h
@@ -27,6 +27,7 @@
NEXTPNR_NAMESPACE_BEGIN
void write_asc(const Context *ctx, std::ostream &out);
+bool read_asc(Context *ctx, std::istream &in);
NEXTPNR_NAMESPACE_END
diff --git a/ice40/main.cc b/ice40/main.cc
index e77bdd34..652196a1 100644
--- a/ice40/main.cc
+++ b/ice40/main.cc
@@ -66,6 +66,12 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)
}
}
+void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2)
+{
+ if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted())
+ log_error((std::string("Conflicting options '") + opt1 + "' and '" + opt2 + "'.").c_str());
+}
+
int main(int argc, char *argv[])
{
try {
@@ -95,6 +101,7 @@ int main(int argc, char *argv[])
options.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
options.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
options.add_options()("asc", po::value<std::string>(), "asc bitstream file to write");
+ options.add_options()("read", po::value<std::string>(), "asc bitstream file to read");
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
options.add_options()("version,V", "show version");
options.add_options()("tmfuzz", "run path delay estimate fuzzer");
@@ -121,13 +128,17 @@ int main(int argc, char *argv[])
po::store(parsed, vm);
po::notify(vm);
- }
-
- catch (std::exception &e) {
+ } catch (std::exception &e) {
std::cout << e.what() << "\n";
return 1;
}
+ conflicting_options(vm, "read", "json");
+#ifndef ICE40_HX1K_ONLY
+ if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") +
+ vm.count("up5k")) > 1)
+ log_error("Only one device type can be set\n");
+#endif
if (vm.count("help") || argc == 1) {
help:
std::cout << boost::filesystem::basename(argv[0])
@@ -353,6 +364,13 @@ int main(int argc, char *argv[])
if (vm.count("no-tmdriv"))
ctx->timing_driven = false;
+ if (vm.count("read")) {
+ std::string filename = vm["read"].as<std::string>();
+ std::ifstream f(filename);
+ if (!read_asc(ctx.get(), f))
+ log_error("Loading ASC failed.\n");
+ }
+
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv);