Merge branch 'master' into xc7
This commit is contained in:
commit
463d9a6920
@ -49,8 +49,9 @@ Getting started
|
|||||||
|
|
||||||
### nextpnr-ice40
|
### nextpnr-ice40
|
||||||
|
|
||||||
To build the iCE40 version of nextpnr, install [icestorm](http://www.clifford.at/icestorm/) with chipdbs installed in `/usr/local/share/icebox`
|
To build the iCE40 version of nextpnr, install [icestorm](http://www.clifford.at/icestorm/) with chipdbs installed in `/usr/local/share/icebox`,
|
||||||
(or another location, which should be passed as -DICEBOX_ROOT=/path/to/icebox to CMake).
|
or another location, which should be passed as `-DICEBOX_ROOT=/path/to/share/icebox` (ensure to point it to `share/icebox` and not where the
|
||||||
|
icebox binaries are installed) to CMake.
|
||||||
Then build and install `nextpnr-ice40` using the following commands:
|
Then build and install `nextpnr-ice40` using the following commands:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -51,7 +51,8 @@ std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F
|
|||||||
CellChain chain;
|
CellChain chain;
|
||||||
CellInfo *end = start;
|
CellInfo *end = start;
|
||||||
while (end != nullptr) {
|
while (end != nullptr) {
|
||||||
chain.cells.push_back(end);
|
if (chained.insert(end->name).second)
|
||||||
|
chain.cells.push_back(end);
|
||||||
end = get_next(ctx, end);
|
end = get_next(ctx, end);
|
||||||
}
|
}
|
||||||
if (chain.cells.size() >= min_length) {
|
if (chain.cells.size() >= min_length) {
|
||||||
|
@ -416,7 +416,7 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::u
|
|||||||
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
|
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
|
||||||
ctx->id("Y"));
|
ctx->id("Y"));
|
||||||
if (tbuf) {
|
if (tbuf) {
|
||||||
replace_port(tbuf, ctx->id("I"), trio, ctx->id("I"));
|
replace_port(tbuf, ctx->id("A"), trio, ctx->id("I"));
|
||||||
// Need to invert E to form T
|
// Need to invert E to form T
|
||||||
std::unique_ptr<CellInfo> inv_lut = create_ecp5_cell(ctx, ctx->id("LUT4"), trio->name.str(ctx) + "$invert_T");
|
std::unique_ptr<CellInfo> inv_lut = create_ecp5_cell(ctx, ctx->id("LUT4"), trio->name.str(ctx) + "$invert_T");
|
||||||
replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("A"));
|
replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("A"));
|
||||||
|
13
ecp5/pack.cc
13
ecp5/pack.cc
@ -299,7 +299,16 @@ class Ecp5Packer
|
|||||||
// iobuf
|
// iobuf
|
||||||
log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx),
|
log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx),
|
||||||
ci->type.c_str(ctx), ci->name.c_str(ctx));
|
ci->type.c_str(ctx), ci->name.c_str(ctx));
|
||||||
|
|
||||||
NetInfo *net = trio->ports.at(ctx->id("B")).net;
|
NetInfo *net = trio->ports.at(ctx->id("B")).net;
|
||||||
|
if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) &&
|
||||||
|
net->users.size() > 1) ||
|
||||||
|
(ci->type == ctx->id("$nextpnr_obuf") &&
|
||||||
|
(net->users.size() > 2 || net->driver.cell != nullptr)) ||
|
||||||
|
(ci->type == ctx->id("$nextpnr_iobuf") && ci->ports.at(ctx->id("I")).net != nullptr &&
|
||||||
|
ci->ports.at(ctx->id("I")).net->driver.cell != nullptr))
|
||||||
|
log_error("Pin B of %s '%s' connected to more than a single top level IO.\n",
|
||||||
|
trio->type.c_str(ctx), trio->name.c_str(ctx));
|
||||||
if (net != nullptr) {
|
if (net != nullptr) {
|
||||||
ctx->nets.erase(net->name);
|
ctx->nets.erase(net->name);
|
||||||
trio->ports.at(ctx->id("B")).net = nullptr;
|
trio->ports.at(ctx->id("B")).net = nullptr;
|
||||||
@ -1497,6 +1506,10 @@ class Ecp5Packer
|
|||||||
iol = create_pio_iologic(pio, ci);
|
iol = create_pio_iologic(pio, ci);
|
||||||
set_iologic_mode(iol, "IDDRX1_ODDRX1");
|
set_iologic_mode(iol, "IDDRX1_ODDRX1");
|
||||||
replace_port(ci, ctx->id("Q"), iol, id_IOLDO);
|
replace_port(ci, ctx->id("Q"), iol, id_IOLDO);
|
||||||
|
if (!pio->ports.count(id_IOLDO)) {
|
||||||
|
pio->ports[id_IOLDO].name = id_IOLDO;
|
||||||
|
pio->ports[id_IOLDO].type = PORT_IN;
|
||||||
|
}
|
||||||
replace_port(pio, id_I, pio, id_IOLDO);
|
replace_port(pio, id_I, pio, id_IOLDO);
|
||||||
pio->params[ctx->id("DATAMUX_ODDR")] = "IOLDO";
|
pio->params[ctx->id("DATAMUX_ODDR")] = "IOLDO";
|
||||||
set_iologic_sclk(iol, ci, ctx->id("SCLK"), false);
|
set_iologic_sclk(iol, ci, ctx->id("SCLK"), false);
|
||||||
|
@ -527,10 +527,23 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->args.type == ArchArgs::UP5K) {
|
if (ctx->args.type == ArchArgs::UP5K) {
|
||||||
|
std::string pullup_resistor = "100K";
|
||||||
|
if (cell.second->attrs.count(ctx->id("PULLUP_RESISTOR")))
|
||||||
|
pullup_resistor = cell.second->attrs.at(ctx->id("PULLUP_RESISTOR"));
|
||||||
|
NPNR_ASSERT(pullup_resistor == "100K" || pullup_resistor == "10K" || pullup_resistor == "6P8K" ||
|
||||||
|
pullup_resistor == "3P3K");
|
||||||
if (iez == 0) {
|
if (iez == 0) {
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39",
|
||||||
|
(!pullup) || (pullup_resistor != "100K"));
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_36", pullup && pullup_resistor == "3P3K");
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_37", pullup && pullup_resistor == "6P8K");
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_38", pullup && pullup_resistor == "10K");
|
||||||
} else if (iez == 1) {
|
} else if (iez == 1) {
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35",
|
||||||
|
(!pullup) || (pullup_resistor != "100K"));
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_32", pullup && pullup_resistor == "3P3K");
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_33", pullup && pullup_resistor == "6P8K");
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_34", pullup && pullup_resistor == "10K");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -70,6 +70,8 @@ po::options_description Ice40CommandHandler::getArchOptions()
|
|||||||
specific.add_options()("no-promote-globals", "disable all global promotion");
|
specific.add_options()("no-promote-globals", "disable all global promotion");
|
||||||
specific.add_options()("opt-timing", "run post-placement timing optimisation pass (experimental)");
|
specific.add_options()("opt-timing", "run post-placement timing optimisation pass (experimental)");
|
||||||
specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
|
specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
|
||||||
|
specific.add_options()("pcf-allow-unconstrained", "don't require PCF to constrain all IO");
|
||||||
|
|
||||||
return specific;
|
return specific;
|
||||||
}
|
}
|
||||||
void Ice40CommandHandler::validate()
|
void Ice40CommandHandler::validate()
|
||||||
@ -87,6 +89,8 @@ void Ice40CommandHandler::customAfterLoad(Context *ctx)
|
|||||||
std::ifstream pcf(filename);
|
std::ifstream pcf(filename);
|
||||||
if (!apply_pcf(ctx, filename, pcf))
|
if (!apply_pcf(ctx, filename, pcf))
|
||||||
log_error("Loading PCF failed.\n");
|
log_error("Loading PCF failed.\n");
|
||||||
|
} else {
|
||||||
|
log_warning("No PCF file specified; IO pins will be placed automatically\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Ice40CommandHandler::customBitstream(Context *ctx)
|
void Ice40CommandHandler::customBitstream(Context *ctx)
|
||||||
@ -164,6 +168,9 @@ std::unique_ptr<Context> Ice40CommandHandler::createContext()
|
|||||||
ctx->settings[ctx->id("no_promote_globals")] = "1";
|
ctx->settings[ctx->id("no_promote_globals")] = "1";
|
||||||
if (vm.count("opt-timing"))
|
if (vm.count("opt-timing"))
|
||||||
ctx->settings[ctx->id("opt_timing")] = "1";
|
ctx->settings[ctx->id("opt_timing")] = "1";
|
||||||
|
if (vm.count("pcf-allow-unconstrained"))
|
||||||
|
ctx->settings[ctx->id("pcf_allow_unconstrained")] = "1";
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,9 +478,6 @@ static void pack_io(Context *ctx)
|
|||||||
}
|
}
|
||||||
packed_cells.insert(ci->name);
|
packed_cells.insert(ci->name);
|
||||||
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
|
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
|
||||||
if (!sb->attrs.count(ctx->id("BEL")))
|
|
||||||
log_warning("IO '%s' is not constrained to a pin and will be automatically placed\n",
|
|
||||||
ci->name.c_str(ctx));
|
|
||||||
} else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) {
|
} else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) {
|
||||||
NetInfo *net = ci->ports.at(ctx->id("PACKAGE_PIN")).net;
|
NetInfo *net = ci->ports.at(ctx->id("PACKAGE_PIN")).net;
|
||||||
if ((net != nullptr) && (net->users.size() > 1))
|
if ((net != nullptr) && (net->users.size() > 1))
|
||||||
@ -1181,20 +1178,26 @@ static void pack_special(Context *ctx)
|
|||||||
log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n", ci->name.c_str(ctx));
|
log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n", ci->name.c_str(ctx));
|
||||||
bool found_lut = false;
|
bool found_lut = false;
|
||||||
bool all_luts = true;
|
bool all_luts = true;
|
||||||
|
bool found_carry = false;
|
||||||
unsigned int lut_count = 0;
|
unsigned int lut_count = 0;
|
||||||
for (const auto &user : port.net->users) {
|
for (const auto &user : port.net->users) {
|
||||||
NPNR_ASSERT(user.cell != nullptr);
|
NPNR_ASSERT(user.cell != nullptr);
|
||||||
if (user.cell->type == ctx->id("ICESTORM_LC")) {
|
if (user.cell->type == ctx->id("ICESTORM_LC")) {
|
||||||
found_lut = true;
|
if (bool_or_default(user.cell->params, ctx->id("CARRY_ENABLE"), false)) {
|
||||||
lut_count++;
|
found_carry = true;
|
||||||
|
all_luts = false;
|
||||||
|
} else {
|
||||||
|
found_lut = true;
|
||||||
|
lut_count++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
all_luts = false;
|
all_luts = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found_lut && all_luts) {
|
if (found_lut && all_luts && lut_count < 8) {
|
||||||
// Every user is a LUT, carry on now.
|
// Every user is a LUT, carry on now.
|
||||||
} else if (found_lut && !all_luts && lut_count < 8) {
|
} else if (found_lut && !all_luts && !found_carry && lut_count < 8) {
|
||||||
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
|
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
|
||||||
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
|
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
|
||||||
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
|
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
|
||||||
|
48
ice40/pcf.cc
48
ice40/pcf.cc
@ -21,6 +21,7 @@
|
|||||||
#include "pcf.h"
|
#include "pcf.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -47,17 +48,43 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
|
|||||||
if (words.size() == 0)
|
if (words.size() == 0)
|
||||||
continue;
|
continue;
|
||||||
std::string cmd = words.at(0);
|
std::string cmd = words.at(0);
|
||||||
|
bool nowarn = false;
|
||||||
if (cmd == "set_io") {
|
if (cmd == "set_io") {
|
||||||
size_t args_end = 1;
|
size_t args_end = 1;
|
||||||
while (args_end < words.size() && words.at(args_end).at(0) == '-')
|
std::vector<std::pair<IdString, std::string>> extra_attrs;
|
||||||
|
while (args_end < words.size() && words.at(args_end).at(0) == '-') {
|
||||||
|
const auto &setting = words.at(args_end);
|
||||||
|
if (setting == "-pullup") {
|
||||||
|
const auto &value = words.at(++args_end);
|
||||||
|
if (value == "yes" || value == "1")
|
||||||
|
extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP"), "1"));
|
||||||
|
else if (value == "no" || value == "0")
|
||||||
|
extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP"), "0"));
|
||||||
|
else
|
||||||
|
log_error("Invalid value '%s' for -pullup (on line %d)\n", value.c_str(), lineno);
|
||||||
|
} else if (setting == "-pullup_resistor") {
|
||||||
|
const auto &value = words.at(++args_end);
|
||||||
|
if (ctx->args.type != ArchArgs::UP5K)
|
||||||
|
log_error("Pullup resistance can only be set on UP5K (on line %d)\n", lineno);
|
||||||
|
if (value != "3P3K" && value != "6P8K" && value != "10K" && value != "100K")
|
||||||
|
log_error("Invalid value '%s' for -pullup_resistor (on line %d)\n", value.c_str(), lineno);
|
||||||
|
extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP_RESISTOR"), value));
|
||||||
|
} else if (setting == "-nowarn") {
|
||||||
|
nowarn = true;
|
||||||
|
} else if (setting == "--warn-no-port") {
|
||||||
|
} else {
|
||||||
|
log_warning("Ignoring PCF setting '%s' (on line %d)\n", setting.c_str(), lineno);
|
||||||
|
}
|
||||||
args_end++;
|
args_end++;
|
||||||
|
}
|
||||||
if (args_end >= words.size() - 1)
|
if (args_end >= words.size() - 1)
|
||||||
log_error("expected PCF syntax 'set_io cell pin' (on line %d)\n", lineno);
|
log_error("expected PCF syntax 'set_io cell pin' (on line %d)\n", lineno);
|
||||||
std::string cell = words.at(args_end);
|
std::string cell = words.at(args_end);
|
||||||
std::string pin = words.at(args_end + 1);
|
std::string pin = words.at(args_end + 1);
|
||||||
auto fnd_cell = ctx->cells.find(ctx->id(cell));
|
auto fnd_cell = ctx->cells.find(ctx->id(cell));
|
||||||
if (fnd_cell == ctx->cells.end()) {
|
if (fnd_cell == ctx->cells.end()) {
|
||||||
log_warning("unmatched constraint '%s' (on line %d)\n", cell.c_str(), lineno);
|
if (!nowarn)
|
||||||
|
log_warning("unmatched constraint '%s' (on line %d)\n", cell.c_str(), lineno);
|
||||||
} else {
|
} else {
|
||||||
BelId pin_bel = ctx->getPackagePinBel(pin);
|
BelId pin_bel = ctx->getPackagePinBel(pin);
|
||||||
if (pin_bel == BelId())
|
if (pin_bel == BelId())
|
||||||
@ -67,11 +94,28 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
|
|||||||
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
|
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
|
||||||
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
|
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
|
||||||
fnd_cell->second->attrs[ctx->id("BEL")].c_str());
|
fnd_cell->second->attrs[ctx->id("BEL")].c_str());
|
||||||
|
for (const auto &attr : extra_attrs)
|
||||||
|
fnd_cell->second->attrs[attr.first] = attr.second;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log_error("unsupported PCF command '%s' (on line %d)\n", cmd.c_str(), lineno);
|
log_error("unsupported PCF command '%s' (on line %d)\n", cmd.c_str(), lineno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_obuf") ||
|
||||||
|
ci->type == ctx->id("$nextpnr_iobuf")) {
|
||||||
|
if (!ci->attrs.count(ctx->id("BEL"))) {
|
||||||
|
if (bool_or_default(ctx->settings, ctx->id("pcf_allow_unconstrained")))
|
||||||
|
log_warning("IO '%s' is unconstrained in PCF and will be automatically placed\n",
|
||||||
|
cell.first.c_str(ctx));
|
||||||
|
else
|
||||||
|
log_error("IO '%s' is unconstrained in PCF (override this error with "
|
||||||
|
"--pcf-allow-unconstrained)\n",
|
||||||
|
cell.first.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ctx->settings.emplace(ctx->id("input/pcf"), filename);
|
ctx->settings.emplace(ctx->id("input/pcf"), filename);
|
||||||
return true;
|
return true;
|
||||||
} catch (log_execution_error_exception) {
|
} catch (log_execution_error_exception) {
|
||||||
|
Loading…
Reference in New Issue
Block a user