aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Shah <davey1576@gmail.com>2018-06-13 10:50:05 +0200
committerDavid Shah <davey1576@gmail.com>2018-06-13 10:50:05 +0200
commita76f5c5678980c8b2e958252a68ba03676d63229 (patch)
treea9f32a39a91164b36ca55d567baf177bbaece1cc
parentddf549b117987c1e52412b58531c48a6050b51d1 (diff)
downloadnextpnr-a76f5c5678980c8b2e958252a68ba03676d63229.tar.gz
nextpnr-a76f5c5678980c8b2e958252a68ba03676d63229.tar.bz2
nextpnr-a76f5c5678980c8b2e958252a68ba03676d63229.zip
Remove IO buffers when fed by SB_IO
Signed-off-by: David Shah <davey1576@gmail.com>
-rw-r--r--common/design_utils.h33
-rw-r--r--frontend/json/jsonparse.cc16
-rw-r--r--ice40/cells.h3
-rw-r--r--ice40/pack.cc59
-rw-r--r--ice40/pack_tests/io_wrapper.v2
5 files changed, 98 insertions, 15 deletions
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 <algorithm>
+
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 <typename F1>
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<IdString> packed_cells;
+ std::vector<CellInfo *> 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(),