diff options
-rw-r--r-- | common/nextpnr.h | 24 | ||||
-rw-r--r-- | common/router1.cc | 19 | ||||
-rw-r--r-- | common/timing.cc | 5 | ||||
-rw-r--r-- | generic/arch.cc | 28 | ||||
-rw-r--r-- | generic/arch.h | 13 | ||||
-rw-r--r-- | gui/designwidget.cc | 14 | ||||
-rw-r--r-- | ice40/arch.cc | 53 | ||||
-rw-r--r-- | ice40/arch.h | 22 | ||||
-rw-r--r-- | ice40/arch_place.cc | 14 | ||||
-rw-r--r-- | ice40/archdefs.h | 3 | ||||
-rw-r--r-- | ice40/bitstream.cc | 1168 | ||||
-rw-r--r-- | ice40/bitstream.h | 1 | ||||
-rw-r--r-- | ice40/main.cc | 24 |
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>> ¶ms, 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>> ¶ms, 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); |