aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.github/ci/build_interchange.sh2
-rw-r--r--common/dynamic_bitarray.h129
-rw-r--r--common/property.h2
-rw-r--r--fpga_interchange/arch.cc77
-rw-r--r--fpga_interchange/arch.h8
-rw-r--r--fpga_interchange/cell_parameters.cc222
-rw-r--r--fpga_interchange/cell_parameters.h55
-rw-r--r--fpga_interchange/chipdb.h32
-rw-r--r--fpga_interchange/site_arch.h7
-rw-r--r--fpga_interchange/site_arch.impl.h45
-rw-r--r--fpga_interchange/site_router.cc26
-rw-r--r--fpga_interchange/site_routing_cache.cc16
-rw-r--r--fpga_interchange/site_routing_cache.h8
13 files changed, 577 insertions, 52 deletions
diff --git a/.github/ci/build_interchange.sh b/.github/ci/build_interchange.sh
index 14b129fa..ec70f478 100755
--- a/.github/ci/build_interchange.sh
+++ b/.github/ci/build_interchange.sh
@@ -26,7 +26,7 @@ popd
RAPIDWRIGHT_PATH="`pwd`/RapidWright"
INTERCHANGE_SCHEMA_PATH="`pwd`/3rdparty/fpga-interchange-schema/interchange"
PYTHON_INTERCHANGE_PATH="`pwd`/python-fpga-interchange"
-PYTHON_INTERCHANGE_TAG="v0.0.2"
+PYTHON_INTERCHANGE_TAG="v0.0.3"
# Install python-fpga-interchange libraries
git clone -b $PYTHON_INTERCHANGE_TAG https://github.com/SymbiFlow/python-fpga-interchange.git $PYTHON_INTERCHANGE_PATH
diff --git a/common/dynamic_bitarray.h b/common/dynamic_bitarray.h
index 605d59d5..be41835b 100644
--- a/common/dynamic_bitarray.h
+++ b/common/dynamic_bitarray.h
@@ -24,6 +24,8 @@
#include <limits>
#include <vector>
+#include "log.h"
+#include "nextpnr_assertions.h"
#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -73,6 +75,133 @@ template <typename Storage = std::vector<uint8_t>> class DynamicBitarray
void clear() { return storage.clear(); }
+ // Convert IntType to a DynamicBitarray of sufficent width
+ template <typename IntType> static DynamicBitarray<Storage> to_bitarray(const IntType &value)
+ {
+ if (std::numeric_limits<IntType>::is_signed) {
+ if (value < 0) {
+ log_error("Expected position value, got %s\n", std::to_string(value).c_str());
+ }
+ }
+
+ DynamicBitarray<Storage> result;
+ result.resize(std::numeric_limits<IntType>::digits);
+ result.fill(false);
+
+ // Use a 1 of the right type (for shifting)
+ IntType one(1);
+
+ for (size_t i = 0; i < std::numeric_limits<IntType>::digits; ++i) {
+ if ((value & (one << i)) != 0) {
+ result.set(i, true);
+ }
+ }
+
+ return result;
+ }
+
+ // Convert binary bitstring to a DynamicBitarray of sufficent width
+ //
+ // string must be satisfy the following regex:
+ //
+ // [01]+
+ //
+ // width can either be specified explicitly, or -1 to use a size wide
+ // enough to store the given string.
+ //
+ // If the width is specified and the width is insufficent it will result
+ // in an error.
+ static DynamicBitarray<Storage> parse_binary_bitstring(int width, const std::string &bits)
+ {
+ NPNR_ASSERT(width == -1 || width > 0);
+
+ DynamicBitarray<Storage> result;
+ // If no width was supplied, use the width from the input data.
+ if (width == -1) {
+ width = bits.size();
+ }
+
+ NPNR_ASSERT(width >= 0);
+ if ((size_t)width < bits.size()) {
+ log_error("String '%s' is wider than specified width %d\n", bits.c_str(), width);
+ }
+ result.resize(width);
+ result.fill(false);
+
+ for (size_t i = 0; i < bits.size(); ++i) {
+ // bits[0] is the MSB!
+ size_t index = width - 1 - i;
+ if (!(bits[i] == '1' || bits[i] == '0')) {
+ log_error("String '%s' is not a valid binary bitstring?\n", bits.c_str());
+ }
+ result.set(index, bits[i] == '1');
+ }
+
+ return result;
+ }
+
+ // Convert hex bitstring to a DynamicBitarray of sufficent width
+ //
+ // string must be satisfy the following regex:
+ //
+ // [0-9a-fA-F]+
+ //
+ // width can either be specified explicitly, or -1 to use a size wide
+ // enough to store the given string.
+ //
+ // If the width is specified and the width is insufficent it will result
+ // in an error.
+ static DynamicBitarray<Storage> parse_hex_bitstring(int width, const std::string &bits)
+ {
+ NPNR_ASSERT(width == -1 || width > 0);
+
+ DynamicBitarray<Storage> result;
+ // If no width was supplied, use the width from the input data.
+ if (width == -1) {
+ // Each character is 4 bits!
+ width = bits.size() * 4;
+ }
+
+ NPNR_ASSERT(width >= 0);
+ int rem = width % 4;
+ size_t check_width = width;
+ if (rem != 0) {
+ check_width += (4 - rem);
+ }
+ if (check_width < bits.size() * 4) {
+ log_error("String '%s' is wider than specified width %d (check_width = %zu)\n", bits.c_str(), width,
+ check_width);
+ }
+
+ result.resize(width);
+ result.fill(false);
+
+ size_t index = 0;
+ for (auto nibble_iter = bits.rbegin(); nibble_iter != bits.rend(); ++nibble_iter) {
+ char nibble = *nibble_iter;
+
+ int value;
+ if (nibble >= '0' && nibble <= '9') {
+ value = nibble - '0';
+ } else if (nibble >= 'a' && nibble <= 'f') {
+ value = 10 + (nibble - 'a');
+ } else if (nibble >= 'A' && nibble <= 'F') {
+ value = 10 + (nibble - 'A');
+ } else {
+ log_error("Invalid hex string '%s'?\n", bits.c_str());
+ }
+ NPNR_ASSERT(value >= 0);
+ NPNR_ASSERT(value < 16);
+
+ // Insert nibble into bitarray.
+ for (size_t i = 0; i < 4; ++i) {
+ result.set(index++, (value & (1 << i)) != 0);
+ }
+ }
+
+ return result;
+ }
+
private:
Storage storage;
};
diff --git a/common/property.h b/common/property.h
index 79fbf881..7a377b97 100644
--- a/common/property.h
+++ b/common/property.h
@@ -78,7 +78,7 @@ struct Property
result.push_back(c == S1);
return result;
}
- std::string as_string() const
+ const std::string &as_string() const
{
NPNR_ASSERT(is_string);
return str;
diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc
index d3bc9b40..a8b62f95 100644
--- a/fpga_interchange/arch.cc
+++ b/fpga_interchange/arch.cc
@@ -283,6 +283,7 @@ void Arch::init()
lookahead.init(getCtx(), getCtx());
#endif
dedicated_interconnect.init(getCtx());
+ cell_parameters.init(getCtx());
}
// -----------------------------------------------------------------------
@@ -1098,14 +1099,14 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants)
for (const auto &parameter_pin_map : cell_pin_map.parameter_pins) {
IdString param_key(parameter_pin_map.key);
- std::string param_value = IdString(parameter_pin_map.value).c_str(this);
+ IdString param_value(parameter_pin_map.value);
auto iter = cell->params.find(param_key);
if (iter == cell->params.end()) {
continue;
}
- if (param_value != iter->second.as_string()) {
+ if (!cell_parameters.compare_property(getCtx(), cell->type, param_key, iter->second, param_value)) {
continue;
}
@@ -1404,44 +1405,6 @@ void Arch::report_invalid_bel(BelId bel, CellInfo *cell) const
nameOfBel(bel), mapping);
}
-void Arch::read_lut_equation(DynamicBitarray<> *equation, const Property &equation_parameter) const
-{
- equation->fill(false);
- std::string eq_str = equation_parameter.as_string();
- std::smatch results;
- if (std::regex_match(eq_str, results, raw_bin_constant)) {
- size_t bit_idx = 0;
- const std::string &bits = results[0];
- NPNR_ASSERT(bits.size() <= equation->size());
- for (auto bit = bits.rbegin(); bit != bits.rend(); ++bit) {
- if (*bit == '0') {
- equation->set(bit_idx++, false);
- } else {
- NPNR_ASSERT(*bit == '1');
- equation->set(bit_idx++, true);
- }
- }
- } else if (std::regex_match(eq_str, results, verilog_bin_constant)) {
- int iwidth = std::stoi(results[1]);
- NPNR_ASSERT(iwidth >= 0);
- size_t width = iwidth;
- std::string bits = results[2];
- NPNR_ASSERT(width <= equation->size());
- NPNR_ASSERT(bits.size() <= width);
- size_t bit_idx = 0;
- for (auto bit = bits.rbegin(); bit != bits.rend(); ++bit) {
- if (*bit == '0') {
- equation->set(bit_idx++, false);
- } else {
- NPNR_ASSERT(*bit == '1');
- equation->set(bit_idx++, true);
- }
- }
- } else {
- NPNR_ASSERT(false);
- }
-}
-
void Arch::decode_lut_cells()
{
for (auto &cell_pair : cells) {
@@ -1464,7 +1427,8 @@ void Arch::decode_lut_cells()
IdString equation_parameter(lut_cell.parameter);
const Property &equation = cell->params.at(equation_parameter);
cell->lut_cell.equation.resize(1 << cell->lut_cell.pins.size());
- read_lut_equation(&cell->lut_cell.equation, equation);
+
+ cell->lut_cell.equation = cell_parameters.parse_int_like(getCtx(), cell->type, equation_parameter, equation);
}
}
@@ -1804,6 +1768,37 @@ bool Arch::checkPipAvail(PipId pip) const { return checkPipAvailForNet(pip, null
std::string Arch::get_chipdb_hash() const { return chipdb_hash; }
+bool Arch::is_inverting(PipId pip) const
+{
+ auto &tile_type = loc_info(chip_info, pip);
+ auto &pip_info = tile_type.pip_data[pip.index];
+ if (pip_info.site == -1) {
+ // FIXME: Some routing pips are inverters, but this is missing from
+ // the chipdb.
+ return false;
+ }
+
+ auto &bel_data = tile_type.bel_data[pip_info.bel];
+
+ // Is a fixed inverter if the non_inverting_pin is another pin.
+ return bel_data.non_inverting_pin != pip_info.extra_data && bel_data.inverting_pin == pip_info.extra_data;
+}
+
+bool Arch::can_invert(PipId pip) const
+{
+ auto &tile_type = loc_info(chip_info, pip);
+ auto &pip_info = tile_type.pip_data[pip.index];
+ if (pip_info.site == -1) {
+ return false;
+ }
+
+ auto &bel_data = tile_type.bel_data[pip_info.bel];
+
+ // Can optionally invert if this pip is both the non_inverting_pin and
+ // inverting pin.
+ return bel_data.non_inverting_pin == pip_info.extra_data && bel_data.inverting_pin == pip_info.extra_data;
+}
+
// Instance constraint templates.
template void Arch::ArchConstraints::bindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange);
template void Arch::ArchConstraints::unbindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange);
diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h
index 70862b1b..0522034f 100644
--- a/fpga_interchange/arch.h
+++ b/fpga_interchange/arch.h
@@ -34,6 +34,7 @@
#include "relptr.h"
#include "arch_iterators.h"
+#include "cell_parameters.h"
#include "chipdb.h"
#include "dedicated_interconnect.h"
#include "lookahead.h"
@@ -1027,6 +1028,12 @@ struct Arch : ArchAPI<ArchRanges>
return wire_data.site != -1;
}
+ // Does this pip always invert its signal?
+ bool is_inverting(PipId pip) const;
+
+ // Can this pip optional invert its signal?
+ bool can_invert(PipId pip) const;
+
void merge_constant_nets();
void report_invalid_bel(BelId bel, CellInfo *cell) const;
@@ -1047,6 +1054,7 @@ struct Arch : ArchAPI<ArchRanges>
Lookahead lookahead;
mutable RouteNodeStorage node_storage;
mutable SiteRoutingCache site_routing_cache;
+ CellParameters cell_parameters;
std::string chipdb_hash;
std::string get_chipdb_hash() const;
diff --git a/fpga_interchange/cell_parameters.cc b/fpga_interchange/cell_parameters.cc
new file mode 100644
index 00000000..e008fea8
--- /dev/null
+++ b/fpga_interchange/cell_parameters.cc
@@ -0,0 +1,222 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2021 Symbiflow Authors
+ *
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "cell_parameters.h"
+
+#include <limits>
+
+#include "DeviceResources.capnp.h"
+#include "context.h"
+#include "log.h"
+#include "nextpnr_assertions.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+CellParameters::CellParameters()
+ // 1'b0
+ : verilog_binary_re("([1-9][0-9]*)'b([01]+)$"),
+ // 8'hF
+ verilog_hex_re("([1-9][0-9]*)'h([0-9a-fA-F]+)$"),
+ // 0b10
+ c_binary_re("0b([01]+)$"),
+ // 0xF
+ c_hex_re("0x([0-9a-fA-F]+)$")
+{
+}
+
+void CellParameters::init(const Context *ctx)
+{
+ for (const CellParameterPOD &cell_parameter : ctx->chip_info->cell_map->cell_parameters) {
+ IdString cell_type(cell_parameter.cell_type);
+ IdString parameter(cell_parameter.parameter);
+ auto result = parameters.emplace(std::make_pair(cell_type, parameter), &cell_parameter);
+ NPNR_ASSERT(result.second);
+ }
+}
+
+static bool parse_int(const std::string &data, int64_t *result)
+{
+ NPNR_ASSERT(result != nullptr);
+ try {
+ *result = boost::lexical_cast<int64_t>(data);
+ return true;
+ } catch (boost::bad_lexical_cast &e) {
+ return false;
+ }
+}
+
+DynamicBitarray<> CellParameters::parse_int_like(const Context *ctx, IdString cell_type, IdString parameter,
+ const Property &property) const
+{
+ const CellParameterPOD *definition = parameters.at(std::make_pair(cell_type, parameter));
+ DeviceResources::Device::ParameterFormat format;
+ format = static_cast<DeviceResources::Device::ParameterFormat>(definition->format);
+
+ DynamicBitarray<> result;
+ switch (format) {
+ case DeviceResources::Device::ParameterFormat::BOOLEAN:
+ result.resize(1);
+ if (property.is_string) {
+ if (property.as_string() == "TRUE" || property.as_string() == "1") {
+ result.set(0, true);
+ } else if (property.as_string() == "FALSE" || property.as_string() == "0") {
+ result.set(0, false);
+ } else {
+ log_error("Property value %s not expected for BOOLEAN type.\n", property.c_str());
+ }
+ } else {
+ if (property.intval == 1) {
+ result.set(0, true);
+ } else if (property.intval == 0) {
+ result.set(0, false);
+ } else {
+ log_error("Property value %lu not expected for BOOLEAN type.\n", property.intval);
+ }
+ }
+
+ return result;
+ case DeviceResources::Device::ParameterFormat::INTEGER:
+ if (property.is_string) {
+ char *endptr;
+ std::uintmax_t value = strtoumax(property.c_str(), &endptr, /*base=*/10);
+ if (endptr != (property.c_str() + property.size())) {
+ log_error("Property value %s not expected for INTEGER type.\n", property.c_str());
+ }
+
+ return DynamicBitarray<>::to_bitarray(value);
+ } else {
+ return DynamicBitarray<>::to_bitarray(property.intval);
+ }
+ break;
+ case DeviceResources::Device::ParameterFormat::VERILOG_BINARY:
+ if (property.is_string) {
+ std::smatch m;
+ if (!std::regex_match(property.as_string(), m, verilog_binary_re)) {
+ log_error("Property value %s not expected for VERILOG_BINARY type.\n", property.c_str());
+ }
+
+ int64_t width;
+ if (!parse_int(m[1], &width)) {
+ log_error("Failed to parse width from property value %s of type VERILOG_BINARY.\n", property.c_str());
+ }
+ if (width < 0) {
+ log_error("Expected width to be positive for property value %s\n", property.c_str());
+ }
+
+ return DynamicBitarray<>::parse_binary_bitstring(width, m[2]);
+ } else {
+ return DynamicBitarray<>::to_bitarray(property.intval);
+ }
+ break;
+ case DeviceResources::Device::ParameterFormat::VERILOG_HEX:
+ if (property.is_string) {
+ std::smatch m;
+ if (!std::regex_match(property.as_string(), m, verilog_hex_re)) {
+ log_error("Property value %s not expected for VERILOG_HEX type.\n", property.c_str());
+ }
+
+ int64_t width;
+ if (!parse_int(m[1], &width)) {
+ log_error("Failed to parse width from property value %s of type VERILOG_HEX.\n", property.c_str());
+ }
+ if (width < 0) {
+ log_error("Expected width to be positive for property value %s\n", property.c_str());
+ }
+
+ return DynamicBitarray<>::parse_hex_bitstring(width, m[2]);
+ } else {
+ return DynamicBitarray<>::to_bitarray(property.intval);
+ }
+ break;
+ case DeviceResources::Device::ParameterFormat::C_BINARY:
+ if (property.is_string) {
+ std::smatch m;
+ if (!std::regex_match(property.as_string(), m, c_binary_re)) {
+ log_error("Property value %s not expected for C_BINARY type.\n", property.c_str());
+ }
+
+ return DynamicBitarray<>::parse_binary_bitstring(/*width=*/-1, m[1]);
+ } else {
+ return DynamicBitarray<>::to_bitarray(property.intval);
+ }
+ break;
+ case DeviceResources::Device::ParameterFormat::C_HEX:
+ if (property.is_string) {
+ std::smatch m;
+ if (!std::regex_match(property.as_string(), m, c_hex_re)) {
+ log_error("Property value %s not expected for C_HEX type.\n", property.c_str());
+ }
+
+ return DynamicBitarray<>::parse_hex_bitstring(/*width=*/-1, m[1]);
+ } else {
+ return DynamicBitarray<>::to_bitarray(property.intval);
+ }
+ break;
+ default:
+ log_error("Format %d is not int-like\n", definition->format);
+ }
+
+ // Unreachable!
+ NPNR_ASSERT(false);
+}
+
+bool CellParameters::compare_property(const Context *ctx, IdString cell_type, IdString parameter,
+ const Property &property, IdString value_to_compare) const
+{
+ const CellParameterPOD *definition = parameters.at(std::make_pair(cell_type, parameter));
+ DeviceResources::Device::ParameterFormat format;
+ format = static_cast<DeviceResources::Device::ParameterFormat>(definition->format);
+
+ switch (format) {
+ case DeviceResources::Device::ParameterFormat::STRING:
+ return value_to_compare.c_str(ctx) == property.as_string();
+ case DeviceResources::Device::ParameterFormat::FLOATING_POINT:
+ // Note: Comparing floating point is pretty weird
+ log_warning("Doing direct comparisions on floating points values is pretty weird, double check this. Cell "
+ "type %s parameter %s\n",
+ cell_type.c_str(ctx), parameter.c_str(ctx));
+ return value_to_compare.c_str(ctx) == property.as_string();
+ case DeviceResources::Device::ParameterFormat::BOOLEAN:
+ case DeviceResources::Device::ParameterFormat::INTEGER:
+ case DeviceResources::Device::ParameterFormat::VERILOG_BINARY:
+ case DeviceResources::Device::ParameterFormat::VERILOG_HEX:
+ case DeviceResources::Device::ParameterFormat::C_BINARY:
+ case DeviceResources::Device::ParameterFormat::C_HEX: {
+ if (property.is_string) {
+ // Given that string presentations should be equivalent if
+ // formatted consistently, this should work most and or all of
+ // the time. If there are important exceptions, revisit this.
+ return property.as_string() == value_to_compare.c_str(ctx);
+ } else {
+ int64_t int_to_compare;
+ if (!parse_int(value_to_compare.c_str(ctx), &int_to_compare)) {
+ log_error("Comparision failed, to compare value %s is not int-like\n", value_to_compare.c_str(ctx));
+ }
+
+ return property.intval == int_to_compare;
+ }
+ }
+ }
+
+ // Unreachable!
+ NPNR_ASSERT(false);
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/fpga_interchange/cell_parameters.h b/fpga_interchange/cell_parameters.h
new file mode 100644
index 00000000..3507a81f
--- /dev/null
+++ b/fpga_interchange/cell_parameters.h
@@ -0,0 +1,55 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2021 Symbiflow Authors
+ *
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef CELL_PARAMETERS_H
+#define CELL_PARAMETERS_H
+
+#include <regex>
+
+#include "chipdb.h"
+#include "dynamic_bitarray.h"
+#include "hash_table.h"
+#include "nextpnr_namespaces.h"
+#include "property.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct Context;
+
+struct CellParameters
+{
+ CellParameters();
+ void init(const Context *ctx);
+ DynamicBitarray<> parse_int_like(const Context *ctx, IdString cell_type, IdString parameter,
+ const Property &property) const;
+ bool compare_property(const Context *ctx, IdString cell_type, IdString parameter, const Property &property,
+ IdString value_to_compare) const;
+
+ HashTables::HashMap<std::pair<IdString, IdString>, const CellParameterPOD *> parameters;
+
+ std::regex verilog_binary_re;
+ std::regex verilog_hex_re;
+ std::regex c_binary_re;
+ std::regex c_hex_re;
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* CELL_PARAMETERS_H */
diff --git a/fpga_interchange/chipdb.h b/fpga_interchange/chipdb.h
index 2f82dcc9..5c9a9c52 100644
--- a/fpga_interchange/chipdb.h
+++ b/fpga_interchange/chipdb.h
@@ -34,7 +34,7 @@ NEXTPNR_NAMESPACE_BEGIN
* kExpectedChipInfoVersion
*/
-static constexpr int32_t kExpectedChipInfoVersion = 2;
+static constexpr int32_t kExpectedChipInfoVersion = 4;
// Flattened site indexing.
//
@@ -71,6 +71,15 @@ NPNR_PACKED_STRUCT(struct BelInfoPOD {
int8_t lut_element;
RelPtr<int32_t> pin_map; // Index into CellMapPOD::cell_bel_map
+
+ // If this BEL is a site routing BEL with inverting pins, these values
+ // will be [0, num_bel_wires). If this BEL is either not a site routing
+ // BEL or this site routing has no inversion capabilities, then these will
+ // both be -1.
+ int8_t non_inverting_pin;
+ int8_t inverting_pin;
+
+ int16_t padding;
});
enum BELCategory
@@ -198,6 +207,14 @@ NPNR_PACKED_STRUCT(struct CellConstraintPOD {
RelSlice<int32_t> states; // State indicies
});
+// Cell parameters metadata
+NPNR_PACKED_STRUCT(struct CellParameterPOD {
+ int32_t cell_type; // constid
+ int32_t parameter; // constid
+ int32_t format; // ParameterFormat enum
+ int32_t default_value; // constid
+});
+
NPNR_PACKED_STRUCT(struct CellBelMapPOD {
RelSlice<CellBelPinPOD> common_pins;
RelSlice<ParameterPinsPOD> parameter_pins;
@@ -218,6 +235,7 @@ NPNR_PACKED_STRUCT(struct CellMapPOD {
RelSlice<CellBelMapPOD> cell_bel_map;
RelSlice<LutCellPOD> lut_cells;
+ RelSlice<CellParameterPOD> cell_parameters;
});
NPNR_PACKED_STRUCT(struct PackagePinPOD {
@@ -252,6 +270,10 @@ NPNR_PACKED_STRUCT(struct ConstantsPOD {
// Name to use for the global VCC constant net
int32_t vcc_net_name; // constid
+
+ // If a choice is available, which constant net should be used?
+ // Can be ''/0 if either constant net are equivilent.
+ int32_t best_constant_net; // constid
});
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
@@ -306,6 +328,14 @@ inline const SiteInstInfoPOD &site_inst_info(const ChipInfoPOD *chip_info, int32
return chip_info->sites[chip_info->tiles[tile].sites[site]];
}
+enum SyntheticType
+{
+ NOT_SYNTH = 0,
+ SYNTH_SIGNAL = 1,
+ SYNTH_GND = 2,
+ SYNTH_VCC = 3,
+};
+
NEXTPNR_NAMESPACE_END
#endif /* CHIPDB_H */
diff --git a/fpga_interchange/site_arch.h b/fpga_interchange/site_arch.h
index f8524586..95b6fcba 100644
--- a/fpga_interchange/site_arch.h
+++ b/fpga_interchange/site_arch.h
@@ -289,6 +289,12 @@ struct SiteArch
inline SiteWire getPipSrcWire(const SitePip &site_pip) const NPNR_ALWAYS_INLINE;
inline SiteWire getPipDstWire(const SitePip &site_pip) const NPNR_ALWAYS_INLINE;
+ // Does this site pip always invert its signal?
+ inline bool isInverting(const SitePip &site_pip) const NPNR_ALWAYS_INLINE;
+
+ // Can this site pip optional invert its signal?
+ inline bool canInvert(const SitePip &site_pip) const NPNR_ALWAYS_INLINE;
+
inline SitePipDownhillRange getPipsDownhill(const SiteWire &site_wire) const NPNR_ALWAYS_INLINE;
inline SitePipUphillRange getPipsUphill(const SiteWire &site_wire) const NPNR_ALWAYS_INLINE;
SiteWireRange getWires() const;
@@ -341,6 +347,7 @@ struct SiteArch
void archcheck();
bool is_pip_synthetic(const SitePip &pip) const NPNR_ALWAYS_INLINE;
+ SyntheticType pip_synthetic_type(const SitePip &pip) const NPNR_ALWAYS_INLINE;
};
struct SitePipDownhillIterator
diff --git a/fpga_interchange/site_arch.impl.h b/fpga_interchange/site_arch.impl.h
index 4702b592..0be298c9 100644
--- a/fpga_interchange/site_arch.impl.h
+++ b/fpga_interchange/site_arch.impl.h
@@ -202,6 +202,20 @@ inline bool SiteArch::is_pip_synthetic(const SitePip &pip) const
}
}
+inline SyntheticType SiteArch::pip_synthetic_type(const SitePip &pip) const
+{
+ if (pip.type != SitePip::SITE_PORT) {
+ // This isn't a site port, so its valid!
+ return NOT_SYNTH;
+ }
+
+ auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type];
+ auto &pip_data = tile_type.pip_data[pip.pip.index];
+ NPNR_ASSERT(pip_data.site != -1);
+ auto &bel_data = tile_type.bel_data[pip_data.bel];
+ return SyntheticType(bel_data.synthetic);
+}
+
inline SitePip SitePipDownhillIterator::operator*() const
{
switch (state) {
@@ -250,6 +264,37 @@ inline SitePipDownhillIterator SitePipDownhillRange::begin() const
return b;
}
+inline bool SiteArch::isInverting(const SitePip &site_pip) const
+{
+ if (site_pip.type != SitePip::SITE_PIP) {
+ return false;
+ }
+
+ auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type];
+ auto &pip_data = tile_type.pip_data[site_pip.pip.index];
+ NPNR_ASSERT(pip_data.site != -1);
+ auto &bel_data = tile_type.bel_data[pip_data.bel];
+
+ // Is a fixed inverter if the non_inverting_pin is another pin.
+ return bel_data.non_inverting_pin != pip_data.extra_data && bel_data.inverting_pin == pip_data.extra_data;
+}
+
+inline bool SiteArch::canInvert(const SitePip &site_pip) const
+{
+ if (site_pip.type != SitePip::SITE_PIP) {
+ return false;
+ }
+
+ auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type];
+ auto &pip_data = tile_type.pip_data[site_pip.pip.index];
+ NPNR_ASSERT(pip_data.site != -1);
+ auto &bel_data = tile_type.bel_data[pip_data.bel];
+
+ // Can optionally invert if this pip is both the non_inverting_pin and
+ // inverting pin.
+ return bel_data.non_inverting_pin == pip_data.extra_data && bel_data.inverting_pin == pip_data.extra_data;
+}
+
NEXTPNR_NAMESPACE_END
#endif /* SITE_ARCH_H */
diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc
index 3252cac1..630db37f 100644
--- a/fpga_interchange/site_router.cc
+++ b/fpga_interchange/site_router.cc
@@ -95,18 +95,16 @@ bool check_initial_wires(const Context *ctx, SiteInformation *site_info)
return true;
}
-bool is_invalid_site_port(const SiteArch *ctx, const SiteNetInfo *net, const SitePip &pip)
+static bool is_invalid_site_port(const SiteArch *ctx, const SiteNetInfo *net, const SitePip &pip)
{
- if (ctx->is_pip_synthetic(pip)) {
- // FIXME: Not all synthetic pips are for constant networks.
- // FIXME: Need to mark if synthetic site ports are for the GND or VCC
- // network, and only allow the right one. Otherwise site router
- // could route a VCC on a GND only net (or equiv).
+ SyntheticType type = ctx->pip_synthetic_type(pip);
+ if (type == SYNTH_GND) {
IdString gnd_net_name(ctx->ctx->chip_info->constants->gnd_net_name);
+ return net->net->name != gnd_net_name;
+ } else if (type == SYNTH_VCC) {
IdString vcc_net_name(ctx->ctx->chip_info->constants->vcc_net_name);
- return net->net->name != gnd_net_name && net->net->name != vcc_net_name;
+ return net->net->name != vcc_net_name;
} else {
- // All non-synthetic site ports are valid
return false;
}
}
@@ -313,6 +311,8 @@ struct SiteExpansionLoop
std::vector<SitePip>::const_iterator solution_begin(size_t idx) const { return solution.solution_begin(idx); }
std::vector<SitePip>::const_iterator solution_end(size_t idx) const { return solution.solution_end(idx); }
+ bool solution_inverted(size_t idx) const { return solution.solution_inverted(idx); }
+ bool solution_can_invert(size_t idx) const { return solution.solution_can_invert(idx); }
};
void print_current_state(const SiteArch *site_arch)
@@ -366,6 +366,8 @@ struct PossibleSolutions
SiteNetInfo *net = nullptr;
std::vector<SitePip>::const_iterator pips_begin;
std::vector<SitePip>::const_iterator pips_end;
+ bool inverted = false;
+ bool can_invert = false;
};
bool test_solution(SiteArch *ctx, SiteNetInfo *net, std::vector<SitePip>::const_iterator pips_begin,
@@ -570,6 +572,12 @@ bool route_site(SiteArch *ctx, SiteRoutingCache *site_routing_cache, RouteNodeSt
for (const auto *expansion : expansions) {
for (size_t idx = 0; idx < expansion->num_solutions(); ++idx) {
+ if (expansion->solution_inverted(idx)) {
+ // FIXME: May prefer an inverted solution if constant net
+ // type.
+ continue;
+ }
+
SiteWire wire = expansion->solution_sink(idx);
auto begin = expansion->solution_begin(idx);
auto end = expansion->solution_end(idx);
@@ -583,6 +591,8 @@ bool route_site(SiteArch *ctx, SiteRoutingCache *site_routing_cache, RouteNodeSt
solution.net = ctx->wire_to_nets.at(wire).net;
solution.pips_begin = begin;
solution.pips_end = end;
+ solution.inverted = expansion->solution_inverted(idx);
+ solution.can_invert = expansion->solution_can_invert(idx);
for (auto iter = begin; iter != end; ++iter) {
NPNR_ASSERT(ctx->getPipDstWire(*iter) == wire);
diff --git a/fpga_interchange/site_routing_cache.cc b/fpga_interchange/site_routing_cache.cc
index f7321a46..e6f4dc70 100644
--- a/fpga_interchange/site_routing_cache.cc
+++ b/fpga_interchange/site_routing_cache.cc
@@ -31,14 +31,27 @@ void SiteRoutingSolution::store_solution(const SiteArch *ctx, const RouteNodeSto
clear();
solution_sinks.reserve(solutions.size());
+ inverted.reserve(solutions.size());
+ can_invert.reserve(solutions.size());
for (size_t route : solutions) {
+ bool sol_inverted = false;
+ bool sol_can_invert = false;
+
SiteWire wire = node_storage->get_node(route)->wire;
solution_sinks.push_back(wire);
solution_offsets.push_back(solution_storage.size());
Node cursor = node_storage->get_node(route);
while (cursor.has_parent()) {
+ if (ctx->isInverting(cursor->pip) && !sol_can_invert) {
+ sol_inverted = !sol_inverted;
+ }
+ if (ctx->canInvert(cursor->pip)) {
+ sol_inverted = false;
+ sol_can_invert = true;
+ }
+
solution_storage.push_back(cursor->pip);
Node parent = cursor.parent();
NPNR_ASSERT(ctx->getPipDstWire(cursor->pip) == cursor->wire);
@@ -46,6 +59,9 @@ void SiteRoutingSolution::store_solution(const SiteArch *ctx, const RouteNodeSto
cursor = parent;
}
+ inverted.push_back(sol_inverted);
+ can_invert.push_back(sol_can_invert);
+
NPNR_ASSERT(cursor->wire == driver);
}
diff --git a/fpga_interchange/site_routing_cache.h b/fpga_interchange/site_routing_cache.h
index 942375dc..6ad218c7 100644
--- a/fpga_interchange/site_routing_cache.h
+++ b/fpga_interchange/site_routing_cache.h
@@ -40,6 +40,8 @@ struct SiteRoutingSolution
solution_offsets.clear();
solution_storage.clear();
solution_sinks.clear();
+ inverted.clear();
+ can_invert.clear();
}
size_t num_solutions() const { return solution_sinks.size(); }
@@ -58,9 +60,15 @@ struct SiteRoutingSolution
return solution_storage.begin() + solution_offsets.at(solution + 1);
}
+ bool solution_inverted(size_t solution) const { return inverted.at(solution) != 0; }
+
+ bool solution_can_invert(size_t solution) const { return can_invert.at(solution) != 0; }
+
std::vector<size_t> solution_offsets;
std::vector<SitePip> solution_storage;
std::vector<SiteWire> solution_sinks;
+ std::vector<uint8_t> inverted;
+ std::vector<uint8_t> can_invert;
};
struct SiteRoutingKey