diff options
-rwxr-xr-x | .github/ci/build_interchange.sh | 2 | ||||
-rw-r--r-- | common/dynamic_bitarray.h | 129 | ||||
-rw-r--r-- | common/property.h | 2 | ||||
-rw-r--r-- | fpga_interchange/arch.cc | 77 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 8 | ||||
-rw-r--r-- | fpga_interchange/cell_parameters.cc | 222 | ||||
-rw-r--r-- | fpga_interchange/cell_parameters.h | 55 | ||||
-rw-r--r-- | fpga_interchange/chipdb.h | 32 | ||||
-rw-r--r-- | fpga_interchange/site_arch.h | 7 | ||||
-rw-r--r-- | fpga_interchange/site_arch.impl.h | 45 | ||||
-rw-r--r-- | fpga_interchange/site_router.cc | 26 | ||||
-rw-r--r-- | fpga_interchange/site_routing_cache.cc | 16 | ||||
-rw-r--r-- | fpga_interchange/site_routing_cache.h | 8 |
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 ¶meter_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 |