Merge branch 'master' of github.com:YosysHQ/nextpnr into reroute

This commit is contained in:
Clifford Wolf 2018-08-02 13:02:07 +02:00
commit f9a9da6cdb
17 changed files with 583 additions and 94 deletions

18
COPYING Normal file
View File

@ -0,0 +1,18 @@
Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
Copyright (C) 2018 David Shah <david@symbioticeda.com>
Copyright (C) 2018 Dan Gisselquist <dan@symbioticeda.com>
Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
Copyright (C) 2018 Eddie Hung <eddieh@ece.ubc.ca>
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.

View File

@ -90,9 +90,9 @@ sudo make install
```
- For an ECP5 blinky on the 45k ULX3S board, first synthesise using `yosys blinky.ys` in `ecp5/synth`.
- Then run ECP5 place-and route using `./nextpnr-ecp5 --json ecp5/synth/blinky.json --basecfg ecp5/synth/ulx3s_empty.config --bit ecp5/synth/ulx3s.bit`
- Then run ECP5 place-and route using `./nextpnr-ecp5 --json ecp5/synth/blinky.json --basecfg ecp5/synth/ulx3s_empty.config --textcfg ecp5/synth/ulx3s_out.config`
- Create a bitstream using `ecppack ulx3s_out.config ulx3s.bit`
- Note that `ulx3s_empty.config` contains fixed/unknown bits to be copied to the output bitstream
- You can also use `--textcfg out.config` to write a text file describing the bitstream for debugging
- More examples of the ECP5 flow for a range of boards can be found in the [Project Trellis Examples](https://github.com/SymbiFlow/prjtrellis/tree/master/examples).

View File

@ -193,7 +193,6 @@ class SAPlacer
if (temp <= 1e-3 && n_no_progress >= 5) {
if (iter % 5 != 0)
log_info(" at iteration #%d: temp = %f, cost = %f\n", iter, temp, double(curr_metric));
ctx->unlock();
break;
}

View File

@ -496,4 +496,15 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return
bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; }
std::vector<std::pair<std::string, std::string>> Arch::getTilesAtLocation(int row, int col)
{
std::vector<std::pair<std::string, std::string>> ret;
auto &tileloc = chip_info->tile_info[row * chip_info->width + col];
for (int i = 0; i < tileloc.num_tiles; i++) {
ret.push_back(std::make_pair(tileloc.tile_names[i].name.get(),
chip_info->tiletype_names[tileloc.tile_names[i].type_idx].get()));
}
return ret;
}
NEXTPNR_NAMESPACE_END

View File

@ -117,6 +117,17 @@ NPNR_PACKED_STRUCT(struct PackageInfoPOD {
RelPtr<PackagePinPOD> pin_data;
});
NPNR_PACKED_STRUCT(struct TileNamePOD {
RelPtr<char> name;
int16_t type_idx;
int16_t padding;
});
NPNR_PACKED_STRUCT(struct TileInfoPOD {
int32_t num_tiles;
RelPtr<TileNamePOD> tile_names;
});
enum TapDirection : int8_t
{
TAP_DIR_LEFT = 0,
@ -148,6 +159,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
RelPtr<RelPtr<char>> tiletype_names;
RelPtr<PackageInfoPOD> package_info;
RelPtr<PIOInfoPOD> pio_info;
RelPtr<TileInfoPOD> tile_info;
});
#if defined(_MSC_VER)
@ -747,6 +759,16 @@ struct Arch : BaseCtx
return range;
}
std::string getPipTilename(PipId pip) const
{
auto &tileloc = chip_info->tile_info[pip.location.y * chip_info->width + pip.location.x];
for (int i = 0; i < tileloc.num_tiles; i++) {
if (tileloc.tile_names[i].type_idx == locInfo(pip)->pip_data[pip.index].tile_type)
return tileloc.tile_names[i].name.get();
}
NPNR_ASSERT_FALSE("failed to find Pip tile");
}
std::string getPipTiletype(PipId pip) const
{
return chip_info->tiletype_names[locInfo(pip)->pip_data[pip.index].tile_type].get();
@ -818,6 +840,28 @@ struct Arch : BaseCtx
// Helper function for above
bool slicesCompatible(const std::vector<const CellInfo *> &cells) const;
std::vector<std::pair<std::string, std::string>> getTilesAtLocation(int row, int col);
std::string getTileByTypeAndLocation(int row, int col, std::string type) const
{
auto &tileloc = chip_info->tile_info[row * chip_info->width + col];
for (int i = 0; i < tileloc.num_tiles; i++) {
if (chip_info->tiletype_names[tileloc.tile_names[i].type_idx].get() == type)
return tileloc.tile_names[i].name.get();
}
NPNR_ASSERT_FALSE_STR("no tile at (" + std::to_string(col) + ", " + std::to_string(row) + ") with type " +
type);
}
std::string getTileByTypeAndLocation(int row, int col, const std::set<std::string> &type) const
{
auto &tileloc = chip_info->tile_info[row * chip_info->width + col];
for (int i = 0; i < tileloc.num_tiles; i++) {
if (type.count(chip_info->tiletype_names[tileloc.tile_names[i].type_idx].get()))
return tileloc.tile_names[i].name.get();
}
NPNR_ASSERT_FALSE_STR("no tile at (" + std::to_string(col) + ", " + std::to_string(row) + ") with type in set");
}
IdString id_trellis_slice;
IdString id_clk, id_lsr;
IdString id_clkmux, id_lsrmux;

View File

@ -19,17 +19,10 @@
#include "bitstream.h"
// From Project Trellis
#include "BitDatabase.hpp"
#include "Bitstream.hpp"
#include "Chip.hpp"
#include "ChipConfig.hpp"
#include "Tile.hpp"
#include "TileConfig.hpp"
#include <fstream>
#include <streambuf>
#include "config.h"
#include "io.h"
#include "log.h"
#include "util.h"
@ -49,13 +42,13 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire)
return basename;
std::string rel_prefix;
if (wire.location.y < loc.y)
rel_prefix += "N" + to_string(loc.y - wire.location.y);
rel_prefix += "N" + std::to_string(loc.y - wire.location.y);
if (wire.location.y > loc.y)
rel_prefix += "S" + to_string(wire.location.y - loc.y);
rel_prefix += "S" + std::to_string(wire.location.y - loc.y);
if (wire.location.x > loc.x)
rel_prefix += "E" + to_string(wire.location.x - loc.x);
rel_prefix += "E" + std::to_string(wire.location.x - loc.x);
if (wire.location.x < loc.x)
rel_prefix += "W" + to_string(loc.x - wire.location.x);
rel_prefix += "W" + std::to_string(loc.x - wire.location.x);
return rel_prefix + "_" + basename;
}
@ -69,7 +62,7 @@ static std::vector<bool> int_to_bitvector(int val, int size)
}
// Get the PIO tile corresponding to a PIO bel
static std::string get_pio_tile(Context *ctx, Trellis::Chip &chip, BelId bel)
static std::string get_pio_tile(Context *ctx, BelId bel)
{
static const std::set<std::string> pioabcd_l = {"PICL1", "PICL1_DQS0", "PICL1_DQS3"};
static const std::set<std::string> pioabcd_r = {"PICR1", "PICR1_DQS0", "PICR1_DQS3"};
@ -79,31 +72,31 @@ static std::string get_pio_tile(Context *ctx, Trellis::Chip &chip, BelId bel)
std::string pio_name = ctx->locInfo(bel)->bel_data[bel.index].name.get();
if (bel.location.y == 0) {
if (pio_name == "PIOA") {
return chip.get_tile_by_position_and_type(0, bel.location.x, "PIOT0");
return ctx->getTileByTypeAndLocation(0, bel.location.x, "PIOT0");
} else if (pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(0, bel.location.x + 1, "PIOT1");
return ctx->getTileByTypeAndLocation(0, bel.location.x + 1, "PIOT1");
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.y == ctx->chip_info->height - 1) {
if (pio_name == "PIOA") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pioa_b);
return ctx->getTileByTypeAndLocation(bel.location.y, bel.location.x, pioa_b);
} else if (pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, piob_b);
return ctx->getTileByTypeAndLocation(bel.location.y, bel.location.x + 1, piob_b);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.x == 0) {
return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_l);
return ctx->getTileByTypeAndLocation(bel.location.y + 1, bel.location.x, pioabcd_l);
} else if (bel.location.x == ctx->chip_info->width - 1) {
return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_r);
return ctx->getTileByTypeAndLocation(bel.location.y + 1, bel.location.x, pioabcd_r);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
}
// Get the PIC tile corresponding to a PIO bel
static std::string get_pic_tile(Context *ctx, Trellis::Chip &chip, BelId bel)
static std::string get_pic_tile(Context *ctx, BelId bel)
{
static const std::set<std::string> picab_l = {"PICL0", "PICL0_DQS2"};
static const std::set<std::string> piccd_l = {"PICL2", "PICL2_DQS1", "MIB_CIB_LR"};
@ -116,33 +109,33 @@ static std::string get_pic_tile(Context *ctx, Trellis::Chip &chip, BelId bel)
std::string pio_name = ctx->locInfo(bel)->bel_data[bel.index].name.get();
if (bel.location.y == 0) {
if (pio_name == "PIOA") {
return chip.get_tile_by_position_and_type(1, bel.location.x, "PICT0");
return ctx->getTileByTypeAndLocation(1, bel.location.x, "PICT0");
} else if (pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(1, bel.location.x + 1, "PICT1");
return ctx->getTileByTypeAndLocation(1, bel.location.x + 1, "PICT1");
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.y == ctx->chip_info->height - 1) {
if (pio_name == "PIOA") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pica_b);
return ctx->getTileByTypeAndLocation(bel.location.y, bel.location.x, pica_b);
} else if (pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, picb_b);
return ctx->getTileByTypeAndLocation(bel.location.y, bel.location.x + 1, picb_b);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.x == 0) {
if (pio_name == "PIOA" || pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_l);
return ctx->getTileByTypeAndLocation(bel.location.y, bel.location.x, picab_l);
} else if (pio_name == "PIOC" || pio_name == "PIOD") {
return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_l);
return ctx->getTileByTypeAndLocation(bel.location.y + 2, bel.location.x, piccd_l);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.x == ctx->chip_info->width - 1) {
if (pio_name == "PIOA" || pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_r);
return ctx->getTileByTypeAndLocation(bel.location.y, bel.location.x, picab_r);
} else if (pio_name == "PIOC" || pio_name == "PIOD") {
return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_r);
return ctx->getTileByTypeAndLocation(bel.location.y + 2, bel.location.x, piccd_r);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
@ -151,11 +144,9 @@ static std::string get_pic_tile(Context *ctx, Trellis::Chip &chip, BelId bel)
}
}
void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file,
std::string bitstream_file)
void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file)
{
Trellis::Chip empty_chip(ctx->getChipName());
Trellis::ChipConfig cc;
ChipConfig cc;
std::set<std::string> cib_tiles = {"CIB", "CIB_LR", "CIB_LR_S", "CIB_EFB0", "CIB_EFB1"};
@ -164,8 +155,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
if (!config_file) {
log_error("failed to open base config file '%s'\n", base_config_file.c_str());
}
std::string str((std::istreambuf_iterator<char>(config_file)), std::istreambuf_iterator<char>());
cc = Trellis::ChipConfig::from_string(str);
config_file >> cc;
} else {
cc.chip_name = ctx->getChipName();
// TODO: .bit metadata
@ -175,8 +165,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
for (auto pip : ctx->getPips()) {
if (ctx->getBoundPipNet(pip) != IdString()) {
if (ctx->getPipClass(pip) == 0) { // ignore fixed pips
std::string tile = empty_chip.get_tile_by_position_and_type(pip.location.y, pip.location.x,
ctx->getPipTiletype(pip));
std::string tile = ctx->getPipTilename(pip);
std::string source = get_trellis_wirename(ctx, pip.location, ctx->getPipSrcWire(pip));
std::string sink = get_trellis_wirename(ctx, pip.location, ctx->getPipDstWire(pip));
cc.tiles[tile].add_arc(sink, source);
@ -214,15 +203,20 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
}
// Set all bankref tiles to appropriate VccIO
for (const auto &tile : empty_chip.tiles) {
std::string type = tile.second->info.type;
if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") {
int bank = std::stoi(type.substr(7));
if (bankVcc.find(bank) != bankVcc.end())
cc.tiles[tile.first].add_enum("BANK.VCCIO", iovoltage_to_str(bankVcc[bank]));
if (bankLvds[bank]) {
cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON");
cc.tiles[tile.first].add_enum("BANK.LVDSO", "ON");
for (int y = 0; y < ctx->getGridDimY(); y++) {
for (int x = 0; x < ctx->getGridDimX(); x++) {
auto tiles = ctx->getTilesAtLocation(y, x);
for (auto tile : tiles) {
std::string type = tile.second;
if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") {
int bank = std::stoi(type.substr(7));
if (bankVcc.find(bank) != bankVcc.end())
cc.tiles[tile.first].add_enum("BANK.VCCIO", iovoltage_to_str(bankVcc[bank]));
if (bankLvds[bank]) {
cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON");
cc.tiles[tile.first].add_enum("BANK.LVDSO", "ON");
}
}
}
}
}
@ -235,7 +229,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
}
BelId bel = ci->bel;
if (ci->type == ctx->id("TRELLIS_SLICE")) {
std::string tname = empty_chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, "PLC2");
std::string tname = ctx->getTileByTypeAndLocation(bel.location.y, bel.location.x, "PLC2");
std::string slice = ctx->locInfo(bel)->bel_data[bel.index].name.get();
int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL"));
int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL"));
@ -267,8 +261,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
std::string pio = ctx->locInfo(bel)->bel_data[bel.index].name.get();
std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33");
std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT");
std::string pio_tile = get_pio_tile(ctx, empty_chip, bel);
std::string pic_tile = get_pic_tile(ctx, empty_chip, bel);
std::string pio_tile = get_pio_tile(ctx, bel);
std::string pic_tile = get_pic_tile(ctx, bel);
cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
if (is_differential(ioType_from_str(iotype))) {
@ -293,7 +287,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
PipId jpt_pip = *ctx->getPipsUphill(jpt_wire).begin();
WireId cib_wire = ctx->getPipSrcWire(jpt_pip);
std::string cib_tile =
empty_chip.get_tile_by_position_and_type(cib_wire.location.y, cib_wire.location.x, cib_tiles);
ctx->getTileByTypeAndLocation(cib_wire.location.y, cib_wire.location.x, cib_tiles);
std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get();
cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0");
}
@ -306,13 +300,9 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
}
// Configure chip
Trellis::Chip cfg_chip = cc.to_chip();
if (!bitstream_file.empty()) {
Trellis::Bitstream::serialise_chip(cfg_chip).write_bit_py(bitstream_file);
}
if (!text_config_file.empty()) {
std::ofstream out_config(text_config_file);
out_config << cc.to_string();
out_config << cc;
}
}

View File

@ -24,8 +24,7 @@
NEXTPNR_NAMESPACE_BEGIN
void write_bitstream(Context *ctx, std::string base_config_file = "", std::string text_config_file = "",
std::string bitstream_file = "");
void write_bitstream(Context *ctx, std::string base_config_file = "", std::string text_config_file = "");
NEXTPNR_NAMESPACE_END

304
ecp5/config.cc Normal file
View File

@ -0,0 +1,304 @@
/*
* nextpnr -- Next Generation Place and Route
*
* 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 "config.h"
#include <boost/range/adaptor/reversed.hpp>
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
#define fmt(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())
inline std::string to_string(const std::vector<bool> &bv)
{
std::ostringstream os;
for (auto bit : boost::adaptors::reverse(bv))
os << (bit ? '1' : '0');
return os.str();
}
inline std::istream &operator>>(std::istream &in, std::vector<bool> &bv)
{
bv.clear();
std::string s;
in >> s;
for (auto c : boost::adaptors::reverse(s)) {
assert((c == '0') || (c == '1'));
bv.push_back((c == '1'));
}
return in;
}
struct ConfigBit
{
int frame;
int bit;
bool inv;
};
static ConfigBit cbit_from_str(const std::string &s)
{
size_t idx = 0;
ConfigBit b;
if (s[idx] == '!') {
b.inv = true;
++idx;
} else {
b.inv = false;
}
NPNR_ASSERT(s[idx] == 'F');
++idx;
size_t b_pos = s.find('B');
NPNR_ASSERT(b_pos != std::string::npos);
b.frame = stoi(s.substr(idx, b_pos - idx));
b.bit = stoi(s.substr(b_pos + 1));
return b;
}
inline std::string to_string(ConfigBit b)
{
std::ostringstream ss;
if (b.inv)
ss << "!";
ss << "F" << b.frame;
ss << "B" << b.bit;
return ss.str();
}
// Skip whitespace, optionally including newlines
inline void skip_blank(std::istream &in, bool nl = false)
{
int c = in.peek();
while (in && (((c == ' ') || (c == '\t')) || (nl && ((c == '\n') || (c == '\r'))))) {
in.get();
c = in.peek();
}
}
// Return true if end of line (or file)
inline bool skip_check_eol(std::istream &in)
{
skip_blank(in, false);
if (!in)
return false;
int c = in.peek();
// Comments count as end of line
if (c == '#') {
in.get();
c = in.peek();
while (in && c != EOF && c != '\n') {
in.get();
c = in.peek();
}
return true;
}
return (c == EOF || c == '\n');
}
// Skip past blank lines and comments
inline void skip(std::istream &in)
{
skip_blank(in, true);
while (in && (in.peek() == '#')) {
// Skip comment line
skip_check_eol(in);
skip_blank(in, true);
}
}
// Return true if at the end of a record (or file)
inline bool skip_check_eor(std::istream &in)
{
skip(in);
int c = in.peek();
return (c == EOF || c == '.');
}
// Return true if at the end of file
inline bool skip_check_eof(std::istream &in)
{
skip(in);
int c = in.peek();
return (c == EOF);
}
std::ostream &operator<<(std::ostream &out, const ConfigArc &arc)
{
out << "arc: " << arc.sink << " " << arc.source << std::endl;
return out;
}
std::istream &operator>>(std::istream &in, ConfigArc &arc)
{
in >> arc.sink;
in >> arc.source;
return in;
}
std::ostream &operator<<(std::ostream &out, const ConfigWord &cw)
{
out << "word: " << cw.name << " " << to_string(cw.value) << std::endl;
return out;
}
std::istream &operator>>(std::istream &in, ConfigWord &cw)
{
in >> cw.name;
in >> cw.value;
return in;
}
std::ostream &operator<<(std::ostream &out, const ConfigEnum &cw)
{
out << "enum: " << cw.name << " " << cw.value << std::endl;
return out;
}
std::istream &operator>>(std::istream &in, ConfigEnum &ce)
{
in >> ce.name;
in >> ce.value;
return in;
}
std::ostream &operator<<(std::ostream &out, const ConfigUnknown &cu)
{
out << "unknown: " << to_string(ConfigBit{cu.frame, cu.bit, false}) << std::endl;
return out;
}
std::istream &operator>>(std::istream &in, ConfigUnknown &cu)
{
std::string s;
in >> s;
ConfigBit c = cbit_from_str(s);
cu.frame = c.frame;
cu.bit = c.bit;
assert(!c.inv);
return in;
}
std::ostream &operator<<(std::ostream &out, const TileConfig &tc)
{
for (const auto &arc : tc.carcs)
out << arc;
for (const auto &cword : tc.cwords)
out << cword;
for (const auto &cenum : tc.cenums)
out << cenum;
for (const auto &cunk : tc.cunknowns)
out << cunk;
return out;
}
std::istream &operator>>(std::istream &in, TileConfig &tc)
{
tc.carcs.clear();
tc.cwords.clear();
tc.cenums.clear();
while (!skip_check_eor(in)) {
std::string type;
in >> type;
if (type == "arc:") {
ConfigArc a;
in >> a;
tc.carcs.push_back(a);
} else if (type == "word:") {
ConfigWord w;
in >> w;
tc.cwords.push_back(w);
} else if (type == "enum:") {
ConfigEnum e;
in >> e;
tc.cenums.push_back(e);
} else if (type == "unknown:") {
ConfigUnknown u;
in >> u;
tc.cunknowns.push_back(u);
} else {
NPNR_ASSERT_FALSE_STR("unexpected token " + type + " while reading config text");
}
}
return in;
}
void TileConfig::add_arc(const std::string &sink, const std::string &source) { carcs.push_back({sink, source}); }
void TileConfig::add_word(const std::string &name, const std::vector<bool> &value) { cwords.push_back({name, value}); }
void TileConfig::add_enum(const std::string &name, const std::string &value) { cenums.push_back({name, value}); }
void TileConfig::add_unknown(int frame, int bit) { cunknowns.push_back({frame, bit}); }
std::string TileConfig::to_string() const
{
std::stringstream ss;
ss << *this;
return ss.str();
}
TileConfig TileConfig::from_string(const std::string &str)
{
std::stringstream ss(str);
TileConfig tc;
ss >> tc;
return tc;
}
bool TileConfig::empty() const { return carcs.empty() && cwords.empty() && cenums.empty() && cunknowns.empty(); }
std::ostream &operator<<(std::ostream &out, const ChipConfig &cc)
{
out << ".device " << cc.chip_name << std::endl << std::endl;
for (const auto &meta : cc.metadata)
out << ".comment " << meta << std::endl;
out << std::endl;
for (const auto &tile : cc.tiles) {
if (!tile.second.empty()) {
out << ".tile " << tile.first << std::endl;
out << tile.second;
out << std::endl;
}
}
return out;
}
std::istream &operator>>(std::istream &in, ChipConfig &cc)
{
while (!skip_check_eof(in)) {
std::string verb;
in >> verb;
if (verb == ".device") {
in >> cc.chip_name;
} else if (verb == ".comment") {
std::string line;
getline(in, line);
cc.metadata.push_back(line);
} else if (verb == ".tile") {
std::string tilename;
in >> tilename;
TileConfig tc;
in >> tc;
cc.tiles[tilename] = tc;
} else {
log_error("unrecognised config entry %s\n", verb.c_str());
}
}
return in;
}
NEXTPNR_NAMESPACE_END

115
ecp5/config.h Normal file
View File

@ -0,0 +1,115 @@
/*
* nextpnr -- Next Generation Place and Route
*
* 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.
*
*/
#ifndef ECP5_CONFIG_H
#define ECP5_CONFIG_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
// This represents configuration at "FASM" level, in terms of routing arcs and non-routing configuration settings -
// either words or enums.
// A connection in a tile
struct ConfigArc
{
std::string sink;
std::string source;
inline bool operator==(const ConfigArc &other) const { return other.source == source && other.sink == sink; }
};
std::ostream &operator<<(std::ostream &out, const ConfigArc &arc);
std::istream &operator>>(std::istream &in, ConfigArc &arc);
// A configuration setting in a tile that takes one or more bits (such as LUT init)
struct ConfigWord
{
std::string name;
std::vector<bool> value;
inline bool operator==(const ConfigWord &other) const { return other.name == name && other.value == value; }
};
std::ostream &operator<<(std::ostream &out, const ConfigWord &cw);
std::istream &operator>>(std::istream &in, ConfigWord &cw);
// A configuration setting in a tile that takes an enumeration value (such as IO type)
struct ConfigEnum
{
std::string name;
std::string value;
inline bool operator==(const ConfigEnum &other) const { return other.name == name && other.value == value; }
};
std::ostream &operator<<(std::ostream &out, const ConfigEnum &ce);
std::istream &operator>>(std::istream &in, ConfigEnum &ce);
// An unknown bit, specified by position only
struct ConfigUnknown
{
int frame, bit;
inline bool operator==(const ConfigUnknown &other) const { return other.frame == frame && other.bit == bit; }
};
std::ostream &operator<<(std::ostream &out, const ConfigUnknown &tc);
std::istream &operator>>(std::istream &in, ConfigUnknown &ce);
struct TileConfig
{
std::vector<ConfigArc> carcs;
std::vector<ConfigWord> cwords;
std::vector<ConfigEnum> cenums;
std::vector<ConfigUnknown> cunknowns;
int total_known_bits = 0;
void add_arc(const std::string &sink, const std::string &source);
void add_word(const std::string &name, const std::vector<bool> &value);
void add_enum(const std::string &name, const std::string &value);
void add_unknown(int frame, int bit);
std::string to_string() const;
static TileConfig from_string(const std::string &str);
bool empty() const;
};
std::ostream &operator<<(std::ostream &out, const TileConfig &tc);
std::istream &operator>>(std::istream &in, TileConfig &ce);
// This represents the configuration of a chip at a high level
class ChipConfig
{
public:
std::string chip_name;
std::vector<std::string> metadata;
std::map<std::string, TileConfig> tiles;
};
std::ostream &operator<<(std::ostream &out, const ChipConfig &cc);
std::istream &operator>>(std::istream &in, ChipConfig &cc);
NEXTPNR_NAMESPACE_END
#endif

View File

@ -62,11 +62,3 @@ else()
endforeach (target)
endforeach (dev)
endif()
find_library(TRELLIS_LIB trellis PATHS ${TRELLIS_ROOT}/libtrellis)
foreach (target ${family_targets})
target_compile_definitions(${target} PRIVATE TRELLIS_ROOT="${TRELLIS_ROOT}")
target_include_directories(${target} PRIVATE ${TRELLIS_ROOT}/libtrellis/include)
target_link_libraries(${target} PRIVATE ${TRELLIS_LIB})
endforeach (target)

View File

@ -32,10 +32,6 @@
#include <fstream>
#include <iostream>
#include "Chip.hpp"
#include "Database.hpp"
#include "Tile.hpp"
#include "log.h"
#include "nextpnr.h"
#include "version.h"
@ -75,7 +71,6 @@ int main(int argc, char *argv[])
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
options.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format");
options.add_options()("bit", po::value<std::string>(), "bitstream file to write");
options.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
po::positional_options_description pos;
@ -115,8 +110,6 @@ int main(int argc, char *argv[])
return 1;
}
Trellis::load_database(TRELLIS_ROOT "/database");
ArchArgs args;
args.type = ArchArgs::LFE5U_45F;
@ -189,14 +182,10 @@ int main(int argc, char *argv[])
if (vm.count("basecfg"))
basecfg = vm["basecfg"].as<std::string>();
std::string bitstream;
if (vm.count("bit"))
bitstream = vm["bit"].as<std::string>();
std::string textcfg;
if (vm.count("textcfg"))
textcfg = vm["textcfg"].as<std::string>();
write_bitstream(ctx.get(), basecfg, textcfg, bitstream);
write_bitstream(ctx.get(), basecfg, textcfg);
}
#ifndef NO_PYTHON

View File

@ -325,19 +325,22 @@ class Ecp5Packer
lut_to_slice(ctx, lut0, slice.get(), 0);
lut_to_slice(ctx, lut1, slice.get(), 1);
auto ff0 = lutffPairs.find(lut0->name), ff1 = lutffPairs.find(lut1->name);
auto ff0 = lutffPairs.find(lut0->name);
if (ff0 != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff0->second).get(), slice.get(), 0, true);
packed_cells.insert(ff0->second);
lutffPairs.erase(lut0->name);
fflutPairs.erase(ff0->second);
lutffPairs.erase(lut0->name);
}
auto ff1 = lutffPairs.find(lut1->name);
if (ff1 != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff1->second).get(), slice.get(), 1, true);
packed_cells.insert(ff1->second);
lutffPairs.erase(lut1->name);
fflutPairs.erase(ff1->second);
lutffPairs.erase(lut1->name);
}
new_cells.push_back(std::move(slice));
@ -363,8 +366,8 @@ class Ecp5Packer
if (ff != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff->second).get(), slice.get(), 0, true);
packed_cells.insert(ff->second);
lutffPairs.erase(ci->name);
fflutPairs.erase(ff->second);
lutffPairs.erase(ci->name);
}
new_cells.push_back(std::move(slice));

View File

@ -1 +1,3 @@
*.bit
*_out.config

View File

@ -136,7 +136,7 @@ def process_loc_globals(chip):
tapdrv = chip.global_data.get_tap_driver(y, x)
global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col)
def write_database(dev_name, ddrg, endianness):
def write_database(dev_name, chip, ddrg, endianness):
def write_loc(loc, sym_name):
bba.u16(loc.x, "%s.x" % sym_name)
bba.u16(loc.y, "%s.y" % sym_name)
@ -221,6 +221,20 @@ def write_database(dev_name, ddrg, endianness):
bba.r("loc%d_wires" % idx if len(loctype.wires) > 0 else None, "wire_data")
bba.r("loc%d_pips" % idx if len(loctype.arcs) > 0 else None, "pips_data")
for y in range(0, max_row+1):
for x in range(0, max_col+1):
bba.l("tile_info_%d_%d" % (x, y), "TileNamePOD")
for tile in chip.get_tiles_by_position(y, x):
bba.s(tile.info.name, "name")
bba.u16(get_tiletype_index(tile.info.type), "type_idx")
bba.u16(0, "padding")
bba.l("tiles_info", "TileInfoPOD")
for y in range(0, max_row+1):
for x in range(0, max_col+1):
bba.u32(len(chip.get_tiles_by_position(y, x)), "num_tiles")
bba.r("tile_info_%d_%d" % (x, y), "tile_names")
bba.l("location_types", "int32_t")
for y in range(0, max_row+1):
for x in range(0, max_col+1):
@ -278,6 +292,7 @@ def write_database(dev_name, ddrg, endianness):
bba.r("tiletype_names", "tiletype_names")
bba.r("package_data", "package_info")
bba.r("pio_info", "pio_info")
bba.r("tiles_info", "tile_info")
bba.pop()
return bba
@ -311,7 +326,7 @@ def main():
process_pio_db(ddrg, args.device)
process_loc_globals(chip)
# print("{} unique location types".format(len(ddrg.locationTypes)))
bba = write_database(args.device, ddrg, "le")
bba = write_database(args.device, chip, ddrg, "le")

View File

@ -70,12 +70,14 @@ void IdStringList::updateElements(Context *ctx, std::vector<IdString> elements)
}
// For any elements that are in managed_ but not in new, delete them.
for (auto &pair : managed_) {
if (element_set.count(pair.first) != 0) {
continue;
auto it = managed_.begin();
while (it != managed_.end()) {
if (element_set.count(it->first) != 0) {
++it;
} else {
it = managed_.erase(it);
changed = true;
}
managed_.erase(pair.first);
changed = true;
}
// Return early if there are no changes.

View File

@ -62,6 +62,8 @@ class Item
void addChild(Item *child) { children_.append(child); }
void deleteChild(Item *child) { children_.removeAll(child); }
public:
Item(QString name, Item *parent) : name_(name), parent_(parent)
{
@ -100,7 +102,12 @@ class Item
virtual bool canFetchMore() const { return false; }
virtual void fetchMore() {}
~Item() {}
~Item()
{
if (parent_ != nullptr) {
parent_->deleteChild(this);
}
}
};
// IdString is an Item that corresponds to a real element in Arch.

View File

@ -462,9 +462,8 @@ void json_import_ports(Context *ctx, const string &modname, const std::vector<Id
ground_net(ctx, net.get());
log_info(" Floating wire node value, "
"\'%s\' of port \'%s\' "
"in cell \'%s\' of module \'%s\'\n, converted to zero driver",
wire_node->data_string.c_str(), port_name.c_str(), obj_name.c_str(), modname.c_str());
"'%s' on '%s'/'%s', converted to zero driver\n",
this_port.name.c_str(ctx), modname.c_str(), obj_name.c_str());
} else
log_error(" Unknown fixed type wire node "