Merge master

This commit is contained in:
Miodrag Milanovic 2019-06-25 18:14:51 +02:00
commit ec47ce2320
15 changed files with 157 additions and 32 deletions

View File

@ -14,6 +14,9 @@ We hope to see Xilinx 7 Series thanks to
supported in the future. We would love your help in developing this
awesome new project!
A brief (academic) paper describing the Yosys+nextpnr flow can be found
on [arXiv](https://arxiv.org/abs/1903.10407).
Here is a screenshot of nextpnr for iCE40. Build instructions and
[getting started notes](#getting-started) can be found below.

View File

@ -546,7 +546,8 @@ struct Timing
for (size_t i = 0; i < sink_net->users.size(); i++) {
auto &user = sink_net->users.at(i);
if (user.cell == drv.cell && user.port == port.first) {
sink_nd.min_required.at(i) = net_min_required - comb_delay.maxDelay();
sink_nd.min_required.at(i) = std::min(sink_nd.min_required.at(i),
net_min_required - comb_delay.maxDelay());
break;
}
}
@ -752,7 +753,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
}
if (clock_reports.empty()) {
log_warning("No clocks found in design");
log_warning("No clocks found in design\n");
}
std::sort(xclock_paths.begin(), xclock_paths.end(), [ctx](const ClockPair &a, const ClockPair &b) {

View File

@ -458,7 +458,7 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
int dx = abs(src_loc.first - dst_loc.first), dy = abs(src_loc.second - dst_loc.second);
return (130 - 25 * args.speed) *
return (120 - 22 * args.speed) *
(6 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
}
@ -487,7 +487,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
int dx = abs(driver_loc.x - sink_loc.x), dy = abs(driver_loc.y - sink_loc.y);
return (130 - 25 * args.speed) *
return (120 - 22 * args.speed) *
(6 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
}
@ -504,6 +504,8 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
}
}
delay_t Arch::getRipupDelayPenalty() const { return 400; }
// -----------------------------------------------------------------------
bool Arch::place()
@ -512,7 +514,7 @@ bool Arch::place()
if (placer == "heap") {
PlacerHeapCfg cfg(getCtx());
cfg.criticalityExponent = 7;
cfg.criticalityExponent = 4;
cfg.ioBufTypes.insert(id_TRELLIS_IO);
if (!placer_heap(getCtx(), cfg))
return false;

View File

@ -942,7 +942,7 @@ struct Arch : BaseCtx
delay_t estimateDelay(WireId src, WireId dst) const;
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 400; }
delay_t getRipupDelayPenalty() const;
float getDelayNS(delay_t v) const { return v * 0.001; }
DelayInfo getDelayFromNS(float ns) const
{

View File

@ -134,7 +134,7 @@ inline int chtohex(char c)
return hex.find(c);
}
std::vector<bool> parse_init_str(const std::string &str, int length)
std::vector<bool> parse_init_str(const std::string &str, int length, const char *cellname)
{
// Parse a string that may be binary or hex
std::vector<bool> result;
@ -161,7 +161,8 @@ std::vector<bool> parse_init_str(const std::string &str, int length)
log_error("hex string value too long, expected up to %d bits and found %d.\n", length, int(str.length()));
for (int i = 0; i < int(str.length()); i++) {
char c = str.at((str.size() - i) - 1);
NPNR_ASSERT(c == '0' || c == '1' || c == 'X' || c == 'x');
if (c != '0' && c != '1' && c != 'X' && c != 'x')
log_error("Found illegal character '%c' while processing parameters for cell '%s'\n", c, cellname);
result.at(i) = (c == '1');
}
}
@ -970,7 +971,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
for (int i = 0; i <= 0x3F; i++) {
IdString param = ctx->id("INITVAL_" +
fmt_str(std::hex << std::uppercase << std::setw(2) << std::setfill('0') << i));
auto value = parse_init_str(str_or_default(ci->params, param, "0"), 320);
auto value = parse_init_str(str_or_default(ci->params, param, "0"), 320, ci->name.c_str(ctx));
for (int j = 0; j < 16; j++) {
// INIT parameter consists of 16 18-bit words with 2-bit padding
int ofs = 20 * j;
@ -1078,17 +1079,21 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
tg.config.add_enum(dsp + ".MASKPAT_SOURCE",
str_or_default(ci->params, ctx->id("MASKPAT_SOURCE"), "STATIC"));
tg.config.add_word(dsp + ".MASK01",
parse_init_str(str_or_default(ci->params, ctx->id("MASK01"), "0x00000000000000"), 56));
parse_init_str(str_or_default(ci->params, ctx->id("MASK01"), "0x00000000000000"), 56,
ci->name.c_str(ctx)));
tg.config.add_enum(dsp + ".CLK0_DIV", str_or_default(ci->params, ctx->id("CLK0_DIV"), "ENABLED"));
tg.config.add_enum(dsp + ".CLK1_DIV", str_or_default(ci->params, ctx->id("CLK1_DIV"), "ENABLED"));
tg.config.add_enum(dsp + ".CLK2_DIV", str_or_default(ci->params, ctx->id("CLK2_DIV"), "ENABLED"));
tg.config.add_enum(dsp + ".CLK3_DIV", str_or_default(ci->params, ctx->id("CLK3_DIV"), "ENABLED"));
tg.config.add_word(dsp + ".MCPAT",
parse_init_str(str_or_default(ci->params, ctx->id("MCPAT"), "0x00000000000000"), 56));
parse_init_str(str_or_default(ci->params, ctx->id("MCPAT"), "0x00000000000000"), 56,
ci->name.c_str(ctx)));
tg.config.add_word(dsp + ".MASKPAT",
parse_init_str(str_or_default(ci->params, ctx->id("MASKPAT"), "0x00000000000000"), 56));
parse_init_str(str_or_default(ci->params, ctx->id("MASKPAT"), "0x00000000000000"), 56,
ci->name.c_str(ctx)));
tg.config.add_word(dsp + ".RNDPAT",
parse_init_str(str_or_default(ci->params, ctx->id("RNDPAT"), "0x00000000000000"), 56));
parse_init_str(str_or_default(ci->params, ctx->id("RNDPAT"), "0x00000000000000"), 56,
ci->name.c_str(ctx)));
tg.config.add_enum(dsp + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED"));
tg.config.add_enum(dsp + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC"));
tg.config.add_enum(dsp + ".FORCE_ZERO_BARREL_SHIFT",

View File

@ -20,12 +20,12 @@
*/
#include "application.h"
#include "log.h"
#include <QOpenGLContext>
#include <QMessageBox>
#include <QOpenGLContext>
#include <QSurfaceFormat>
#include <QTextStream>
#include <exception>
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN

View File

@ -1052,6 +1052,14 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
return TMG_COMB_INPUT;
} else if (cell->type == id_SB_WARMBOOT) {
return TMG_ENDPOINT;
} else if (cell->type == id_SB_LED_DRV_CUR) {
if (port == id_LEDPU)
return TMG_IGNORE;
return TMG_ENDPOINT;
} else if (cell->type == id_SB_RGB_DRV) {
if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2 || port == id_RGBPU)
return TMG_IGNORE;
return TMG_ENDPOINT;
} else if (cell->type == id_SB_RGBA_DRV) {
if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2)
return TMG_IGNORE;

View File

@ -159,6 +159,10 @@ struct ArchCellInfo
{
bool forPadIn;
} gbInfo;
struct
{
bool ledCurConnected;
} ledInfo;
};
};

