/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Clifford Wolf * Copyright (C) 2018 David Shah * * 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 #include #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("unable to find config bit " + name); } std::tuple 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> &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); } 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; std::vector>>> 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(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 lc(20, false); // From arachne-pnr static std::vector 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); } 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 { 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 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 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