diff --git a/common/design_utils.h b/common/design_utils.h index 2acc7d20..daf6e050 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -22,6 +22,8 @@ #ifndef DESIGN_UTILS_H #define DESIGN_UTILS_H +#include + NEXTPNR_NAMESPACE_BEGIN /* @@ -35,23 +37,36 @@ void replace_port(CellInfo *old_cell, IdString old_name, CellInfo *rep_cell, // If a net drives a given port of a cell matching a predicate (in many // cases more than one cell type, e.g. SB_DFFxx so a predicate is used), return // the first instance of that cell (otherwise nullptr). If exclusive is set to -// true, then this cell must be the only load +// true, then this cell must be the only load. If ignore_cell is set, that cell +// is not considered template CellInfo *net_only_drives(NetInfo *net, F1 cell_pred, IdString port, - bool exclusive = false) + bool exclusive = false, CellInfo *exclude = nullptr) { if (net == nullptr) return nullptr; - if (exclusive && (net->users.size() != 1)) { - return nullptr; - } else { - for (const auto &load : net->users) { - if (cell_pred(load.cell) && load.port == port) { - return load.cell; + if (exclusive) { + if (exclude == nullptr) { + if (net->users.size() != 1) + return nullptr; + } else { + if (net->users.size() > 2) { + return nullptr; + } else if (net->users.size() == 2) { + if (std::find_if(net->users.begin(), net->users.end(), + [exclude](const PortRef &ref) { + return ref.cell == exclude; + }) == net->users.end()) + return nullptr; } } - return nullptr; } + for (const auto &load : net->users) { + if (load.cell != exclude && cell_pred(load.cell) && load.port == port) { + return load.cell; + } + } + return nullptr; } // If a net is driven by a given port of a cell matching a predicate, return diff --git a/frontend/json/jsonparse.cc b/frontend/json/jsonparse.cc index df3a298e..79ee0a4d 100644 --- a/frontend/json/jsonparse.cc +++ b/frontend/json/jsonparse.cc @@ -693,12 +693,16 @@ static void insert_iobuf(Design *design, NetInfo *net, PortType type, design->cells[iobuf->name] = iobuf; } -void json_import_toplevel_port(Design *design, const string &modname, const string& portname, JsonNode *node) { +void json_import_toplevel_port(Design *design, const string &modname, + const string &portname, JsonNode *node) +{ JsonNode *dir_node = node->data_dict.at("direction"); JsonNode *nets_node = node->data_dict.at("bits"); - json_import_ports(design, modname, "Top Level IO", portname, dir_node, nets_node, [design](PortType type, const std::string &name, NetInfo *net){ - insert_iobuf(design, net, type, name); - }); + json_import_ports( + design, modname, "Top Level IO", portname, dir_node, nets_node, + [design](PortType type, const std::string &name, NetInfo *net) { + insert_iobuf(design, net, type, name); + }); } void json_import(Design *design, string modname, JsonNode *node) @@ -738,7 +742,9 @@ void json_import(Design *design, string modname, JsonNode *node) here = ports_parent->data_dict.at( ports_parent->data_dict_keys[portid]); - json_import_toplevel_port(design, modname, ports_parent->data_dict_keys[portid], here); + json_import_toplevel_port(design, modname, + ports_parent->data_dict_keys[portid], + here); } } check_all_nets_driven(design); diff --git a/ice40/cells.h b/ice40/cells.h index 34a034cd..da4e2bd8 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -47,6 +47,9 @@ inline bool is_ff(const CellInfo *cell) cell->type == "SB_DFFNESS" || cell->type == "SB_DFFNES"; } +// Return true if a cell is a SB_IO +inline bool is_sb_io(const CellInfo *cell) { return cell->type == "SB_IO"; } + // Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports // as needed. Set no_dff if a DFF is not being used, so that the output // can be reconnected diff --git a/ice40/pack.cc b/ice40/pack.cc index 8f770a07..73ba0e3d 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -134,10 +134,69 @@ static void pack_constants(Design *design) } } +static bool is_nextpnr_iob(CellInfo *cell) +{ + return cell->type == "$nextpnr_ibuf" || cell->type == "$nextpnr_obuf" || + cell->type == "$nextpnr_iobuf"; +} + +// Pack IO buffers +static void pack_io(Design *design) +{ + std::unordered_set packed_cells; + std::vector new_cells; + + for (auto cell : design->cells) { + CellInfo *ci = cell.second; + if (is_nextpnr_iob(ci)) { + if (ci->type == "$nextpnr_ibuf" || ci->type == "$nextpnr_iobuf") { + CellInfo *sb = net_only_drives(ci->ports.at("O").net, is_sb_io, + "PACKAGE_PIN", true, ci); + if (sb != nullptr) { + // Trivial case, SB_IO used. Just destroy the net and the + // iobuf + packed_cells.insert(ci->name); + log_info("%s feeds SB_IO %s, removing %s %s.\n", + ci->name.c_str(), sb->name.c_str(), + ci->type.c_str(), ci->name.c_str()); + NetInfo *net = sb->ports.at("PACKAGE_PIN").net; + if (net != nullptr) { + design->nets.erase(net->name); + sb->ports.at("PACKAGE_PIN").net = nullptr; + } + } + } else if (ci->type == "$nextpnr_obuf") { + CellInfo *sb = net_only_drives(ci->ports.at("I").net, is_sb_io, + "PACKAGE_PIN", true, ci); + if (sb != nullptr) { + // Trivial case, SB_IO used. Just destroy the net and the + // iobuf + packed_cells.insert(ci->name); + log_info("%s feeds SB_IO %s, removing %s %s.\n", + ci->name.c_str(), sb->name.c_str(), + ci->type.c_str(), ci->name.c_str()); + NetInfo *net = sb->ports.at("PACKAGE_PIN").net; + if (net != nullptr) { + design->nets.erase(net->name); + sb->ports.at("PACKAGE_PIN").net = nullptr; + } + } + } + } + } + for (auto pcell : packed_cells) { + design->cells.erase(pcell); + } + for (auto ncell : new_cells) { + design->cells[ncell->name] = ncell; + } +} + // Main pack function void pack_design(Design *design) { pack_constants(design); + pack_io(design); pack_lut_lutffs(design); pack_nonlut_ffs(design); } diff --git a/ice40/pack_tests/io_wrapper.v b/ice40/pack_tests/io_wrapper.v index b58d6c0c..e10ec419 100644 --- a/ice40/pack_tests/io_wrapper.v +++ b/ice40/pack_tests/io_wrapper.v @@ -153,7 +153,7 @@ module io_wrapper(input clk_pin, cen_pin, rst_pin, ina_pin, inb_pin, .PULLUP(1'b0), .NEG_TRIGGER(1'b0) ) outd_iob ( - .PACKAGE_PIN(outa_pin), + .PACKAGE_PIN(outd_pin), .LATCH_INPUT_VALUE(), .CLOCK_ENABLE(), .INPUT_CLK(),