View File

@ -610,6 +610,14 @@ void write_asc(const Context *ctx, std::ostream &out)
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_LED_DRV_CUR")) {
set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "LED_DRV_CUR_EN", true,
"IpConfig.");
} else if (cell.second->type == ctx->id("SB_RGB_DRV")) {
const std::vector<std::pair<std::string, int>> rgb_params = {
{"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}};
configure_extra_cell(config, ctx, cell.second.get(), rgb_params, true, std::string("IpConfig."));
set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGB_DRV_EN", true, "IpConfig.");
} else if (cell.second->type == ctx->id("SB_RGBA_DRV")) {
const std::vector<std::pair<std::string, int>> rgba_params = {
{"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}};

View File

@ -260,6 +260,22 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
add_port(ctx, new_cell.get(), "RGB0", PORT_OUT);
add_port(ctx, new_cell.get(), "RGB1", PORT_OUT);
add_port(ctx, new_cell.get(), "RGB2", PORT_OUT);
} else if (type == ctx->id("SB_LED_DRV_CUR")) {
add_port(ctx, new_cell.get(), "EN", PORT_IN);
add_port(ctx, new_cell.get(), "LEDPU", PORT_OUT);
} else if (type == ctx->id("SB_RGB_DRV")) {
new_cell->params[ctx->id("RGB0_CURRENT")] = "0b000000";
new_cell->params[ctx->id("RGB1_CURRENT")] = "0b000000";
new_cell->params[ctx->id("RGB2_CURRENT")] = "0b000000";
add_port(ctx, new_cell.get(), "RGBPU", PORT_IN);
add_port(ctx, new_cell.get(), "RGBLEDEN", PORT_IN);
add_port(ctx, new_cell.get(), "RGB0PWM", PORT_IN);
add_port(ctx, new_cell.get(), "RGB1PWM", PORT_IN);
add_port(ctx, new_cell.get(), "RGB2PWM", PORT_IN);
add_port(ctx, new_cell.get(), "RGB0", PORT_OUT);
add_port(ctx, new_cell.get(), "RGB1", PORT_OUT);
add_port(ctx, new_cell.get(), "RGB2", PORT_OUT);
} else if (type == ctx->id("SB_LEDDA_IP")) {
add_port(ctx, new_cell.get(), "LEDDCS", PORT_IN);
add_port(ctx, new_cell.get(), "LEDDCLK", PORT_IN);

View File

@ -76,6 +76,13 @@ inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell-
inline bool is_sb_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGBA_DRV"); }
inline bool is_sb_rgb_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGB_DRV"); }
inline bool is_sb_led_drv_cur(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_LED_DRV_CUR");
}
inline bool is_sb_ledda_ip(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LEDDA_IP"); }
inline bool is_sb_i2c(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_I2C"); }

View File

@ -81,6 +81,8 @@ constids["SPI"] = constids["SB_SPI"]
constids["LEDDA_IP"] = constids["SB_LEDDA_IP"]
constids["RGBA_DRV"] = constids["SB_RGBA_DRV"]
constids["SPRAM"] = constids["ICESTORM_SPRAM"]
constids["LED_DRV_CUR"] = constids["SB_LED_DRV_CUR"]
constids["RGB_DRV"] = constids["SB_RGB_DRV"]
with open(args.gfxh) as f:
state = 0

View File

@ -355,6 +355,9 @@ X(PWMOUT0)
X(PWMOUT1)
X(PWMOUT2)
X(LEDPU)
X(EN)
X(RGBPU)
X(CURREN)
X(RGB0PWM)
X(RGB1PWM)
@ -438,6 +441,8 @@ X(IO_I3C)
X(SB_LEDDA_IP)
X(SB_RGBA_DRV)
X(ICESTORM_SPRAM)
X(SB_LED_DRV_CUR)
X(SB_RGB_DRV)
// cell parameters
X(DFF_ENABLE)

View File

@ -465,7 +465,8 @@ static void pack_io(Context *ctx)
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
NetInfo *net = ci->ports.at(ctx->id("I")).net;
sb = net_only_drives(ctx, net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci);
if (net && net->driver.cell && is_sb_rgba_drv(ctx, net->driver.cell))
if (net && net->driver.cell &&
(is_sb_rgba_drv(ctx, net->driver.cell) || is_sb_rgb_drv(ctx, net->driver.cell)))
rgb = net->driver.cell;
}
if (sb != nullptr) {
@ -491,7 +492,8 @@ static void pack_io(Context *ctx)
}
}
} else if (rgb != nullptr) {
log_info("%s use by SB_RGBA_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx), rgb->name.c_str(ctx));
log_info("%s use by SB_RGBA_DRV/SB_RGB_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx),
rgb->name.c_str(ctx));
disconnect_port(ctx, ci, ctx->id("I"));
packed_cells.insert(ci->name);
continue;
@ -1053,6 +1055,27 @@ static void pack_special(Context *ctx)
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
// Handle LED_DRV_CUR first to set the ledCurConnected flag before RGB_DRV is handled below.
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_sb_led_drv_cur(ctx, ci)) {
/* Force placement (no choices anyway) */
cell_place_unique(ctx, ci);
NetInfo *ledpu_net = ci->ports.at(ctx->id("LEDPU")).net;
for (auto &user : ledpu_net->users) {
if (!is_sb_rgb_drv(ctx, user.cell)) {
log_error("SB_LED_DRV_CUR LEDPU port can only be connected to SB_RGB_DRV!\n");
} else {
user.cell->ledInfo.ledCurConnected = true;
user.cell->ports.at(user.port).net = nullptr;
}
}
ci->ports.erase(ctx->id("LEDPU"));
ctx->nets.erase(ledpu_net->name);
}
}
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_sb_lfosc(ctx, ci)) {
@ -1128,7 +1151,7 @@ static void pack_special(Context *ctx)
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_rgba_drv(ctx, ci)) {
} else if (is_sb_rgba_drv(ctx, ci) || is_sb_rgb_drv(ctx, ci)) {
/* Force placement (no choices anyway) */
cell_place_unique(ctx, ci);
@ -1140,14 +1163,20 @@ static void pack_special(Context *ctx)
if (net == nullptr)
continue;
if ((pi.name != ctx->id("RGB0")) && (pi.name != ctx->id("RGB1")) && (pi.name != ctx->id("RGB2")))
continue;
if (net->users.size() > 0)
log_error("SB_RGBA_DRV port connected to more than just package pin !\n");
log_error("SB_RGB_DRV/SB_RGBA_DRV port connected to more than just package pin !\n");
ctx->nets.erase(net->name);
}
if (is_sb_rgb_drv(ctx, ci) && !ci->ledInfo.ledCurConnected)
log_error("Port RGBPU of SB_RGB_DRV should be driven by port LEDPU of SB_LED_DRV_CUR!\n");
ci->ports.erase(ctx->id("RGBPU"));
ci->ports.erase(ctx->id("RGB0"));
ci->ports.erase(ctx->id("RGB1"));
ci->ports.erase(ctx->id("RGB2"));

View File

@ -285,7 +285,7 @@ void vcc_net(Context *ctx, NetInfo *net)
// true, false otherwise
bool is_blackbox(JsonNode *node)
{
JsonNode *attr_node, *bbox_node;
JsonNode *attr_node, *bbox_node = nullptr, *wbox_node = nullptr;
if (node->data_dict.count("attributes") == 0)
return false;
@ -296,14 +296,19 @@ bool is_blackbox(JsonNode *node)
return false;
if (GetSize(attr_node->data_dict) == 0)
return false;
if (attr_node->data_dict.count("blackbox") == 0)
if (attr_node->data_dict.count("blackbox"))
bbox_node = attr_node->data_dict.at("blackbox");
if (attr_node->data_dict.count("whitebox"))
wbox_node = attr_node->data_dict.at("whitebox");
if (bbox_node == NULL && wbox_node == NULL)
return false;
bbox_node = attr_node->data_dict.at("blackbox");
if (bbox_node == NULL)
if (bbox_node && bbox_node->type != 'N')
log_error("JSON module blackbox attribute value is not a number\n");
if (bbox_node && bbox_node->data_number == 0)
return false;
if (bbox_node->type != 'N')
log_error("JSON module blackbox is not a number\n");
if (bbox_node->data_number == 0)
if (wbox_node && wbox_node->type != 'N')
log_error("JSON module whitebox attribute value is not a number\n");
if (wbox_node && wbox_node->data_number == 0)
return false;
return true;
}
@ -381,7 +386,7 @@ static int const_net_idx = 0;
template <typename F>
void json_import_ports(Context *ctx, const string &modname, const std::vector<IdString> &netnames,
const string &obj_name, const string &port_name, JsonNode *dir_node, JsonNode *wire_group_node,
F visitor)
bool upto, int start_offset, F visitor)
{
// Examine a port of a cell or the design. For every bit of the port,
// the connected net will be processed and `visitor` will be called
@ -450,8 +455,11 @@ void json_import_ports(Context *ctx, const string &modname, const std::vector<Id
wire_node = wire_group_node->data_array[index];
//
// Pick a name for this port
int ndx = index + start_offset;
if (upto)
ndx = start_offset + wire_group_node->data_array.size() - index - 1;
if (is_bus)
this_port.name = ctx->id(port_info.name.str(ctx) + "[" + std::to_string(index) + "]");
this_port.name = ctx->id(port_info.name.str(ctx) + "[" + std::to_string(ndx) + "]");
else
this_port.name = port_info.name;
this_port.type = port_info.type;
@ -628,7 +636,7 @@ void json_import_cell(Context *ctx, string modname, const std::vector<IdString>
dir_node = pdir_node->data_dict.at(port_name);
wire_group_node = connections->data_dict.at(port_name);
json_import_ports(ctx, modname, netnames, cell->name.str(ctx), port_name, dir_node, wire_group_node,
json_import_ports(ctx, modname, netnames, cell->name.str(ctx), port_name, dir_node, wire_group_node, false, 0,
[&cell, ctx](PortType type, const std::string &name, NetInfo *net) {
cell->ports[ctx->id(name)] = PortInfo{ctx->id(name), net, type};
PortRef pr;
@ -732,8 +740,20 @@ void json_import_toplevel_port(Context *ctx, const string &modname, const std::v
{
JsonNode *dir_node = node->data_dict.at("direction");
JsonNode *nets_node = node->data_dict.at("bits");
bool upto = false;
int start_offset = 0;
if (node->data_dict.count("upto") != 0) {
JsonNode *val = node->data_dict.at("upto");
if (val->type == 'N')
upto = val->data_number != 0;
}
if (node->data_dict.count("offset") != 0) {
JsonNode *val = node->data_dict.at("offset");
if (val->type == 'N')
start_offset = val->data_number;
}
json_import_ports(
ctx, modname, netnames, "Top Level IO", portname, dir_node, nets_node,
ctx, modname, netnames, "Top Level IO", portname, dir_node, nets_node, upto, start_offset,
[ctx](PortType type, const std::string &name, NetInfo *net) { insert_iobuf(ctx, net, type, name); });
}
@ -789,6 +809,18 @@ void json_import(Context *ctx, string modname, JsonNode *node)
here = cell_parent->data_dict.at(cell_parent->data_dict_keys[nnid]);
std::string basename = cell_parent->data_dict_keys[nnid];
bool upto = false;
int start_offset = 0;
if (here->data_dict.count("upto") != 0) {
JsonNode *val = here->data_dict.at("upto");
if (val->type == 'N')
upto = val->data_number != 0;
}
if (here->data_dict.count("offset") != 0) {
JsonNode *val = here->data_dict.at("offset");
if (val->type == 'N')
start_offset = val->data_number;
}
if (here->data_dict.count("bits")) {
JsonNode *bits = here->data_dict.at("bits");
assert(bits->type == 'A');
@ -797,8 +829,11 @@ void json_import(Context *ctx, string modname, JsonNode *node)
int netid = bits->data_array.at(i)->data_number;
if (netid >= int(netlabels.size()))
netlabels.resize(netid + 1);
int ndx = i + start_offset;
if (upto)
ndx = start_offset + num_bits - i - 1;
std::string name =
basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(i) + std::string("]"));
basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(ndx) + std::string("]"));
if (prefer_netlabel(name, netlabels.at(netid)))
netlabels.at(netid) = name;
}