diff options
Diffstat (limited to 'ice40/bitstream.cc')
-rw-r--r-- | ice40/bitstream.cc | 2086 |
1 files changed, 1043 insertions, 1043 deletions
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 4efb1091..4e54df1d 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -1,1043 +1,1043 @@ -/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
- * Copyright (C) 2018 David Shah <david@symbioticeda.com>
- * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * 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 "cells.h"
-#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);
-};
-
-bool get_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name,
- int index = -1)
-{
- const ConfigEntryPOD &cfg = find_config(ti, name);
- if (index == -1) {
- for (int i = 0; i < cfg.num_bits; i++) {
- return tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
- }
- } else {
- return tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
- }
- return false;
-}
-
-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\n", 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]\n", name.c_str(), index);
- }
-}
-
-// Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled.
-// On {HX,LP}1K devices these bits are active low, so we need to invert them.
-void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg,
- const std::string &name, bool value)
-{
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- set_config(ti, tile_cfg, name, !value);
- } else {
- set_config(ti, tile_cfg, name, value);
- }
-}
-
-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, std::string prefix)
-{
- 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), prefix + 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, std::string prefix)
-{
- 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), prefix);
- } else {
- for (int i = 0; i < p.second; i++) {
- set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix);
- }
- }
- }
-}
-
-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);
- }
-}
-
-static BelPin get_one_bel_pin(const Context *ctx, WireId wire)
-{
- auto pins = ctx->getWireBelPins(wire);
- NPNR_ASSERT(pins.begin() != pins.end());
- return *pins.begin();
-}
-
-// Permute LUT init value given map (LUT input -> ext input)
-unsigned permute_lut(unsigned orig_init, const std::unordered_map<int, int> &input_permute)
-{
- unsigned new_init = 0;
-
- for (int i = 0; i < 16; i++) {
- int permute_address = 0;
- for (int j = 0; j < 4; j++) {
- if ((i >> j) & 0x1)
- permute_address |= (1 << input_permute.at(j));
- }
- if ((orig_init >> i) & 0x1) {
- new_init |= (1 << permute_address);
- }
- }
-
- return new_init;
-}
-
-void write_asc(const Context *ctx, std::ostream &out)
-{
-
- static const std::vector<int> lut_perm = {
- 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
- };
-
- // [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] != nullptr) {
- const PipInfoPOD &pi = ci.pip_data[pip.index];
- const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
- int sw_bel_idx = swi.bel;
- if (sw_bel_idx >= 0) {
- const BelInfoPOD &beli = ci.bel_data[sw_bel_idx];
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
- BelId sw_bel;
- sw_bel.index = sw_bel_idx;
- NPNR_ASSERT(ctx->getBelType(sw_bel) == id_ICESTORM_LC);
-
- if (ci.wire_data[ctx->getPipDstWire(pip).index].type == WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT)
- continue; // Permutation pips
- BelPin output = get_one_bel_pin(ctx, ctx->getPipDstWire(pip));
- NPNR_ASSERT(output.bel == sw_bel && output.pin == id_O);
- unsigned lut_init;
-
- WireId permWire;
- for (auto permPip : ctx->getPipsUphill(ctx->getPipSrcWire(pip))) {
- if (ctx->getBoundPipNet(permPip) != nullptr) {
- permWire = ctx->getPipSrcWire(permPip);
- }
- }
- NPNR_ASSERT(permWire != WireId());
- std::string dName = ci.wire_data[permWire.index].name.get();
-
- switch (dName.back()) {
- case '0':
- lut_init = 2;
- break;
- case '1':
- lut_init = 4;
- break;
- case '2':
- lut_init = 16;
- break;
- case '3':
- lut_init = 256;
- break;
- default:
- NPNR_ASSERT_FALSE("bad feedthru LUT input");
- }
- std::vector<bool> lc(20, false);
- for (int i = 0; i < 16; i++) {
- if ((lut_init >> i) & 0x1)
- lc.at(lut_perm.at(i)) = true;
- }
-
- for (int i = 0; i < 20; i++)
- set_config(ti, config.at(beli.y).at(beli.x), "LC_" + std::to_string(beli.z), lc.at(i), i);
- } else {
- 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;
- }
- }
- }
- }
-
- std::unordered_set<Loc> sb_io_used_by_pll;
- std::unordered_set<Loc> sb_io_used_by_io;
-
- // 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);
-
- // Discover permutation
- std::unordered_map<int, int> input_perm;
- std::set<int> unused;
- for (int i = 0; i < 4; i++)
- unused.insert(i);
- for (int i = 0; i < 4; i++) {
- WireId lut_wire = ctx->getBelPinWire(bel, IdString(ID_I0 + i));
- for (auto pip : ctx->getPipsUphill(lut_wire)) {
- if (ctx->getBoundPipNet(pip) != nullptr) {
- std::string name = ci.wire_data[ctx->getPipSrcWire(pip).index].name.get();
- switch (name.back()) {
- case '0':
- input_perm[i] = 0;
- unused.erase(0);
- break;
- case '1':
- input_perm[i] = 1;
- unused.erase(1);
- break;
- case '2':
- input_perm[i] = 2;
- unused.erase(2);
- break;
- case '3':
- input_perm[i] = 3;
- unused.erase(3);
- break;
- default:
- NPNR_ASSERT_FALSE("failed to determine LUT permutation");
- }
- break;
- }
- }
- }
- for (int i = 0; i < 4; i++) {
- if (!input_perm.count(i)) {
- NPNR_ASSERT(!unused.empty());
- input_perm[i] = *(unused.begin());
- unused.erase(input_perm[i]);
- }
- }
- lut_init = permute_lut(lut_init, input_perm);
- 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;
- sb_io_used_by_io.insert(Loc(x, y, 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"));
- bool lvds = get_param_str_or_def(cell.second.get(), ctx->id("IO_STANDARD")) == "SB_LVDS_INPUT";
-
- 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;
- if (lvds) {
- input_en = false;
- pullup = false;
- } else {
- if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
- (ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
- input_en = true;
- } else {
- input_en = false;
- }
- }
-
- 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);
- }
- }
-
- if (lvds) {
- NPNR_ASSERT(z == 0);
- set_config(ti, config.at(y).at(x), "IoCtrl.LVDS", true);
- // Set comp IO config
- auto comp_ieren = get_ieren(bi, x, y, 1);
- int ciex, ciey, ciez;
- std::tie(ciex, ciey, ciez) = comp_ieren;
-
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), !input_en);
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
- } else {
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), input_en);
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
- }
-
- if (ctx->args.type == ArchArgs::UP5K) {
- if (ciez == 0) {
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_39", !pullup);
- } else if (iez == 1) {
- set_config(ti, config.at(ciey).at(ciex), "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, std::string("IpConfig."));
- } else if (cell.second->type == ctx->id("ICESTORM_HFOSC")) {
- const std::vector<std::pair<std::string, int>> hfosc_params = {{"CLKHF_DIV", 2}, {"TRIM_EN", 1}};
- configure_extra_cell(config, ctx, cell.second.get(), hfosc_params, true, std::string("IpConfig."));
-
- } else if (cell.second->type == ctx->id("ICESTORM_PLL")) {
- const std::vector<std::pair<std::string, int>> pll_params = {{"DELAY_ADJMODE_FB", 1},
- {"DELAY_ADJMODE_REL", 1},
- {"DIVF", 7},
- {"DIVQ", 3},
- {"DIVR", 4},
- {"FDA_FEEDBACK", 4},
- {"FDA_RELATIVE", 4},
- {"FEEDBACK_PATH", 3},
- {"FILTER_RANGE", 3},
- {"PLLOUT_SELECT_A", 2},
- {"PLLOUT_SELECT_B", 2},
- {"PLLTYPE", 3},
- {"SHIFTREG_DIV_MODE", 1},
- {"TEST_MODE", 1}};
- configure_extra_cell(config, ctx, cell.second.get(), pll_params, false, std::string("PLL."));
-
- // Configure the SB_IOs that the clock outputs are going through.
- for (auto &port : cell.second->ports) {
- // If this port is not a PLLOUT port, ignore it.
- if (port.second.name != ctx->id("PLLOUT_A") && port.second.name != ctx->id("PLLOUT_B"))
- continue;
-
- // If the output is not driving any net, ignore it.
- if (port.second.net == nullptr)
- continue;
-
- // Get IO Bel that this PLL port goes through by finding sibling
- // Bel driving the same wire via PIN_D_IN_0.
- auto wire = ctx->getBelPinWire(cell.second->bel, port.second.name);
- BelId io_bel;
- for (auto pin : ctx->getWireBelPins(wire)) {
- if (pin.pin == id_D_IN_0) {
- io_bel = pin.bel;
- break;
- }
- }
- NPNR_ASSERT(io_bel.index != -1);
- auto io_bel_loc = ctx->getBelLocation(io_bel);
-
- // Check that this SB_IO is either unused or just used as an output.
- if (sb_io_used_by_io.count(io_bel_loc)) {
- log_error("SB_IO '%s' already in use, cannot route PLL through\n", ctx->getBelName(bel).c_str(ctx));
- }
- sb_io_used_by_pll.insert(io_bel_loc);
-
- // Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html)
- auto ieren = get_ieren(bi, io_bel_loc.x, io_bel_loc.y, io_bel_loc.z);
- int iex, iey, iez;
- std::tie(iex, iey, iez) = ieren;
- NPNR_ASSERT(iez != -1);
-
- // Write config.
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
- // Enable input buffer and disable pull-up resistor in block
- // (this is used by the PLL).
- set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
- set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
- // PINTYPE[0] passes the PLL through to the fabric.
- set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x),
- "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true);
- }
-
- } else {
- NPNR_ASSERT(false);
- }
- }
- // Set config bits in unused IO and RAM
- for (auto bel : ctx->getBels()) {
- if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_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;
- if (sb_io_used_by_pll.count(Loc(x, y, z))) {
- continue;
- }
-
- auto ieren = get_ieren(bi, x, y, z);
- int iex, iey, iez;
- std::tie(iex, iey, iez) = ieren;
- if (iez != -1) {
- // IO is not actually unused if part of an LVDS pair
- if (z == 1) {
- BelId lvds0 = ctx->getBelByLocation(Loc{x, y, 0});
- const CellInfo *lvds0cell = ctx->getBoundBelCell(lvds0);
- if (lvds0cell != nullptr && lvds0cell->ioInfo.lvds)
- continue;
- }
- set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
- set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
- }
- } else if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_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);
- } else if (ctx->args.type == ArchArgs::LP384) {
- setColBufCtrl = false;
- }
- 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)));
- 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()) {
- NetInfo *net = ctx->getBoundWireNet(wire);
- if (net != nullptr)
- out << ".sym " << wire.index << " " << net->name.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");
-
- IdString netName = ctx->id(name);
-
- if (ctx->nets.find(netName) == ctx->nets.end()) {
- std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
- created_net->name = netName;
- ctx->nets[netName] = std::move(created_net);
- }
-
- WireId wire;
- wire.index = wireIndex;
- ctx->bindWire(wire, ctx->nets.at(netName).get(), 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);
-
- // Set pips
- for (auto pip : ctx->getPips()) {
- const PipInfoPOD &pi = ci.pip_data[pip.index];
- const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
- bool isUsed = true;
- 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);
- isUsed &= !(bool(cbit) ^ val);
- }
- if (isUsed) {
- NetInfo *net = ctx->wire_to_net[pi.dst];
- if (net != nullptr) {
- WireId wire;
- wire.index = pi.dst;
- ctx->unbindWire(wire);
- ctx->bindPip(pip, net, STRENGTH_WEAK);
- }
- }
- }
- for (auto bel : ctx->getBels()) {
- if (ctx->getBelType(bel) == id_ICESTORM_LC) {
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- std::vector<bool> lc(20, false);
- bool isUsed = false;
- for (int i = 0; i < 20; i++) {
- lc.at(i) = get_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), i);
- isUsed |= lc.at(i);
- }
- bool neg_clk = get_config(ti, config.at(y).at(x), "NegClk");
- isUsed |= neg_clk;
- bool carry_set = get_config(ti, config.at(y).at(x), "CarryInSet");
- isUsed |= carry_set;
-
- if (isUsed) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets and assign values of properties
- }
- }
- if (ctx->getBelType(bel) == id_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;
- bool isUsed = false;
- for (int i = 0; i < 6; i++) {
- isUsed |= get_config(ti, config.at(y).at(x),
- "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i));
- }
- bool neg_trigger = get_config(ti, config.at(y).at(x), "NegClk");
- isUsed |= neg_trigger;
-
- if (isUsed) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets and assign values of properties
- }
- }
- }
- // Add cells that are without change in initial state of configuration
- for (auto &net : ctx->nets) {
- for (auto w : net.second->wires) {
- if (w.second.pip == PipId()) {
- WireId wire = w.first;
- for (auto belpin : ctx->getWireBelPins(wire)) {
-
- if (ctx->checkBelAvail(belpin.bel)) {
- if (ctx->getBelType(belpin.bel) == id_ICESTORM_LC) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- if (ctx->getBelType(belpin.bel) == id_SB_IO) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- if (ctx->getBelType(belpin.bel) == id_SB_GB) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_GB"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- if (ctx->getBelType(belpin.bel) == id_SB_WARMBOOT) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_WARMBOOT"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- if (ctx->getBelType(belpin.bel) == id_ICESTORM_LFOSC) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- }
- }
- }
- }
- }
- for (auto &cell : ctx->cells) {
- if (cell.second->bel != BelId()) {
- for (auto &port : cell.second->ports) {
- IdString pin = port.first;
- WireId wire = ctx->getBelPinWire(cell.second->bel, pin);
- if (wire != WireId()) {
- NetInfo *net = ctx->getBoundWireNet(wire);
- if (net != nullptr) {
- port.second.net = net;
- PortRef ref;
- ref.cell = cell.second.get();
- ref.port = port.second.name;
-
- if (port.second.type == PORT_OUT)
- net->driver = ref;
- else
- net->users.push_back(ref);
- }
- }
- }
- }
- }
- return true;
- } catch (log_execution_error_exception) {
- return false;
- }
-}
-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> + * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +#include "bitstream.h" +#include <cctype> +#include <vector> +#include "cells.h" +#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); +}; + +bool get_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, + int index = -1) +{ + const ConfigEntryPOD &cfg = find_config(ti, name); + if (index == -1) { + for (int i = 0; i < cfg.num_bits; i++) { + return tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col); + } + } else { + return tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col); + } + return false; +} + +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\n", 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]\n", name.c_str(), index); + } +} + +// Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled. +// On {HX,LP}1K devices these bits are active low, so we need to invert them. +void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, + const std::string &name, bool value) +{ + if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) { + set_config(ti, tile_cfg, name, !value); + } else { + set_config(ti, tile_cfg, name, value); + } +} + +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, std::string prefix) +{ + 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), prefix + 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, std::string prefix) +{ + 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), prefix); + } else { + for (int i = 0; i < p.second; i++) { + set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix); + } + } + } +} + +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); + } +} + +static BelPin get_one_bel_pin(const Context *ctx, WireId wire) +{ + auto pins = ctx->getWireBelPins(wire); + NPNR_ASSERT(pins.begin() != pins.end()); + return *pins.begin(); +} + +// Permute LUT init value given map (LUT input -> ext input) +unsigned permute_lut(unsigned orig_init, const std::unordered_map<int, int> &input_permute) +{ + unsigned new_init = 0; + + for (int i = 0; i < 16; i++) { + int permute_address = 0; + for (int j = 0; j < 4; j++) { + if ((i >> j) & 0x1) + permute_address |= (1 << input_permute.at(j)); + } + if ((orig_init >> i) & 0x1) { + new_init |= (1 << permute_address); + } + } + + return new_init; +} + +void write_asc(const Context *ctx, std::ostream &out) +{ + + static const std::vector<int> lut_perm = { + 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0, + }; + + // [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] != nullptr) { + const PipInfoPOD &pi = ci.pip_data[pip.index]; + const SwitchInfoPOD &swi = bi.switches[pi.switch_index]; + int sw_bel_idx = swi.bel; + if (sw_bel_idx >= 0) { + const BelInfoPOD &beli = ci.bel_data[sw_bel_idx]; + const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC]; + BelId sw_bel; + sw_bel.index = sw_bel_idx; + NPNR_ASSERT(ctx->getBelType(sw_bel) == id_ICESTORM_LC); + + if (ci.wire_data[ctx->getPipDstWire(pip).index].type == WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT) + continue; // Permutation pips + BelPin output = get_one_bel_pin(ctx, ctx->getPipDstWire(pip)); + NPNR_ASSERT(output.bel == sw_bel && output.pin == id_O); + unsigned lut_init; + + WireId permWire; + for (auto permPip : ctx->getPipsUphill(ctx->getPipSrcWire(pip))) { + if (ctx->getBoundPipNet(permPip) != nullptr) { + permWire = ctx->getPipSrcWire(permPip); + } + } + NPNR_ASSERT(permWire != WireId()); + std::string dName = ci.wire_data[permWire.index].name.get(); + + switch (dName.back()) { + case '0': + lut_init = 2; + break; + case '1': + lut_init = 4; + break; + case '2': + lut_init = 16; + break; + case '3': + lut_init = 256; + break; + default: + NPNR_ASSERT_FALSE("bad feedthru LUT input"); + } + std::vector<bool> lc(20, false); + for (int i = 0; i < 16; i++) { + if ((lut_init >> i) & 0x1) + lc.at(lut_perm.at(i)) = true; + } + + for (int i = 0; i < 20; i++) + set_config(ti, config.at(beli.y).at(beli.x), "LC_" + std::to_string(beli.z), lc.at(i), i); + } else { + 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; + } + } + } + } + + std::unordered_set<Loc> sb_io_used_by_pll; + std::unordered_set<Loc> sb_io_used_by_io; + + // 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); + + // Discover permutation + std::unordered_map<int, int> input_perm; + std::set<int> unused; + for (int i = 0; i < 4; i++) + unused.insert(i); + for (int i = 0; i < 4; i++) { + WireId lut_wire = ctx->getBelPinWire(bel, IdString(ID_I0 + i)); + for (auto pip : ctx->getPipsUphill(lut_wire)) { + if (ctx->getBoundPipNet(pip) != nullptr) { + std::string name = ci.wire_data[ctx->getPipSrcWire(pip).index].name.get(); + switch (name.back()) { + case '0': + input_perm[i] = 0; + unused.erase(0); + break; + case '1': + input_perm[i] = 1; + unused.erase(1); + break; + case '2': + input_perm[i] = 2; + unused.erase(2); + break; + case '3': + input_perm[i] = 3; + unused.erase(3); + break; + default: + NPNR_ASSERT_FALSE("failed to determine LUT permutation"); + } + break; + } + } + } + for (int i = 0; i < 4; i++) { + if (!input_perm.count(i)) { + NPNR_ASSERT(!unused.empty()); + input_perm[i] = *(unused.begin()); + unused.erase(input_perm[i]); + } + } + lut_init = permute_lut(lut_init, input_perm); + 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; + sb_io_used_by_io.insert(Loc(x, y, 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")); + bool lvds = get_param_str_or_def(cell.second.get(), ctx->id("IO_STANDARD")) == "SB_LVDS_INPUT"; + + 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; + if (lvds) { + input_en = false; + pullup = false; + } else { + if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) || + (ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) { + input_en = true; + } else { + input_en = false; + } + } + + 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); + } + } + + if (lvds) { + NPNR_ASSERT(z == 0); + set_config(ti, config.at(y).at(x), "IoCtrl.LVDS", true); + // Set comp IO config + auto comp_ieren = get_ieren(bi, x, y, 1); + int ciex, ciey, ciez; + std::tie(ciex, ciey, ciez) = comp_ieren; + + if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) { + set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), !input_en); + set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup); + } else { + set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), input_en); + set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup); + } + + if (ctx->args.type == ArchArgs::UP5K) { + if (ciez == 0) { + set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_39", !pullup); + } else if (iez == 1) { + set_config(ti, config.at(ciey).at(ciex), "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, std::string("IpConfig.")); + } else if (cell.second->type == ctx->id("ICESTORM_HFOSC")) { + const std::vector<std::pair<std::string, int>> hfosc_params = {{"CLKHF_DIV", 2}, {"TRIM_EN", 1}}; + configure_extra_cell(config, ctx, cell.second.get(), hfosc_params, true, std::string("IpConfig.")); + + } else if (cell.second->type == ctx->id("ICESTORM_PLL")) { + const std::vector<std::pair<std::string, int>> pll_params = {{"DELAY_ADJMODE_FB", 1}, + {"DELAY_ADJMODE_REL", 1}, + {"DIVF", 7}, + {"DIVQ", 3}, + {"DIVR", 4}, + {"FDA_FEEDBACK", 4}, + {"FDA_RELATIVE", 4}, + {"FEEDBACK_PATH", 3}, + {"FILTER_RANGE", 3}, + {"PLLOUT_SELECT_A", 2}, + {"PLLOUT_SELECT_B", 2}, + {"PLLTYPE", 3}, + {"SHIFTREG_DIV_MODE", 1}, + {"TEST_MODE", 1}}; + configure_extra_cell(config, ctx, cell.second.get(), pll_params, false, std::string("PLL.")); + + // Configure the SB_IOs that the clock outputs are going through. + for (auto &port : cell.second->ports) { + // If this port is not a PLLOUT port, ignore it. + if (port.second.name != ctx->id("PLLOUT_A") && port.second.name != ctx->id("PLLOUT_B")) + continue; + + // If the output is not driving any net, ignore it. + if (port.second.net == nullptr) + continue; + + // Get IO Bel that this PLL port goes through by finding sibling + // Bel driving the same wire via PIN_D_IN_0. + auto wire = ctx->getBelPinWire(cell.second->bel, port.second.name); + BelId io_bel; + for (auto pin : ctx->getWireBelPins(wire)) { + if (pin.pin == id_D_IN_0) { + io_bel = pin.bel; + break; + } + } + NPNR_ASSERT(io_bel.index != -1); + auto io_bel_loc = ctx->getBelLocation(io_bel); + + // Check that this SB_IO is either unused or just used as an output. + if (sb_io_used_by_io.count(io_bel_loc)) { + log_error("SB_IO '%s' already in use, cannot route PLL through\n", ctx->getBelName(bel).c_str(ctx)); + } + sb_io_used_by_pll.insert(io_bel_loc); + + // Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html) + auto ieren = get_ieren(bi, io_bel_loc.x, io_bel_loc.y, io_bel_loc.z); + int iex, iey, iez; + std::tie(iex, iey, iez) = ieren; + NPNR_ASSERT(iez != -1); + + // Write config. + const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; + // Enable input buffer and disable pull-up resistor in block + // (this is used by the PLL). + set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true); + set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false); + // PINTYPE[0] passes the PLL through to the fabric. + set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x), + "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true); + } + + } else { + NPNR_ASSERT(false); + } + } + // Set config bits in unused IO and RAM + for (auto bel : ctx->getBels()) { + if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_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; + if (sb_io_used_by_pll.count(Loc(x, y, z))) { + continue; + } + + auto ieren = get_ieren(bi, x, y, z); + int iex, iey, iez; + std::tie(iex, iey, iez) = ieren; + if (iez != -1) { + // IO is not actually unused if part of an LVDS pair + if (z == 1) { + BelId lvds0 = ctx->getBelByLocation(Loc{x, y, 0}); + const CellInfo *lvds0cell = ctx->getBoundBelCell(lvds0); + if (lvds0cell != nullptr && lvds0cell->ioInfo.lvds) + continue; + } + set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true); + set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false); + } + } else if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_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); + } else if (ctx->args.type == ArchArgs::LP384) { + setColBufCtrl = false; + } + 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))); + 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()) { + NetInfo *net = ctx->getBoundWireNet(wire); + if (net != nullptr) + out << ".sym " << wire.index << " " << net->name.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"); + + IdString netName = ctx->id(name); + + if (ctx->nets.find(netName) == ctx->nets.end()) { + std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo); + created_net->name = netName; + ctx->nets[netName] = std::move(created_net); + } + + WireId wire; + wire.index = wireIndex; + ctx->bindWire(wire, ctx->nets.at(netName).get(), 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); + + // Set pips + for (auto pip : ctx->getPips()) { + const PipInfoPOD &pi = ci.pip_data[pip.index]; + const SwitchInfoPOD &swi = bi.switches[pi.switch_index]; + bool isUsed = true; + 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); + isUsed &= !(bool(cbit) ^ val); + } + if (isUsed) { + NetInfo *net = ctx->wire_to_net[pi.dst]; + if (net != nullptr) { + WireId wire; + wire.index = pi.dst; + ctx->unbindWire(wire); + ctx->bindPip(pip, net, STRENGTH_WEAK); + } + } + } + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) == id_ICESTORM_LC) { + const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC]; + const BelInfoPOD &beli = ci.bel_data[bel.index]; + int x = beli.x, y = beli.y, z = beli.z; + std::vector<bool> lc(20, false); + bool isUsed = false; + for (int i = 0; i < 20; i++) { + lc.at(i) = get_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), i); + isUsed |= lc.at(i); + } + bool neg_clk = get_config(ti, config.at(y).at(x), "NegClk"); + isUsed |= neg_clk; + bool carry_set = get_config(ti, config.at(y).at(x), "CarryInSet"); + isUsed |= carry_set; + + if (isUsed) { + std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC")); + IdString name = created->name; + ctx->cells[name] = std::move(created); + ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK); + // TODO: Add port mapping to nets and assign values of properties + } + } + if (ctx->getBelType(bel) == id_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; + bool isUsed = false; + for (int i = 0; i < 6; i++) { + isUsed |= get_config(ti, config.at(y).at(x), + "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i)); + } + bool neg_trigger = get_config(ti, config.at(y).at(x), "NegClk"); + isUsed |= neg_trigger; + + if (isUsed) { + std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO")); + IdString name = created->name; + ctx->cells[name] = std::move(created); + ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK); + // TODO: Add port mapping to nets and assign values of properties + } + } + } + // Add cells that are without change in initial state of configuration + for (auto &net : ctx->nets) { + for (auto w : net.second->wires) { + if (w.second.pip == PipId()) { + WireId wire = w.first; + for (auto belpin : ctx->getWireBelPins(wire)) { + + if (ctx->checkBelAvail(belpin.bel)) { + if (ctx->getBelType(belpin.bel) == id_ICESTORM_LC) { + std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC")); + IdString name = created->name; + ctx->cells[name] = std::move(created); + ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); + // TODO: Add port mapping to nets + } + if (ctx->getBelType(belpin.bel) == id_SB_IO) { + std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO")); + IdString name = created->name; + ctx->cells[name] = std::move(created); + ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); + // TODO: Add port mapping to nets + } + if (ctx->getBelType(belpin.bel) == id_SB_GB) { + std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_GB")); + IdString name = created->name; + ctx->cells[name] = std::move(created); + ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); + // TODO: Add port mapping to nets + } + if (ctx->getBelType(belpin.bel) == id_SB_WARMBOOT) { + std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_WARMBOOT")); + IdString name = created->name; + ctx->cells[name] = std::move(created); + ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); + // TODO: Add port mapping to nets + } + if (ctx->getBelType(belpin.bel) == id_ICESTORM_LFOSC) { + std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC")); + IdString name = created->name; + ctx->cells[name] = std::move(created); + ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); + // TODO: Add port mapping to nets + } + } + } + } + } + } + for (auto &cell : ctx->cells) { + if (cell.second->bel != BelId()) { + for (auto &port : cell.second->ports) { + IdString pin = port.first; + WireId wire = ctx->getBelPinWire(cell.second->bel, pin); + if (wire != WireId()) { + NetInfo *net = ctx->getBoundWireNet(wire); + if (net != nullptr) { + port.second.net = net; + PortRef ref; + ref.cell = cell.second.get(); + ref.port = port.second.name; + + if (port.second.type == PORT_OUT) + net->driver = ref; + else + net->users.push_back(ref); + } + } + } + } + } + return true; + } catch (log_execution_error_exception) { + return false; + } +} +NEXTPNR_NAMESPACE_END |