diff options
author | Miodrag Milanovic <mmicko@gmail.com> | 2019-06-25 18:14:51 +0200 |
---|---|---|
committer | Miodrag Milanovic <mmicko@gmail.com> | 2019-06-25 18:14:51 +0200 |
commit | ec47ce2320fdc9d698e71c07af0844f7ecc2445c (patch) | |
tree | b7a3b232daed5ef0ff7521dc6b5765613c37206b | |
parent | 9affcf82d969bf34aae449a28391451a4f4fdfd8 (diff) | |
parent | 76ff7919132a58971b197a40e7fba119f0b021f5 (diff) | |
download | nextpnr-ec47ce2320fdc9d698e71c07af0844f7ecc2445c.tar.gz nextpnr-ec47ce2320fdc9d698e71c07af0844f7ecc2445c.tar.bz2 nextpnr-ec47ce2320fdc9d698e71c07af0844f7ecc2445c.zip |
Merge master
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | common/timing.cc | 5 | ||||
-rw-r--r-- | ecp5/arch.cc | 8 | ||||
-rw-r--r-- | ecp5/arch.h | 2 | ||||
-rw-r--r-- | ecp5/bitstream.cc | 19 | ||||
-rw-r--r-- | gui/application.cc | 4 | ||||
-rw-r--r-- | ice40/arch.cc | 8 | ||||
-rw-r--r-- | ice40/archdefs.h | 4 | ||||
-rw-r--r-- | ice40/bitstream.cc | 8 | ||||
-rw-r--r-- | ice40/cells.cc | 16 | ||||
-rw-r--r-- | ice40/cells.h | 7 | ||||
-rw-r--r-- | ice40/chipdb.py | 2 | ||||
-rw-r--r-- | ice40/constids.inc | 5 | ||||
-rw-r--r-- | ice40/pack.cc | 37 | ||||
-rw-r--r-- | json/jsonparse.cc | 61 |
15 files changed, 157 insertions, 32 deletions
@@ -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. diff --git a/common/timing.cc b/common/timing.cc index 582c347f..60aef3fa 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -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) { diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 02f73ca2..5b3cc660 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -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; diff --git a/ecp5/arch.h b/ecp5/arch.h index 3de06a42..cee071e7 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -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 { diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index a9c82524..d549a727 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -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", diff --git a/gui/application.cc b/gui/application.cc index d5f0f6a9..33a106bc 100644 --- a/gui/application.cc +++ b/gui/application.cc @@ -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 diff --git a/ice40/arch.cc b/ice40/arch.cc index a0c64bb8..d58951dd 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -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; diff --git a/ice40/archdefs.h b/ice40/archdefs.h index 956fcb4c..89591af5 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -159,6 +159,10 @@ struct ArchCellInfo { bool forPadIn; } gbInfo; + struct + { + bool ledCurConnected; + } ledInfo; }; }; diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 9b85dff5..7632b443 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -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}}; diff --git a/ice40/cells.cc b/ice40/cells.cc index 5744fe50..a2abcea4 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -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); diff --git a/ice40/cells.h b/ice40/cells.h index ec4d560d..3d9358da 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -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"); } diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 42ca6ac1..cc7be01f 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -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 diff --git a/ice40/constids.inc b/ice40/constids.inc index 366a3a9d..6aa5c4c0 100644 --- a/ice40/constids.inc +++ b/ice40/constids.inc @@ -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) diff --git a/ice40/pack.cc b/ice40/pack.cc index a8f8c76e..66c5c0e8 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -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")); diff --git a/json/jsonparse.cc b/json/jsonparse.cc index 81b0c3be..29dcac0a 100644 --- a/json/jsonparse.cc +++ b/json/jsonparse.cc @@ -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,9 +740,21 @@ 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](PortType type, const std::string &name, NetInfo *net) { insert_iobuf(ctx, net, type, name); }); + 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); }); } void json_import(Context *ctx, string modname, JsonNode *node) @@ -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; } |