From 54e0ef9cf7ce15d5f1561135edd41da38a32949a Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 6 Jan 2020 15:04:38 +0000 Subject: Adding archdefs and bba PODs Signed-off-by: David Shah --- .gitignore | 1 + CMakeLists.txt | 2 +- nexus/arch.h | 172 ++++++++++++++++++++++++++++++++++++++++++ nexus/archdefs.h | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/constids.inc | 0 nexus/family.cmake | 0 6 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 nexus/arch.h create mode 100644 nexus/archdefs.h create mode 100644 nexus/constids.inc create mode 100644 nexus/family.cmake diff --git a/.gitignore b/.gitignore index ca7b6e7d..96e6e3cd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /nextpnr-generic* /nextpnr-ice40* /nextpnr-ecp5* +/nextpnr-nexus* cmake-build-*/ Makefile cmake_install.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index d9c18554..fa46956b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,7 @@ endif() set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables") # List of families to build -set(FAMILIES generic ice40 ecp5) +set(FAMILIES generic ice40 ecp5 nexus) set(ARCH "" CACHE STRING "Architecture family for nextpnr build") set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES}) diff --git a/nexus/arch.h b/nexus/arch.h new file mode 100644 index 00000000..8be6208d --- /dev/null +++ b/nexus/arch.h @@ -0,0 +1,172 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 NEXTPNR_H +#error Include "arch.h" via "nextpnr.h" only. +#endif + +#include + +#include + +NEXTPNR_NAMESPACE_BEGIN + +template struct RelPtr +{ + int32_t offset; + + // void set(const T *ptr) { + // offset = reinterpret_cast(ptr) - + // reinterpret_cast(this); + // } + + const T *get() const + { + return reinterpret_cast(reinterpret_cast(this) + int64_t(offset) * 4); + } + + const T &operator[](size_t index) const { return get()[index]; } + + const T &operator*() const { return *(get()); } + + const T *operator->() const { return get(); } +}; + +/* + Fully deduplicated database + + There are two key data structures in the database: + + Locations (aka tile but not called this to avoid confusion + with Lattice terminology), are a (x, y) location. + + Local wires; pips and bels are all stored once per variety of location + (called a location type) with a separate grid containing the location type + at a (x, y) coordinate. + + Each location also has _neighbours_, other locations with interconnected + wires. The set of neighbours for a location are called a _neighbourhood_. + + Each variety of _neighbourhood_ for a location type is also stored once, + using relative coordinates. + +*/ + +NPNR_PACKED_STRUCT(struct BelWirePOD { + uint32_t port; + uint16_t type; + uint16_t wire_index; // wire index in tile +}); + +NPNR_PACKED_STRUCT(struct BelInfoPOD { + int32_t name; // bel name in tile IdString + int32_t type; // bel type IdString + int16_t rel_x, rel_y; // bel location relative to parent + RelPtr ports; // ports, sorted by name IdString + int32_t num_ports; // number of ports +}); + +NPNR_PACKED_STRUCT(struct BelPinPOD { + uint32_t bel; // bel index in tile + int32_t pin; // bel pin name IdString +}); + +enum TileWireFlags : uint32_t { + WIRE_PRIMARY = 0x80000000, +} + +NPNR_PACKED_STRUCT(struct LocWireInfoPOD { + int32_t name; // wire name in tile IdString + uint32_t flags; + int32_t num_uphill, num_downhill, num_bpins; + // Note this pip lists exclude neighbourhood pips + RelPtr pips_uh, pips_dh; // list of uphill/downhill pip indices in tile + RelPtr bel_pins; +}); + +NPNR_PACKED_STRUCT(struct PipInfoPOD { + uint16_t from_wire, to_wire; + int32_t tile_type; +}); + +enum RelLocFlags +{ + REL_GLOBAL = 0x80, + REL_BRANCH = 0x40, + REL_SPINE = 0x20, + REL_HROW = 0x10 +}; + +enum ArcFlags +{ + LOGICAL_TO_PRIMARY = 0x80, + PHYSICAL_DOWNHILL = 0x08, +}; + +NPNR_PACKED_STRUCT(struct RelWireInfoPOD { + int16_t rel_x, rel_y; + uint16_t wire_index; + uint8_t loc_flags; + uint8_t arc_flags; +}); + +NPNR_PACKED_STRUCT(struct WireNeighboursInfoPOD { + uint16_t num_uphill, num_downhill; + RelPtr wires_uh, wires_dh; +}); + +NPNR_PACKED_STRUCT(struct LocNeighourhoodPOD { RelPtr wire_neighbours; }); + +NPNR_PACKED_STRUCT(struct LocTypePOD { + uint32_t num_bels, num_wires, num_pips, num_nhtypes; + RelPtr bels; + RelPtr wires; + RelPtr pips; + RelPtr neighbourhoods; +}); + +// A physical (bitstream) tile; of which there may be more than +// one in a logical tile (XY grid location). +// Tile name is reconstructed {prefix}R{row}C{col}:{tiletype} +NPNR_PACKED_STRUCT(struct PhysicalTileInfoPOD { + int32_t prefix; // tile name prefix IdString + int32_t tiletype; // tile type IdString +}); + +NPNR_PACKED_STRUCT(struct GridLocationPOD { + uint32_t loc_type; + uint16_t neighbourhood_type; + uint16_t num_phys_tiles; + RelPtr phys_tiles; +}); + +NPNR_PACKED_STRUCT(struct ChipInfoPOD { + RelPtr device_name; + uint16_t width; + uint16_t height; + RelPtr grid; +}); + +NPNR_PACKED_STRUCT(struct DatabasePOD { + uint32_t num_chips; + RelPtr chips; +}); + +NEXTPNR_NAMESPACE_END diff --git a/nexus/archdefs.h b/nexus/archdefs.h new file mode 100644 index 00000000..32c48cdf --- /dev/null +++ b/nexus/archdefs.h @@ -0,0 +1,215 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * 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 NEXTPNR_H +#error Include "archdefs.h" via "nextpnr.h" only. +#endif + +NEXTPNR_NAMESPACE_BEGIN + +typedef int delay_t; + +struct DelayInfo +{ + delay_t min_delay = 0, max_delay = 0; + + delay_t minRaiseDelay() const { return min_delay; } + delay_t maxRaiseDelay() const { return max_delay; } + + delay_t minFallDelay() const { return min_delay; } + delay_t maxFallDelay() const { return max_delay; } + + delay_t minDelay() const { return min_delay; } + delay_t maxDelay() const { return max_delay; } + + DelayInfo operator+(const DelayInfo &other) const + { + DelayInfo ret; + ret.min_delay = this->min_delay + other.min_delay; + ret.max_delay = this->max_delay + other.max_delay; + return ret; + } +}; + +enum ConstIds +{ + ID_NONE +#define X(t) , ID_##t +#include "constids.inc" +#undef X +}; + +#define X(t) static constexpr auto id_##t = IdString(ID_##t); +#include "constids.inc" +#undef X + +struct BelId +{ + int32_t tile = -1; + // PIP index in tile + int32_t index = -1; + + bool operator==(const BelId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const BelId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const BelId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct WireId +{ + int32_t tile = -1; + // Node wires: tile == -1; index = node index in chipdb + // Tile wires: tile != -1; index = wire index in tile + int32_t index = -1; + + bool operator==(const WireId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const WireId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const WireId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct PipId +{ + int32_t tile = -1; + // PIP index in tile + int32_t index = -1; + + bool operator==(const PipId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const PipId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const PipId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct GroupId +{ + enum : int8_t + { + TYPE_NONE, + } type = TYPE_NONE; + int8_t x = 0, y = 0; + + bool operator==(const GroupId &other) const { return (type == other.type) && (x == other.x) && (y == other.y); } + bool operator!=(const GroupId &other) const { return (type != other.type) || (x != other.x) || (y == other.y); } +}; + +struct DecalId +{ + enum : int8_t + { + TYPE_NONE, + TYPE_BEL, + TYPE_WIRE, + TYPE_PIP, + TYPE_GROUP + } type = TYPE_NONE; + int32_t index = -1; + bool active = false; + + bool operator==(const DecalId &other) const { return (type == other.type) && (index == other.index); } + bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); } +}; + +struct ArchNetInfo +{ + bool is_global = false; + bool is_clock, is_reset = false, is_enable = false; +}; + +struct ArchCellInfo +{ + union + { + struct + { + bool is_memory, is_carry; + int input_count; + } lutInfo; + struct + { + bool is_clkinv, is_lsrinv, is_ceinv, is_async, gsr; + NetInfo *clk, *lsr, *ce, *d; + } ffInfo; + }; +}; + +NEXTPNR_NAMESPACE_END + +namespace std { +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(bel.tile)); + boost::hash_combine(seed, hash()(bel.index)); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(wire.tile)); + boost::hash_combine(seed, hash()(wire.index)); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(pip.tile)); + boost::hash_combine(seed, hash()(pip.index)); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(group.type)); + boost::hash_combine(seed, hash()(group.x)); + boost::hash_combine(seed, hash()(group.y)); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(decal.type)); + boost::hash_combine(seed, hash()(decal.index)); + return seed; + } +}; +} // namespace std diff --git a/nexus/constids.inc b/nexus/constids.inc new file mode 100644 index 00000000..e69de29b diff --git a/nexus/family.cmake b/nexus/family.cmake new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 60c6510b3b5687741f2f1939a37ab579451dbdae Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 6 Jan 2020 15:42:06 +0000 Subject: nexus: Arch utilities Signed-off-by: David Shah --- nexus/arch.h | 93 ++++++++++++++++++++++++++++++++++++++++++++++++--- nexus/archdefs.h | 15 +++++++-- nexus/bba_version.inc | 1 + 3 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 nexus/bba_version.inc diff --git a/nexus/arch.h b/nexus/arch.h index 8be6208d..e4a7e8ee 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -88,9 +88,10 @@ NPNR_PACKED_STRUCT(struct BelPinPOD { int32_t pin; // bel pin name IdString }); -enum TileWireFlags : uint32_t { +enum TileWireFlags : uint32_t +{ WIRE_PRIMARY = 0x80000000, -} +}; NPNR_PACKED_STRUCT(struct LocWireInfoPOD { int32_t name; // wire name in tile IdString @@ -128,8 +129,8 @@ NPNR_PACKED_STRUCT(struct RelWireInfoPOD { }); NPNR_PACKED_STRUCT(struct WireNeighboursInfoPOD { - uint16_t num_uphill, num_downhill; - RelPtr wires_uh, wires_dh; + uint32_t num_nwires; + RelPtr neigh_wires; }); NPNR_PACKED_STRUCT(struct LocNeighourhoodPOD { RelPtr wire_neighbours; }); @@ -165,8 +166,92 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { }); NPNR_PACKED_STRUCT(struct DatabasePOD { + uint32_t version; uint32_t num_chips; + RelPtr family; RelPtr chips; + uint32_t num_loctypes; + RelPtr loctypes; }); +const int bba_version = +#include "bba_version.inc" + ; + +struct ArchArgs +{ + std::string chipdb; + std::string device; + std::string package; +}; + +struct Arch : BaseCtx +{ + ArchArgs args; + Arch(ArchArgs args); + + boost::iostreams::mapped_file_source blob_file; + const DatabasePOD *db; + const ChipInfoPOD *chip_info; + + std::string getChipName() const; + + IdString archId() const { return id("nexus"); } + ArchArgs archArgs() const { return args; } + IdString archArgsToId(ArchArgs args) const; + + int getGridDimX() const { return chip_info->width; } + int getGridDimY() const { return chip_info->height; } + int getTileBelDimZ(int, int) const { return 256; } + int getTilePipDimZ(int, int) const { return 1; } + + template const LocTypePOD &loc_data(Id &id) const + { + return db->loctypes[chip_info->grid[id.tile].loc_type]; + } + + template const LocNeighourhoodPOD &nh_data(Id &id) const + { + auto &t = chip_info->grid[id.tile]; + return db->loctypes[t.loc_type].neighbourhoods[t.neighbourhood_type]; + } + + inline const BelInfoPOD &bel_data(BelId id) const { return loc_data(id).bels[id.index]; } + inline const LocWireInfoPOD &wire_data(WireId &id) const { return loc_data(id).wires[id.index]; } + inline const PipInfoPOD &pip_data(PipId &id) const { return loc_data(id).pips[id.index]; } + inline bool rel_tile(int32_t base, int16_t rel_x, int16_t rel_y, int32_t &next) + { + int32_t curr_x = base % chip_info->width; + int32_t curr_y = base / chip_info->width; + int32_t new_x = curr_x + rel_x; + int32_t new_y = curr_y + rel_y; + if (new_x < 0 || new_x >= chip_info->width) + return false; + if (new_y < 0 || new_y >= chip_info->height) + return false; + next = new_y * chip_info->width + new_x; + return true; + } + inline const WireId canonical_wire(int32_t tile, uint16_t index) + { + WireId wire{tile, index}; + // `tile` is the primary location for the wire, so ID is already canonical + if (wire_data(wire).flags & WIRE_PRIMARY) + return wire; + // Not primary; find the primary location which forms the canonical ID + auto &nd = nh_data(wire); + auto &wn = nd.wire_neighbours[index]; + for (size_t i = 0; i < wn.num_nwires; i++) { + auto &nw = wn.neigh_wires[i]; + if (nw.arc_flags & LOGICAL_TO_PRIMARY) { + if (rel_tile(tile, nw.rel_x, nw.rel_y, wire.tile)) { + wire.index = nw.wire_index; + break; + } + } + } + return wire; + } +}; + NEXTPNR_NAMESPACE_END diff --git a/nexus/archdefs.h b/nexus/archdefs.h index 32c48cdf..ef5c4c3c 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -65,6 +65,9 @@ struct BelId // PIP index in tile int32_t index = -1; + BelId() = default; + inline BelId(int32_t tile, int32_t index) : tile(tile), index(index){}; + bool operator==(const BelId &other) const { return tile == other.tile && index == other.index; } bool operator!=(const BelId &other) const { return tile != other.tile || index != other.index; } bool operator<(const BelId &other) const @@ -80,6 +83,9 @@ struct WireId // Tile wires: tile != -1; index = wire index in tile int32_t index = -1; + WireId() = default; + inline WireId(int32_t tile, int32_t index) : tile(tile), index(index){}; + bool operator==(const WireId &other) const { return tile == other.tile && index == other.index; } bool operator!=(const WireId &other) const { return tile != other.tile || index != other.index; } bool operator<(const WireId &other) const @@ -94,6 +100,9 @@ struct PipId // PIP index in tile int32_t index = -1; + PipId() = default; + inline PipId(int32_t tile, int32_t index) : tile(tile), index(index){}; + bool operator==(const PipId &other) const { return tile == other.tile && index == other.index; } bool operator!=(const PipId &other) const { return tile != other.tile || index != other.index; } bool operator<(const PipId &other) const @@ -133,10 +142,12 @@ struct DecalId struct ArchNetInfo { - bool is_global = false; - bool is_clock, is_reset = false, is_enable = false; + bool is_global; + bool is_clock, is_reset; }; +struct NetInfo; + struct ArchCellInfo { union diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/nexus/bba_version.inc @@ -0,0 +1 @@ +1 -- cgit v1.2.3 From c7f00b4760493c9ac6d57caaa20d077b0cd37cbc Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 6 Jan 2020 19:04:43 +0000 Subject: nexus: Add iterator types based on nextpnr-xilinx Signed-off-by: David Shah --- nexus/arch.h | 371 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 332 insertions(+), 39 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index e4a7e8ee..5163d9c3 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -162,6 +162,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { RelPtr device_name; uint16_t width; uint16_t height; + uint32_t num_tiles; RelPtr grid; }); @@ -174,6 +175,330 @@ NPNR_PACKED_STRUCT(struct DatabasePOD { RelPtr loctypes; }); +// ----------------------------------------------------------------------- + +// Helper functions for database access +namespace { +template const LocTypePOD &chip_loc_data(const DatabasePOD *db, const ChipInfoPOD *chip, Id &id) +{ + return db->loctypes[chip->grid[id.tile].loc_type]; +} + +template const LocNeighourhoodPOD &chip_nh_data(const DatabasePOD *db, const ChipInfoPOD *chip, Id &id) +{ + auto &t = chip->grid[id.tile]; + return db->loctypes[t.loc_type].neighbourhoods[t.neighbourhood_type]; +} + +inline const BelInfoPOD &chip_bel_data(const DatabasePOD *db, const ChipInfoPOD *chip, BelId id) +{ + return chip_loc_data(db, chip, id).bels[id.index]; +} +inline const LocWireInfoPOD &chip_wire_data(const DatabasePOD *db, const ChipInfoPOD *chip, WireId &id) +{ + return chip_loc_data(db, chip, id).wires[id.index]; +} +inline const PipInfoPOD &chip_pip_data(const DatabasePOD *db, const ChipInfoPOD *chip, PipId &id) +{ + return chip_loc_data(db, chip, id).pips[id.index]; +} +inline bool chip_rel_tile(const ChipInfoPOD *chip, int32_t base, int16_t rel_x, int16_t rel_y, int32_t &next) +{ + int32_t curr_x = base % chip->width; + int32_t curr_y = base / chip->width; + int32_t new_x = curr_x + rel_x; + int32_t new_y = curr_y + rel_y; + if (new_x < 0 || new_x >= chip->width) + return false; + if (new_y < 0 || new_y >= chip->height) + return false; + next = new_y * chip->width + new_x; + return true; +} +inline WireId chip_canonical_wire(const DatabasePOD *db, const ChipInfoPOD *chip, int32_t tile, uint16_t index) +{ + WireId wire{tile, index}; + // `tile` is the primary location for the wire, so ID is already canonical + if (chip_wire_data(db, chip, wire).flags & WIRE_PRIMARY) + return wire; + // Not primary; find the primary location which forms the canonical ID + auto &nd = chip_nh_data(db, chip, wire); + auto &wn = nd.wire_neighbours[index]; + for (size_t i = 0; i < wn.num_nwires; i++) { + auto &nw = wn.neigh_wires[i]; + if (nw.arc_flags & LOGICAL_TO_PRIMARY) { + if (chip_rel_tile(chip, tile, nw.rel_x, nw.rel_y, wire.tile)) { + wire.index = nw.wire_index; + break; + } + } + } + return wire; +} +inline bool chip_wire_is_primary(const DatabasePOD *db, const ChipInfoPOD *chip, int32_t tile, uint16_t index) +{ + WireId wire{tile, index}; + // `tile` is the primary location for the wire, so ID is already canonical + if (chip_wire_data(db, chip, wire).flags & WIRE_PRIMARY) + return true; + // Not primary; find the primary location which forms the canonical ID + auto &nd = chip_nh_data(db, chip, wire); + auto &wn = nd.wire_neighbours[index]; + for (size_t i = 0; i < wn.num_nwires; i++) { + auto &nw = wn.neigh_wires[i]; + if (nw.arc_flags & LOGICAL_TO_PRIMARY) { + if (chip_rel_tile(chip, tile, nw.rel_x, nw.rel_y, wire.tile)) { + return false; + } + } + } + return true; +} +} // namespace + +// ----------------------------------------------------------------------- + +struct BelIterator +{ + const DatabasePOD *db; + const ChipInfoPOD *chip; + int cursor_index; + int cursor_tile; + + BelIterator operator++() + { + cursor_index++; + while (cursor_tile < int(chip->num_tiles) && + cursor_index >= int(db->loctypes[chip->grid[cursor_tile].loc_type].num_bels)) { + cursor_index = 0; + cursor_tile++; + } + return *this; + } + BelIterator operator++(int) + { + BelIterator prior(*this); + ++(*this); + return prior; + } + + bool operator!=(const BelIterator &other) const + { + return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; + } + + bool operator==(const BelIterator &other) const + { + return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; + } + + BelId operator*() const + { + BelId ret; + ret.tile = cursor_tile; + ret.index = cursor_index; + return ret; + } +}; + +struct BelRange +{ + BelIterator b, e; + BelIterator begin() const { return b; } + BelIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct WireIterator +{ + const DatabasePOD *db; + const ChipInfoPOD *chip; + int cursor_index; + int cursor_tile = 0; + + WireIterator operator++() + { + // Iterate over nodes first, then tile wires that aren't nodes + do { + cursor_index++; + while (cursor_tile < int(chip->num_tiles) && + cursor_index >= int(db->loctypes[chip->grid[cursor_tile].loc_type].num_wires)) { + cursor_index = 0; + cursor_tile++; + } + } while (cursor_tile < int(chip->num_tiles) && !chip_wire_is_primary(db, chip, cursor_tile, cursor_index)); + + return *this; + } + WireIterator operator++(int) + { + WireIterator prior(*this); + ++(*this); + return prior; + } + + bool operator!=(const WireIterator &other) const + { + return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; + } + + bool operator==(const WireIterator &other) const + { + return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; + } + + WireId operator*() const + { + WireId ret; + ret.tile = cursor_tile; + ret.index = cursor_index; + return ret; + } +}; + +struct WireRange +{ + WireIterator b, e; + WireIterator begin() const { return b; } + WireIterator end() const { return e; } +}; + +// Iterate over all neighour wires for a wire +struct TileWireIterator +{ + const DatabasePOD *db; + const ChipInfoPOD *chip; + WireId baseWire; + int cursor = -1; + + void operator++() + { + auto &wn = chip_nh_data(db, chip, baseWire).wire_neighbours[baseWire.index]; + int32_t tile; + do + cursor++; + while (cursor < int(wn.num_nwires) && + ((wn.neigh_wires[cursor].arc_flags & LOGICAL_TO_PRIMARY) || + !chip_rel_tile(chip, baseWire.tile, wn.neigh_wires[cursor].rel_x, wn.neigh_wires[cursor].rel_y, tile))); + } + bool operator!=(const TileWireIterator &other) const { return cursor != other.cursor; } + + // Returns a *denormalised* identifier that may be a non-primary wire (and thus should _not_ be used + // as a WireId in general as it will break invariants) + WireId operator*() const + { + if (cursor == -1) { + return baseWire; + } else { + auto &nw = chip_nh_data(db, chip, baseWire).wire_neighbours[baseWire.index].neigh_wires[cursor]; + WireId result; + result.index = nw.wire_index; + if (!chip_rel_tile(chip, baseWire.tile, nw.rel_x, nw.rel_y, result.tile)) + return WireId(); + return result; + } + } +}; + +// ----------------------------------------------------------------------- + +struct AllPipIterator +{ + const DatabasePOD *db; + const ChipInfoPOD *chip; + int cursor_index; + int cursor_tile; + + AllPipIterator operator++() + { + cursor_index++; + while (cursor_tile < int(chip->num_tiles) && + cursor_index >= int(db->loctypes[chip->grid[cursor_tile].loc_type].num_pips)) { + cursor_index = 0; + cursor_tile++; + } + return *this; + } + AllPipIterator operator++(int) + { + AllPipIterator prior(*this); + ++(*this); + return prior; + } + + bool operator!=(const AllPipIterator &other) const + { + return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; + } + + bool operator==(const AllPipIterator &other) const + { + return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; + } + + PipId operator*() const + { + PipId ret; + ret.tile = cursor_tile; + ret.index = cursor_index; + return ret; + } +}; + +struct AllPipRange +{ + AllPipIterator b, e; + AllPipIterator begin() const { return b; } + AllPipIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct UpDownhillPipIterator +{ + const DatabasePOD *db; + const ChipInfoPOD *chip; + TileWireIterator twi, twi_end; + int cursor = -1; + bool uphill = false; + + void operator++() + { + cursor++; + while (true) { + if (!(twi != twi_end)) + break; + WireId w = *twi; + auto &tile = db->loctypes[chip->grid[w.tile].loc_type]; + if (cursor < int(uphill ? tile.wires[w.index].num_uphill : tile.wires[w.index].num_downhill)) + break; + ++twi; + cursor = 0; + } + } + bool operator!=(const UpDownhillPipIterator &other) const { return twi != other.twi || cursor != other.cursor; } + + PipId operator*() const + { + PipId ret; + WireId w = *twi; + ret.tile = w.tile; + auto &tile = db->loctypes[chip->grid[w.tile].loc_type]; + ret.index = uphill ? tile.wires[w.index].pips_uh[cursor] : tile.wires[w.index].pips_dh[cursor]; + return ret; + } +}; + +struct UpDownhillPipRange +{ + UpDownhillPipIterator b, e; + UpDownhillPipIterator begin() const { return b; } + UpDownhillPipIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + const int bba_version = #include "bba_version.inc" ; @@ -205,52 +530,20 @@ struct Arch : BaseCtx int getTileBelDimZ(int, int) const { return 256; } int getTilePipDimZ(int, int) const { return 1; } - template const LocTypePOD &loc_data(Id &id) const - { - return db->loctypes[chip_info->grid[id.tile].loc_type]; - } + template const LocTypePOD &loc_data(Id &id) const { return chip_loc_data(db, chip_info, id); } - template const LocNeighourhoodPOD &nh_data(Id &id) const - { - auto &t = chip_info->grid[id.tile]; - return db->loctypes[t.loc_type].neighbourhoods[t.neighbourhood_type]; - } + template const LocNeighourhoodPOD &nh_data(Id &id) const { return chip_nh_data(db, chip_info, id); } - inline const BelInfoPOD &bel_data(BelId id) const { return loc_data(id).bels[id.index]; } - inline const LocWireInfoPOD &wire_data(WireId &id) const { return loc_data(id).wires[id.index]; } - inline const PipInfoPOD &pip_data(PipId &id) const { return loc_data(id).pips[id.index]; } + inline const BelInfoPOD &bel_data(BelId id) const { return chip_bel_data(db, chip_info, id); } + inline const LocWireInfoPOD &wire_data(WireId &id) const { return chip_wire_data(db, chip_info, id); } + inline const PipInfoPOD &pip_data(PipId &id) const { return chip_pip_data(db, chip_info, id); } inline bool rel_tile(int32_t base, int16_t rel_x, int16_t rel_y, int32_t &next) { - int32_t curr_x = base % chip_info->width; - int32_t curr_y = base / chip_info->width; - int32_t new_x = curr_x + rel_x; - int32_t new_y = curr_y + rel_y; - if (new_x < 0 || new_x >= chip_info->width) - return false; - if (new_y < 0 || new_y >= chip_info->height) - return false; - next = new_y * chip_info->width + new_x; - return true; + return chip_rel_tile(chip_info, base, rel_x, rel_y, next); } inline const WireId canonical_wire(int32_t tile, uint16_t index) { - WireId wire{tile, index}; - // `tile` is the primary location for the wire, so ID is already canonical - if (wire_data(wire).flags & WIRE_PRIMARY) - return wire; - // Not primary; find the primary location which forms the canonical ID - auto &nd = nh_data(wire); - auto &wn = nd.wire_neighbours[index]; - for (size_t i = 0; i < wn.num_nwires; i++) { - auto &nw = wn.neigh_wires[i]; - if (nw.arc_flags & LOGICAL_TO_PRIMARY) { - if (rel_tile(tile, nw.rel_x, nw.rel_y, wire.tile)) { - wire.index = nw.wire_index; - break; - } - } - } - return wire; + return chip_canonical_wire(db, chip_info, tile, index); } }; -- cgit v1.2.3 From 35de36dd59e18cd0c9204b08bc68fc8bed214332 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Jan 2020 11:13:48 +0000 Subject: Implementing some Arch functions Signed-off-by: David Shah --- nexus/arch.h | 551 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 537 insertions(+), 14 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 5163d9c3..349935cd 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -79,6 +79,7 @@ NPNR_PACKED_STRUCT(struct BelInfoPOD { int32_t name; // bel name in tile IdString int32_t type; // bel type IdString int16_t rel_x, rel_y; // bel location relative to parent + uint32_t z; // bel location absolute Z RelPtr ports; // ports, sorted by name IdString int32_t num_ports; // number of ports }); @@ -166,12 +167,18 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { RelPtr grid; }); +NPNR_PACKED_STRUCT(struct IdStringDBPOD { + uint32_t num_file_ids; // number of IDs loaded from constids.inc + uint32_t num_bba_ids; // number of IDs in BBA file + RelPtr> bba_id_strs; +}); + NPNR_PACKED_STRUCT(struct DatabasePOD { uint32_t version; uint32_t num_chips; + uint32_t num_loctypes; RelPtr family; RelPtr chips; - uint32_t num_loctypes; RelPtr loctypes; }); @@ -179,12 +186,13 @@ NPNR_PACKED_STRUCT(struct DatabasePOD { // Helper functions for database access namespace { -template const LocTypePOD &chip_loc_data(const DatabasePOD *db, const ChipInfoPOD *chip, Id &id) +template const LocTypePOD &chip_loc_data(const DatabasePOD *db, const ChipInfoPOD *chip, const Id &id) { return db->loctypes[chip->grid[id.tile].loc_type]; } -template const LocNeighourhoodPOD &chip_nh_data(const DatabasePOD *db, const ChipInfoPOD *chip, Id &id) +template +const LocNeighourhoodPOD &chip_nh_data(const DatabasePOD *db, const ChipInfoPOD *chip, const Id &id) { auto &t = chip->grid[id.tile]; return db->loctypes[t.loc_type].neighbourhoods[t.neighbourhood_type]; @@ -194,11 +202,11 @@ inline const BelInfoPOD &chip_bel_data(const DatabasePOD *db, const ChipInfoPOD { return chip_loc_data(db, chip, id).bels[id.index]; } -inline const LocWireInfoPOD &chip_wire_data(const DatabasePOD *db, const ChipInfoPOD *chip, WireId &id) +inline const LocWireInfoPOD &chip_wire_data(const DatabasePOD *db, const ChipInfoPOD *chip, WireId id) { return chip_loc_data(db, chip, id).wires[id.index]; } -inline const PipInfoPOD &chip_pip_data(const DatabasePOD *db, const ChipInfoPOD *chip, PipId &id) +inline const PipInfoPOD &chip_pip_data(const DatabasePOD *db, const ChipInfoPOD *chip, PipId id) { return chip_loc_data(db, chip, id).pips[id.index]; } @@ -365,7 +373,7 @@ struct WireRange }; // Iterate over all neighour wires for a wire -struct TileWireIterator +struct NeighWireIterator { const DatabasePOD *db; const ChipInfoPOD *chip; @@ -382,7 +390,7 @@ struct TileWireIterator ((wn.neigh_wires[cursor].arc_flags & LOGICAL_TO_PRIMARY) || !chip_rel_tile(chip, baseWire.tile, wn.neigh_wires[cursor].rel_x, wn.neigh_wires[cursor].rel_y, tile))); } - bool operator!=(const TileWireIterator &other) const { return cursor != other.cursor; } + bool operator!=(const NeighWireIterator &other) const { return cursor != other.cursor; } // Returns a *denormalised* identifier that may be a non-primary wire (and thus should _not_ be used // as a WireId in general as it will break invariants) @@ -401,6 +409,13 @@ struct TileWireIterator } }; +struct NeighWireRange +{ + NeighWireIterator b, e; + NeighWireIterator begin() const { return b; } + NeighWireIterator end() const { return e; } +}; + // ----------------------------------------------------------------------- struct AllPipIterator @@ -459,7 +474,7 @@ struct UpDownhillPipIterator { const DatabasePOD *db; const ChipInfoPOD *chip; - TileWireIterator twi, twi_end; + NeighWireIterator twi, twi_end; int cursor = -1; bool uphill = false; @@ -497,6 +512,46 @@ struct UpDownhillPipRange UpDownhillPipIterator end() const { return e; } }; +struct WireBelPinIterator +{ + const DatabasePOD *db; + const ChipInfoPOD *chip; + NeighWireIterator twi, twi_end; + int cursor = -1; + + void operator++() + { + cursor++; + while (true) { + if (!(twi != twi_end)) + break; + if (cursor < chip_wire_data(db, chip, *twi).num_bpins) + break; + ++twi; + cursor = 0; + } + } + bool operator!=(const WireBelPinIterator &other) const { return twi != other.twi || cursor != other.cursor; } + + BelPin operator*() const + { + BelPin ret; + WireId w = *twi; + auto &bp = chip_wire_data(db, chip, w).bel_pins[cursor]; + ret.bel.tile = w.tile; + ret.bel.index = bp.bel; + ret.pin = IdString(bp.pin); + return ret; + } +}; + +struct WireBelPinRange +{ + WireBelPinIterator b, e; + WireBelPinIterator begin() const { return b; } + WireBelPinIterator end() const { return e; } +}; + // ----------------------------------------------------------------------- const int bba_version = @@ -515,10 +570,24 @@ struct Arch : BaseCtx ArchArgs args; Arch(ArchArgs args); + // ------------------------------------------------- + + // Database references boost::iostreams::mapped_file_source blob_file; const DatabasePOD *db; const ChipInfoPOD *chip_info; + // Binding states + struct TileStatus + { + std::vector boundcells; + }; + std::vector tileStatus; + std::unordered_map wire_to_net; + std::unordered_map pip_to_net; + + // ------------------------------------------------- + std::string getChipName() const; IdString archId() const { return id("nexus"); } @@ -530,21 +599,475 @@ struct Arch : BaseCtx int getTileBelDimZ(int, int) const { return 256; } int getTilePipDimZ(int, int) const { return 1; } - template const LocTypePOD &loc_data(Id &id) const { return chip_loc_data(db, chip_info, id); } + // ------------------------------------------------- + + BelId getBelByName(IdString name) const; + + IdString getBelName(BelId bel) const + { + std::string name = "X"; + name += std::to_string(bel.tile % chip_info->width); + name += "Y"; + name += std::to_string(bel.tile / chip_info->width); + name += "/"; + name += nameOf(IdString(bel_data(bel).name)); + return id(name); + } + + uint32_t getBelChecksum(BelId bel) const { return (bel.tile << 16) ^ bel.index; } + + void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) + { + NPNR_ASSERT(bel != BelId()); + NPNR_ASSERT(tileStatus[bel.tile].boundcells[bel.index] == nullptr); + tileStatus[bel.tile].boundcells[bel.index] = cell; + cell->bel = bel; + cell->belStrength = strength; + refreshUiBel(bel); + } + + void unbindBel(BelId bel) + { + NPNR_ASSERT(bel != BelId()); + NPNR_ASSERT(tileStatus[bel.tile].boundcells[bel.index] != nullptr); + tileStatus[bel.tile].boundcells[bel.index]->bel = BelId(); + tileStatus[bel.tile].boundcells[bel.index]->belStrength = STRENGTH_NONE; + tileStatus[bel.tile].boundcells[bel.index] = nullptr; + refreshUiBel(bel); + } + + bool checkBelAvail(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return tileStatus[bel.tile].boundcells[bel.index] == nullptr; + } + + CellInfo *getBoundBelCell(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return tileStatus[bel.tile].boundcells[bel.index]; + } + + CellInfo *getConflictingBelCell(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return tileStatus[bel.tile].boundcells[bel.index]; + } + + BelRange getBels() const + { + BelRange range; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.chip = chip_info; + range.b.db = db; + ++range.b; //-1 and then ++ deals with the case of no bels in the first tile + range.e.cursor_tile = chip_info->width * chip_info->height; + range.e.cursor_index = 0; + range.e.chip = chip_info; + range.e.db = db; + return range; + } + + Loc getBelLocation(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + Loc loc; + loc.x = bel.tile % chip_info->width; + loc.y = bel.tile / chip_info->width; + loc.z = bel_data(bel).z; + return loc; + } + + BelId getBelByLocation(Loc loc) const; + BelRange getBelsByTile(int x, int y) const; + + bool getBelGlobalBuf(BelId bel) const { return false; } + + IdString getBelType(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return IdString(bel_data(bel).type); + } + + std::vector> getBelAttrs(BelId bel) const; + + WireId getBelPinWire(BelId bel, IdString pin) const; + PortType getBelPinType(BelId bel, IdString pin) const; + std::vector getBelPins(BelId bel) const; + + // ------------------------------------------------- + + WireId getWireByName(IdString name) const; + IdString getWireName(WireId wire) const + { + std::string name = "X"; + name += std::to_string(wire.tile % chip_info->width); + name += "Y"; + name += std::to_string(wire.tile / chip_info->width); + name += "/"; + name += nameOf(IdString(wire_data(wire).name)); + return id(name); + } + + IdString getWireType(WireId wire) const; + std::vector> getWireAttrs(WireId wire) const; + + uint32_t getWireChecksum(WireId wire) const { return (wire.tile << 16) ^ wire.index; } + + void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) + { + NPNR_ASSERT(wire != WireId()); + NPNR_ASSERT(wire_to_net[wire] == nullptr); + wire_to_net[wire] = net; + net->wires[wire].pip = PipId(); + net->wires[wire].strength = strength; + refreshUiWire(wire); + } + + void unbindWire(WireId wire) + { + NPNR_ASSERT(wire != WireId()); + NPNR_ASSERT(wire_to_net[wire] != nullptr); + + auto &net_wires = wire_to_net[wire]->wires; + auto it = net_wires.find(wire); + NPNR_ASSERT(it != net_wires.end()); + + auto pip = it->second.pip; + if (pip != PipId()) { + pip_to_net[pip] = nullptr; + } + + net_wires.erase(it); + wire_to_net[wire] = nullptr; + refreshUiWire(wire); + } + + bool checkWireAvail(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + auto w2n = wire_to_net.find(wire); + return w2n == wire_to_net.end() || w2n->second == nullptr; + } + + NetInfo *getBoundWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + auto w2n = wire_to_net.find(wire); + return w2n == wire_to_net.end() ? nullptr : w2n->second; + } + + WireId getConflictingWireWire(WireId wire) const { return wire; } + + DelayInfo getWireDelay(WireId wire) const + { + DelayInfo delay; + delay.min_delay = 0; + delay.max_delay = 0; + return delay; + } + + WireBelPinRange getWireBelPins(WireId wire) const + { + WireBelPinRange range; + NPNR_ASSERT(wire != WireId()); + NeighWireRange nwr = neigh_wire_range(wire); + range.b.chip = chip_info; + range.b.db = db; + range.b.twi = nwr.b; + range.b.twi_end = nwr.e; + range.b.cursor = -1; + ++range.b; + range.e.chip = chip_info; + range.e.db = db; + range.e.twi = nwr.e; + range.e.twi_end = nwr.e; + range.e.cursor = 0; + return range; + } + + WireRange getWires() const + { + WireRange range; + range.b.chip = chip_info; + range.b.db = db; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + ++range.b; //-1 and then ++ deals with the case of no wires in the first tile + range.e.chip = chip_info; + range.e.db = db; + range.e.cursor_tile = chip_info->num_tiles; + range.e.cursor_index = 0; + return range; + } + + // ------------------------------------------------- + + PipId getPipByName(IdString name) const; + IdString getPipName(PipId pip) const; + + void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) + { + NPNR_ASSERT(pip != PipId()); + NPNR_ASSERT(pip_to_net[pip] == nullptr); + + WireId dst = canonical_wire(pip.tile, pip_data(pip).to_wire); + NPNR_ASSERT(wire_to_net[dst] == nullptr || wire_to_net[dst] == net); - template const LocNeighourhoodPOD &nh_data(Id &id) const { return chip_nh_data(db, chip_info, id); } + pip_to_net[pip] = net; + + wire_to_net[dst] = net; + net->wires[dst].pip = pip; + net->wires[dst].strength = strength; + refreshUiPip(pip); + refreshUiWire(dst); + } + + void unbindPip(PipId pip) + { + NPNR_ASSERT(pip != PipId()); + NPNR_ASSERT(pip_to_net[pip] != nullptr); + + WireId dst = canonical_wire(pip.tile, pip_data(pip).to_wire); + NPNR_ASSERT(wire_to_net[dst] != nullptr); + wire_to_net[dst] = nullptr; + pip_to_net[pip]->wires.erase(dst); + + pip_to_net[pip] = nullptr; + refreshUiPip(pip); + refreshUiWire(dst); + } + + bool checkPipAvail(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + return pip_to_net.find(pip) == pip_to_net.end() || pip_to_net.at(pip) == nullptr; + } + + NetInfo *getBoundPipNet(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + auto p2n = pip_to_net.find(pip); + return p2n == pip_to_net.end() ? nullptr : p2n->second; + } + + WireId getConflictingPipWire(PipId pip) const { return getPipDstWire(pip); } + + NetInfo *getConflictingPipNet(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + auto p2n = pip_to_net.find(pip); + return p2n == pip_to_net.end() ? nullptr : p2n->second; + } + + AllPipRange getPips() const + { + AllPipRange range; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.chip = chip_info; + range.b.db = db; + ++range.b; //-1 and then ++ deals with the case of no pips in the first tile + range.e.cursor_tile = chip_info->width * chip_info->height; + range.e.cursor_index = 0; + range.e.chip = chip_info; + range.e.db = db; + return range; + } + + Loc getPipLocation(PipId pip) const + { + Loc loc; + loc.x = pip.tile % chip_info->width; + loc.y = pip.tile / chip_info->width; + loc.z = 0; + return loc; + } + + IdString getPipType(PipId pip) const; + std::vector> getPipAttrs(PipId pip) const; + + uint32_t getPipChecksum(PipId pip) const { return pip.tile << 16 | pip.index; } + + WireId getPipSrcWire(PipId pip) const { return canonical_wire(pip.tile, pip_data(pip).from_wire); } + + WireId getPipDstWire(PipId pip) const { return canonical_wire(pip.tile, pip_data(pip).to_wire); } + + DelayInfo getPipDelay(PipId pip) const { return getDelayFromNS(0.1); } + + UpDownhillPipRange getPipsDownhill(WireId wire) const + { + UpDownhillPipRange range; + NPNR_ASSERT(wire != WireId()); + NeighWireRange nwr = neigh_wire_range(wire); + range.b.chip = chip_info; + range.b.db = db; + range.b.twi = nwr.b; + range.b.twi_end = nwr.e; + range.b.cursor = -1; + range.b.uphill = false; + ++range.b; + range.e.chip = chip_info; + range.e.db = db; + range.e.twi = nwr.e; + range.e.twi_end = nwr.e; + range.e.cursor = 0; + range.e.uphill = false; + return range; + } + + UpDownhillPipRange getPipsUphill(WireId wire) const + { + UpDownhillPipRange range; + NPNR_ASSERT(wire != WireId()); + NeighWireRange nwr = neigh_wire_range(wire); + range.b.chip = chip_info; + range.b.db = db; + range.b.twi = nwr.b; + range.b.twi_end = nwr.e; + range.b.cursor = -1; + range.b.uphill = true; + ++range.b; + range.e.chip = chip_info; + range.e.db = db; + range.e.twi = nwr.e; + range.e.twi_end = nwr.e; + range.e.cursor = 0; + range.e.uphill = true; + return range; + } + + UpDownhillPipRange getWireAliases(WireId wire) const + { + UpDownhillPipRange range; + range.b.cursor = 0; + range.b.twi.cursor = 0; + range.e.cursor = 0; + range.e.twi.cursor = 0; + return range; + } + + // ------------------------------------------------- + + GroupId getGroupByName(IdString name) const { return GroupId(); } + IdString getGroupName(GroupId group) const { return IdString(); } + std::vector getGroups() const { return {}; } + std::vector getGroupBels(GroupId group) const { return {}; } + std::vector getGroupWires(GroupId group) const { return {}; } + std::vector getGroupPips(GroupId group) const { return {}; } + std::vector getGroupGroups(GroupId group) const { return {}; } + + // ------------------------------------------------- + + delay_t estimateDelay(WireId src, WireId dst, bool debug = false) const; + delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const; + delay_t getDelayEpsilon() const { return 20; } + delay_t getRipupDelayPenalty() const { return 120; } + delay_t getWireRipupDelayPenalty(WireId wire) const; + float getDelayNS(delay_t v) const { return v * 0.001; } + DelayInfo getDelayFromNS(float ns) const + { + DelayInfo del; + del.min_delay = delay_t(ns * 1000); + del.max_delay = delay_t(ns * 1000); + return del; + } + uint32_t getDelayChecksum(delay_t v) const { return v; } + bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; + + // ------------------------------------------------- + + // Get the delay through a cell from one port to another, returning false + // if no path exists. This only considers combinational delays, as required by the Arch API + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; + // getCellDelayInternal is similar to the above, but without false path checks and including clock to out delays + // for internal arch use only + bool getCellDelayInternal(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; + // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const; + // Get the TimingClockingInfo of a port + TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const; + // Return true if a net is global + bool isGlobalNet(const NetInfo *net) const; + + // ------------------------------------------------- + + // Perform placement validity checks, returning false on failure (all + // implemented in arch_place.cc) + + // Whether or not a given cell can be placed at a given Bel + // This is not intended for Bel type checks, but finer-grained constraints + // such as conflicting set/reset signals, etc + bool isValidBelForCell(CellInfo *cell, BelId bel) const; + + // Return true whether all Bels at a given location are valid + bool isBelLocationValid(BelId bel) const; + + // ------------------------------------------------- + + bool pack(); + bool place(); + bool route(); + + // ------------------------------------------------- + // Assign architecure-specific arguments to nets and cells, which must be + // called between packing or further + // netlist modifications, and validity checks + void assignArchInfo(); + void assignCellInfo(CellInfo *cell); + + // ------------------------------------------------- + + std::vector getDecalGraphics(DecalId decal) const; + + DecalXY getBelDecal(BelId bel) const; + DecalXY getWireDecal(WireId wire) const; + DecalXY getPipDecal(PipId pip) const; + DecalXY getGroupDecal(GroupId group) const; + + // ------------------------------------------------- + + static const std::string defaultPlacer; + static const std::vector availablePlacers; + + // ------------------------------------------------- + + template const LocTypePOD &loc_data(const Id &id) const { return chip_loc_data(db, chip_info, id); } + + template const LocNeighourhoodPOD &nh_data(const Id &id) const + { + return chip_nh_data(db, chip_info, id); + } inline const BelInfoPOD &bel_data(BelId id) const { return chip_bel_data(db, chip_info, id); } - inline const LocWireInfoPOD &wire_data(WireId &id) const { return chip_wire_data(db, chip_info, id); } - inline const PipInfoPOD &pip_data(PipId &id) const { return chip_pip_data(db, chip_info, id); } - inline bool rel_tile(int32_t base, int16_t rel_x, int16_t rel_y, int32_t &next) + inline const LocWireInfoPOD &wire_data(WireId id) const { return chip_wire_data(db, chip_info, id); } + inline const PipInfoPOD &pip_data(PipId id) const { return chip_pip_data(db, chip_info, id); } + inline bool rel_tile(int32_t base, int16_t rel_x, int16_t rel_y, int32_t &next) const { return chip_rel_tile(chip_info, base, rel_x, rel_y, next); } - inline const WireId canonical_wire(int32_t tile, uint16_t index) + inline const WireId canonical_wire(int32_t tile, uint16_t index) const { return chip_canonical_wire(db, chip_info, tile, index); } + + // ------------------------------------------------- + + NeighWireRange neigh_wire_range(WireId wire) const + { + NeighWireRange range; + range.b.chip = chip_info; + range.b.db = db; + range.b.baseWire = wire; + range.b.cursor = -1; + + range.e.chip = chip_info; + range.e.db = db; + range.e.baseWire = wire; + range.e.cursor = nh_data(wire).wire_neighbours[wire.index].num_nwires; + return range; + } }; NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From ae1a486520c8a4289e067644f3bb71f8980147b7 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Jan 2020 11:55:55 +0000 Subject: Add Arch constructor Signed-off-by: David Shah --- nexus/arch.cc | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/arch.h | 13 +++++-- 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 nexus/arch.cc diff --git a/nexus/arch.cc b/nexus/arch.cc new file mode 100644 index 00000000..bd1728dc --- /dev/null +++ b/nexus/arch.cc @@ -0,0 +1,109 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 + +#include "log.h" +#include "nextpnr.h" +#include "placer1.h" +#include "placer_heap.h" +#include "router1.h" +#include "timing.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +// ----------------------------------------------------------------------- + +void IdString::initialize_arch(const BaseCtx *ctx) +{ +#define X(t) initialize_add(ctx, #t, ID_##t); + +#include "constids.inc" + +#undef X +} + +// ----------------------------------------------------------------------- + +static const DatabasePOD *get_chipdb(const RelPtr *ptr) { return ptr->get(); } + +// ----------------------------------------------------------------------- + +Arch::Arch(ArchArgs args) : args(args) +{ + // Parse device string + if (boost::starts_with(args.device, "LIFCL")) { + family = "LIFCL"; + } else { + log_error("Unknown device string '%s' (expected device name like 'LIFCL-40-8SG72C')\n", args.device.c_str()); + } + auto last_sep = args.device.rfind('-'); + if (last_sep == std::string::npos) + log_error("Unknown device string '%s' (expected device name like 'LIFCL-40-8SG72C')\n", args.device.c_str()); + device = args.device.substr(0, last_sep); + speed = args.device.substr(last_sep + 1, 1); + auto package_end = args.device.find_last_of("0123456789"); + if (package_end == std::string::npos || package_end < last_sep) + log_error("Unknown device string '%s' (expected device name like 'LIFCL-40-8SG72C')\n", args.device.c_str()); + package = args.device.substr(last_sep + 2, (package_end - (last_sep + 2)) + 1); + rating = args.device.substr(package_end + 1); + // Load database (FIXME: baked-in databases too) + try { + blob_file.open(args.chipdb); + if (!blob_file.is_open()) + log_error("Unable to read chipdb %s\n", args.chipdb.c_str()); + const char *blob = reinterpret_cast(blob_file.data()); + db = get_chipdb(reinterpret_cast *>(blob)); + } catch (...) { + log_error("Unable to read chipdb %s\n", args.chipdb.c_str()); + } + // Check database version and family + if (db->version != bba_version) { + log_error("Provided database version %d is %s than nextpnr version %d, please rebuild database/nextpnr.\n", + db->version, (db->version > bba_version) ? "newer" : "older", bba_version); + } + if (db->family.get() != family) { + log_error("Database is for family '%s' but provided device is family '%s'.\n", db->family.get(), + family.c_str()); + } + // Set up chip_info + chip_info = nullptr; + for (size_t i = 0; i < db->num_chips; i++) { + auto &chip = db->chips[i]; + if (chip.device_name.get() == device) { + chip_info = &chip; + break; + } + } + if (!chip_info) + log_error("Unknown device '%s'.\n", device.c_str()); + // Set up bba IdStrings + for (size_t i = 0; i < db->ids->num_bba_ids; i++) { + IdString::initialize_add(this, db->ids->bba_id_strs[i].get(), uint32_t(i) + db->ids->num_file_ids); + } + // Set up validity structures + tileStatus.resize(chip_info->num_tiles); + for (size_t i = 0; i < chip_info->num_tiles; i++) { + tileStatus[i].boundcells.resize(db->loctypes[chip_info->grid[i].loc_type].num_bels); + } +} + +NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/nexus/arch.h b/nexus/arch.h index 349935cd..2c8bad7e 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -180,6 +180,7 @@ NPNR_PACKED_STRUCT(struct DatabasePOD { RelPtr family; RelPtr chips; RelPtr loctypes; + RelPtr ids; }); // ----------------------------------------------------------------------- @@ -562,12 +563,12 @@ struct ArchArgs { std::string chipdb; std::string device; - std::string package; }; struct Arch : BaseCtx { ArchArgs args; + std::string family, device, package, speed, rating; Arch(ArchArgs args); // ------------------------------------------------- @@ -758,6 +759,14 @@ struct Arch : BaseCtx return w2n == wire_to_net.end() ? nullptr : w2n->second; } + NetInfo *getConflictingWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + auto w2n = wire_to_net.find(wire); + return w2n == wire_to_net.end() ? nullptr : w2n->second; + } + + WireId getConflictingWireWire(WireId wire) const { return wire; } DelayInfo getWireDelay(WireId wire) const @@ -1047,7 +1056,7 @@ struct Arch : BaseCtx { return chip_rel_tile(chip_info, base, rel_x, rel_y, next); } - inline const WireId canonical_wire(int32_t tile, uint16_t index) const + inline WireId canonical_wire(int32_t tile, uint16_t index) const { return chip_canonical_wire(db, chip_info, tile, index); } -- cgit v1.2.3 From 65a59e9dcc9a87ff9d7f6ed10db63082d53d267e Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Jan 2020 12:17:09 +0000 Subject: nexus: Implement bel, wire and pip Arch functions Signed-off-by: David Shah --- nexus/arch.cc | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- nexus/arch.h | 1 - 2 files changed, 209 insertions(+), 5 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index bd1728dc..19fef385 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -30,6 +30,22 @@ NEXTPNR_NAMESPACE_BEGIN +namespace { +static std::tuple split_identifier_name(const std::string &name) +{ + size_t first_slash = name.find('/'); + NPNR_ASSERT(first_slash != std::string::npos); + size_t second_slash = name.find('/', first_slash + 1); + NPNR_ASSERT(second_slash != std::string::npos); + return std::make_tuple(std::stoi(name.substr(1, first_slash)), + std::stoi(name.substr(first_slash + 2, second_slash - first_slash)), + name.substr(second_slash + 1)); +}; + +static const DatabasePOD *get_chipdb(const RelPtr *ptr) { return ptr->get(); } + +} // namespace + // ----------------------------------------------------------------------- void IdString::initialize_arch(const BaseCtx *ctx) @@ -43,10 +59,6 @@ void IdString::initialize_arch(const BaseCtx *ctx) // ----------------------------------------------------------------------- -static const DatabasePOD *get_chipdb(const RelPtr *ptr) { return ptr->get(); } - -// ----------------------------------------------------------------------- - Arch::Arch(ArchArgs args) : args(args) { // Parse device string @@ -106,4 +118,197 @@ Arch::Arch(ArchArgs args) : args(args) } } +// ----------------------------------------------------------------------- + +std::string Arch::getChipName() const { return args.device; } +IdString Arch::archArgsToId(ArchArgs args) const { return id(args.device); } + +// ----------------------------------------------------------------------- + +BelId Arch::getBelByName(IdString name) const +{ + int x, y; + std::string belname; + std::tie(x, y, belname) = split_identifier_name(name.str(this)); + NPNR_ASSERT(x >= 0 && x < chip_info->width); + NPNR_ASSERT(y >= 0 && y < chip_info->height); + auto &tile = db->loctypes[chip_info->grid[y * chip_info->width + x].loc_type]; + IdString bn = id(belname); + for (size_t i = 0; i < tile.num_bels; i++) { + if (tile.bels[i].name == bn.index) { + BelId ret; + ret.tile = y * chip_info->width + x; + ret.index = i; + return ret; + } + } + return BelId(); +} + +BelRange Arch::getBelsByTile(int x, int y) const +{ + BelRange br; + NPNR_ASSERT(x >= 0 && x < chip_info->width); + NPNR_ASSERT(y >= 0 && y < chip_info->height); + br.b.cursor_tile = y * chip_info->width + x; + br.e.cursor_tile = y * chip_info->width + x; + br.b.cursor_index = 0; + br.e.cursor_index = db->loctypes[chip_info->grid[br.b.cursor_tile].loc_type].num_bels; + br.b.chip = chip_info; + br.b.db = db; + br.e.chip = chip_info; + br.e.db = db; + if (br.e.cursor_index == -1) + ++br.e.cursor_index; + else + ++br.e; + return br; +} + +WireId Arch::getBelPinWire(BelId bel, IdString pin) const +{ + // Binary search on wire IdString, by ID + int num_bel_wires = bel_data(bel).num_ports; + const BelWirePOD *bel_ports = bel_data(bel).ports.get(); + + if (num_bel_wires < 7) { + for (int i = 0; i < num_bel_wires; i++) { + if (int(bel_ports[i].port) == pin.index) { + return canonical_wire(bel.tile, bel_ports[i].wire_index); + } + } + } else { + int b = 0, e = num_bel_wires - 1; + while (b <= e) { + int i = (b + e) / 2; + if (int(bel_ports[i].port) == pin.index) { + return canonical_wire(bel.tile, bel_ports[i].wire_index); + } + if (int(bel_ports[i].port) > pin.index) + e = i - 1; + else + b = i + 1; + } + } + + return WireId(); +} + +PortType Arch::getBelPinType(BelId bel, IdString pin) const +{ + // Binary search on wire IdString, by ID + int num_bel_wires = bel_data(bel).num_ports; + const BelWirePOD *bel_ports = bel_data(bel).ports.get(); + + if (num_bel_wires < 7) { + for (int i = 0; i < num_bel_wires; i++) { + if (int(bel_ports[i].port) == pin.index) { + return PortType(bel_ports[i].type); + } + } + } else { + int b = 0, e = num_bel_wires - 1; + while (b <= e) { + int i = (b + e) / 2; + if (int(bel_ports[i].port) == pin.index) { + return PortType(bel_ports[i].type); + } + if (int(bel_ports[i].port) > pin.index) + e = i - 1; + else + b = i + 1; + } + } + + NPNR_ASSERT_FALSE("unknown bel pin"); +} + +std::vector Arch::getBelPins(BelId bel) const +{ + std::vector ret; + int num_bel_wires = bel_data(bel).num_ports; + const BelWirePOD *bel_ports = bel_data(bel).ports.get(); + for (int i = 0; i < num_bel_wires; i++) + ret.push_back(IdString(bel_ports[i].port)); + return ret; +} + +// ----------------------------------------------------------------------- + +WireId Arch::getWireByName(IdString name) const +{ + int x, y; + std::string wirename; + std::tie(x, y, wirename) = split_identifier_name(name.str(this)); + NPNR_ASSERT(x >= 0 && x < chip_info->width); + NPNR_ASSERT(y >= 0 && y < chip_info->height); + auto &tile = db->loctypes[chip_info->grid[y * chip_info->width + x].loc_type]; + IdString wn = id(wirename); + for (size_t i = 0; i < tile.num_wires; i++) { + if (tile.wires[i].name == wn.index) { + WireId ret; + ret.tile = y * chip_info->width + x; + ret.index = i; + return ret; + } + } + return WireId(); +} + +IdString Arch::getWireType(WireId wire) const { return id("WIRE"); } + +std::vector> Arch::getWireAttrs(WireId wire) const +{ + std::vector> ret; + + ret.emplace_back(id("INDEX"), stringf("%d", wire.index)); + + ret.emplace_back(id("GRID_X"), stringf("%d", wire.tile % chip_info->width)); + ret.emplace_back(id("GRID_Y"), stringf("%d", wire.tile / chip_info->width)); + ret.emplace_back(id("FLAGS"), stringf("%u", wire_data(wire).flags)); + + return ret; +} + +// ----------------------------------------------------------------------- + +PipId Arch::getPipByName(IdString name) const +{ + int x, y; + std::string pipname; + std::tie(x, y, pipname) = split_identifier_name(name.str(this)); + NPNR_ASSERT(x >= 0 && x < chip_info->width); + NPNR_ASSERT(y >= 0 && y < chip_info->height); + PipId ret; + ret.tile = y * chip_info->width + x; + auto sep_pos = pipname.find(':'); + ret.index = std::stoi(pipname.substr(0, sep_pos)); + return ret; +} + +IdString Arch::getPipName(PipId pip) const +{ + NPNR_ASSERT(pip != PipId()); + return id(stringf("X%d/Y%d/%d:%s->%s", pip.tile % chip_info->width, pip.tile / chip_info->width, pip.index, + nameOf(loc_data(pip).wires[pip_data(pip).from_wire].name), + nameOf(loc_data(pip).wires[pip_data(pip).to_wire].name))); +} + +IdString Arch::getPipType(PipId pip) const { return IdString(); } + +std::vector> Arch::getPipAttrs(PipId pip) const +{ + std::vector> ret; + + ret.emplace_back(id("INDEX"), stringf("%d", pip.index)); + + ret.emplace_back(id("GRID_X"), stringf("%d", pip.tile % chip_info->width)); + ret.emplace_back(id("GRID_Y"), stringf("%d", pip.tile / chip_info->width)); + + ret.emplace_back(id("FROM_TILE_WIRE"), nameOf(loc_data(pip).wires[pip_data(pip).from_wire].name)); + ret.emplace_back(id("TO_TILE_WIRE"), nameOf(loc_data(pip).wires[pip_data(pip).to_wire].name)); + + return ret; +} + NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/nexus/arch.h b/nexus/arch.h index 2c8bad7e..91bc5f25 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -766,7 +766,6 @@ struct Arch : BaseCtx return w2n == wire_to_net.end() ? nullptr : w2n->second; } - WireId getConflictingWireWire(WireId wire) const { return wire; } DelayInfo getWireDelay(WireId wire) const -- cgit v1.2.3 From 259659c83898682cecd5e60db2bcfc1c09a493d4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Jan 2020 13:06:37 +0000 Subject: nexus: Add more placeholder Arch functions Signed-off-by: David Shah --- nexus/arch.cc | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/arch.h | 21 +++++++-- nexus/arch_place.cc | 30 +++++++++++++ 3 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 nexus/arch_place.cc diff --git a/nexus/arch.cc b/nexus/arch.cc index 19fef385..d9d30dba 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -311,4 +311,125 @@ std::vector> Arch::getPipAttrs(PipId pip) const return ret; } +// ----------------------------------------------------------------------- + +std::vector Arch::getDecalGraphics(DecalId decal) const +{ + std::vector ret; + + return ret; +} + +DecalXY Arch::getBelDecal(BelId bel) const +{ + DecalXY decalxy; + decalxy.decal.index = -1; + decalxy.x = 0; + decalxy.y = 0; + return decalxy; +} + +DecalXY Arch::getWireDecal(WireId wire) const { return {}; } + +DecalXY Arch::getPipDecal(PipId pip) const { return {}; }; + +DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; + +// ----------------------------------------------------------------------- + +bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const +{ + return false; +} + +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const +{ + return TMG_IGNORE; +} + +TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const { return {}; } + +// ----------------------------------------------------------------------- + +delay_t Arch::estimateDelay(WireId src, WireId dst) const +{ + int src_x = src.tile % chip_info->width, src_y = src.tile / chip_info->width; + int dst_x = dst.tile % chip_info->width, dst_y = dst.tile / chip_info->width; + int dist_x = std::abs(src_x - dst_x); + int dist_y = std::abs(src_y - dst_y); + return 100 * dist_x + 100 * dist_y; +} +delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +{ + if (net_info->driver.cell == nullptr || net_info->driver.cell->bel == BelId() || sink.cell->bel == BelId()) + return 0; + int src_x = net_info->driver.cell->bel.tile % chip_info->width, + src_y = net_info->driver.cell->bel.tile / chip_info->width; + + int dst_x = sink.cell->bel.tile % chip_info->width, dst_y = sink.cell->bel.tile / chip_info->width; + int dist_x = std::abs(src_x - dst_x); + int dist_y = std::abs(src_y - dst_y); + return 100 * dist_x + 100 * dist_y; +} + +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } + +// ----------------------------------------------------------------------- + +bool Arch::pack() +{ + // FIXME + getCtx()->attrs[getCtx()->id("step")] = std::string("pack"); + archInfoToAttributes(); + return true; +} + +bool Arch::place() +{ + std::string placer = str_or_default(settings, id("placer"), defaultPlacer); + + if (placer == "heap") { + PlacerHeapCfg cfg(getCtx()); + cfg.criticalityExponent = 7; + if (!placer_heap(getCtx(), cfg)) + return false; + } else if (placer == "sa") { + if (!placer1(getCtx(), Placer1Cfg(getCtx()))) + return false; + } else { + log_error("Nexus architecture does not support placer '%s'\n", placer.c_str()); + } + getCtx()->attrs[getCtx()->id("step")] = std::string("place"); + archInfoToAttributes(); + return true; +} + +bool Arch::route() +{ + assign_budget(getCtx(), true); + bool result = router1(getCtx(), Router1Cfg(getCtx())); + getCtx()->attrs[getCtx()->id("step")] = std::string("route"); + archInfoToAttributes(); + return result; +} + +// ----------------------------------------------------------------------- + +void Arch::assignArchInfo() {} + +void assignCellInfo(CellInfo *cell) {} + +// ----------------------------------------------------------------------- + +#ifdef WITH_HEAP +const std::string Arch::defaultPlacer = "heap"; +#else +const std::string Arch::defaultPlacer = "sa"; +#endif + +const std::vector Arch::availablePlacers = {"sa", +#ifdef WITH_HEAP + "heap" +#endif +}; NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/nexus/arch.h b/nexus/arch.h index 91bc5f25..e65a17b8 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -79,7 +79,7 @@ NPNR_PACKED_STRUCT(struct BelInfoPOD { int32_t name; // bel name in tile IdString int32_t type; // bel type IdString int16_t rel_x, rel_y; // bel location relative to parent - uint32_t z; // bel location absolute Z + int32_t z; // bel location absolute Z RelPtr ports; // ports, sorted by name IdString int32_t num_ports; // number of ports }); @@ -680,7 +680,22 @@ struct Arch : BaseCtx return loc; } - BelId getBelByLocation(Loc loc) const; + BelId getBelByLocation(Loc loc) const + { + BelId ret; + auto &t = db->loctypes[chip_info->grid[loc.y * chip_info->width + loc.x].loc_type]; + if (loc.x >= 0 && loc.x < chip_info->width && loc.y >= 0 && loc.y < chip_info->height) { + for (size_t i = 0; i < t.num_bels; i++) { + if (t.bels[i].z == loc.z) { + ret.tile = loc.y * chip_info->width + loc.x; + ret.index = i; + break; + } + } + } + return ret; + } + BelRange getBelsByTile(int x, int y) const; bool getBelGlobalBuf(BelId bel) const { return false; } @@ -968,7 +983,7 @@ struct Arch : BaseCtx // ------------------------------------------------- - delay_t estimateDelay(WireId src, WireId dst, bool debug = false) const; + 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 120; } diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc new file mode 100644 index 00000000..ae1b5c79 --- /dev/null +++ b/nexus/arch_place.cc @@ -0,0 +1,30 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 "log.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } + +bool Arch::isBelLocationValid(BelId bel) const { return true; } + +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From 1e940323b038f1a708286489a7b2ddd1ed1735dc Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Jan 2020 13:34:58 +0000 Subject: nexus: Add main Signed-off-by: David Shah --- nexus/arch.cc | 2 +- nexus/arch.h | 5 +--- nexus/main.cc | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 nexus/main.cc diff --git a/nexus/arch.cc b/nexus/arch.cc index d9d30dba..4b924a86 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -90,7 +90,7 @@ Arch::Arch(ArchArgs args) : args(args) // Check database version and family if (db->version != bba_version) { log_error("Provided database version %d is %s than nextpnr version %d, please rebuild database/nextpnr.\n", - db->version, (db->version > bba_version) ? "newer" : "older", bba_version); + int(db->version), (db->version > bba_version) ? "newer" : "older", int(bba_version)); } if (db->family.get() != family) { log_error("Database is for family '%s' but provided device is family '%s'.\n", db->family.get(), diff --git a/nexus/arch.h b/nexus/arch.h index e65a17b8..48dc137b 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -37,10 +37,7 @@ template struct RelPtr // reinterpret_cast(this); // } - const T *get() const - { - return reinterpret_cast(reinterpret_cast(this) + int64_t(offset) * 4); - } + const T *get() const { return reinterpret_cast(reinterpret_cast(this) + offset); } const T &operator[](size_t index) const { return get()[index]; } diff --git a/nexus/main.cc b/nexus/main.cc new file mode 100644 index 00000000..77f739a8 --- /dev/null +++ b/nexus/main.cc @@ -0,0 +1,74 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * 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. + * + */ + +#ifdef MAIN_EXECUTABLE + +#include +#include "command.h" +#include "design_utils.h" +#include "jsonwrite.h" +#include "log.h" +#include "timing.h" + +USING_NEXTPNR_NAMESPACE + +class NexusCommandHandler : public CommandHandler +{ + public: + NexusCommandHandler(int argc, char **argv); + virtual ~NexusCommandHandler(){}; + std::unique_ptr createContext(std::unordered_map &values) override; + void setupArchContext(Context *ctx) override{}; + void customBitstream(Context *ctx) override; + void customAfterLoad(Context *ctx) override; + + protected: + po::options_description getArchOptions() override; +}; + +NexusCommandHandler::NexusCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} + +po::options_description NexusCommandHandler::getArchOptions() +{ + po::options_description specific("Architecture specific options"); + specific.add_options()("chipdb", po::value(), "name of chip database binary"); + specific.add_options()("device", po::value(), "device name"); + + return specific; +} + +void NexusCommandHandler::customBitstream(Context *ctx) {} + +std::unique_ptr NexusCommandHandler::createContext(std::unordered_map &values) +{ + ArchArgs chipArgs; + chipArgs.chipdb = vm["chipdb"].as(); + chipArgs.device = vm["device"].as(); + return std::unique_ptr(new Context(chipArgs)); +} + +void NexusCommandHandler::customAfterLoad(Context *ctx) {} + +int main(int argc, char *argv[]) +{ + NexusCommandHandler handler(argc, argv); + return handler.exec(); +} + +#endif -- cgit v1.2.3 From b00595f3dac72d971b29c45357e4d0886c08e939 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Jan 2020 16:09:49 +0000 Subject: nexus: Add constids Signed-off-by: David Shah --- nexus/constids.inc | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/nexus/constids.inc b/nexus/constids.inc index e69de29b..bd192484 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -0,0 +1,98 @@ +X(BEL) + +X(OXIDE_FF) +X(CLK) +X(CE) +X(LSR) +X(DI) +X(M) +X(Q) + +X(OXIDE_COMB) +X(A) +X(B) +X(C) +X(D) +X(F) +X(FCI) +X(FCO) +X(SEL) +X(F1) +X(OFX) +X(WAD0) +X(WAD1) +X(WAD2) +X(WAD3) +X(WD) +X(WCK) +X(WRE) + +X(RAMW) +X(A0) +X(A1) +X(B0) +X(B1) +X(C0) +X(C1) +X(D0) +X(D1) +X(WADO0) +X(WADO1) +X(WADO2) +X(WADO3) +X(WCKO) +X(WREO) +X(WDO0) +X(WDO1) +X(WDO2) +X(WDO3) + +X(SEIO33_CORE) +X(T) +X(I) +X(O) +X(I3CRESEN) +X(I3CWKPU) + +X(SEIO18_CORE) +X(DOLP) +X(INLP) +X(INADC) + +X(LUT4) +X(INIT) +X(Z) + +X(WIDEFN9) +X(INIT0) +X(INIT1) + +X(INV) +X(BB) + +X(FD1P3BX) +X(FD1P3DX) +X(FD1P3IX) +X(FD1P3JX) +X(CK) +X(SP) +X(PD) +X(CD) +X(GSR) + +X(CCU2) +X(CIN) +X(COUT) +X(S0) +X(S1) + +X(CLKMUX) +X(CEMUX) +X(LSRMUX) +X(REGDDR) +X(SRMODE) +X(REGSET) +X(LSRMODE) + +X(MODE) +X(INJECT) -- cgit v1.2.3 From 871f3c66bcb63eae7a9ddd4503347b6c0bef4e3f Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Jan 2020 20:52:19 +0000 Subject: nexus: Logic tile validity check functions Signed-off-by: David Shah --- nexus/arch.h | 20 +++++++++++++++ nexus/arch_place.cc | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/archdefs.h | 21 +++++++++++++--- 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 48dc137b..ffa7ef78 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -576,10 +576,26 @@ struct Arch : BaseCtx const ChipInfoPOD *chip_info; // Binding states + struct LogicTileStatus + { + struct SliceStatus + { + bool valid = true, dirty = true; + } slices[4]; + struct HalfTileStatus + { + bool valid = true, dirty = true; + } halfs[2]; + CellInfo *cells[32]; + }; + struct TileStatus { std::vector boundcells; + LogicTileStatus *lts = nullptr; + ~TileStatus() { delete lts; } }; + std::vector tileStatus; std::unordered_map wire_to_net; std::unordered_map pip_to_net; @@ -1088,6 +1104,10 @@ struct Arch : BaseCtx range.e.cursor = nh_data(wire).wire_neighbours[wire.index].num_nwires; return range; } + + // ------------------------------------------------- + + bool nexus_logic_tile_valid(LogicTileStatus <s) const; }; NEXTPNR_NAMESPACE_END diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc index ae1b5c79..6444d1e9 100644 --- a/nexus/arch_place.cc +++ b/nexus/arch_place.cc @@ -23,6 +23,76 @@ NEXTPNR_NAMESPACE_BEGIN +enum LogicBelZ +{ + BEL_LUT0 = 0, + BEL_LUT1 = 1, + BEL_FF0 = 2, + BEL_FF1 = 3, + BEL_RAMW = 4, +}; + +bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const +{ + for (int s = 0; s < 4; s++) { + if (lts.slices[s].dirty) { + lts.slices[s].valid = false; + lts.slices[s].dirty = false; + CellInfo *lut0 = lts.cells[(s << 3) | BEL_LUT0]; + CellInfo *lut1 = lts.cells[(s << 3) | BEL_LUT1]; + CellInfo *ff0 = lts.cells[(s << 3) | BEL_FF0]; + CellInfo *ff1 = lts.cells[(s << 3) | BEL_FF1]; + if (lut0 != nullptr) { + // Check for overuse of M signal + if (lut0->lutInfo.mux2_used && ff0 != nullptr && ff0->ffInfo.m != nullptr) + return false; + } + // Check for correct use of FF0 DI + if (ff0 != nullptr && ff0->ffInfo.di != nullptr && + (lut0 == nullptr || (ff0->ffInfo.di != lut0->lutInfo.f && ff0->ffInfo.di != lut0->lutInfo.ofx))) + return false; + if (lut1 != nullptr) { + // LUT1 cannot contain a MUX2 + if (lut1->lutInfo.mux2_used) + return false; + // If LUT1 is carry then LUT0 must be carry too + if (lut1->lutInfo.is_carry && (lut0 == nullptr || !lut0->lutInfo.is_carry)) + return false; + } + // Check for correct use of FF1 DI + if (ff1 != nullptr && ff1->ffInfo.di != nullptr && (lut1 == nullptr || ff1->ffInfo.di != lut1->lutInfo.f)) + return false; + lts.slices[s].valid = true; + } else if (!lts.slices[s].valid) { + return false; + } + } + for (int h = 0; h < 2; h++) { + if (lts.halfs[h].dirty) { + bool found_ff = false; + FFControlSet ctrlset; + for (int i = 0; i < 1; i++) { + for (auto bel : {BEL_FF0, BEL_FF1, BEL_RAMW}) { + if (bel == BEL_RAMW && (h != 1 || i != 0)) + continue; + CellInfo *ci = lts.cells[(h * 2 + i) << 3 | bel]; + if (ci == nullptr) + continue; + if (!found_ff) { + ctrlset = ci->ffInfo.ctrlset; + found_ff = true; + } else if (ci->ffInfo.ctrlset != ctrlset) { + return false; + } + } + } + } else if (!lts.halfs[h].valid) { + return false; + } + } + return true; +} + bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } bool Arch::isBelLocationValid(BelId bel) const { return true; } diff --git a/nexus/archdefs.h b/nexus/archdefs.h index ef5c4c3c..098333b9 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -148,19 +148,34 @@ struct ArchNetInfo struct NetInfo; +struct FFControlSet +{ + int clkmux, cemux, lsrmux; + bool async, regddr_en, gsr_en; + NetInfo *clk, *lsr, *ce; +}; + +inline bool operator!=(const FFControlSet &a, const FFControlSet &b) +{ + return (a.clkmux != b.clkmux) || (a.cemux != b.cemux) || (a.lsrmux != b.lsrmux) || (a.async != b.async) || + (a.regddr_en != b.regddr_en) || (a.gsr_en != b.gsr_en) || (a.clk != b.clk) || (a.lsr != b.lsr) || + (a.ce != b.ce); +} + struct ArchCellInfo { union { struct { - bool is_memory, is_carry; + bool is_memory, is_carry, mux2_used; int input_count; + NetInfo *f, *ofx; } lutInfo; struct { - bool is_clkinv, is_lsrinv, is_ceinv, is_async, gsr; - NetInfo *clk, *lsr, *ce, *d; + FFControlSet ctrlset; + NetInfo *di, *m; } ffInfo; }; }; -- cgit v1.2.3 From 8e53b35fcc9aad873225b90405d0d509cbb6e869 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Jan 2020 21:04:33 +0000 Subject: nexus/arch: Add tile loc flags and package structs Signed-off-by: David Shah --- nexus/arch.h | 38 +++++++++++++++++++++++++++++++++----- nexus/bba_version.inc | 2 +- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index ffa7ef78..2f6c5f7e 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -149,13 +149,46 @@ NPNR_PACKED_STRUCT(struct PhysicalTileInfoPOD { int32_t tiletype; // tile type IdString }); +enum LocFlagsPOD : uint32_t +{ + LOC_LOGIC = 0x000001, + LOC_IO18 = 0x000002, + LOC_IO33 = 0x000004, + LOC_BRAM = 0x000008, + LOC_DSP = 0x000010, + LOC_IP = 0x000020, + LOC_CIB = 0x000040, + LOC_TAP = 0x001000, + LOC_SPINE = 0x002000, + LOC_TRUNK = 0x004000, + LOC_MIDMUX = 0x008000, + LOC_CMUX = 0x010000, +}; + NPNR_PACKED_STRUCT(struct GridLocationPOD { uint32_t loc_type; + uint32_t loc_flags; uint16_t neighbourhood_type; uint16_t num_phys_tiles; RelPtr phys_tiles; }); +NPNR_PACKED_STRUCT(struct PinInfo { + RelPtr pin_name; + int32_t dqs_func; // DQS function IdString + int32_t clk_func; // Clock function IdStrinng + int16_t bank; // IO bank + uint16_t tile_x; // IO tile X + uint16_t tile_y; // IO tile Y + uint16_t bel_z; // IO bel Z +}); + +NPNR_PACKED_STRUCT(struct PackageInfoPOD { + RelPtr package_name; + uint32_t num_pins; + RelPtr pins; +}); + NPNR_PACKED_STRUCT(struct ChipInfoPOD { RelPtr device_name; uint16_t width; @@ -1017,15 +1050,10 @@ struct Arch : BaseCtx // Get the delay through a cell from one port to another, returning false // if no path exists. This only considers combinational delays, as required by the Arch API bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; - // getCellDelayInternal is similar to the above, but without false path checks and including clock to out delays - // for internal arch use only - bool getCellDelayInternal(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const; // Get the TimingClockingInfo of a port TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const; - // Return true if a net is global - bool isGlobalNet(const NetInfo *net) const; // ------------------------------------------------- diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc index d00491fd..0cfbf088 100644 --- a/nexus/bba_version.inc +++ b/nexus/bba_version.inc @@ -1 +1 @@ -1 +2 -- cgit v1.2.3 From eb15463406c5784f7c3599163ba40571188d57c7 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 9 Jan 2020 19:02:01 +0000 Subject: nexus: Add routing FASM export Signed-off-by: David Shah --- nexus/arch.h | 21 +++++++- nexus/bba_version.inc | 2 +- nexus/fasm.cc | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/main.cc | 13 ++++- 4 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 nexus/fasm.cc diff --git a/nexus/arch.h b/nexus/arch.h index 2f6c5f7e..f46f51c7 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -100,8 +100,15 @@ NPNR_PACKED_STRUCT(struct LocWireInfoPOD { RelPtr bel_pins; }); +enum PipFlags +{ + PIP_FIXED_CONN = 0x8000, +}; + NPNR_PACKED_STRUCT(struct PipInfoPOD { uint16_t from_wire, to_wire; + uint16_t flags; + uint16_t padding; int32_t tile_type; }); @@ -1115,7 +1122,16 @@ struct Arch : BaseCtx { return chip_canonical_wire(db, chip_info, tile, index); } - + IdString pip_src_wire_name(PipId pip) const + { + int wire = pip_data(pip).from_wire; + return db->loctypes[chip_info->grid[pip.tile].loc_type].wires[wire].name; + } + IdString pip_dst_wire_name(PipId pip) const + { + int wire = pip_data(pip).to_wire; + return db->loctypes[chip_info->grid[pip.tile].loc_type].wires[wire].name; + } // ------------------------------------------------- NeighWireRange neigh_wire_range(WireId wire) const @@ -1136,6 +1152,9 @@ struct Arch : BaseCtx // ------------------------------------------------- bool nexus_logic_tile_valid(LogicTileStatus <s) const; + + // ------------------------------------------------- + void write_fasm(std::ostream &out) const; }; NEXTPNR_NAMESPACE_END diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc index 0cfbf088..00750edc 100644 --- a/nexus/bba_version.inc +++ b/nexus/bba_version.inc @@ -1 +1 @@ -2 +3 diff --git a/nexus/fasm.cc b/nexus/fasm.cc new file mode 100644 index 00000000..36656ec4 --- /dev/null +++ b/nexus/fasm.cc @@ -0,0 +1,143 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 "log.h" +#include "nextpnr.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN +namespace { +struct NexusFasmWriter +{ + const Context *ctx; + std::ostream &out; + std::vector fasm_ctx; + + void push(const std::string &x) { fasm_ctx.push_back(x); } + + void pop() { fasm_ctx.pop_back(); } + + void pop(int N) + { + for (int i = 0; i < N; i++) + fasm_ctx.pop_back(); + } + bool last_was_blank = true; + void blank() + { + if (!last_was_blank) + out << std::endl; + last_was_blank = true; + } + + void write_prefix() + { + for (auto &x : fasm_ctx) + out << x << "."; + last_was_blank = false; + } + + void write_bit(const std::string &name, bool value = true) + { + if (value) { + write_prefix(); + out << name << std::endl; + } + } + void write_comment(const std::string &cmt) { out << "# " << cmt << std::endl; } + + void write_vector(const std::string &name, const std::vector &value, bool invert = false) + { + write_prefix(); + out << name << " = " << int(value.size()) << "'b"; + for (auto bit : boost::adaptors::reverse(value)) + out << ((bit ^ invert) ? '1' : '0'); + out << std::endl; + } + + void write_int_vector(const std::string &name, uint64_t value, int width, bool invert = false) + { + std::vector bits(width, false); + for (int i = 0; i < width; i++) + bits[i] = (value & (1ULL << i)) != 0; + write_vector(name, bits, invert); + } + + NexusFasmWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out) {} + std::string tile_name(int loc, const PhysicalTileInfoPOD &tile) + { + int r = loc / ctx->chip_info->width; + int c = loc % ctx->chip_info->width; + return stringf("%sR%dC%d__%s", ctx->nameOf(tile.prefix), r, c, ctx->nameOf(tile.tiletype)); + } + const PhysicalTileInfoPOD &tile_by_type_and_loc(int loc, IdString type) + { + auto &ploc = ctx->chip_info->grid[loc]; + for (int i = 0; i < ploc.num_phys_tiles; i++) { + if (ploc.phys_tiles[i].tiletype == type.index) + return ploc.phys_tiles[i]; + } + log_error("No tile of type %s found at location R%dC%d", ctx->nameOf(type), loc / ctx->chip_info->width, + loc % ctx->chip_info->width); + } + std::string escape_name(const std::string &name) + { + std::string escaped; + for (char c : name) { + if (c == ':') + escaped += "__"; + else + escaped += c; + } + return escaped; + } + void write_pip(PipId pip) + { + auto &pd = ctx->pip_data(pip); + if (pd.flags & PIP_FIXED_CONN) + return; + std::string tile = tile_name(pip.tile, tile_by_type_and_loc(pip.tile, pd.tile_type)); + std::string source_wire = escape_name(ctx->pip_src_wire_name(pip).str(ctx)); + std::string dest_wire = escape_name(ctx->pip_src_wire_name(pip).str(ctx)); + write_bit(stringf("%s.PIP.%s.%s", tile.c_str(), dest_wire.c_str(), source_wire.c_str())); + } + void write_net(const NetInfo *net) + { + write_comment(stringf("Net %s", ctx->nameOf(net))); + std::set sorted_pips; + for (auto &w : net->wires) + sorted_pips.insert(w.second.pip); + for (auto p : sorted_pips) + write_pip(p); + blank(); + } + void operator()() + { + // Write routing + for (auto n : sorted(ctx->nets)) { + write_net(n.second); + } + } +}; +} // namespace + +void Arch::write_fasm(std::ostream &out) const { NexusFasmWriter(getCtx(), out)(); } + +NEXTPNR_NAMESPACE_END diff --git a/nexus/main.cc b/nexus/main.cc index 77f739a8..9549a573 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -49,11 +49,20 @@ po::options_description NexusCommandHandler::getArchOptions() po::options_description specific("Architecture specific options"); specific.add_options()("chipdb", po::value(), "name of chip database binary"); specific.add_options()("device", po::value(), "device name"); - + specific.add_options()("fasm", po::value(), "fasm file to write"); return specific; } -void NexusCommandHandler::customBitstream(Context *ctx) {} +void NexusCommandHandler::customBitstream(Context *ctx) +{ + if (vm.count("fasm")) { + std::string filename = vm["fasm"].as(); + std::ofstream out(filename); + if (!out) + log_error("Failed to open output FASM file %s.\n", filename.c_str()); + ctx->write_fasm(out); + } +} std::unique_ptr NexusCommandHandler::createContext(std::unordered_map &values) { -- cgit v1.2.3 From 2c49f812d9c2bb3df423b8e6b510be8e73948e38 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 10 Jan 2020 20:21:01 +0000 Subject: nexus: Add packing framework Signed-off-by: David Shah --- nexus/arch.cc | 8 --- nexus/pack.cc | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 nexus/pack.cc diff --git a/nexus/arch.cc b/nexus/arch.cc index 4b924a86..b0a22894 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -376,14 +376,6 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay // ----------------------------------------------------------------------- -bool Arch::pack() -{ - // FIXME - getCtx()->attrs[getCtx()->id("step")] = std::string("pack"); - archInfoToAttributes(); - return true; -} - bool Arch::place() { std::string placer = str_or_default(settings, id("placer"), defaultPlacer); diff --git a/nexus/pack.cc b/nexus/pack.cc new file mode 100644 index 00000000..5a18f6d4 --- /dev/null +++ b/nexus/pack.cc @@ -0,0 +1,175 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 "design_utils.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct NexusPacker +{ + Context *ctx; + + // Generic cell transformation + // Given cell name map and port map + // If port name is not found in port map; it will be copied as-is but stripping [] + struct XFormRule + { + IdString new_type; + std::unordered_map port_xform; + std::unordered_map> port_multixform; + std::unordered_map param_xform; + std::vector> set_attrs; + std::vector> set_params; + }; + + void xform_cell(const std::unordered_map &rules, CellInfo *ci) + { + auto &rule = rules.at(ci->type); + ci->type = rule.new_type; + std::vector orig_port_names; + for (auto &port : ci->ports) + orig_port_names.push_back(port.first); + + for (auto pname : orig_port_names) { + if (rule.port_multixform.count(pname)) { + auto old_port = ci->ports.at(pname); + disconnect_port(ctx, ci, pname); + ci->ports.erase(pname); + for (auto new_name : rule.port_multixform.at(pname)) { + ci->ports[new_name].name = new_name; + ci->ports[new_name].type = old_port.type; + connect_port(ctx, old_port.net, ci, new_name); + } + } else { + IdString new_name; + if (rule.port_xform.count(pname)) { + new_name = rule.port_xform.at(pname); + } else { + std::string stripped_name; + for (auto c : pname.str(ctx)) + if (c != '[' && c != ']') + stripped_name += c; + new_name = ctx->id(stripped_name); + } + if (new_name != pname) { + rename_port(ctx, ci, pname, new_name); + } + } + } + + std::vector xform_params; + for (auto ¶m : ci->params) + if (rule.param_xform.count(param.first)) + xform_params.push_back(param.first); + for (auto param : xform_params) + ci->params[rule.param_xform.at(param)] = ci->params[param]; + + for (auto &attr : rule.set_attrs) + ci->attrs[attr.first] = attr.second; + + for (auto ¶m : rule.set_params) + ci->params[param.first] = param.second; + } + + void generic_xform(const std::unordered_map &rules, bool print_summary = false) + { + std::map cell_count; + std::map new_types; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (rules.count(ci->type)) { + cell_count[ci->type.str(ctx)]++; + xform_cell(rules, ci); + new_types[ci->type.str(ctx)]++; + } + } + if (print_summary) { + for (auto &nt : new_types) { + log_info(" Created %d %s cells from:\n", nt.second, nt.first.c_str()); + for (auto &cc : cell_count) { + if (rules.at(ctx->id(cc.first)).new_type != ctx->id(nt.first)) + continue; + log_info(" %6dx %s\n", cc.second, cc.first.c_str()); + } + } + } + } + + void pack_luts() + { + log_info("Packing LUTs...\n"); + std::unordered_map lut_rules; + lut_rules[id_LUT4].new_type = id_OXIDE_COMB; + lut_rules[id_LUT4].port_xform[id_Z] = id_F; + generic_xform(lut_rules); + } + + void pack_ffs() + { + log_info("Packing FFs...\n"); + std::unordered_map ff_rules; + for (auto type : {id_FD1P3BX, id_FD1P3DX, id_FD1P3IX, id_FD1P3JX}) { + ff_rules[type].new_type = id_OXIDE_FF; + ff_rules[type].port_xform[id_CK] = id_CLK; + ff_rules[type].port_xform[id_D] = id_M; // will be rerouted to DI later if applicable + ff_rules[type].port_xform[id_SP] = id_CE; + ff_rules[type].port_xform[id_Q] = id_Q; + } + // Async preload + ff_rules[id_FD1P3BX].set_params.emplace_back(id_SRMODE, std::string("ASYNC")); + ff_rules[id_FD1P3BX].set_params.emplace_back(id_REGSET, std::string("SET")); + ff_rules[id_FD1P3BX].port_xform[id_PD] = id_LSR; + // Async clear + ff_rules[id_FD1P3DX].set_params.emplace_back(id_SRMODE, std::string("ASYNC")); + ff_rules[id_FD1P3DX].set_params.emplace_back(id_REGSET, std::string("RESET")); + ff_rules[id_FD1P3DX].port_xform[id_CD] = id_LSR; + // Sync preload + ff_rules[id_FD1P3JX].set_params.emplace_back(id_SRMODE, std::string("LSR_OVER_CE")); + ff_rules[id_FD1P3JX].set_params.emplace_back(id_REGSET, std::string("SET")); + ff_rules[id_FD1P3JX].port_xform[id_PD] = id_LSR; + // Sync clear + ff_rules[id_FD1P3IX].set_params.emplace_back(id_SRMODE, std::string("LSR_OVER_CE")); + ff_rules[id_FD1P3IX].set_params.emplace_back(id_REGSET, std::string("RESET")); + ff_rules[id_FD1P3IX].port_xform[id_CD] = id_LSR; + + generic_xform(ff_rules, true); + } + + explicit NexusPacker(Context *ctx) : ctx(ctx) {} + + void operator()() + { + pack_ffs(); + pack_luts(); + } +}; + +bool Arch::pack() +{ + (NexusPacker(getCtx()))(); + attrs[id("step")] = std::string("pack"); + archInfoToAttributes(); + return true; +} + +NEXTPNR_NAMESPACE_END \ No newline at end of file -- cgit v1.2.3 From 689922bc21ec151ebf495813b639aed4758f9587 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 10 Jan 2020 21:35:21 +0000 Subject: nexus: Add FASM export for comb logic and FFs Signed-off-by: David Shah --- nexus/constids.inc | 5 +++++ nexus/fasm.cc | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/nexus/constids.inc b/nexus/constids.inc index bd192484..56c54aa0 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -96,3 +96,8 @@ X(LSRMODE) X(MODE) X(INJECT) + +X(PLC) +X(CIB) +X(CIB_T) +X(CIB_LR) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 36656ec4..2441df4a 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -80,6 +80,17 @@ struct NexusFasmWriter write_vector(name, bits, invert); } + void write_enum(const CellInfo *cell, const std::string &name, const std::string &defval = "") + { + auto fnd = cell->params.find(ctx->id(name)); + if (fnd == cell->params.end()) { + if (!defval.empty()) + write_bit(stringf("%s.%s", name.c_str(), defval.c_str())); + } else { + write_bit(stringf("%s.%s", name.c_str(), fnd->second.c_str())); + } + } + NexusFasmWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out) {} std::string tile_name(int loc, const PhysicalTileInfoPOD &tile) { @@ -108,6 +119,7 @@ struct NexusFasmWriter } return escaped; } + void push_tile(int loc, IdString tile_type) { push(tile_name(loc, tile_by_type_and_loc(loc, tile_type))); } void write_pip(PipId pip) { auto &pd = ctx->pip_data(pip); @@ -128,12 +140,60 @@ struct NexusFasmWriter write_pip(p); blank(); } + void write_comb(const CellInfo *cell) + { + BelId bel = cell->bel; + int z = ctx->bel_data(bel).z; + int k = z & 0x1; + char slice = 'A' + (z >> 8); + push_tile(bel.tile, id_PLC); + push(stringf("SLICE%c", slice)); + if (cell->params.count(id_INIT)) + write_int_vector(stringf("K%d.INIT", k), int_or_default(cell->params, id_INIT, 0), 16); + if (cell->lutInfo.is_carry) { + write_bit("MODE.CCU2"); + write_enum(cell, "INJECT", "NO"); + } + pop(2); + } + void write_ff(const CellInfo *cell) + { + BelId bel = cell->bel; + int z = ctx->bel_data(bel).z; + int k = z & 0x1; + char slice = 'A' + (z >> 8); + push_tile(bel.tile, id_PLC); + push(stringf("SLICE%c", slice)); + push(stringf("FF%d", k)); + write_bit("USED.YES"); + write_enum(cell, "REGSET", "RESET"); + write_enum(cell, "LSRMODE", "LSR"); + write_enum(cell, "SEL", "DF"); + pop(); + write_enum(cell, "REGDDR"); + write_enum(cell, "SRMODE"); + write_enum(cell, "CLKMUX"); + write_enum(cell, "CEMUX"); + write_enum(cell, "LSRMUX"); + write_enum(cell, "GSR"); + pop(2); + } void operator()() { // Write routing for (auto n : sorted(ctx->nets)) { write_net(n.second); } + // Write cell config + for (auto c : sorted(ctx->cells)) { + const CellInfo *ci = c.second; + write_comment(stringf("# Cell %s", ctx->nameOf(ci))); + if (ci->type == id_OXIDE_COMB) + write_comb(ci); + else if (ci->type == id_OXIDE_FF) + write_ff(ci); + blank(); + } } }; } // namespace -- cgit v1.2.3 From cef0e1db8fc2e8a2c3ced4cffaf385e1d7e0f5ca Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 11 Jan 2020 10:20:21 +0000 Subject: nexus: Minimal IO FASM output Signed-off-by: David Shah --- nexus/constids.inc | 2 ++ nexus/fasm.cc | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/nexus/constids.inc b/nexus/constids.inc index 56c54aa0..8a7d688e 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -101,3 +101,5 @@ X(PLC) X(CIB) X(CIB_T) X(CIB_LR) + +X(IO_TYPE) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 2441df4a..25c13b2b 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -108,6 +108,12 @@ struct NexusFasmWriter log_error("No tile of type %s found at location R%dC%d", ctx->nameOf(type), loc / ctx->chip_info->width, loc % ctx->chip_info->width); } + const PhysicalTileInfoPOD &tile_at_loc(int loc) + { + auto &ploc = ctx->chip_info->grid[loc]; + NPNR_ASSERT(ploc.num_phys_tiles == 1); + return ploc.phys_tiles[0]; + } std::string escape_name(const std::string &name) { std::string escaped; @@ -120,6 +126,8 @@ struct NexusFasmWriter return escaped; } void push_tile(int loc, IdString tile_type) { push(tile_name(loc, tile_by_type_and_loc(loc, tile_type))); } + void push_tile(int loc) { push(tile_name(loc, tile_at_loc(loc))); } + void push_belname(BelId bel) { push(ctx->nameOf(ctx->bel_data(bel).name)); } void write_pip(PipId pip) { auto &pd = ctx->pip_data(pip); @@ -178,6 +186,22 @@ struct NexusFasmWriter write_enum(cell, "GSR"); pop(2); } + void write_io33(const CellInfo *cell) + { + BelId bel = cell->bel; + push_tile(bel.tile); + push_belname(bel); + const NetInfo *t = get_net_or_empty(cell, id_T); + bool is_input = false, is_output = false; + if (t == nullptr || t->name == ctx->id("$PACKER_VCC_NET")) { + is_input = true; + } else if (t->name == ctx->id("$PACKER_GND_NET")) { + is_output = true; + } + const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); + write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS33").c_str())); + pop(2); + } void operator()() { // Write routing @@ -192,6 +216,8 @@ struct NexusFasmWriter write_comb(ci); else if (ci->type == id_OXIDE_FF) write_ff(ci); + else if (ci->type == id_SEIO33_CORE) + write_io33(ci); blank(); } } -- cgit v1.2.3 From 1ff42e9936f1ae037b5bb3b6fba4761e94d831a4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 11 Jan 2020 19:49:37 +0000 Subject: nexus: Fixes Signed-off-by: David Shah --- nexus/fasm.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 25c13b2b..d2492983 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -135,7 +135,7 @@ struct NexusFasmWriter return; std::string tile = tile_name(pip.tile, tile_by_type_and_loc(pip.tile, pd.tile_type)); std::string source_wire = escape_name(ctx->pip_src_wire_name(pip).str(ctx)); - std::string dest_wire = escape_name(ctx->pip_src_wire_name(pip).str(ctx)); + std::string dest_wire = escape_name(ctx->pip_dst_wire_name(pip).str(ctx)); write_bit(stringf("%s.PIP.%s.%s", tile.c_str(), dest_wire.c_str(), source_wire.c_str())); } void write_net(const NetInfo *net) @@ -143,7 +143,8 @@ struct NexusFasmWriter write_comment(stringf("Net %s", ctx->nameOf(net))); std::set sorted_pips; for (auto &w : net->wires) - sorted_pips.insert(w.second.pip); + if (w.second.pip != PipId()) + sorted_pips.insert(w.second.pip); for (auto p : sorted_pips) write_pip(p); blank(); @@ -157,11 +158,13 @@ struct NexusFasmWriter push_tile(bel.tile, id_PLC); push(stringf("SLICE%c", slice)); if (cell->params.count(id_INIT)) - write_int_vector(stringf("K%d.INIT", k), int_or_default(cell->params, id_INIT, 0), 16); + write_int_vector(stringf("K%d.INIT[15:0]", k), int_or_default(cell->params, id_INIT, 0), 16); +#if 0 if (cell->lutInfo.is_carry) { write_bit("MODE.CCU2"); write_enum(cell, "INJECT", "NO"); } +#endif pop(2); } void write_ff(const CellInfo *cell) -- cgit v1.2.3 From ee4e30ce8ba11ae889ec3b8b19eef49d5459c69a Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 11 Jan 2020 20:02:31 +0000 Subject: nexus/fasm: Fix SLICE lettering Signed-off-by: David Shah --- nexus/fasm.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index d2492983..7db5a1ba 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -154,7 +154,7 @@ struct NexusFasmWriter BelId bel = cell->bel; int z = ctx->bel_data(bel).z; int k = z & 0x1; - char slice = 'A' + (z >> 8); + char slice = 'A' + (z >> 3); push_tile(bel.tile, id_PLC); push(stringf("SLICE%c", slice)); if (cell->params.count(id_INIT)) @@ -172,7 +172,7 @@ struct NexusFasmWriter BelId bel = cell->bel; int z = ctx->bel_data(bel).z; int k = z & 0x1; - char slice = 'A' + (z >> 8); + char slice = 'A' + (z >> 3); push_tile(bel.tile, id_PLC); push(stringf("SLICE%c", slice)); push(stringf("FF%d", k)); -- cgit v1.2.3 From 2fc75bae79e605ae2f778469999a6567d0c6b4dc Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 11 Jan 2020 20:12:50 +0000 Subject: nexus: Fix FF naming Signed-off-by: David Shah --- nexus/fasm.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 7db5a1ba..45c02514 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -175,7 +175,7 @@ struct NexusFasmWriter char slice = 'A' + (z >> 3); push_tile(bel.tile, id_PLC); push(stringf("SLICE%c", slice)); - push(stringf("FF%d", k)); + push(stringf("REG%d", k)); write_bit("USED.YES"); write_enum(cell, "REGSET", "RESET"); write_enum(cell, "LSRMODE", "LSR"); -- cgit v1.2.3 From 3c8a740374f5ab64916486a8200bc162044072c7 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 12 Jan 2020 19:09:47 +0000 Subject: nexus: Add global info to BBA Signed-off-by: David Shah --- nexus/arch.h | 50 ++++++++++++++++++++++++++++++++++++++++++-------- nexus/bba_version.inc | 2 +- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index f46f51c7..6393ef30 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -112,12 +112,15 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD { int32_t tile_type; }); -enum RelLocFlags +enum RelLocType : uint8_t { - REL_GLOBAL = 0x80, - REL_BRANCH = 0x40, - REL_SPINE = 0x20, - REL_HROW = 0x10 + REL_LOC_XY = 0, + REL_LOC_GLOBAL = 1, + REL_LOC_BRANCH = 2, + REL_LOC_BRANCH_L = 3, + REL_LOC_BRANCH_R = 4, + REL_LOC_SPINE = 5, + REL_LOC_HROW = 6, }; enum ArcFlags @@ -129,7 +132,7 @@ enum ArcFlags NPNR_PACKED_STRUCT(struct RelWireInfoPOD { int16_t rel_x, rel_y; uint16_t wire_index; - uint8_t loc_flags; + uint8_t loc_type; uint8_t arc_flags; }); @@ -180,7 +183,7 @@ NPNR_PACKED_STRUCT(struct GridLocationPOD { RelPtr phys_tiles; }); -NPNR_PACKED_STRUCT(struct PinInfo { +NPNR_PACKED_STRUCT(struct PinInfoPOD { RelPtr pin_name; int32_t dqs_func; // DQS function IdString int32_t clk_func; // Clock function IdStrinng @@ -190,10 +193,40 @@ NPNR_PACKED_STRUCT(struct PinInfo { uint16_t bel_z; // IO bel Z }); +NPNR_PACKED_STRUCT(struct GlobalBranchInfoPOD { + uint16_t branch_col; + uint16_t from_col; + uint16_t tap_driver_col; + uint16_t tap_side; + uint16_t to_col; + uint16_t padding; +}); + +NPNR_PACKED_STRUCT(struct GlobalSpineInfoPOD { + uint16_t from_row; + uint16_t to_row; + uint16_t spine_row; + uint16_t padding; +}); + +NPNR_PACKED_STRUCT(struct GlobalHrowInfoPOD { + uint16_t hrow_col; + uint16_t padding; + uint32_t num_spine_cols; + RelPtr spine_cols; +}); + +NPNR_PACKED_STRUCT(struct GlobalInfoPOD { + uint32_t num_branches, num_spines, num_hrows; + RelPtr branches; + RelPtr spines; + RelPtr hrows; +}); + NPNR_PACKED_STRUCT(struct PackageInfoPOD { RelPtr package_name; uint32_t num_pins; - RelPtr pins; + RelPtr pins; }); NPNR_PACKED_STRUCT(struct ChipInfoPOD { @@ -202,6 +235,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { uint16_t height; uint32_t num_tiles; RelPtr grid; + RelPtr globals; }); NPNR_PACKED_STRUCT(struct IdStringDBPOD { diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc index 00750edc..b8626c4c 100644 --- a/nexus/bba_version.inc +++ b/nexus/bba_version.inc @@ -1 +1 @@ -3 +4 -- cgit v1.2.3 From 140baf70374b9f0f57793f46b06df6906627b119 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 12 Jan 2020 20:16:28 +0000 Subject: nexus: Globals implementation Signed-off-by: David Shah --- nexus/arch.h | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 6393ef30..9a92d9af 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -295,6 +295,110 @@ inline bool chip_rel_tile(const ChipInfoPOD *chip, int32_t base, int16_t rel_x, next = new_y * chip->width + new_x; return true; } +inline int32_t chip_tile_from_xy(const ChipInfoPOD *chip, int32_t x, int32_t y) { return y * chip->width + x; } +inline bool chip_get_branch_loc(const ChipInfoPOD *chip, int32_t x, int32_t &branch_x) +{ + for (int i = 0; i < int(chip->globals->num_branches); i++) { + auto &b = chip->globals->branches[i]; + if (x >= b.from_col && x <= b.to_col) { + branch_x = b.branch_col; + return true; + } + } + return false; +} +inline bool chip_get_spine_loc(const ChipInfoPOD *chip, int32_t x, int32_t y, int32_t &spine_x, int32_t &spine_y) +{ + bool y_found = false; + for (int i = 0; i < int(chip->globals->num_spines); i++) { + auto &s = chip->globals->spines[i]; + if (y >= s.from_row && y <= s.to_row) { + spine_y = s.spine_row; + y_found = true; + break; + } + } + if (!y_found) + return false; + for (int i = 0; i < int(chip->globals->num_hrows); i++) { + auto &hr = chip->globals->hrows[i]; + for (int j = 0; j < int(hr.num_spine_cols); j++) { + int32_t sc = hr.spine_cols[j]; + if (std::abs(sc - x) < 3) { + spine_x = sc; + return true; + } + } + } + return false; +} +inline bool chip_get_hrow_loc(const ChipInfoPOD *chip, int32_t x, int32_t y, int32_t &hrow_x, int32_t &hrow_y) +{ + bool y_found = false; + for (int i = 0; i < int(chip->globals->num_spines); i++) { + auto &s = chip->globals->spines[i]; + if (std::abs(y - s.spine_row) < 3) { + hrow_y = s.spine_row; + y_found = true; + break; + } + } + if (!y_found) + return false; + for (int i = 0; i < int(chip->globals->num_hrows); i++) { + auto &hr = chip->globals->hrows[i]; + for (int j = 0; j < int(hr.num_spine_cols); j++) { + int32_t sc = hr.spine_cols[j]; + if (std::abs(sc - x) < 3) { + hrow_x = hr.hrow_col; + return true; + } + } + } + return false; +} +inline bool chip_branch_tile(const ChipInfoPOD *chip, int32_t x, int32_t y, int32_t &next) +{ + int32_t branch_x; + if (!chip_get_branch_loc(chip, x, branch_x)) + return false; + next = chip_tile_from_xy(chip, x, y); + return true; +} +inline bool chip_rel_loc_tile(const ChipInfoPOD *chip, int32_t base, const RelWireInfoPOD &rel, int32_t &next) +{ + int32_t curr_x = base % chip->width; + int32_t curr_y = base / chip->width; + switch (rel.loc_type) { + case REL_LOC_XY: + return chip_rel_tile(chip, base, rel.rel_x, rel.rel_y, next); + case REL_LOC_BRANCH: + return chip_branch_tile(chip, curr_x, curr_y, next); + case REL_LOC_BRANCH_L: + return chip_branch_tile(chip, curr_x - 2, curr_y, next); + case REL_LOC_BRANCH_R: + return chip_branch_tile(chip, curr_x + 2, curr_y, next); + case REL_LOC_SPINE: { + int32_t spine_x, spine_y; + if (!chip_get_spine_loc(chip, curr_x, curr_y, spine_x, spine_y)) + return false; + next = chip_tile_from_xy(chip, spine_x, spine_y); + return true; + } + case REL_LOC_HROW: { + int32_t hrow_x, hrow_y; + if (!chip_get_hrow_loc(chip, curr_x, curr_y, hrow_x, hrow_y)) + return false; + next = chip_tile_from_xy(chip, hrow_x, hrow_y); + return true; + } + case REL_LOC_GLOBAL: + next = 0; + return true; + default: + return false; + } +} inline WireId chip_canonical_wire(const DatabasePOD *db, const ChipInfoPOD *chip, int32_t tile, uint16_t index) { WireId wire{tile, index}; @@ -307,7 +411,7 @@ inline WireId chip_canonical_wire(const DatabasePOD *db, const ChipInfoPOD *chip for (size_t i = 0; i < wn.num_nwires; i++) { auto &nw = wn.neigh_wires[i]; if (nw.arc_flags & LOGICAL_TO_PRIMARY) { - if (chip_rel_tile(chip, tile, nw.rel_x, nw.rel_y, wire.tile)) { + if (chip_rel_loc_tile(chip, tile, nw, wire.tile)) { wire.index = nw.wire_index; break; } @@ -327,7 +431,7 @@ inline bool chip_wire_is_primary(const DatabasePOD *db, const ChipInfoPOD *chip, for (size_t i = 0; i < wn.num_nwires; i++) { auto &nw = wn.neigh_wires[i]; if (nw.arc_flags & LOGICAL_TO_PRIMARY) { - if (chip_rel_tile(chip, tile, nw.rel_x, nw.rel_y, wire.tile)) { + if (chip_rel_loc_tile(chip, tile, nw, wire.tile)) { return false; } } -- cgit v1.2.3 From 44f98c545b353c823692f91953c1ca74b6be2d4f Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 12 Jan 2020 20:44:22 +0000 Subject: nexus: Add global networks Signed-off-by: David Shah --- common/nextpnr.cc | 4 ++-- nexus/arch.cc | 3 +++ nexus/arch.h | 5 +++-- nexus/constids.inc | 5 +++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index 07b88471..9a856b99 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -506,7 +506,7 @@ void Context::check() const } } } - +#ifdef CHECK_WIRES for (auto w : getWires()) { auto ni = getBoundWireNet(w); if (ni != nullptr) { @@ -514,7 +514,7 @@ void Context::check() const CHECK_FAIL("wire '%s' missing in wires map of bound net '%s'\n", nameOfWire(w), nameOf(ni)); } } - +#endif for (auto &c : cells) { auto ci = c.second.get(); if (c.first != ci->name) diff --git a/nexus/arch.cc b/nexus/arch.cc index b0a22894..663941ff 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -382,6 +382,9 @@ bool Arch::place() if (placer == "heap") { PlacerHeapCfg cfg(getCtx()); + cfg.ioBufTypes.insert(id_SEIO33_CORE); + cfg.ioBufTypes.insert(id_SEIO18_CORE); + cfg.ioBufTypes.insert(id_OSC_CORE); cfg.criticalityExponent = 7; if (!placer_heap(getCtx(), cfg)) return false; diff --git a/nexus/arch.h b/nexus/arch.h index 9a92d9af..f0f51271 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -362,7 +362,7 @@ inline bool chip_branch_tile(const ChipInfoPOD *chip, int32_t x, int32_t y, int3 int32_t branch_x; if (!chip_get_branch_loc(chip, x, branch_x)) return false; - next = chip_tile_from_xy(chip, x, y); + next = chip_tile_from_xy(chip, branch_x, y); return true; } inline bool chip_rel_loc_tile(const ChipInfoPOD *chip, int32_t base, const RelWireInfoPOD &rel, int32_t &next) @@ -1258,7 +1258,8 @@ struct Arch : BaseCtx } inline WireId canonical_wire(int32_t tile, uint16_t index) const { - return chip_canonical_wire(db, chip_info, tile, index); + WireId c = chip_canonical_wire(db, chip_info, tile, index); + return c; } IdString pip_src_wire_name(PipId pip) const { diff --git a/nexus/constids.inc b/nexus/constids.inc index 8a7d688e..edde3bc9 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -103,3 +103,8 @@ X(CIB_T) X(CIB_LR) X(IO_TYPE) + +X(OSC_CORE) +X(HFCLKOUT) +X(LFCLKOUT) + -- cgit v1.2.3 From f8f3ee06c71aa1726f32821a42193705b8056b82 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 13 Jan 2020 12:17:34 +0000 Subject: nexus: Add SEIO18 support Signed-off-by: David Shah --- nexus/fasm.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 45c02514..5a2880da 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -205,6 +205,23 @@ struct NexusFasmWriter write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS33").c_str())); pop(2); } + void write_io18(const CellInfo *cell) + { + BelId bel = cell->bel; + push_tile(bel.tile); + push_belname(bel); + push("SEIO18"); + const NetInfo *t = get_net_or_empty(cell, id_T); + bool is_input = false, is_output = false; + if (t == nullptr || t->name == ctx->id("$PACKER_VCC_NET")) { + is_input = true; + } else if (t->name == ctx->id("$PACKER_GND_NET")) { + is_output = true; + } + const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); + write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str())); + pop(3); + } void operator()() { // Write routing @@ -221,6 +238,8 @@ struct NexusFasmWriter write_ff(ci); else if (ci->type == id_SEIO33_CORE) write_io33(ci); + else if (ci->type == id_SEIO18_CORE) + write_io18(ci); blank(); } } -- cgit v1.2.3 From a280f893d4c7f516372302a03aee5299bbe13d5e Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 13 Jan 2020 16:01:08 +0000 Subject: nexus: Add oscillator support Signed-off-by: David Shah --- nexus/constids.inc | 2 ++ nexus/fasm.cc | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/nexus/constids.inc b/nexus/constids.inc index edde3bc9..38df628e 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -107,4 +107,6 @@ X(IO_TYPE) X(OSC_CORE) X(HFCLKOUT) X(LFCLKOUT) +X(HF_CLK_DIV) + diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 5a2880da..f7841bde 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -222,6 +222,19 @@ struct NexusFasmWriter write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str())); pop(3); } + void write_osc(const CellInfo *cell) + { + BelId bel = cell->bel; + push_tile(bel.tile); + push_belname(bel); + write_enum(cell, "HF_OSC_EN"); + write_enum(cell, "HF_FABRIC_EN"); + write_enum(cell, "HFDIV_FABRIC_EN", "ENABLED"); + write_enum(cell, "LF_FABRIC_EN"); + write_enum(cell, "LF_OUTPUT_EN"); + write_int_vector(stringf("HF_CLK_DIV[7:0]"), int_or_default(cell->params, id_HF_CLK_DIV, 0), 8); + pop(2); + } void operator()() { // Write routing @@ -240,6 +253,8 @@ struct NexusFasmWriter write_io33(ci); else if (ci->type == id_SEIO18_CORE) write_io18(ci); + else if (ci->type == id_OSC_CORE) + write_osc(ci); blank(); } } -- cgit v1.2.3 From b5d46a0235464dbf27e49f804ef69e7fa434828c Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 13 Jan 2020 21:02:11 +0000 Subject: nexus: Add Python bindings Signed-off-by: David Shah --- common/arch_pybindings_shared.h | 5 --- nexus/arch_pybindings.cc | 76 ++++++++++++++++++++++++++++++++ nexus/arch_pybindings.h | 98 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 nexus/arch_pybindings.cc create mode 100644 nexus/arch_pybindings.h diff --git a/common/arch_pybindings_shared.h b/common/arch_pybindings_shared.h index 3f7efcc1..8bb93a80 100644 --- a/common/arch_pybindings_shared.h +++ b/common/arch_pybindings_shared.h @@ -103,11 +103,6 @@ fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getPipDelay"); -fn_wrapper_1a, - pass_through>::def_wrap(ctx_cls, "getPackagePinBel"); -fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getBelPackagePin"); - fn_wrapper_0a>::def_wrap( ctx_cls, "getChipName"); fn_wrapper_0a>::def_wrap(ctx_cls, diff --git a/nexus/arch_pybindings.cc b/nexus/arch_pybindings.cc new file mode 100644 index 00000000..438b77a5 --- /dev/null +++ b/nexus/arch_pybindings.cc @@ -0,0 +1,76 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 NO_PYTHON + +#include "arch_pybindings.h" +#include "nextpnr.h" +#include "pybindings.h" + +NEXTPNR_NAMESPACE_BEGIN + +void arch_wrap_python() +{ + using namespace PythonConversion; + class_("ArchArgs").def_readwrite("chipdb", &ArchArgs::chipdb).def_readwrite("device", &ArchArgs::device); + + class_("BelId").def_readwrite("index", &BelId::index).def_readwrite("tile", &BelId::tile); + + class_("WireId").def_readwrite("index", &WireId::index).def_readwrite("tile", &WireId::tile); + + class_("PipId").def_readwrite("index", &PipId::index).def_readwrite("tile", &PipId::tile); + + class_("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); + + auto arch_cls = class_, boost::noncopyable>("Arch", init()); + auto ctx_cls = class_, boost::noncopyable>("Context", no_init) + .def("checksum", &Context::checksum) + .def("pack", &Context::pack) + .def("place", &Context::place) + .def("route", &Context::route); + + typedef std::unordered_map> CellMap; + typedef std::unordered_map> NetMap; + typedef std::unordered_map HierarchyMap; + typedef std::unordered_map AliasMap; + + auto belpin_cls = class_>("BelPin", no_init); + readonly_wrapper>::def_wrap(belpin_cls, "bel"); + readonly_wrapper>::def_wrap(belpin_cls, "pin"); + + typedef UpDownhillPipRange PipRange; + typedef WireBelPinRange BelPinRange; + +#include "arch_pybindings_shared.h" + + WRAP_RANGE(Bel, conv_to_str); + WRAP_RANGE(Wire, conv_to_str); + WRAP_RANGE(AllPip, conv_to_str); + WRAP_RANGE(UpDownhillPip, conv_to_str); + WRAP_RANGE(WireBelPin, wrap_context); + + WRAP_MAP_UPTR(CellMap, "IdCellMap"); + WRAP_MAP_UPTR(NetMap, "IdNetMap"); + WRAP_MAP(HierarchyMap, wrap_context, "HierarchyMap"); +} + +NEXTPNR_NAMESPACE_END + +#endif // NO_PYTHON diff --git a/nexus/arch_pybindings.h b/nexus/arch_pybindings.h new file mode 100644 index 00000000..326af306 --- /dev/null +++ b/nexus/arch_pybindings.h @@ -0,0 +1,98 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * 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 ARCH_PYBINDINGS_H +#define ARCH_PYBINDINGS_H +#ifndef NO_PYTHON + +#include "nextpnr.h" +#include "pybindings.h" +#include "pywrappers.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace PythonConversion { + +template <> struct string_converter +{ + BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); } + + std::string to_str(Context *ctx, BelId id) + { + if (id == BelId()) + throw bad_wrap(); + return ctx->getBelName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); } + + std::string to_str(Context *ctx, WireId id) + { + if (id == WireId()) + throw bad_wrap(); + return ctx->getWireName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); } + + std::string to_str(Context *ctx, WireId id) + { + if (id == WireId()) + throw bad_wrap(); + return ctx->getWireName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); } + + std::string to_str(Context *ctx, PipId id) + { + if (id == PipId()) + throw bad_wrap(); + return ctx->getPipName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + BelPin from_str(Context *ctx, std::string name) + { + NPNR_ASSERT_FALSE("string_converter::from_str not implemented"); + } + + std::string to_str(Context *ctx, BelPin pin) + { + if (pin.bel == BelId()) + throw bad_wrap(); + return ctx->getBelName(pin.bel).str(ctx) + "/" + pin.pin.str(ctx); + } +}; + +} // namespace PythonConversion + +NEXTPNR_NAMESPACE_END +#endif +#endif -- cgit v1.2.3 From 46010f33ad1ffa1d1b7f6ec053fcb9c3596e514a Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 15 Jan 2020 12:59:20 +0000 Subject: nexus: Globals and naming fixes Signed-off-by: David Shah --- nexus/arch.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index f0f51271..f787c2b3 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -213,7 +213,7 @@ NPNR_PACKED_STRUCT(struct GlobalHrowInfoPOD { uint16_t hrow_col; uint16_t padding; uint32_t num_spine_cols; - RelPtr spine_cols; + RelPtr spine_cols; }); NPNR_PACKED_STRUCT(struct GlobalInfoPOD { @@ -799,7 +799,7 @@ struct Arch : BaseCtx { std::string name = "X"; name += std::to_string(bel.tile % chip_info->width); - name += "Y"; + name += "/Y"; name += std::to_string(bel.tile / chip_info->width); name += "/"; name += nameOf(IdString(bel_data(bel).name)); @@ -910,7 +910,7 @@ struct Arch : BaseCtx { std::string name = "X"; name += std::to_string(wire.tile % chip_info->width); - name += "Y"; + name += "/Y"; name += std::to_string(wire.tile / chip_info->width); name += "/"; name += nameOf(IdString(wire_data(wire).name)); -- cgit v1.2.3 From 887d7c717b957a16e068d082a04d5da58d3eda84 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 20 Jan 2020 15:20:00 +0000 Subject: nexus: Adding pin definitions Signed-off-by: David Shah --- nexus/arch.h | 12 ++++++++++++ nexus/pack.cc | 10 ++++++++++ nexus/pins.cc | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 nexus/pins.cc diff --git a/nexus/arch.h b/nexus/arch.h index f787c2b3..29acc5f5 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1271,6 +1271,18 @@ struct Arch : BaseCtx int wire = pip_data(pip).to_wire; return db->loctypes[chip_info->grid[pip.tile].loc_type].wires[wire].name; } + + // ------------------------------------------------- + + // Get a map cell type -> pins that can be inverted + void get_invertible_pins(std::unordered_map> &pins) const; + // Get a map cell -> pin -> value _it takes_ if disconnected + void get_pins_floating_value(std::unordered_map> &pins) const; + // Get a map cell -> pin -> value _it must be connected to_ if disconnected + // Default value for all pins, if not specified is 0 + void + get_pins_default_value(std::unordered_map> &pins) const; + // ------------------------------------------------- NeighWireRange neigh_wire_range(WireId wire) const diff --git a/nexus/pack.cc b/nexus/pack.cc index 5a18f6d4..c1e3664f 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -40,6 +40,7 @@ struct NexusPacker std::unordered_map param_xform; std::vector> set_attrs; std::vector> set_params; + std::vector> default_params; }; void xform_cell(const std::unordered_map &rules, CellInfo *ci) @@ -87,6 +88,10 @@ struct NexusPacker for (auto &attr : rule.set_attrs) ci->attrs[attr.first] = attr.second; + for (auto ¶m : rule.default_params) + if (!ci->params.count(param.first)) + ci->params[param.first] = param.second; + for (auto ¶m : rule.set_params) ci->params[param.first] = param.second; } @@ -134,6 +139,11 @@ struct NexusPacker ff_rules[type].port_xform[id_D] = id_M; // will be rerouted to DI later if applicable ff_rules[type].port_xform[id_SP] = id_CE; ff_rules[type].port_xform[id_Q] = id_Q; + + ff_rules[id_FD1P3BX].default_params.emplace_back(id_CLKMUX, std::string("CLK")); + ff_rules[id_FD1P3BX].default_params.emplace_back(id_CEMUX, std::string("CE")); + ff_rules[id_FD1P3BX].default_params.emplace_back(id_LSRMUX, std::string("LSR")); + ff_rules[id_FD1P3BX].set_params.emplace_back(id_LSRMODE, std::string("LSR")); } // Async preload ff_rules[id_FD1P3BX].set_params.emplace_back(id_SRMODE, std::string("ASYNC")); diff --git a/nexus/pins.cc b/nexus/pins.cc new file mode 100644 index 00000000..5627f557 --- /dev/null +++ b/nexus/pins.cc @@ -0,0 +1,53 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +void Arch::get_invertible_pins(std::unordered_map> &pins) const +{ + pins[id_OXIDE_FF] = {id_CLK, id_LSR, id_CE}; + pins[id_RAMW] = {id_WCK}; + pins[id_SEIO18_CORE] = {id_T}; + pins[id_SEIO33_CORE] = {id_T}; +} + +void Arch::get_pins_floating_value(std::unordered_map> &pins) const +{ + pins[id_OXIDE_COMB] = {{id_A, true}, {id_B, true}, {id_C, true}, {id_D, true}, {id_SEL, true}}; + pins[id_OXIDE_FF] = {{id_CLK, false}, {id_LSR, true}, {id_CE, true}}; + pins[id_SEIO18_CORE] = {{id_T, true}}; + pins[id_SEIO33_CORE] = {{id_T, true}}; +} + +void Arch::get_pins_default_value( + std::unordered_map> &pins) const +{ + pins[id_OXIDE_COMB] = {{id_A, Property::S1}, {id_B, Property::S1}, {id_C, Property::S1}, + {id_D, Property::S1}, {id_SEL, Property::S1}, {id_WAD0, Property::Sx}, + {id_WAD1, Property::Sx}, {id_WAD2, Property::Sx}, {id_WAD3, Property::Sx}, + {id_WCK, Property::Sx}, {id_WRE, Property::Sx}, {id_WD, Property::Sx}}; + pins[id_OXIDE_FF] = {{id_CE, Property::S1}, {id_DI, Property::Sx}}; + pins[id_SEIO18_CORE] = {{id_T, Property::S1}}; + pins[id_SEIO33_CORE] = {{id_T, Property::S1}}; +} + +NEXTPNR_NAMESPACE_END \ No newline at end of file -- cgit v1.2.3 From 84d542624213ab8639bac31ade79ce27097f4e06 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 20 Jan 2020 15:57:06 +0000 Subject: nexus: Working on validity checking Signed-off-by: David Shah --- common/design_utils.h | 7 +++++++ nexus/arch.cc | 6 ------ nexus/arch.h | 46 +++++++++++++++++++++++++++++++++++++++++++++- nexus/arch_place.cc | 28 +++++++++++++++++----------- nexus/archdefs.h | 1 - nexus/pack.cc | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 19 deletions(-) diff --git a/common/design_utils.h b/common/design_utils.h index 1ae1d648..301547c6 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -82,6 +82,13 @@ template CellInfo *net_driven_by(const Context *ctx, const NetInfo } } +// Check if a port is used +inline bool port_used(CellInfo *cell, IdString port_name) +{ + auto port_fnd = cell->ports.find(port_name); + return port_fnd != cell->ports.end() && port_fnd->second.net != nullptr; +} + // Connect a net to a port void connect_port(const Context *ctx, NetInfo *net, CellInfo *cell, IdString port_name); diff --git a/nexus/arch.cc b/nexus/arch.cc index 663941ff..7efc5c61 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -410,12 +410,6 @@ bool Arch::route() // ----------------------------------------------------------------------- -void Arch::assignArchInfo() {} - -void assignCellInfo(CellInfo *cell) {} - -// ----------------------------------------------------------------------- - #ifdef WITH_HEAP const std::string Arch::defaultPlacer = "heap"; #else diff --git a/nexus/arch.h b/nexus/arch.h index 29acc5f5..4f1a84ba 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -159,7 +159,7 @@ NPNR_PACKED_STRUCT(struct PhysicalTileInfoPOD { int32_t tiletype; // tile type IdString }); -enum LocFlagsPOD : uint32_t +enum LocFlags : uint32_t { LOC_LOGIC = 0x000001, LOC_IO18 = 0x000002, @@ -816,12 +816,19 @@ struct Arch : BaseCtx cell->bel = bel; cell->belStrength = strength; refreshUiBel(bel); + + if (tile_is(bel, LOC_LOGIC)) + update_logic_bel(bel, cell); } void unbindBel(BelId bel) { NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(tileStatus[bel.tile].boundcells[bel.index] != nullptr); + + if (tile_is(bel, LOC_LOGIC)) + update_logic_bel(bel, nullptr); + tileStatus[bel.tile].boundcells[bel.index]->bel = BelId(); tileStatus[bel.tile].boundcells[bel.index]->belStrength = STRENGTH_NONE; tileStatus[bel.tile].boundcells[bel.index] = nullptr; @@ -1302,6 +1309,43 @@ struct Arch : BaseCtx // ------------------------------------------------- + template uint32_t tile_loc_flags(TId id) const { return chip_info->grid[id.tile].loc_flags; } + + template bool tile_is(TId id, LocFlags lf) const { return tile_loc_flags(id) & lf; } + + // ------------------------------------------------- + + enum LogicBelZ + { + BEL_LUT0 = 0, + BEL_LUT1 = 1, + BEL_FF0 = 2, + BEL_FF1 = 3, + BEL_RAMW = 4, + }; + + void update_logic_bel(BelId bel, CellInfo *cell) + { + int z = bel_data(bel).z; + NPNR_ASSERT(z < 32); + auto &tts = tileStatus[bel.tile]; + if (tts.lts == nullptr) + tts.lts = new LogicTileStatus(); + auto &ts = *(tts.lts); + ts.cells[z] = cell; + switch (z & 0x7) { + case BEL_FF0: + case BEL_FF1: + case BEL_RAMW: + ts.halfs[(z >> 3) / 2].dirty = true; + /* fall-through */ + case BEL_LUT0: + case BEL_LUT1: + ts.slices[(z >> 3)].dirty = true; + break; + } + } + bool nexus_logic_tile_valid(LogicTileStatus <s) const; // ------------------------------------------------- diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc index 6444d1e9..fa1a75e7 100644 --- a/nexus/arch_place.cc +++ b/nexus/arch_place.cc @@ -23,15 +23,6 @@ NEXTPNR_NAMESPACE_BEGIN -enum LogicBelZ -{ - BEL_LUT0 = 0, - BEL_LUT1 = 1, - BEL_FF0 = 2, - BEL_FF1 = 3, - BEL_RAMW = 4, -}; - bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const { for (int s = 0; s < 4; s++) { @@ -93,8 +84,23 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const return true; } -bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } +bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const +{ + // FIXME + return true; +} -bool Arch::isBelLocationValid(BelId bel) const { return true; } +bool Arch::isBelLocationValid(BelId bel) const +{ + if (tile_is(bel, LOC_LOGIC)) { + LogicTileStatus *lts = tileStatus[bel.tile].lts; + if (lts == nullptr) + return true; + else + return nexus_logic_tile_valid(*lts); + } else { + return true; + } +} NEXTPNR_NAMESPACE_END diff --git a/nexus/archdefs.h b/nexus/archdefs.h index 098333b9..8709cc9c 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -169,7 +169,6 @@ struct ArchCellInfo struct { bool is_memory, is_carry, mux2_used; - int input_count; NetInfo *f, *ofx; } lutInfo; struct diff --git a/nexus/pack.cc b/nexus/pack.cc index c1e3664f..2b88a095 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -25,6 +25,10 @@ NEXTPNR_NAMESPACE_BEGIN +namespace { +bool is_enabled(CellInfo *ci, IdString prop) { return str_or_default(ci->params, prop, "") == "ENABLED"; } +} // namespace + struct NexusPacker { Context *ctx; @@ -182,4 +186,48 @@ bool Arch::pack() return true; } +// ----------------------------------------------------------------------- + +void Arch::assignArchInfo() +{ + for (auto cell : sorted(cells)) { + assignCellInfo(cell.second); + } +} + +void Arch::assignCellInfo(CellInfo *cell) +{ + if (cell->type == id_OXIDE_COMB) { + cell->lutInfo.is_memory = str_or_default(cell->params, id_MODE, "LOGIC") == "DPRAM"; + cell->lutInfo.is_carry = str_or_default(cell->params, id_MODE, "LOGIC") == "CCU2"; + cell->lutInfo.mux2_used = port_used(cell, id_OFX); + cell->lutInfo.f = get_net_or_empty(cell, id_F); + cell->lutInfo.ofx = get_net_or_empty(cell, id_OFX); + } else if (cell->type == id_OXIDE_FF) { + cell->ffInfo.ctrlset.async = str_or_default(cell->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC"; + cell->ffInfo.ctrlset.regddr_en = is_enabled(cell, id_REGDDR); + cell->ffInfo.ctrlset.gsr_en = is_enabled(cell, id_GSR); + cell->ffInfo.ctrlset.clkmux = id(str_or_default(cell->params, id_CLKMUX, "CLK")).index; + cell->ffInfo.ctrlset.cemux = id(str_or_default(cell->params, id_CEMUX, "CE")).index; + cell->ffInfo.ctrlset.lsrmux = id(str_or_default(cell->params, id_LSRMUX, "LSR")).index; + cell->ffInfo.ctrlset.clk = get_net_or_empty(cell, id_CLK); + cell->ffInfo.ctrlset.ce = get_net_or_empty(cell, id_CE); + cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); + cell->ffInfo.di = get_net_or_empty(cell, id_DI); + cell->ffInfo.m = get_net_or_empty(cell, id_M); + } else if (cell->type == ID_RAMW) { + cell->ffInfo.ctrlset.async = false; + cell->ffInfo.ctrlset.regddr_en = false; + cell->ffInfo.ctrlset.gsr_en = false; + cell->ffInfo.ctrlset.clkmux = id(str_or_default(cell->params, id_CLKMUX, "CLK")).index; + cell->ffInfo.ctrlset.cemux = ID_CE; + cell->ffInfo.ctrlset.lsrmux = id(str_or_default(cell->params, id_LSRMUX, "LSR")).index; + cell->ffInfo.ctrlset.clk = get_net_or_empty(cell, id_CLK); + cell->ffInfo.ctrlset.ce = nullptr; + cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); + cell->ffInfo.di = nullptr; + cell->ffInfo.m = nullptr; + } +} + NEXTPNR_NAMESPACE_END \ No newline at end of file -- cgit v1.2.3 From a586bfc290548d3a0f9a89f56743bddff408046c Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 22 Jan 2020 15:53:45 +0000 Subject: nexus: Support Lattice-style parameters Signed-off-by: David Shah --- nexus/arch.h | 5 ++++ nexus/fasm.cc | 2 +- nexus/pack.cc | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/nexus/arch.h b/nexus/arch.h index 4f1a84ba..3b5bcc48 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1292,6 +1292,11 @@ struct Arch : BaseCtx // ------------------------------------------------- + // Parse a possibly-Lattice-style (C literal in Verilog string) style parameter + Property parse_lattice_param(const CellInfo *ci, IdString prop, int width, int64_t defval) const; + + // ------------------------------------------------- + NeighWireRange neigh_wire_range(WireId wire) const { NeighWireRange range; diff --git a/nexus/fasm.cc b/nexus/fasm.cc index f7841bde..762512ba 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -232,7 +232,7 @@ struct NexusFasmWriter write_enum(cell, "HFDIV_FABRIC_EN", "ENABLED"); write_enum(cell, "LF_FABRIC_EN"); write_enum(cell, "LF_OUTPUT_EN"); - write_int_vector(stringf("HF_CLK_DIV[7:0]"), int_or_default(cell->params, id_HF_CLK_DIV, 0), 8); + write_int_vector(stringf("HF_CLK_DIV[7:0]"), ctx->parse_lattice_param(cell, id_HF_CLK_DIV, 8, 0).intval, 8); pop(2); } void operator()() diff --git a/nexus/pack.cc b/nexus/pack.cc index 2b88a095..35d67fb0 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -23,12 +23,78 @@ #include "nextpnr.h" #include "util.h" +#include + NEXTPNR_NAMESPACE_BEGIN namespace { bool is_enabled(CellInfo *ci, IdString prop) { return str_or_default(ci->params, prop, "") == "ENABLED"; } } // namespace +// Parse a possibly-Lattice-style (C literal in Verilog string) style parameter +Property Arch::parse_lattice_param(const CellInfo *ci, IdString prop, int width, int64_t defval) const +{ + auto fnd = ci->params.find(prop); + if (fnd == ci->params.end()) + return Property(defval, width); + const auto &val = fnd->second; + if (val.is_string) { + const std::string &s = val.str; + Property temp; + + if (boost::starts_with(s, "0b")) { + for (int i = int(s.length()) - 1; i >= 2; i--) { + char c = s.at(i); + if (c != '0' && c != '1' && c != 'x') + log_error("Invalid binary digit '%c' in property %s.%s\n", c, nameOf(ci), nameOf(prop)); + temp.str.push_back(c); + } + } else if (boost::starts_with(s, "0x")) { + for (int i = int(s.length()) - 1; i >= 2; i--) { + char c = s.at(i); + int nibble; + if (c >= '0' && c <= '9') + nibble = (c - '0'); + else if (c >= 'a' && c <= 'f') + nibble = (c - 'a'); + else if (c >= 'A' && c <= 'F') + nibble = (c - 'A'); + else + log_error("Invalid hex digit '%c' in property %s.%s\n", c, nameOf(ci), nameOf(prop)); + for (int j = 0; j < 4; j++) + temp.str.push_back(((nibble >> j) & 0x1) ? Property::S1 : Property::S0); + } + } else { + int64_t ival = 0; + try { + if (boost::starts_with(s, "0d")) + ival = std::stoll(s.substr(2)); + else + ival = std::stoll(s); + } catch (std::runtime_error &e) { + log_error("Invalid decimal value for property %s.%s", nameOf(ci), nameOf(prop)); + } + temp = Property(ival); + } + + for (auto b : temp.str.substr(width)) { + if (b == Property::S1) + log_error("Found value for property %s.%s with width greater than %d\n", nameOf(ci), nameOf(prop), + width); + } + temp.update_intval(); + return temp.extract(0, width); + } else { + for (auto b : val.str.substr(width)) { + if (b == Property::S1) + log_error("Found bitvector value for property %s.%s with width greater than %d - perhaps a string was " + "converted to bits?\n", + nameOf(ci), nameOf(prop), width); + } + return val.extract(0, width); + } +} + struct NexusPacker { Context *ctx; @@ -45,6 +111,7 @@ struct NexusPacker std::vector> set_attrs; std::vector> set_params; std::vector> default_params; + std::vector> parse_params; }; void xform_cell(const std::unordered_map &rules, CellInfo *ci) @@ -96,6 +163,16 @@ struct NexusPacker if (!ci->params.count(param.first)) ci->params[param.first] = param.second; + { + IdString old_param, new_param; + int width; + int64_t def; + for (const auto &p : rule.parse_params) { + std::tie(old_param, new_param, width, def) = p; + ci->params[new_param] = ctx->parse_lattice_param(ci, old_param, width, def); + } + } + for (auto ¶m : rule.set_params) ci->params[param.first] = param.second; } @@ -130,6 +207,7 @@ struct NexusPacker std::unordered_map lut_rules; lut_rules[id_LUT4].new_type = id_OXIDE_COMB; lut_rules[id_LUT4].port_xform[id_Z] = id_F; + lut_rules[id_LUT4].parse_params.emplace_back(id_INIT, id_INIT, 16, 0); generic_xform(lut_rules); } -- cgit v1.2.3 From 2b13b24cbe8e0a8bfb6e19e54258ccd95a6cee6f Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 3 Oct 2020 14:24:38 +0100 Subject: nexus: Bring up to date Signed-off-by: David Shah --- nexus/arch.cc | 42 ++++++++++++++++++++++++++++++++++++++++-- nexus/arch.h | 3 +++ nexus/arch_pybindings.cc | 36 +++++++++++++++++++----------------- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 7efc5c61..3799b167 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -25,6 +25,7 @@ #include "placer1.h" #include "placer_heap.h" #include "router1.h" +#include "router2.h" #include "timing.h" #include "util.h" @@ -374,6 +375,29 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } +ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const +{ + ArcBounds bb; + + int src_x = src.tile % chip_info->width, src_y = src.tile / chip_info->width; + int dst_x = dst.tile % chip_info->width, dst_y = dst.tile / chip_info->width; + + bb.x0 = src_x; + bb.y0 = src_y; + bb.x1 = src_x; + bb.y1 = src_y; + + auto extend = [&](int x, int y) { + bb.x0 = std::min(bb.x0, x); + bb.x1 = std::max(bb.x1, x); + bb.y0 = std::min(bb.y0, y); + bb.y1 = std::max(bb.y1, y); + }; + extend(dst_x, dst_y); + + return bb; +} + // ----------------------------------------------------------------------- bool Arch::place() @@ -402,7 +426,16 @@ bool Arch::place() bool Arch::route() { assign_budget(getCtx(), true); - bool result = router1(getCtx(), Router1Cfg(getCtx())); + std::string router = str_or_default(settings, id("router"), defaultRouter); + bool result; + if (router == "router1") { + result = router1(getCtx(), Router1Cfg(getCtx())); + } else if (router == "router2") { + router2(getCtx(), Router2Cfg(getCtx())); + result = true; + } else { + log_error("iCE40 architecture does not support router '%s'\n", router.c_str()); + } getCtx()->attrs[getCtx()->id("step")] = std::string("route"); archInfoToAttributes(); return result; @@ -420,5 +453,10 @@ const std::vector Arch::availablePlacers = {"sa", #ifdef WITH_HEAP "heap" #endif + }; -NEXTPNR_NAMESPACE_END \ No newline at end of file + +const std::string Arch::defaultRouter = "router1"; +const std::vector Arch::availableRouters = {"router1", "router2"}; + +NEXTPNR_NAMESPACE_END diff --git a/nexus/arch.h b/nexus/arch.h index 3b5bcc48..ffe630a8 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1196,6 +1196,7 @@ struct Arch : BaseCtx } uint32_t getDelayChecksum(delay_t v) const { return v; } bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; + ArcBounds getRouteBoundingBox(WireId src, WireId dst) const; // ------------------------------------------------- @@ -1246,6 +1247,8 @@ struct Arch : BaseCtx static const std::string defaultPlacer; static const std::vector availablePlacers; + static const std::string defaultRouter; + static const std::vector availableRouters; // ------------------------------------------------- diff --git a/nexus/arch_pybindings.cc b/nexus/arch_pybindings.cc index 438b77a5..17c097a7 100644 --- a/nexus/arch_pybindings.cc +++ b/nexus/arch_pybindings.cc @@ -26,21 +26,23 @@ NEXTPNR_NAMESPACE_BEGIN -void arch_wrap_python() +void arch_wrap_python(py::module &m) { using namespace PythonConversion; - class_("ArchArgs").def_readwrite("chipdb", &ArchArgs::chipdb).def_readwrite("device", &ArchArgs::device); + py::class_(m, "ArchArgs") + .def_readwrite("chipdb", &ArchArgs::chipdb) + .def_readwrite("device", &ArchArgs::device); - class_("BelId").def_readwrite("index", &BelId::index).def_readwrite("tile", &BelId::tile); + py::class_(m, "BelId").def_readwrite("index", &BelId::index).def_readwrite("tile", &BelId::tile); - class_("WireId").def_readwrite("index", &WireId::index).def_readwrite("tile", &WireId::tile); + py::class_(m, "WireId").def_readwrite("index", &WireId::index).def_readwrite("tile", &WireId::tile); - class_("PipId").def_readwrite("index", &PipId::index).def_readwrite("tile", &PipId::tile); + py::class_(m, "PipId").def_readwrite("index", &PipId::index).def_readwrite("tile", &PipId::tile); - class_("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); + py::class_(m, "BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); - auto arch_cls = class_, boost::noncopyable>("Arch", init()); - auto ctx_cls = class_, boost::noncopyable>("Context", no_init) + auto arch_cls = py::class_(m, "Arch").def(py::init()); + auto ctx_cls = py::class_(m, "Context") .def("checksum", &Context::checksum) .def("pack", &Context::pack) .def("place", &Context::place) @@ -51,7 +53,7 @@ void arch_wrap_python() typedef std::unordered_map HierarchyMap; typedef std::unordered_map AliasMap; - auto belpin_cls = class_>("BelPin", no_init); + auto belpin_cls = py::class_>(m, "BelPin"); readonly_wrapper>::def_wrap(belpin_cls, "bel"); readonly_wrapper>::def_wrap(belpin_cls, "pin"); @@ -60,15 +62,15 @@ void arch_wrap_python() #include "arch_pybindings_shared.h" - WRAP_RANGE(Bel, conv_to_str); - WRAP_RANGE(Wire, conv_to_str); - WRAP_RANGE(AllPip, conv_to_str); - WRAP_RANGE(UpDownhillPip, conv_to_str); - WRAP_RANGE(WireBelPin, wrap_context); + WRAP_RANGE(m, Bel, conv_to_str); + WRAP_RANGE(m, Wire, conv_to_str); + WRAP_RANGE(m, AllPip, conv_to_str); + WRAP_RANGE(m, UpDownhillPip, conv_to_str); + WRAP_RANGE(m, WireBelPin, wrap_context); - WRAP_MAP_UPTR(CellMap, "IdCellMap"); - WRAP_MAP_UPTR(NetMap, "IdNetMap"); - WRAP_MAP(HierarchyMap, wrap_context, "HierarchyMap"); + WRAP_MAP_UPTR(m, CellMap, "IdCellMap"); + WRAP_MAP_UPTR(m, NetMap, "IdNetMap"); + WRAP_MAP(m, HierarchyMap, wrap_context, "HierarchyMap"); } NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From e54aa836a4b56462497e354b3a94472c49312c68 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 3 Oct 2020 15:00:45 +0100 Subject: nexus: Updates Signed-off-by: David Shah --- nexus/arch_pybindings.cc | 4 ---- nexus/constids.inc | 2 ++ nexus/pack.cc | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/nexus/arch_pybindings.cc b/nexus/arch_pybindings.cc index 17c097a7..8bee7713 100644 --- a/nexus/arch_pybindings.cc +++ b/nexus/arch_pybindings.cc @@ -53,10 +53,6 @@ void arch_wrap_python(py::module &m) typedef std::unordered_map HierarchyMap; typedef std::unordered_map AliasMap; - auto belpin_cls = py::class_>(m, "BelPin"); - readonly_wrapper>::def_wrap(belpin_cls, "bel"); - readonly_wrapper>::def_wrap(belpin_cls, "pin"); - typedef UpDownhillPipRange PipRange; typedef WireBelPinRange BelPinRange; diff --git a/nexus/constids.inc b/nexus/constids.inc index 38df628e..d37857a6 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -69,6 +69,8 @@ X(INIT1) X(INV) X(BB) +X(VHI) +X(VLO) X(FD1P3BX) X(FD1P3DX) diff --git a/nexus/pack.cc b/nexus/pack.cc index 35d67fb0..46295597 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -208,6 +208,20 @@ struct NexusPacker lut_rules[id_LUT4].new_type = id_OXIDE_COMB; lut_rules[id_LUT4].port_xform[id_Z] = id_F; lut_rules[id_LUT4].parse_params.emplace_back(id_INIT, id_INIT, 16, 0); + + lut_rules[id_INV].new_type = id_OXIDE_COMB; + lut_rules[id_INV].port_xform[id_Z] = id_F; + lut_rules[id_INV].port_xform[id_A] = id_A; + lut_rules[id_INV].set_params.emplace_back(id_INIT, 0x5555); + + lut_rules[id_VHI].new_type = id_OXIDE_COMB; + lut_rules[id_VHI].port_xform[id_Z] = id_F; + lut_rules[id_VHI].set_params.emplace_back(id_INIT, 0xFFFF); + + lut_rules[id_VLO].new_type = id_OXIDE_COMB; + lut_rules[id_VLO].port_xform[id_Z] = id_F; + lut_rules[id_VLO].set_params.emplace_back(id_INIT, 0x0000); + generic_xform(lut_rules); } @@ -308,4 +322,4 @@ void Arch::assignCellInfo(CellInfo *cell) } } -NEXTPNR_NAMESPACE_END \ No newline at end of file +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From 682351df7fc22db2918d1b9ca9cd7ce0ba890360 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 3 Oct 2020 15:13:53 +0100 Subject: nexus: Updates and fixes Signed-off-by: David Shah --- nexus/arch_place.cc | 5 ++++- nexus/fasm.cc | 1 + nexus/pack.cc | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc index fa1a75e7..518f122e 100644 --- a/nexus/arch_place.cc +++ b/nexus/arch_place.cc @@ -25,6 +25,9 @@ NEXTPNR_NAMESPACE_BEGIN bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const { +#if 1 + return true; +#endif for (int s = 0; s < 4; s++) { if (lts.slices[s].dirty) { lts.slices[s].valid = false; @@ -62,7 +65,7 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const if (lts.halfs[h].dirty) { bool found_ff = false; FFControlSet ctrlset; - for (int i = 0; i < 1; i++) { + for (int i = 0; i < 2; i++) { for (auto bel : {BEL_FF0, BEL_FF1, BEL_RAMW}) { if (bel == BEL_RAMW && (h != 1 || i != 0)) continue; diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 762512ba..0429e83a 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -232,6 +232,7 @@ struct NexusFasmWriter write_enum(cell, "HFDIV_FABRIC_EN", "ENABLED"); write_enum(cell, "LF_FABRIC_EN"); write_enum(cell, "LF_OUTPUT_EN"); + write_enum(cell, "DEBUG_N", "DISABLED"); write_int_vector(stringf("HF_CLK_DIV[7:0]"), ctx->parse_lattice_param(cell, id_HF_CLK_DIV, 8, 0).intval, 8); pop(2); } diff --git a/nexus/pack.cc b/nexus/pack.cc index 46295597..bd237117 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -56,9 +56,9 @@ Property Arch::parse_lattice_param(const CellInfo *ci, IdString prop, int width, if (c >= '0' && c <= '9') nibble = (c - '0'); else if (c >= 'a' && c <= 'f') - nibble = (c - 'a'); + nibble = (c - 'a') + 10; else if (c >= 'A' && c <= 'F') - nibble = (c - 'A'); + nibble = (c - 'A') + 10; else log_error("Invalid hex digit '%c' in property %s.%s\n", c, nameOf(ci), nameOf(prop)); for (int j = 0; j < 4; j++) -- cgit v1.2.3 From 53d1c2c3097c48e2daa62cce98304f67fa840cec Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 3 Oct 2020 17:05:27 +0100 Subject: nexus: Fix validity checking Signed-off-by: David Shah --- nexus/arch_place.cc | 3 --- nexus/pack.cc | 9 +++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc index 518f122e..7e50de29 100644 --- a/nexus/arch_place.cc +++ b/nexus/arch_place.cc @@ -25,9 +25,6 @@ NEXTPNR_NAMESPACE_BEGIN bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const { -#if 1 - return true; -#endif for (int s = 0; s < 4; s++) { if (lts.slices[s].dirty) { lts.slices[s].valid = false; diff --git a/nexus/pack.cc b/nexus/pack.cc index bd237117..eb740858 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -236,10 +236,10 @@ struct NexusPacker ff_rules[type].port_xform[id_SP] = id_CE; ff_rules[type].port_xform[id_Q] = id_Q; - ff_rules[id_FD1P3BX].default_params.emplace_back(id_CLKMUX, std::string("CLK")); - ff_rules[id_FD1P3BX].default_params.emplace_back(id_CEMUX, std::string("CE")); - ff_rules[id_FD1P3BX].default_params.emplace_back(id_LSRMUX, std::string("LSR")); - ff_rules[id_FD1P3BX].set_params.emplace_back(id_LSRMODE, std::string("LSR")); + ff_rules[type].default_params.emplace_back(id_CLKMUX, std::string("CLK")); + ff_rules[type].default_params.emplace_back(id_CEMUX, std::string("CE")); + ff_rules[type].default_params.emplace_back(id_LSRMUX, std::string("LSR")); + ff_rules[type].set_params.emplace_back(id_LSRMODE, std::string("LSR")); } // Async preload ff_rules[id_FD1P3BX].set_params.emplace_back(id_SRMODE, std::string("ASYNC")); @@ -275,6 +275,7 @@ bool Arch::pack() (NexusPacker(getCtx()))(); attrs[id("step")] = std::string("pack"); archInfoToAttributes(); + assignArchInfo(); return true; } -- cgit v1.2.3 From 75040cdbdfda2e3fedf15f9243613d92dda80c79 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 3 Oct 2020 17:23:48 +0100 Subject: nexus: Randomise delay to stop router from getting stuck Signed-off-by: David Shah --- nexus/arch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/arch.h b/nexus/arch.h index ffe630a8..60bde0fe 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1115,7 +1115,7 @@ struct Arch : BaseCtx WireId getPipDstWire(PipId pip) const { return canonical_wire(pip.tile, pip_data(pip).to_wire); } - DelayInfo getPipDelay(PipId pip) const { return getDelayFromNS(0.1); } + DelayInfo getPipDelay(PipId pip) const { return getDelayFromNS(0.1 + (pip.index % 30) / 1000.0 ); } UpDownhillPipRange getPipsDownhill(WireId wire) const { -- cgit v1.2.3 From acb4cf9ead16cbae4e2728175948d86783195d25 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 6 Oct 2020 13:35:52 +0100 Subject: nexus: Update to planned IO fields Signed-off-by: David Shah --- nexus/arch.h | 54 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 60bde0fe..b9d6fb3e 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -183,14 +183,44 @@ NPNR_PACKED_STRUCT(struct GridLocationPOD { RelPtr phys_tiles; }); -NPNR_PACKED_STRUCT(struct PinInfoPOD { - RelPtr pin_name; - int32_t dqs_func; // DQS function IdString - int32_t clk_func; // Clock function IdStrinng - int16_t bank; // IO bank - uint16_t tile_x; // IO tile X - uint16_t tile_y; // IO tile Y - uint16_t bel_z; // IO bel Z +enum PioSide : uint8_t +{ + PIO_LEFT = 0, + PIO_RIGHT = 1, + PIO_TOP = 2, + PIO_BOTTOM = 3 +}; + +enum PioDqsFunction : uint8_t +{ + PIO_DQS_DQ = 0, + PIO_DQS_DQS = 1, + PIO_DQS_DQSN = 2 +}; + +NPNR_PACKED_STRUCT(struct PackageInfoPOD { + RelPtr full_name; // full package name, e.g. CABGA400 + RelPtr short_name; // name used in part number, e.g. BG400 +}); + +NPNR_PACKED_STRUCT(struct PadInfoPOD { + int16_t offset; // position offset of tile along side (-1 if not a regular PIO) + int8_t side; // PIO side (see PioSide enum) + int8_t pio_index; // index within IO tile + + int16_t bank; // IO bank + + int16_t dqs_group; // DQS group offset + int8_t dqs_func; // DQS function + + int8_t vref_index; // VREF index in bank, or -1 if N/A + + uint16_t num_funcs; // length of special function list + uint16_t padding; // padding for alignment + + RelPtr func_strs; // list of special function IdStrings + + RelPtr> pins; // package index --> package pin name }); NPNR_PACKED_STRUCT(struct GlobalBranchInfoPOD { @@ -223,12 +253,6 @@ NPNR_PACKED_STRUCT(struct GlobalInfoPOD { RelPtr hrows; }); -NPNR_PACKED_STRUCT(struct PackageInfoPOD { - RelPtr package_name; - uint32_t num_pins; - RelPtr pins; -}); - NPNR_PACKED_STRUCT(struct ChipInfoPOD { RelPtr device_name; uint16_t width; @@ -1115,7 +1139,7 @@ struct Arch : BaseCtx WireId getPipDstWire(PipId pip) const { return canonical_wire(pip.tile, pip_data(pip).to_wire); } - DelayInfo getPipDelay(PipId pip) const { return getDelayFromNS(0.1 + (pip.index % 30) / 1000.0 ); } + DelayInfo getPipDelay(PipId pip) const { return getDelayFromNS(0.1 + (pip.index % 30) / 1000.0); } UpDownhillPipRange getPipsDownhill(WireId wire) const { -- cgit v1.2.3 From 46536773f4432e4ced47b7c82fbcc4d6101308f8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Oct 2020 11:44:19 +0100 Subject: nexus: Rework how constant pins work Signed-off-by: David Shah --- nexus/arch.h | 41 ++++++++++++++++++++------ nexus/constids.inc | 25 ++++++++++++++-- nexus/pins.cc | 84 +++++++++++++++++++++++++++++++++++------------------- 3 files changed, 111 insertions(+), 39 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index b9d6fb3e..0b058bd7 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -754,6 +754,36 @@ struct WireBelPinRange // ----------------------------------------------------------------------- +// This enum captures different 'styles' of cell pins +// This is a combination of the modes available for a pin (tied high, low or inverted) +// and the default value to set it to not connected +enum CellPinStyle +{ + PINOPT_NONE = 0x0, // no options, just signal as-is + PINOPT_HI = 0x1, // can be tied low + PINOPT_LO = 0x2, // can be tied high + PINOPT_INV = 0x4, // can be inverted + + PINOPT_LOHI = 0x3, // can be tied low or high + PINOPT_LOHIINV = 0x7, // can be tied low or high; or inverted + + PINDEF_NONE = 0x00, // leave disconnected + PINDEF_0 = 0x10, // connect to 0 if not used + PINDEF_1 = 0x20, // connect to 1 if not used + + PINSTYLE_CIB = 0x11, // 'CIB' signal, floats high but explicitly zeroed if not used + PINSTYLE_CLK = 0x07, // CLK type signal, invertible and defaults to disconnected + PINSTYLE_CE = 0x27, // CE type signal, invertible and defaults to enabled + PINSTYLE_LSR = 0x17, // LSR type signal, invertible and defaults to not reset + PINSTYLE_DEDI = 0x00, // dedicated signals, leave alone + PINSTYLE_PU = 0x21, // signals that float high and default high + + PINSTYLE_INV_PD = 0x17, // invertible, pull down by default + PINSTYLE_INV_PU = 0x27, // invertible, pull up by default +}; + +// ----------------------------------------------------------------------- + const int bba_version = #include "bba_version.inc" ; @@ -1308,14 +1338,9 @@ struct Arch : BaseCtx // ------------------------------------------------- - // Get a map cell type -> pins that can be inverted - void get_invertible_pins(std::unordered_map> &pins) const; - // Get a map cell -> pin -> value _it takes_ if disconnected - void get_pins_floating_value(std::unordered_map> &pins) const; - // Get a map cell -> pin -> value _it must be connected to_ if disconnected - // Default value for all pins, if not specified is 0 - void - get_pins_default_value(std::unordered_map> &pins) const; + typedef std::unordered_map CellPinsData; + + void get_cell_pin_data(std::unordered_map &cell_pins); // ------------------------------------------------- diff --git a/nexus/constids.inc b/nexus/constids.inc index d37857a6..3ee93ea7 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -110,5 +110,26 @@ X(OSC_CORE) X(HFCLKOUT) X(LFCLKOUT) X(HF_CLK_DIV) - - +X(HFOUTEN) + +X(OXIDE_EBR) +X(CLKA) +X(CLKB) +X(CEA) +X(CEB) +X(CSA0) +X(CSA1) +X(CSA2) +X(CSB0) +X(CSB1) +X(CSB2) +X(ADA0) +X(ADA1) +X(ADA2) +X(ADA3) +X(ADB0) +X(ADB1) +X(WEA) +X(WEB) +X(RSTA) +X(RSTB) diff --git a/nexus/pins.cc b/nexus/pins.cc index 5627f557..fc540059 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -22,32 +22,58 @@ NEXTPNR_NAMESPACE_BEGIN -void Arch::get_invertible_pins(std::unordered_map> &pins) const -{ - pins[id_OXIDE_FF] = {id_CLK, id_LSR, id_CE}; - pins[id_RAMW] = {id_WCK}; - pins[id_SEIO18_CORE] = {id_T}; - pins[id_SEIO33_CORE] = {id_T}; -} - -void Arch::get_pins_floating_value(std::unordered_map> &pins) const -{ - pins[id_OXIDE_COMB] = {{id_A, true}, {id_B, true}, {id_C, true}, {id_D, true}, {id_SEL, true}}; - pins[id_OXIDE_FF] = {{id_CLK, false}, {id_LSR, true}, {id_CE, true}}; - pins[id_SEIO18_CORE] = {{id_T, true}}; - pins[id_SEIO33_CORE] = {{id_T, true}}; -} - -void Arch::get_pins_default_value( - std::unordered_map> &pins) const -{ - pins[id_OXIDE_COMB] = {{id_A, Property::S1}, {id_B, Property::S1}, {id_C, Property::S1}, - {id_D, Property::S1}, {id_SEL, Property::S1}, {id_WAD0, Property::Sx}, - {id_WAD1, Property::Sx}, {id_WAD2, Property::Sx}, {id_WAD3, Property::Sx}, - {id_WCK, Property::Sx}, {id_WRE, Property::Sx}, {id_WD, Property::Sx}}; - pins[id_OXIDE_FF] = {{id_CE, Property::S1}, {id_DI, Property::Sx}}; - pins[id_SEIO18_CORE] = {{id_T, Property::S1}}; - pins[id_SEIO33_CORE] = {{id_T, Property::S1}}; -} - -NEXTPNR_NAMESPACE_END \ No newline at end of file +namespace { + +static const std::unordered_map base_cell_pin_data = { + {id_OXIDE_COMB, + { + {id_WCK, PINSTYLE_DEDI}, + {id_WRE, PINSTYLE_DEDI}, + + {id_FCI, PINSTYLE_DEDI}, + {id_WAD0, PINSTYLE_DEDI}, + {id_WAD1, PINSTYLE_DEDI}, + {id_WAD2, PINSTYLE_DEDI}, + {id_WAD3, PINSTYLE_DEDI}, + {id_WD, PINSTYLE_DEDI}, + + {{}, PINSTYLE_PU}, + }}, + {id_OXIDE_FF, + { + {id_CLK, PINSTYLE_CLK}, + {id_LSR, PINSTYLE_LSR}, + {id_CE, PINSTYLE_CE}, + {{}, PINSTYLE_DEDI}, + }}, + {id_SEIO18_CORE, + { + {id_T, PINSTYLE_CE}, + {id_B, PINSTYLE_DEDI}, + {{}, PINSTYLE_INV_PU}, + }}, + {id_SEIO33_CORE, + { + {id_T, PINSTYLE_CE}, + {id_B, PINSTYLE_DEDI}, + {{}, PINSTYLE_INV_PU}, + }}, + {id_OXIDE_EBR, {{id_CLKA, PINSTYLE_CLK}, {id_CLKB, PINSTYLE_CLK}, {id_CEA, PINSTYLE_CE}, + {id_CEB, PINSTYLE_CE}, {id_CSA0, PINSTYLE_PU}, {id_CSA1, PINSTYLE_PU}, + {id_CSA2, PINSTYLE_PU}, {id_CSB0, PINSTYLE_PU}, {id_CSB1, PINSTYLE_PU}, + {id_CSB2, PINSTYLE_PU}, {id_ADA0, PINSTYLE_INV_PD}, {id_ADA1, PINSTYLE_INV_PD}, + {id_ADA2, PINSTYLE_INV_PD}, {id_ADA2, PINSTYLE_INV_PD}, {id_ADA3, PINSTYLE_INV_PD}, + {id_ADB0, PINSTYLE_INV_PD}, {id_ADB1, PINSTYLE_INV_PD}, {id_WEA, PINSTYLE_INV_PD}, + {id_WEB, PINSTYLE_INV_PD}, {id_RSTA, PINSTYLE_INV_PD}, {id_RSTB, PINSTYLE_INV_PD}, + {{}, PINSTYLE_CIB}}}, + {id_OSC_CORE, + { + {id_HFOUTEN, PINSTYLE_PU}, + {{}, PINSTYLE_CIB}, + }}, +}; +} // namespace + +void Arch::get_cell_pin_data(std::unordered_map &cell_pins) { cell_pins = base_cell_pin_data; } + +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From 2f822a3b79ef322802162e727dfe37fe3872966b Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Oct 2020 12:10:52 +0100 Subject: nexus: Packing helper functions Signed-off-by: David Shah --- nexus/arch.h | 29 +++++++++++++------- nexus/pack.cc | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 0b058bd7..47276f42 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -771,15 +771,26 @@ enum CellPinStyle PINDEF_0 = 0x10, // connect to 0 if not used PINDEF_1 = 0x20, // connect to 1 if not used - PINSTYLE_CIB = 0x11, // 'CIB' signal, floats high but explicitly zeroed if not used - PINSTYLE_CLK = 0x07, // CLK type signal, invertible and defaults to disconnected - PINSTYLE_CE = 0x27, // CE type signal, invertible and defaults to enabled - PINSTYLE_LSR = 0x17, // LSR type signal, invertible and defaults to not reset - PINSTYLE_DEDI = 0x00, // dedicated signals, leave alone - PINSTYLE_PU = 0x21, // signals that float high and default high - - PINSTYLE_INV_PD = 0x17, // invertible, pull down by default - PINSTYLE_INV_PU = 0x27, // invertible, pull up by default + PINGLB_CLK = 0x100, // pin is a 'clock' for global purposes + + PINSTYLE_CIB = 0x011, // 'CIB' signal, floats high but explicitly zeroed if not used + PINSTYLE_CLK = 0x107, // CLK type signal, invertible and defaults to disconnected + PINSTYLE_CE = 0x027, // CE type signal, invertible and defaults to enabled + PINSTYLE_LSR = 0x017, // LSR type signal, invertible and defaults to not reset + PINSTYLE_DEDI = 0x000, // dedicated signals, leave alone + PINSTYLE_PU = 0x021, // signals that float high and default high + + PINSTYLE_INV_PD = 0x017, // invertible, pull down by default + PINSTYLE_INV_PU = 0x027, // invertible, pull up by default +}; + +// This represents the mux options for a pin +enum CellPinMux +{ + PINMUX_SIG = 0, + PINMUX_0 = 1, + PINMUX_1 = 2, + PINMUX_INV = 3, }; // ----------------------------------------------------------------------- diff --git a/nexus/pack.cc b/nexus/pack.cc index eb740858..f367c3f8 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -261,6 +261,92 @@ struct NexusPacker generic_xform(ff_rules, true); } + std::unordered_map reference_bels; + + void autocreate_ports(CellInfo *cell) + { + // Automatically create ports for all inputs of a cell; even if they were left off the instantiation + // so we can tie them to constants as appropriate + // This also checks for any cells that don't have corresponding bels + + if (!reference_bels.count(cell->type)) { + // We need to look up a corresponding bel to get the list of input ports + BelId ref_bel; + for (BelId bel : ctx->getBels()) { + if (ctx->getBelType(bel) != cell->type) + continue; + ref_bel = bel; + break; + } + if (ref_bel == BelId()) + log_error("Cell type '%s' instantiated as '%s' is not supported by this device.\n", + ctx->nameOf(cell->type), ctx->nameOf(cell)); + reference_bels[cell->type] = ref_bel; + } + + BelId bel = reference_bels.at(cell->type); + for (IdString pin : ctx->getBelPins(bel)) { + PortType dir = ctx->getBelPinType(bel, pin); + if (dir != PORT_IN) + continue; + if (cell->ports.count(pin)) + continue; + cell->ports[pin].name = pin; + cell->ports[pin].type = dir; + } + } + + bool is_port_inverted(CellInfo *cell, IdString port) + { + NetInfo *net = get_net_or_empty(cell, port); + if (net == nullptr || net->driver.cell == nullptr) + return false; + return (net->driver.cell->type == id_INV); + } + + void uninvert_port(CellInfo *cell, IdString port) + { + // Rewire a port so it is driven by the input to an inverter + NetInfo *net = get_net_or_empty(cell, port); + NPNR_ASSERT(net != nullptr && net->driver.cell != nullptr && net->driver.cell->type == id_INV); + CellInfo *inv = net->driver.cell; + disconnect_port(ctx, cell, port); + + NetInfo *inv_a = get_net_or_empty(inv, id_A); + if (inv_a != nullptr) { + connect_port(ctx, inv_a, cell, port); + } + } + + void trim_design() + { + // Remove unused inverters and high/low drivers + std::vector trim_cells; + std::vector trim_nets; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type != id_INV && ci->type != id_VLO && ci->type != id_VHI) + continue; + NetInfo *z = get_net_or_empty(ci, id_Z); + if (z == nullptr) { + trim_cells.push_back(ci->name); + continue; + } + if (!z->users.empty()) + continue; + + disconnect_port(ctx, ci, id_A); + + trim_cells.push_back(ci->name); + trim_nets.push_back(z->name); + } + + for (IdString rem_net : trim_nets) + ctx->nets.erase(rem_net); + for (IdString rem_cell : trim_cells) + ctx->cells.erase(rem_cell); + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() -- cgit v1.2.3 From 0a1afe1f797b2d164b739379355a0bc8753abaf2 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 8 Oct 2020 10:20:39 +0100 Subject: nexus: Infrastructure for constant/inv handling Signed-off-by: David Shah --- nexus/arch.cc | 44 +++++++++++++++++ nexus/arch.h | 18 +++++-- nexus/constids.inc | 3 ++ nexus/pack.cc | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 192 insertions(+), 8 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 3799b167..dfcf9510 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -443,6 +443,50 @@ bool Arch::route() // ----------------------------------------------------------------------- +CellPinMux Arch::get_cell_pinmux(CellInfo *cell, IdString pin) const +{ + IdString param = id(stringf("%sMUX", pin.c_str(this))); + auto fnd_param = cell->params.find(param); + if (fnd_param == cell->params.end()) + return PINMUX_SIG; + const std::string &pm = fnd_param->second.as_string(); + if (pm == "0") + return PINMUX_0; + else if (pm == "1") + return PINMUX_1; + else if (pm == "INV") + return PINMUX_INV; + else if (pm == pin.c_str(this)) + return PINMUX_SIG; + else { + log_error("Invalid %s setting '%s' for cell '%s'\n", nameOf(param), pm.c_str(), nameOf(cell)); + NPNR_ASSERT_FALSE("unreachable"); + } +} + +void Arch::set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state) +{ + IdString param = id(stringf("%sMUX", pin.c_str(this))); + switch (state) { + case PINMUX_SIG: + cell->params.erase(param); + break; + case PINMUX_0: + cell->params[param] = std::string("0"); + break; + case PINMUX_1: + cell->params[param] = std::string("1"); + break; + case PINMUX_INV: + cell->params[param] = std::string("INV"); + break; + default: + NPNR_ASSERT_FALSE("unreachable"); + } +} + +// ----------------------------------------------------------------------- + #ifdef WITH_HEAP const std::string Arch::defaultPlacer = "heap"; #else diff --git a/nexus/arch.h b/nexus/arch.h index 47276f42..6cb7323b 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -760,25 +760,32 @@ struct WireBelPinRange enum CellPinStyle { PINOPT_NONE = 0x0, // no options, just signal as-is - PINOPT_HI = 0x1, // can be tied low - PINOPT_LO = 0x2, // can be tied high + PINOPT_LO = 0x1, // can be tied high + PINOPT_HI = 0x2, // can be tied low PINOPT_INV = 0x4, // can be inverted PINOPT_LOHI = 0x3, // can be tied low or high PINOPT_LOHIINV = 0x7, // can be tied low or high; or inverted + PINOPT_MASK = 0x7, + PINDEF_NONE = 0x00, // leave disconnected PINDEF_0 = 0x10, // connect to 0 if not used PINDEF_1 = 0x20, // connect to 1 if not used + PINDEF_MASK = 0x30, + PINGLB_CLK = 0x100, // pin is a 'clock' for global purposes - PINSTYLE_CIB = 0x011, // 'CIB' signal, floats high but explicitly zeroed if not used + PINGLB_MASK = 0x100, + + PINSTYLE_NONE = 0x000, // default + PINSTYLE_CIB = 0x012, // 'CIB' signal, floats high but explicitly zeroed if not used PINSTYLE_CLK = 0x107, // CLK type signal, invertible and defaults to disconnected PINSTYLE_CE = 0x027, // CE type signal, invertible and defaults to enabled PINSTYLE_LSR = 0x017, // LSR type signal, invertible and defaults to not reset PINSTYLE_DEDI = 0x000, // dedicated signals, leave alone - PINSTYLE_PU = 0x021, // signals that float high and default high + PINSTYLE_PU = 0x022, // signals that float high and default high PINSTYLE_INV_PD = 0x017, // invertible, pull down by default PINSTYLE_INV_PU = 0x027, // invertible, pull up by default @@ -1416,6 +1423,9 @@ struct Arch : BaseCtx bool nexus_logic_tile_valid(LogicTileStatus <s) const; + CellPinMux get_cell_pinmux(CellInfo *cell, IdString pin) const; + void set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state); + // ------------------------------------------------- void write_fasm(std::ostream &out) const; }; diff --git a/nexus/constids.inc b/nexus/constids.inc index 3ee93ea7..1aa36a88 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -106,6 +106,9 @@ X(CIB_LR) X(IO_TYPE) + +X(OSCA) +X(OSC) X(OSC_CORE) X(HFCLKOUT) X(LFCLKOUT) diff --git a/nexus/pack.cc b/nexus/pack.cc index f367c3f8..602457f5 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -99,6 +99,8 @@ struct NexusPacker { Context *ctx; + std::unordered_map cell_db; + // Generic cell transformation // Given cell name map and port map // If port name is not found in port map; it will be copied as-is but stripping [] @@ -296,12 +298,68 @@ struct NexusPacker } } - bool is_port_inverted(CellInfo *cell, IdString port) + NetInfo *get_const_net(IdString type) + { + // Gets a constant net, given the driver type (VHI or VLO) + // If one doesn't exist already; then create it + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type != type) + continue; + NetInfo *z = get_net_or_empty(ci, id_Z); + if (z == nullptr) + continue; + return z; + } + + NetInfo *new_net = ctx->createNet(ctx->id(stringf("$CONST_%s_NET_", type.c_str(ctx)))); + CellInfo *new_cell = ctx->createCell(ctx->id(stringf("$CONST_%s_DRV_", type.c_str(ctx))), type); + new_cell->addInput(id_Z); + connect_port(ctx, new_net, new_cell, id_Z); + return new_net; + } + + CellPinStyle get_pin_style(CellInfo *cell, IdString port) + { + // Look up the pin style in the cell database + auto fnd_cell = cell_db.find(cell->type); + if (fnd_cell == cell_db.end()) + return PINSTYLE_NONE; + auto fnd_port = fnd_cell->second.find(port); + if (fnd_port != fnd_cell->second.end()) + return fnd_port->second; + // If there isn't an exact port match, then the empty IdString + // represents a wildcard default match + auto fnd_default = fnd_cell->second.find({}); + if (fnd_default != fnd_cell->second.end()) + return fnd_default->second; + + return PINSTYLE_NONE; + } + + CellPinMux get_pin_needed_muxval(CellInfo *cell, IdString port) { NetInfo *net = get_net_or_empty(cell, port); - if (net == nullptr || net->driver.cell == nullptr) - return false; - return (net->driver.cell->type == id_INV); + if (net == nullptr || net->driver.cell == nullptr) { + // Pin is disconnected, return its default value + CellPinStyle pin_style = get_pin_style(cell, port); + if ((pin_style & PINDEF_MASK) == PINDEF_0) + return PINMUX_0; + else if ((pin_style & PINDEF_MASK) == PINDEF_1) + return PINMUX_1; + else + return PINMUX_SIG; + } + // Look to see if the driver is an inverter or constant + IdString drv_type = net->driver.cell->type; + if (drv_type == id_INV) + return PINMUX_INV; + else if (drv_type == id_VLO) + return PINMUX_0; + else if (drv_type == id_VHI) + return PINMUX_1; + else + return PINMUX_SIG; } void uninvert_port(CellInfo *cell, IdString port) @@ -347,10 +405,79 @@ struct NexusPacker ctx->cells.erase(rem_cell); } + std::string remove_brackets(const std::string &name) + { + std::string new_name; + new_name.reserve(name.size()); + for (char c : name) + if (c != '[' && c != ']') + new_name.push_back(c); + return new_name; + } + + void prim_to_core(CellInfo *cell, IdString new_type = {}) + { + // Convert a primitive to a '_CORE' variant + if (new_type == IdString()) + new_type = ctx->id(cell->type.str(ctx) + "_CORE"); + cell->type = new_type; + std::set port_names; + for (auto port : cell->ports) + port_names.insert(port.first); + for (IdString port : port_names) { + IdString new_name = ctx->id(remove_brackets(port.str(ctx))); + if (new_name != port) + rename_port(ctx, cell, port, new_name); + } + } + + NetInfo *gnd_net = nullptr, *vcc_net = nullptr; + + void process_inv_constants(CellInfo *cell) + { + // Automatically create any extra inputs needed; so we can set them accordingly + autocreate_ports(cell); + + for (auto &port : cell->ports) { + // Iterate over all inputs + if (port.second.type != PORT_IN) + continue; + IdString port_name = port.first; + + CellPinMux req_mux = get_pin_needed_muxval(cell, port_name); + if (req_mux == PINMUX_SIG) { + // No special setting required, ignore + continue; + } + + CellPinStyle pin_style = get_pin_style(cell, port_name); + + if (req_mux == PINMUX_INV) { + // Pin is inverted. If there is a hard inverter; then use it + if ((pin_style & PINOPT_MASK) == PINOPT_INV) { + uninvert_port(cell, port_name); + ctx->set_cell_pinmux(cell, port_name, PINMUX_INV); + } + } else if (req_mux == PINMUX_0 || req_mux == PINMUX_1) { + // Pin is tied to a constant + // If there is a hard constant option; use it + if ((pin_style & int(req_mux)) == req_mux) { + disconnect_port(ctx, cell, port_name); + ctx->set_cell_pinmux(cell, port_name, req_mux); + } else if (port.second.net == nullptr) { + // If the port is disconnected; and there is no hard constant + // then we need to connect it to the relevant soft-constant net + connect_port(ctx, (req_mux == PINMUX_1) ? vcc_net : gnd_net, cell, port_name); + } + } + } + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() { + ctx->get_cell_pin_data(cell_db); pack_ffs(); pack_luts(); } -- cgit v1.2.3 From e203bd3a2b3b5745446db085527656fac963d7dc Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 9 Oct 2020 09:52:18 +0100 Subject: nexus: Skeleton of PDC parser Signed-off-by: David Shah --- nexus/arch.h | 7 ++ nexus/constids.inc | 2 + nexus/pdc.cc | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 nexus/pdc.cc diff --git a/nexus/arch.h b/nexus/arch.h index 6cb7323b..78756594 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1426,6 +1426,13 @@ struct Arch : BaseCtx CellPinMux get_cell_pinmux(CellInfo *cell, IdString pin) const; void set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state); + // ------------------------------------------------- + + // List of IO constraints, used by PDC parser + std::unordered_map> io_attr; + + void parse_pdc(std::istream &in) const; + // ------------------------------------------------- void write_fasm(std::ostream &out) const; }; diff --git a/nexus/constids.inc b/nexus/constids.inc index 1aa36a88..6bf32654 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -136,3 +136,5 @@ X(WEA) X(WEB) X(RSTA) X(RSTB) + +X(LOC) diff --git a/nexus/pdc.cc b/nexus/pdc.cc new file mode 100644 index 00000000..6a571026 --- /dev/null +++ b/nexus/pdc.cc @@ -0,0 +1,301 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 "log.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct TCLEntity +{ + enum EntityType + { + ENTITY_CELL, + ENTITY_PORT, + ENTITY_NET, + } type; + IdString name; + + TCLEntity(EntityType type, IdString name) : type(type), name(name) {} + + const std::string &to_string(Context *ctx) { return name.str(ctx); } + + CellInfo *get_cell(Context *ctx) + { + if (type != ENTITY_CELL) + return nullptr; + return ctx->cells.at(name).get(); + } + + PortInfo *get_port(Context *ctx) + { + if (type != ENTITY_PORT) + return nullptr; + return &ctx->ports.at(name); + } + + NetInfo *get_net(Context *ctx) + { + if (type != ENTITY_NET) + return nullptr; + return ctx->nets.at(name).get(); + } +}; + +struct TCLValue +{ + TCLValue(const std::string &s) : is_string(true), str(s){}; + TCLValue(const std::vector &l) : is_string(false), list(l){}; + + bool is_string; + std::string str; // simple string value + std::vector list; // list of entities +}; + +struct PDCParser +{ + std::string buf; + int pos = 0; + int lineno = 1; + Context *ctx; + + inline bool eof() const { return pos == int(buf.size()); } + + inline char peek() const { return buf.at(pos); } + + inline char get() + { + char c = buf.at(pos++); + if (c == '\n') + ++lineno; + return c; + } + + std::string get(int n) + { + std::string s = buf.substr(pos, n); + pos += n; + return s; + } + + // If next char matches c, take it from the stream and return true + bool check_get(char c) + { + if (peek() == c) { + get(); + return true; + } else { + return false; + } + } + + // If next char matches any in chars, take it from the stream and return true + bool check_get_any(const std::string &chrs) + { + char c = peek(); + if (chrs.find(c) != std::string::npos) { + get(); + return true; + } else { + return false; + } + } + + inline void skip_blank(bool nl = false) + { + while (!eof() && check_get_any(nl ? " \t\n\r" : " \t")) + ; + } + + // Return true if end of line (or file) + inline bool skip_check_eol() + { + skip_blank(false); + if (eof()) + return true; + char c = peek(); + // Comments count as end of line + if (c == '#') { + get(); + while (!eof() && peek() != '\n' && peek() != '\r') + get(); + return true; + } + if (c == ';') { + // Forced end of line + get(); + return true; + } + return (c == '\n' || c == '\r'); + } + + inline std::string get_str() + { + std::string s; + skip_blank(false); + if (eof()) + return ""; + + bool in_quotes = false, in_braces = false, escaped = false; + + char c = get(); + + if (c == '"') + in_quotes = true; + else if (c == '{') + in_braces = true; + else + s += c; + + while (true) { + char c = peek(); + if (!in_quotes && !in_braces && !escaped && std::isblank(c)) { + break; + } + get(); + if (escaped) { + s += c; + escaped = false; + } else if ((in_quotes && c == '"') || (in_braces && c == '}')) { + break; + } else if (c == '\\') { + escaped = true; + } else { + s += c; + } + } + + return s; + } + + TCLValue evaluate(const std::vector &arguments) + { + NPNR_ASSERT(!arguments.empty()); + auto &arg0 = arguments.at(0); + NPNR_ASSERT(arg0.is_string); + const std::string &cmd = arg0.str; + if (cmd == "get_ports") + return cmd_get_ports(arguments); + else if (cmd == "ldc_set_location") + return cmd_ldc_set_location(arguments); + else if (cmd == "ldc_set_port") + return cmd_ldc_set_port(arguments); + } + + std::vector get_arguments() + { + std::vector args; + while (!skip_check_eol()) { + if (check_get('[')) { + // Start of a sub-expression + auto result = evaluate(get_arguments()); + NPNR_ASSERT(check_get(']')); + args.push_back(result); + } else { + args.push_back(get_str()); + } + } + skip_blank(true); + return args; + } + + TCLValue cmd_get_ports(const std::vector &arguments) + { + std::vector ports; + for (int i = 1; i < int(arguments.size()); i++) { + auto &arg = arguments.at(i); + if (!arg.is_string) + log_error("get_ports expected string arguments (line %d)\n", lineno); + std::string s = arg.str; + if (s.at(0) == '-') + log_error("unsupported argument '%s' to get_ports (line %d)\n", s.c_str(), lineno); + IdString id = ctx->id(s); + if (ctx->ports.count(id)) + ports.emplace_back(TCLEntity::ENTITY_PORT, id); + } + return ports; + } + + TCLValue cmd_ldc_set_location(const std::vector &arguments) + { + std::string site; + + for (int i = 1; i < int(arguments.size()); i++) { + auto &arg = arguments.at(i); + if (arg.is_string) { + std::string s = arg.str; + if (s == "-site") { + i++; + auto &val = arguments.at(i); + if (!val.is_string) + log_error("expecting string argument to -site (line %d)\n", lineno); + site = val.str; + } + } else { + if (site.empty()) + log_error("expecting -site before list of objects (line %d)\n", lineno); + for (const auto &ety : arg.list) { + if (ety.type == TCLEntity::ENTITY_PORT) + ctx->io_attr[ety.name][id_LOC] = site; + else if (ety.type == TCLEntity::ENTITY_CELL) + ctx->cells[ety.name]->attrs[id_LOC] = site; + else + log_error("ldc_set_location applies only to cells or IO ports (line %d)\n", lineno); + } + } + } + return std::string{}; + } + + TCLValue cmd_ldc_set_port(const std::vector &arguments) + { + std::unordered_map args; + for (int i = 1; i < int(arguments.size()); i++) { + auto &arg = arguments.at(i); + if (arg.is_string) { + std::string s = arg.str; + if (s == "-iobuf") { + i++; + auto &val = arguments.at(i); + if (!val.is_string) + log_error("expecting string argument to -iobuf (line %d)\n", lineno); + std::stringstream ss(val.str); + std::string k, v; + while (ss >> k >> v) { + args[ctx->id(k)] = v; + } + } else { + log_error("unexpected argument '%s' to ldc_set_port (line %d)\n", s.c_str(), lineno); + } + } else { + for (const auto &ety : arg.list) { + if (ety.type == TCLEntity::ENTITY_PORT) + for (const auto &kv : args) + ctx->io_attr[ety.name][kv.first] = kv.second; + else + log_error("ldc_set_port applies only to IO ports (line %d)\n", lineno); + } + } + } + return std::string{}; + } +}; + +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From e2de234ef17320773f089b9d807aaacbbcae45d2 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Oct 2020 09:39:00 +0100 Subject: nexus: Add pin/pad data to bba Signed-off-by: David Shah --- nexus/arch.h | 2 ++ nexus/bba_version.inc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/nexus/arch.h b/nexus/arch.h index 78756594..89723853 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -260,6 +260,8 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { uint32_t num_tiles; RelPtr grid; RelPtr globals; + RelPtr pads; + RelPtr packages; }); NPNR_PACKED_STRUCT(struct IdStringDBPOD { diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc index b8626c4c..7ed6ff82 100644 --- a/nexus/bba_version.inc +++ b/nexus/bba_version.inc @@ -1 +1 @@ -4 +5 -- cgit v1.2.3 From 12013b4c1f7a7fc653a06d1af8216680de468147 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Oct 2020 10:18:23 +0100 Subject: nexus: Lookup of package and IO pins Signed-off-by: David Shah --- nexus/arch.cc | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/arch.h | 11 +++++++ nexus/bba_version.inc | 2 +- nexus/pack.cc | 18 +++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index dfcf9510..f309c872 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -117,6 +117,22 @@ Arch::Arch(ArchArgs args) : args(args) for (size_t i = 0; i < chip_info->num_tiles; i++) { tileStatus[i].boundcells.resize(db->loctypes[chip_info->grid[i].loc_type].num_bels); } + // Validate and set up package + package_idx = -1; + for (size_t i = 0; i < chip_info->num_packages; i++) { + if (package == chip_info->packages[i].short_name.get()) { + package_idx = i; + break; + } + } + if (package_idx == -1) { + std::string all_packages = ""; + for (size_t i = 0; i < chip_info->num_packages; i++) { + all_packages += " "; + all_packages += chip_info->packages[i].short_name.get(); + } + log_error("Unknown package '%s'. Available package options:%s\n", package.c_str(), all_packages.c_str()); + } } // ----------------------------------------------------------------------- @@ -487,6 +503,79 @@ void Arch::set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state) // ----------------------------------------------------------------------- +const PadInfoPOD *Arch::get_pin_data(const std::string &pin) const +{ + for (size_t i = 0; i < chip_info->num_pads; i++) { + const PadInfoPOD *pad = &(chip_info->pads[i]); + if (pin == pad->pins[package_idx].get()) + return pad; + } + return nullptr; +} + +Loc Arch::get_pad_loc(const PadInfoPOD *pad) const +{ + Loc loc; + switch (pad->side) { + case PIO_LEFT: + loc.x = 0; + loc.y = pad->offset; + break; + case PIO_RIGHT: + loc.x = chip_info->width - 1; + loc.y = pad->offset; + break; + case PIO_TOP: + loc.x = pad->offset; + loc.y = 0; + break; + case PIO_BOTTOM: + loc.x = pad->offset; + loc.y = chip_info->height - 1; + } + loc.z = pad->pio_index; + return loc; +} + +BelId Arch::get_pin_bel(const std::string &pin) const +{ + const PadInfoPOD *pad = get_pin_data(pin); + if (pad == nullptr) + return BelId(); + return getBelByLocation(get_pad_loc(pad)); +} + +const PadInfoPOD *Arch::get_bel_pad(BelId bel) const +{ + Loc loc = getBelLocation(bel); + int side = -1, offset = -1; + // Convert (x, y) to (side, offset) + if (loc.x == 0) { + side = PIO_LEFT; + offset = loc.y; + } else if (loc.x == (chip_info->width - 1)) { + side = PIO_RIGHT; + offset = loc.y; + } else if (loc.y == 0) { + side = PIO_TOP; + offset = loc.x; + } else if (loc.y == (chip_info->height - 1)) { + side = PIO_BOTTOM; + offset = loc.x; + } else { + return nullptr; + } + // Lookup in the list of pads + for (size_t i = 0; i < chip_info->num_pads; i++) { + const PadInfoPOD *pad = &(chip_info->pads[i]); + if (pad->side == side && pad->offset == offset && pad->pio_index == loc.z) + return pad; + } + return nullptr; +} + +// ----------------------------------------------------------------------- + #ifdef WITH_HEAP const std::string Arch::defaultPlacer = "heap"; #else diff --git a/nexus/arch.h b/nexus/arch.h index 89723853..886ee557 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -258,6 +258,8 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { uint16_t width; uint16_t height; uint32_t num_tiles; + uint32_t num_pads; + uint32_t num_packages; RelPtr grid; RelPtr globals; RelPtr pads; @@ -827,6 +829,8 @@ struct Arch : BaseCtx const DatabasePOD *db; const ChipInfoPOD *chip_info; + int package_idx; + // Binding states struct LogicTileStatus { @@ -1430,6 +1434,13 @@ struct Arch : BaseCtx // ------------------------------------------------- + const PadInfoPOD *get_pin_data(const std::string &pin) const; + Loc get_pad_loc(const PadInfoPOD *pad) const; + BelId get_pin_bel(const std::string &pin) const; + const PadInfoPOD *get_bel_pad(BelId bel) const; + + // ------------------------------------------------- + // List of IO constraints, used by PDC parser std::unordered_map> io_attr; diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc index 7ed6ff82..1e8b3149 100644 --- a/nexus/bba_version.inc +++ b/nexus/bba_version.inc @@ -1 +1 @@ -5 +6 diff --git a/nexus/pack.cc b/nexus/pack.cc index 602457f5..688d74ab 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -473,6 +473,23 @@ struct NexusPacker } } + void pack_io() + { + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == id_SEIO33_CORE || ci->type == id_SEIO18_CORE) { + auto fnd_loc = ci->attrs.find(id_LOC); + if (fnd_loc == ci->attrs.end()) + continue; + BelId bel = ctx->get_pin_bel(fnd_loc->second.as_string()); + if (bel == BelId()) + log_error("cannot constrain IO '%s', no PIO pin named '%s'\n", ctx->nameOf(ci), + fnd_loc->second.as_string().c_str()); + ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); + } + } + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() @@ -480,6 +497,7 @@ struct NexusPacker ctx->get_cell_pin_data(cell_db); pack_ffs(); pack_luts(); + pack_io(); } }; -- cgit v1.2.3 From 69b449c8759efa15f52add21b1c7f6dc1ceada3a Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Oct 2020 11:19:31 +0100 Subject: nexus: Initial PDC parser integration Signed-off-by: David Shah --- nexus/arch.h | 2 +- nexus/main.cc | 12 +++++++++++- nexus/pdc.cc | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 886ee557..3beef3cd 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1444,7 +1444,7 @@ struct Arch : BaseCtx // List of IO constraints, used by PDC parser std::unordered_map> io_attr; - void parse_pdc(std::istream &in) const; + void read_pdc(std::istream &in); // ------------------------------------------------- void write_fasm(std::ostream &out) const; diff --git a/nexus/main.cc b/nexus/main.cc index 9549a573..83b22977 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -50,6 +50,7 @@ po::options_description NexusCommandHandler::getArchOptions() specific.add_options()("chipdb", po::value(), "name of chip database binary"); specific.add_options()("device", po::value(), "device name"); specific.add_options()("fasm", po::value(), "fasm file to write"); + specific.add_options()("pdc", po::value(), "physical constraints file"); return specific; } @@ -72,7 +73,16 @@ std::unique_ptr NexusCommandHandler::createContext(std::unordered_map(new Context(chipArgs)); } -void NexusCommandHandler::customAfterLoad(Context *ctx) {} +void NexusCommandHandler::customAfterLoad(Context *ctx) +{ + if (vm.count("pdc")) { + std::string filename = vm["pdc"].as(); + std::ifstream in(filename); + if (!in) + log_error("Failed to open input PDC file %s.\n", filename.c_str()); + ctx->read_pdc(in); + } +} int main(int argc, char *argv[]) { diff --git a/nexus/pdc.cc b/nexus/pdc.cc index 6a571026..60cc511c 100644 --- a/nexus/pdc.cc +++ b/nexus/pdc.cc @@ -21,6 +21,8 @@ #include "log.h" #include "nextpnr.h" +#include + NEXTPNR_NAMESPACE_BEGIN struct TCLEntity @@ -76,6 +78,8 @@ struct PDCParser int lineno = 1; Context *ctx; + PDCParser(const std::string &buf, Context *ctx) : buf(buf), ctx(ctx){}; + inline bool eof() const { return pos == int(buf.size()); } inline char peek() const { return buf.at(pos); } @@ -193,10 +197,14 @@ struct PDCParser const std::string &cmd = arg0.str; if (cmd == "get_ports") return cmd_get_ports(arguments); + else if (cmd == "get_cells") + return cmd_get_cells(arguments); else if (cmd == "ldc_set_location") return cmd_ldc_set_location(arguments); else if (cmd == "ldc_set_port") return cmd_ldc_set_port(arguments); + else + log_error("Unsupported PDC command '%s'\n", cmd.c_str()); } std::vector get_arguments() @@ -208,6 +216,8 @@ struct PDCParser auto result = evaluate(get_arguments()); NPNR_ASSERT(check_get(']')); args.push_back(result); + } else if (peek() == ']') { + break; } else { args.push_back(get_str()); } @@ -233,6 +243,23 @@ struct PDCParser return ports; } + TCLValue cmd_get_cells(const std::vector &arguments) + { + std::vector cells; + for (int i = 1; i < int(arguments.size()); i++) { + auto &arg = arguments.at(i); + if (!arg.is_string) + log_error("get_cells expected string arguments (line %d)\n", lineno); + std::string s = arg.str; + if (s.at(0) == '-') + log_error("unsupported argument '%s' to get_cells (line %d)\n", s.c_str(), lineno); + IdString id = ctx->id(s); + if (ctx->cells.count(id)) + cells.emplace_back(TCLEntity::ENTITY_CELL, id); + } + return cells; + } + TCLValue cmd_ldc_set_location(const std::vector &arguments) { std::string site; @@ -296,6 +323,23 @@ struct PDCParser } return std::string{}; } + + void operator()() + { + while (!eof()) { + skip_blank(true); + auto args = get_arguments(); + if (args.empty()) + continue; + evaluate(args); + } + } }; +void Arch::read_pdc(std::istream &in) +{ + std::string buf(std::istreambuf_iterator(in), {}); + PDCParser(buf, getCtx())(); +} + NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From 518ead2e2dd11ba3bcd19085c2c91178ba9e458c Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Oct 2020 11:41:26 +0100 Subject: nexus: IO pre-packing Signed-off-by: David Shah --- common/util.h | 9 +++++++++ nexus/pack.cc | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/common/util.h b/common/util.h index 9512bd40..d82e984a 100644 --- a/common/util.h +++ b/common/util.h @@ -112,6 +112,15 @@ template std::map sorted(const std::unordered_m return retVal; }; +// Wrap an unordered_map, and allow it to be iterated over sorted by key +template std::map sorted_ref(std::unordered_map &orig) +{ + std::map retVal; + for (auto &item : orig) + retVal.emplace(std::make_pair(item.first, std::ref(item.second))); + return retVal; +}; + // Wrap an unordered_set, and allow it to be iterated over sorted by key template std::set sorted(const std::unordered_set &orig) { diff --git a/nexus/pack.cc b/nexus/pack.cc index 688d74ab..3b53ea55 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -473,6 +473,64 @@ struct NexusPacker } } + void prepare_io() + { + // Find the actual IO buffer corresponding to a port; and copy attributes across to it + // Note that this relies on Yosys to do IO buffer inference, to match vendor tooling behaviour + // In all cases the nextpnr-inserted IO buffers are removed as redundant. + for (auto &port : sorted_ref(ctx->ports)) { + if (!ctx->cells.count(port.first)) + log_error("Port '%s' doesn't seem to have a corresponding top level IO\n", ctx->nameOf(port.first)); + CellInfo *ci = ctx->cells.at(port.first).get(); + + PortRef top_port; + top_port.cell = nullptr; + bool is_npnr_iob = false; + + if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an input buffer (IB etc) connected to it + is_npnr_iob = true; + NetInfo *o = get_net_or_empty(ci, id_O); + if (o == nullptr) + ; + else if (o->users.size() > 1) + log_error("Top level '%s' has multiple input buffers\n", ctx->nameOf(port.first)); + else if (o->users.size() == 1) + top_port = o->users.at(0); + } + if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an output buffer (OB etc) connected to it + is_npnr_iob = true; + NetInfo *i = get_net_or_empty(ci, id_I); + if (i == nullptr && i->driver.cell != nullptr) { + if (top_port.cell != nullptr) + log_error("Top level '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); + top_port = i->driver; + } + } + if (!is_npnr_iob) + log_error("Port '%s' doesn't seem to have a corresponding top level IO (internal cell type mismatch)\n", + ctx->nameOf(port.first)); + + if (top_port.cell == nullptr) { + log_info("Trimming port '%s' as it is unused.\n", ctx->nameOf(port.first)); + } else { + // Copy attributes to real IO buffer + if (ctx->io_attr.count(port.first)) { + for (auto &kv : ctx->io_attr.at(port.first)) { + top_port.cell->attrs[kv.first] = kv.second; + } + } + // Make sure that top level net is set correctly + port.second.net = top_port.cell->ports.at(top_port.port).net; + } + // Now remove the nextpnr-inserted buffer + disconnect_port(ctx, ci, id_I); + disconnect_port(ctx, ci, id_O); + ctx->cells.erase(port.first); + } + } + void pack_io() { for (auto cell : sorted(ctx->cells)) { -- cgit v1.2.3 From df3866a80033feb2ba3cd01f14f8830e1f49cb1a Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Oct 2020 13:40:24 +0100 Subject: nexus: Add IO packing Signed-off-by: David Shah --- nexus/arch.cc | 18 +++++++--- nexus/arch.h | 7 ++-- nexus/constids.inc | 19 +++++++++- nexus/fasm.cc | 27 ++++++++++---- nexus/pack.cc | 101 ++++++++++++++++++++++++++++++++++++++++++++++++----- nexus/pdc.cc | 8 +++-- 6 files changed, 155 insertions(+), 25 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index f309c872..352f789a 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -459,7 +459,7 @@ bool Arch::route() // ----------------------------------------------------------------------- -CellPinMux Arch::get_cell_pinmux(CellInfo *cell, IdString pin) const +CellPinMux Arch::get_cell_pinmux(const CellInfo *cell, IdString pin) const { IdString param = id(stringf("%sMUX", pin.c_str(this))); auto fnd_param = cell->params.find(param); @@ -503,7 +503,7 @@ void Arch::set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state) // ----------------------------------------------------------------------- -const PadInfoPOD *Arch::get_pin_data(const std::string &pin) const +const PadInfoPOD *Arch::get_pkg_pin_data(const std::string &pin) const { for (size_t i = 0; i < chip_info->num_pads; i++) { const PadInfoPOD *pad = &(chip_info->pads[i]); @@ -537,9 +537,8 @@ Loc Arch::get_pad_loc(const PadInfoPOD *pad) const return loc; } -BelId Arch::get_pin_bel(const std::string &pin) const +BelId Arch::get_pad_pio_bel(const PadInfoPOD *pad) const { - const PadInfoPOD *pad = get_pin_data(pin); if (pad == nullptr) return BelId(); return getBelByLocation(get_pad_loc(pad)); @@ -574,6 +573,17 @@ const PadInfoPOD *Arch::get_bel_pad(BelId bel) const return nullptr; } +std::string Arch::get_pad_functions(const PadInfoPOD *pad) const +{ + std::string s; + for (size_t i = 0; i < pad->num_funcs; i++) { + if (!s.empty()) + s += '/'; + s += IdString(pad->func_strs[i]).str(this); + } + return s; +} + // ----------------------------------------------------------------------- #ifdef WITH_HEAP diff --git a/nexus/arch.h b/nexus/arch.h index 3beef3cd..2f84b25d 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1429,15 +1429,16 @@ struct Arch : BaseCtx bool nexus_logic_tile_valid(LogicTileStatus <s) const; - CellPinMux get_cell_pinmux(CellInfo *cell, IdString pin) const; + CellPinMux get_cell_pinmux(const CellInfo *cell, IdString pin) const; void set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state); // ------------------------------------------------- - const PadInfoPOD *get_pin_data(const std::string &pin) const; + const PadInfoPOD *get_pkg_pin_data(const std::string &pin) const; Loc get_pad_loc(const PadInfoPOD *pad) const; - BelId get_pin_bel(const std::string &pin) const; + BelId get_pad_pio_bel(const PadInfoPOD *pad) const; const PadInfoPOD *get_bel_pad(BelId bel) const; + std::string get_pad_functions(const PadInfoPOD *pad) const; // ------------------------------------------------- diff --git a/nexus/constids.inc b/nexus/constids.inc index 6bf32654..b4aa5077 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -59,6 +59,10 @@ X(DOLP) X(INLP) X(INADC) +X(DIFFIO18_CORE) +X(HSRXEN) +X(HSTXEN) + X(LUT4) X(INIT) X(Z) @@ -68,7 +72,6 @@ X(INIT0) X(INIT1) X(INV) -X(BB) X(VHI) X(VLO) @@ -138,3 +141,17 @@ X(RSTA) X(RSTB) X(LOC) + +X(IB) +X(OB) +X(OBZ) +X(BB) +X(BB_I3C_A) + +X(SEIO33) +X(SEIO18) +X(DIFFIO18) +X(IOPAD) +X(PADDO) +X(PADDI) +X(PADDT) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 0429e83a..e9a4572f 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -91,6 +91,17 @@ struct NexusFasmWriter } } + void write_ioattr(const CellInfo *cell, const std::string &name, const std::string &defval = "") + { + auto fnd = cell->attrs.find(ctx->id(name)); + if (fnd == cell->attrs.end()) { + if (!defval.empty()) + write_bit(stringf("%s.%s", name.c_str(), defval.c_str())); + } else { + write_bit(stringf("%s.%s", name.c_str(), fnd->second.c_str())); + } + } + NexusFasmWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out) {} std::string tile_name(int loc, const PhysicalTileInfoPOD &tile) { @@ -195,14 +206,16 @@ struct NexusFasmWriter push_tile(bel.tile); push_belname(bel); const NetInfo *t = get_net_or_empty(cell, id_T); + auto tmux = ctx->get_cell_pinmux(cell, id_T); bool is_input = false, is_output = false; - if (t == nullptr || t->name == ctx->id("$PACKER_VCC_NET")) { - is_input = true; - } else if (t->name == ctx->id("$PACKER_GND_NET")) { + if (tmux == PINMUX_0) { is_output = true; + } else if (tmux == PINMUX_1 || t == nullptr) { + is_input = true; } const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS33").c_str())); + write_ioattr(cell, "PULLMODE", "NONE"); pop(2); } void write_io18(const CellInfo *cell) @@ -212,14 +225,16 @@ struct NexusFasmWriter push_belname(bel); push("SEIO18"); const NetInfo *t = get_net_or_empty(cell, id_T); + auto tmux = ctx->get_cell_pinmux(cell, id_T); bool is_input = false, is_output = false; - if (t == nullptr || t->name == ctx->id("$PACKER_VCC_NET")) { - is_input = true; - } else if (t->name == ctx->id("$PACKER_GND_NET")) { + if (tmux == PINMUX_0) { is_output = true; + } else if (tmux == PINMUX_1 || t == nullptr) { + is_input = true; } const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str())); + write_ioattr(cell, "PULLMODE", "NONE"); pop(3); } void write_osc(const CellInfo *cell) diff --git a/nexus/pack.cc b/nexus/pack.cc index 3b53ea55..0ef3eb79 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -502,7 +502,7 @@ struct NexusPacker // Might have an output buffer (OB etc) connected to it is_npnr_iob = true; NetInfo *i = get_net_or_empty(ci, id_I); - if (i == nullptr && i->driver.cell != nullptr) { + if (i != nullptr && i->driver.cell != nullptr) { if (top_port.cell != nullptr) log_error("Top level '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); top_port = i->driver; @@ -531,21 +531,104 @@ struct NexusPacker } } + BelId get_io_bel(CellInfo *ci) + { + if (!ci->attrs.count(id_BEL)) + return BelId(); + return ctx->getBelByName(ctx->id(ci->attrs.at(id_BEL).as_string())); + } + void pack_io() { + std::unordered_set iob_types = {id_IB, id_OB, id_OBZ, id_BB, + id_BB_I3C_A, id_SEIO33, id_SEIO18, id_DIFFIO18, + id_SEIO33_CORE, id_SEIO18_CORE, id_DIFFIO18_CORE}; + + std::unordered_map io_rules; + + // For the low level primitives, make sure we always preserve their type + io_rules[id_SEIO33_CORE].new_type = id_SEIO33_CORE; + io_rules[id_SEIO18_CORE].new_type = id_SEIO18_CORE; + io_rules[id_DIFFIO18_CORE].new_type = id_DIFFIO18_CORE; + + // Some IO buffer types need a bit of pin renaming, too + io_rules[id_SEIO33].new_type = id_SEIO33_CORE; + io_rules[id_SEIO33].port_xform[id_PADDI] = id_O; + io_rules[id_SEIO33].port_xform[id_PADDO] = id_I; + io_rules[id_SEIO33].port_xform[id_PADDT] = id_T; + io_rules[id_SEIO33].port_xform[id_IOPAD] = id_B; + + io_rules[id_BB_I3C_A] = io_rules[id_SEIO33]; + + io_rules[id_SEIO18] = io_rules[id_SEIO33]; + io_rules[id_SEIO18].new_type = id_SEIO18_CORE; + + io_rules[id_DIFFIO18] = io_rules[id_SEIO33]; + io_rules[id_DIFFIO18].new_type = id_DIFFIO18_CORE; + + // Stage 0: deal with top level inserted IO buffers + prepare_io(); + + // Stage 1: setup constraints for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; - if (ci->type == id_SEIO33_CORE || ci->type == id_SEIO18_CORE) { - auto fnd_loc = ci->attrs.find(id_LOC); - if (fnd_loc == ci->attrs.end()) - continue; - BelId bel = ctx->get_pin_bel(fnd_loc->second.as_string()); - if (bel == BelId()) - log_error("cannot constrain IO '%s', no PIO pin named '%s'\n", ctx->nameOf(ci), - fnd_loc->second.as_string().c_str()); + // Iterate through all IO buffer primitives + if (!iob_types.count(ci->type)) + continue; + // We need all IO constrained so we can pick the right IO bel type + // An improvement would be to allocate unconstrained IO here + if (!ci->attrs.count(id_LOC)) + log_error("Found unconstrained IO '%s', these are currently unsupported\n", ctx->nameOf(ci)); + // Convert package pin constraint to bel constraint + std::string loc = ci->attrs.at(id_LOC).as_string(); + auto pad_info = ctx->get_pkg_pin_data(loc); + if (pad_info == nullptr) + log_error("IO '%s' is constrained to invalid pin '%s'\n", ctx->nameOf(ci), loc.c_str()); + auto func = ctx->get_pad_functions(pad_info); + BelId bel = ctx->get_pad_pio_bel(pad_info); + + if (bel == BelId()) { + log_error("IO '%s' is constrained to pin %s (%s) which is not a general purpose IO pin.\n", + ctx->nameOf(ci), loc.c_str(), func.c_str()); + } else { + + // Get IO type for reporting purposes + std::string io_type = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33"); + + log_info("Constraining %s IO '%s' to pin %s (%s%sbel %s)\n", io_type.c_str(), ctx->nameOf(ci), + loc.c_str(), func.c_str(), func.empty() ? "" : "; ", ctx->nameOfBel(bel)); ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); } } + // Stage 2: apply rules for primitives that need them + generic_xform(io_rules, false); + // Stage 3: all other IO primitives become their bel type + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + // Iterate through all IO buffer primitives + if (!iob_types.count(ci->type)) + continue; + // Skip those dealt with in stage 2 + if (io_rules.count(ci->type)) + continue; + // For non-bidirectional IO, we also need to configure tristate and rename B + if (ci->type == id_IB) { + ctx->set_cell_pinmux(ci, id_T, PINMUX_1); + rename_port(ctx, ci, id_I, id_B); + } else if (ci->type == id_OB) { + ctx->set_cell_pinmux(ci, id_T, PINMUX_0); + rename_port(ctx, ci, id_O, id_B); + } else if (ci->type == id_OBZ) { + ctx->set_cell_pinmux(ci, id_T, PINMUX_SIG); + rename_port(ctx, ci, id_O, id_B); + } + // Get the IO bel + BelId bel = get_io_bel(ci); + // Set the cell type to the bel type + IdString type = ctx->getBelType(bel); + NPNR_ASSERT(type != IdString()); + ci->type = type; + } } explicit NexusPacker(Context *ctx) : ctx(ctx) {} diff --git a/nexus/pdc.cc b/nexus/pdc.cc index 60cc511c..98a44276 100644 --- a/nexus/pdc.cc +++ b/nexus/pdc.cc @@ -304,8 +304,12 @@ struct PDCParser if (!val.is_string) log_error("expecting string argument to -iobuf (line %d)\n", lineno); std::stringstream ss(val.str); - std::string k, v; - while (ss >> k >> v) { + std::string kv; + while (ss >> kv) { + auto eqp = kv.find('='); + if (eqp == std::string::npos) + log_error("expected key-value pair separated by '=' (line %d)", lineno); + std::string k = kv.substr(0, eqp), v = kv.substr(eqp + 1); args[ctx->id(k)] = v; } } else { -- cgit v1.2.3 From 10ad7d9cfec20e2c2417347e267c633e2f7e2623 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Oct 2020 13:58:58 +0100 Subject: nexus: IO packing fix Signed-off-by: David Shah --- nexus/pack.cc | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index 0ef3eb79..386c5f4b 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -494,7 +494,7 @@ struct NexusPacker if (o == nullptr) ; else if (o->users.size() > 1) - log_error("Top level '%s' has multiple input buffers\n", ctx->nameOf(port.first)); + log_error("Top level pin '%s' has multiple input buffers\n", ctx->nameOf(port.first)); else if (o->users.size() == 1) top_port = o->users.at(0); } @@ -504,9 +504,22 @@ struct NexusPacker NetInfo *i = get_net_or_empty(ci, id_I); if (i != nullptr && i->driver.cell != nullptr) { if (top_port.cell != nullptr) - log_error("Top level '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); + log_error("Top level pin '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); top_port = i->driver; } + // Edge case of a bidirectional buffer driving an output pin + if (i->users.size() > 2) { + log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); + } else if (i->users.size() == 2) { + if (top_port.cell != nullptr) + log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); + for (auto &usr : i->users) { + if (usr.cell->type == ctx->id("$nextpnr_obuf") || usr.cell->type == ctx->id("$nextpnr_iobuf")) + continue; + top_port = usr; + break; + } + } } if (!is_npnr_iob) log_error("Port '%s' doesn't seem to have a corresponding top level IO (internal cell type mismatch)\n", -- cgit v1.2.3 From af3af59df48f2752912d1c788ad56d83a73839ff Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Oct 2020 15:57:33 +0100 Subject: nexus: Use tilegroups for IO bitgen Signed-off-by: David Shah --- nexus/fasm.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index e9a4572f..777947e0 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -139,6 +139,17 @@ struct NexusFasmWriter void push_tile(int loc, IdString tile_type) { push(tile_name(loc, tile_by_type_and_loc(loc, tile_type))); } void push_tile(int loc) { push(tile_name(loc, tile_at_loc(loc))); } void push_belname(BelId bel) { push(ctx->nameOf(ctx->bel_data(bel).name)); } + void push_belgroup(BelId bel) + { + int r = bel.tile / ctx->chip_info->width; + int c = bel.tile % ctx->chip_info->width; + auto bel_data = ctx->bel_data(bel); + r += bel_data.rel_y; + c += bel_data.rel_x; + std::string s = stringf("R%dC%d_%s", r, c, ctx->nameOf(ctx->bel_data(bel).name)); + push(s); + } + void write_pip(PipId pip) { auto &pd = ctx->pip_data(pip); @@ -203,7 +214,7 @@ struct NexusFasmWriter void write_io33(const CellInfo *cell) { BelId bel = cell->bel; - push_tile(bel.tile); + push_belgroup(bel); push_belname(bel); const NetInfo *t = get_net_or_empty(cell, id_T); auto tmux = ctx->get_cell_pinmux(cell, id_T); @@ -221,7 +232,7 @@ struct NexusFasmWriter void write_io18(const CellInfo *cell) { BelId bel = cell->bel; - push_tile(bel.tile); + push_belgroup(bel); push_belname(bel); push("SEIO18"); const NetInfo *t = get_net_or_empty(cell, id_T); -- cgit v1.2.3 From d849e2f8b47c2da442db44af4ee613c353d7efb9 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Oct 2020 18:10:23 +0100 Subject: nexus: Fix PDC string handling Signed-off-by: David Shah --- nexus/pdc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/pdc.cc b/nexus/pdc.cc index 98a44276..2622bfe1 100644 --- a/nexus/pdc.cc +++ b/nexus/pdc.cc @@ -170,7 +170,7 @@ struct PDCParser while (true) { char c = peek(); - if (!in_quotes && !in_braces && !escaped && std::isblank(c)) { + if (!in_quotes && !in_braces && !escaped && (std::isblank(c) || c == ']')) { break; } get(); -- cgit v1.2.3 From 7f03f4d8960deee8c3edb08277bf59308af305bc Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 13 Oct 2020 08:49:02 +0100 Subject: nexus: Tidy up FASM backend Signed-off-by: David Shah --- nexus/arch.h | 2 +- nexus/fasm.cc | 41 ++++++++++++++++++++++++++++++++--------- nexus/pins.cc | 5 ++++- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 2f84b25d..700df103 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1364,7 +1364,7 @@ struct Arch : BaseCtx typedef std::unordered_map CellPinsData; - void get_cell_pin_data(std::unordered_map &cell_pins); + void get_cell_pin_data(std::unordered_map &cell_pins) const; // ------------------------------------------------- diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 777947e0..71b4c5c2 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -30,30 +30,38 @@ struct NexusFasmWriter std::ostream &out; std::vector fasm_ctx; + std::unordered_map cell_pins_db; + + NexusFasmWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out) {} + + // Add a 'dot' prefix to the FASM context stack void push(const std::string &x) { fasm_ctx.push_back(x); } + // Remove a prefix from the FASM context stack void pop() { fasm_ctx.pop_back(); } + // Remove N prefices from the FASM context stack void pop(int N) { for (int i = 0; i < N; i++) fasm_ctx.pop_back(); } bool last_was_blank = true; + // Insert a blank line if the last wasn't blank void blank() { if (!last_was_blank) out << std::endl; last_was_blank = true; } - + // Write out all prefices from the stack, interspersed with . void write_prefix() { for (auto &x : fasm_ctx) out << x << "."; last_was_blank = false; } - + // Write a single config bit; if value is true void write_bit(const std::string &name, bool value = true) { if (value) { @@ -61,8 +69,9 @@ struct NexusFasmWriter out << name << std::endl; } } + // Write a FASM comment void write_comment(const std::string &cmt) { out << "# " << cmt << std::endl; } - + // Write a FASM bitvector; optionally inverting the values in the process void write_vector(const std::string &name, const std::vector &value, bool invert = false) { write_prefix(); @@ -71,7 +80,7 @@ struct NexusFasmWriter out << ((bit ^ invert) ? '1' : '0'); out << std::endl; } - + // Write a FASM bitvector given an integer value void write_int_vector(const std::string &name, uint64_t value, int width, bool invert = false) { std::vector bits(width, false); @@ -79,7 +88,7 @@ struct NexusFasmWriter bits[i] = (value & (1ULL << i)) != 0; write_vector(name, bits, invert); } - + // Look up an enum value in a cell's parameters and write it to the FASM in name.value format void write_enum(const CellInfo *cell, const std::string &name, const std::string &defval = "") { auto fnd = cell->params.find(ctx->id(name)); @@ -90,7 +99,7 @@ struct NexusFasmWriter write_bit(stringf("%s.%s", name.c_str(), fnd->second.c_str())); } } - + // Look up an IO attribute in the cell's attributes and write it to the FASM in name.value format void write_ioattr(const CellInfo *cell, const std::string &name, const std::string &defval = "") { auto fnd = cell->attrs.find(ctx->id(name)); @@ -101,14 +110,14 @@ struct NexusFasmWriter write_bit(stringf("%s.%s", name.c_str(), fnd->second.c_str())); } } - - NexusFasmWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out) {} + // Gets the full name of a tile std::string tile_name(int loc, const PhysicalTileInfoPOD &tile) { int r = loc / ctx->chip_info->width; int c = loc % ctx->chip_info->width; return stringf("%sR%dC%d__%s", ctx->nameOf(tile.prefix), r, c, ctx->nameOf(tile.tiletype)); } + // Look up a tile by location index and tile type const PhysicalTileInfoPOD &tile_by_type_and_loc(int loc, IdString type) { auto &ploc = ctx->chip_info->grid[loc]; @@ -119,12 +128,14 @@ struct NexusFasmWriter log_error("No tile of type %s found at location R%dC%d", ctx->nameOf(type), loc / ctx->chip_info->width, loc % ctx->chip_info->width); } + // Gets the single tile at a location const PhysicalTileInfoPOD &tile_at_loc(int loc) { auto &ploc = ctx->chip_info->grid[loc]; NPNR_ASSERT(ploc.num_phys_tiles == 1); return ploc.phys_tiles[0]; } + // Escape an internal prjoxide name for FASM by replacing : with __ std::string escape_name(const std::string &name) { std::string escaped; @@ -136,9 +147,12 @@ struct NexusFasmWriter } return escaped; } + // Push a tile name onto the prefix stack void push_tile(int loc, IdString tile_type) { push(tile_name(loc, tile_by_type_and_loc(loc, tile_type))); } void push_tile(int loc) { push(tile_name(loc, tile_at_loc(loc))); } + // Push a bel name onto the prefix stack void push_belname(BelId bel) { push(ctx->nameOf(ctx->bel_data(bel).name)); } + // Push the tile group name corresponding to a bel onto the prefix stack void push_belgroup(BelId bel) { int r = bel.tile / ctx->chip_info->width; @@ -149,7 +163,7 @@ struct NexusFasmWriter std::string s = stringf("R%dC%d_%s", r, c, ctx->nameOf(ctx->bel_data(bel).name)); push(s); } - + // Write out a pip in tile.dst.src format void write_pip(PipId pip) { auto &pd = ctx->pip_data(pip); @@ -160,6 +174,7 @@ struct NexusFasmWriter std::string dest_wire = escape_name(ctx->pip_dst_wire_name(pip).str(ctx)); write_bit(stringf("%s.PIP.%s.%s", tile.c_str(), dest_wire.c_str(), source_wire.c_str())); } + // Write out all the pips corresponding to a net void write_net(const NetInfo *net) { write_comment(stringf("Net %s", ctx->nameOf(net))); @@ -171,6 +186,7 @@ struct NexusFasmWriter write_pip(p); blank(); } + // Write config for an OXIDE_COMB cell void write_comb(const CellInfo *cell) { BelId bel = cell->bel; @@ -189,6 +205,7 @@ struct NexusFasmWriter #endif pop(2); } + // Write config for an OXIDE_FF cell void write_ff(const CellInfo *cell) { BelId bel = cell->bel; @@ -211,6 +228,7 @@ struct NexusFasmWriter write_enum(cell, "GSR"); pop(2); } + // Write config for an SEIO33_CORE cell void write_io33(const CellInfo *cell) { BelId bel = cell->bel; @@ -229,6 +247,7 @@ struct NexusFasmWriter write_ioattr(cell, "PULLMODE", "NONE"); pop(2); } + // Write config for an SEIO18_CORE cell void write_io18(const CellInfo *cell) { BelId bel = cell->bel; @@ -248,6 +267,7 @@ struct NexusFasmWriter write_ioattr(cell, "PULLMODE", "NONE"); pop(3); } + // Write config for an OSC_CORE cell void write_osc(const CellInfo *cell) { BelId bel = cell->bel; @@ -262,8 +282,11 @@ struct NexusFasmWriter write_int_vector(stringf("HF_CLK_DIV[7:0]"), ctx->parse_lattice_param(cell, id_HF_CLK_DIV, 8, 0).intval, 8); pop(2); } + // Write out FASM for the whole design void operator()() { + // Setup pin DB + ctx->get_cell_pin_data(cell_pins_db); // Write routing for (auto n : sorted(ctx->nets)) { write_net(n.second); diff --git a/nexus/pins.cc b/nexus/pins.cc index fc540059..6d449438 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -74,6 +74,9 @@ static const std::unordered_map base_cell_pin_data }; } // namespace -void Arch::get_cell_pin_data(std::unordered_map &cell_pins) { cell_pins = base_cell_pin_data; } +void Arch::get_cell_pin_data(std::unordered_map &cell_pins) const +{ + cell_pins = base_cell_pin_data; +} NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From 0eb5c72cc5b95a7fb6b848e8d09ea9b235bce9b3 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 13 Oct 2020 09:49:53 +0100 Subject: nexus: Refactor cell pin style db Signed-off-by: David Shah --- nexus/arch.cc | 1 + nexus/arch.h | 5 ++++- nexus/fasm.cc | 4 ---- nexus/pack.cc | 25 ++----------------------- nexus/pins.cc | 19 +++++++++++++++++-- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 352f789a..63977d6f 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -117,6 +117,7 @@ Arch::Arch(ArchArgs args) : args(args) for (size_t i = 0; i < chip_info->num_tiles; i++) { tileStatus[i].boundcells.resize(db->loctypes[chip_info->grid[i].loc_type].num_bels); } + init_cell_pin_data(); // Validate and set up package package_idx = -1; for (size_t i = 0; i < chip_info->num_packages; i++) { diff --git a/nexus/arch.h b/nexus/arch.h index 700df103..d2349162 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1364,7 +1364,10 @@ struct Arch : BaseCtx typedef std::unordered_map CellPinsData; - void get_cell_pin_data(std::unordered_map &cell_pins) const; + std::unordered_map cell_pins_db; + CellPinStyle get_cell_pin_style(CellInfo *cell, IdString port) const; + + void init_cell_pin_data(); // ------------------------------------------------- diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 71b4c5c2..7d25de58 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -30,8 +30,6 @@ struct NexusFasmWriter std::ostream &out; std::vector fasm_ctx; - std::unordered_map cell_pins_db; - NexusFasmWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out) {} // Add a 'dot' prefix to the FASM context stack @@ -285,8 +283,6 @@ struct NexusFasmWriter // Write out FASM for the whole design void operator()() { - // Setup pin DB - ctx->get_cell_pin_data(cell_pins_db); // Write routing for (auto n : sorted(ctx->nets)) { write_net(n.second); diff --git a/nexus/pack.cc b/nexus/pack.cc index 386c5f4b..b9472ed1 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -99,8 +99,6 @@ struct NexusPacker { Context *ctx; - std::unordered_map cell_db; - // Generic cell transformation // Given cell name map and port map // If port name is not found in port map; it will be copied as-is but stripping [] @@ -319,30 +317,12 @@ struct NexusPacker return new_net; } - CellPinStyle get_pin_style(CellInfo *cell, IdString port) - { - // Look up the pin style in the cell database - auto fnd_cell = cell_db.find(cell->type); - if (fnd_cell == cell_db.end()) - return PINSTYLE_NONE; - auto fnd_port = fnd_cell->second.find(port); - if (fnd_port != fnd_cell->second.end()) - return fnd_port->second; - // If there isn't an exact port match, then the empty IdString - // represents a wildcard default match - auto fnd_default = fnd_cell->second.find({}); - if (fnd_default != fnd_cell->second.end()) - return fnd_default->second; - - return PINSTYLE_NONE; - } - CellPinMux get_pin_needed_muxval(CellInfo *cell, IdString port) { NetInfo *net = get_net_or_empty(cell, port); if (net == nullptr || net->driver.cell == nullptr) { // Pin is disconnected, return its default value - CellPinStyle pin_style = get_pin_style(cell, port); + CellPinStyle pin_style = ctx->get_cell_pin_style(cell, port); if ((pin_style & PINDEF_MASK) == PINDEF_0) return PINMUX_0; else if ((pin_style & PINDEF_MASK) == PINDEF_1) @@ -450,7 +430,7 @@ struct NexusPacker continue; } - CellPinStyle pin_style = get_pin_style(cell, port_name); + CellPinStyle pin_style = ctx->get_cell_pin_style(cell, port_name); if (req_mux == PINMUX_INV) { // Pin is inverted. If there is a hard inverter; then use it @@ -648,7 +628,6 @@ struct NexusPacker void operator()() { - ctx->get_cell_pin_data(cell_db); pack_ffs(); pack_luts(); pack_io(); diff --git a/nexus/pins.cc b/nexus/pins.cc index 6d449438..1f0ef363 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -74,9 +74,24 @@ static const std::unordered_map base_cell_pin_data }; } // namespace -void Arch::get_cell_pin_data(std::unordered_map &cell_pins) const +void Arch::init_cell_pin_data() { cell_pins_db = base_cell_pin_data; } + +CellPinStyle Arch::get_cell_pin_style(CellInfo *cell, IdString port) const { - cell_pins = base_cell_pin_data; + // Look up the pin style in the cell database + auto fnd_cell = cell_pins_db.find(cell->type); + if (fnd_cell == cell_pins_db.end()) + return PINSTYLE_NONE; + auto fnd_port = fnd_cell->second.find(port); + if (fnd_port != fnd_cell->second.end()) + return fnd_port->second; + // If there isn't an exact port match, then the empty IdString + // represents a wildcard default match + auto fnd_default = fnd_cell->second.find({}); + if (fnd_default != fnd_cell->second.end()) + return fnd_default->second; + + return PINSTYLE_NONE; } NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From 1bb509897c77b7a8931f10b84dc1de98668c04bd Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 13 Oct 2020 10:07:28 +0100 Subject: nexus: More pin styles and FASM pinmux gen Signed-off-by: David Shah --- common/util.h | 9 +++++++++ nexus/arch.h | 30 ++++++++++++++++++------------ nexus/fasm.cc | 24 ++++++++++++++++++++++++ nexus/pins.cc | 2 +- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/common/util.h b/common/util.h index d82e984a..07ebac75 100644 --- a/common/util.h +++ b/common/util.h @@ -121,6 +121,15 @@ template std::map sorted_ref(std::unordered_map return retVal; }; +// Wrap an unordered_map, and allow it to be iterated over sorted by key +template std::map sorted_cref(const std::unordered_map &orig) +{ + std::map retVal; + for (auto &item : orig) + retVal.emplace(std::make_pair(item.first, std::ref(item.second))); + return retVal; +}; + // Wrap an unordered_set, and allow it to be iterated over sorted by key template std::set sorted(const std::unordered_set &orig) { diff --git a/nexus/arch.h b/nexus/arch.h index d2349162..4da60706 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -764,8 +764,8 @@ struct WireBelPinRange enum CellPinStyle { PINOPT_NONE = 0x0, // no options, just signal as-is - PINOPT_LO = 0x1, // can be tied high - PINOPT_HI = 0x2, // can be tied low + PINOPT_LO = 0x1, // can be tied low + PINOPT_HI = 0x2, // can be tied high PINOPT_INV = 0x4, // can be inverted PINOPT_LOHI = 0x3, // can be tied low or high @@ -783,16 +783,22 @@ enum CellPinStyle PINGLB_MASK = 0x100, - PINSTYLE_NONE = 0x000, // default - PINSTYLE_CIB = 0x012, // 'CIB' signal, floats high but explicitly zeroed if not used - PINSTYLE_CLK = 0x107, // CLK type signal, invertible and defaults to disconnected - PINSTYLE_CE = 0x027, // CE type signal, invertible and defaults to enabled - PINSTYLE_LSR = 0x017, // LSR type signal, invertible and defaults to not reset - PINSTYLE_DEDI = 0x000, // dedicated signals, leave alone - PINSTYLE_PU = 0x022, // signals that float high and default high + PINBIT_GATED = 0x1000, // pin must be enabled in bitstream if used + PINBIT_1 = 0x2000, // pin has an explicit bit that must be set if tied to 1 - PINSTYLE_INV_PD = 0x017, // invertible, pull down by default - PINSTYLE_INV_PU = 0x027, // invertible, pull up by default + PINSTYLE_NONE = 0x0000, // default + PINSTYLE_CIB = 0x0012, // 'CIB' signal, floats high but explicitly zeroed if not used + PINSTYLE_CLK = 0x0107, // CLK type signal, invertible and defaults to disconnected + PINSTYLE_CE = 0x0027, // CE type signal, invertible and defaults to enabled + PINSTYLE_LSR = 0x0017, // LSR type signal, invertible and defaults to not reset + PINSTYLE_DEDI = 0x0000, // dedicated signals, leave alone + PINSTYLE_PU = 0x0022, // signals that float high and default high + + PINSTYLE_INV_PD = 0x0017, // invertible, pull down by default + PINSTYLE_INV_PU = 0x0027, // invertible, pull up by default + + PINSTYLE_IOL_CE = 0x2027, // CE type signal, with explicit 'const-1' config bit + PINSTYLE_GATE = 0x1011, // gated signal that defaults to 0 }; // This represents the mux options for a pin @@ -1365,7 +1371,7 @@ struct Arch : BaseCtx typedef std::unordered_map CellPinsData; std::unordered_map cell_pins_db; - CellPinStyle get_cell_pin_style(CellInfo *cell, IdString port) const; + CellPinStyle get_cell_pin_style(const CellInfo *cell, IdString port) const; void init_cell_pin_data(); diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 7d25de58..50b41e06 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -184,6 +184,30 @@ struct NexusFasmWriter write_pip(p); blank(); } + // Write out the mux config for a cell + void write_cell_muxes(const CellInfo *cell) + { + for (auto port : sorted_cref(cell->ports)) { + // Only relevant to inputs + if (port.second.type != PORT_IN) + continue; + auto pin_style = ctx->get_cell_pin_style(cell, port.first); + auto pin_mux = ctx->get_cell_pinmux(cell, port.first); + // Invertible pins + if (pin_style & PINOPT_INV) { + if (pin_mux == PINMUX_INV || pin_mux == PINMUX_0) + write_bit(stringf("%sMUX.INV", ctx->nameOf(port.first))); + else if (pin_mux == PINMUX_SIG) + write_bit(stringf("%sMUX.%s", ctx->nameOf(port.first), ctx->nameOf(port.first))); + } + // Pins that must be explictly enabled + if ((pin_style & PINBIT_GATED) && (pin_mux == PINMUX_SIG)) + write_bit(stringf("%sMUX.%s", ctx->nameOf(port.first), ctx->nameOf(port.first))); + // Pins that must be explictly set to 1 rather than just left floating + if ((pin_style & PINBIT_1) && (pin_mux == PINMUX_1)) + write_bit(stringf("%sMUX.1", ctx->nameOf(port.first))); + } + } // Write config for an OXIDE_COMB cell void write_comb(const CellInfo *cell) { diff --git a/nexus/pins.cc b/nexus/pins.cc index 1f0ef363..33015add 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -76,7 +76,7 @@ static const std::unordered_map base_cell_pin_data void Arch::init_cell_pin_data() { cell_pins_db = base_cell_pin_data; } -CellPinStyle Arch::get_cell_pin_style(CellInfo *cell, IdString port) const +CellPinStyle Arch::get_cell_pin_style(const CellInfo *cell, IdString port) const { // Look up the pin style in the cell database auto fnd_cell = cell_pins_db.find(cell->type); -- cgit v1.2.3 From da1e3c8612e9b63c91340142a9bf4529ac5f056a Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 13 Oct 2020 14:15:29 +0100 Subject: nexus: Add constant/inversion packing Signed-off-by: David Shah --- nexus/arch.h | 12 +++++++----- nexus/fasm.cc | 47 ++++++++++++++++++++++++++++++++++++++++++----- nexus/pack.cc | 28 +++++++++++++++++++++++++--- nexus/pins.cc | 8 ++++---- 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 4da60706..d5481bac 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -783,19 +783,21 @@ enum CellPinStyle PINGLB_MASK = 0x100, - PINBIT_GATED = 0x1000, // pin must be enabled in bitstream if used - PINBIT_1 = 0x2000, // pin has an explicit bit that must be set if tied to 1 + PINBIT_GATED = 0x1000, // pin must be enabled in bitstream if used + PINBIT_1 = 0x2000, // pin has an explicit bit that must be set if tied to 1 + PINBIT_CIBMUX = 0x4000, // pin's CIBMUX must be floating for pin to be 1 PINSTYLE_NONE = 0x0000, // default - PINSTYLE_CIB = 0x0012, // 'CIB' signal, floats high but explicitly zeroed if not used + PINSTYLE_CIB = 0x4012, // 'CIB' signal, floats high but explicitly zeroed if not used PINSTYLE_CLK = 0x0107, // CLK type signal, invertible and defaults to disconnected PINSTYLE_CE = 0x0027, // CE type signal, invertible and defaults to enabled PINSTYLE_LSR = 0x0017, // LSR type signal, invertible and defaults to not reset PINSTYLE_DEDI = 0x0000, // dedicated signals, leave alone - PINSTYLE_PU = 0x0022, // signals that float high and default high + PINSTYLE_PU = 0x4022, // signals that float high and default high + PINSTYLE_T = 0x4027, // PIO 'T' signal PINSTYLE_INV_PD = 0x0017, // invertible, pull down by default - PINSTYLE_INV_PU = 0x0027, // invertible, pull up by default + PINSTYLE_INV_PU = 0x4027, // invertible, pull up by default PINSTYLE_IOL_CE = 0x2027, // CE type signal, with explicit 'const-1' config bit PINSTYLE_GATE = 0x1011, // gated signal that defaults to 0 diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 50b41e06..3029f4dc 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -170,7 +170,7 @@ struct NexusFasmWriter std::string tile = tile_name(pip.tile, tile_by_type_and_loc(pip.tile, pd.tile_type)); std::string source_wire = escape_name(ctx->pip_src_wire_name(pip).str(ctx)); std::string dest_wire = escape_name(ctx->pip_dst_wire_name(pip).str(ctx)); - write_bit(stringf("%s.PIP.%s.%s", tile.c_str(), dest_wire.c_str(), source_wire.c_str())); + out << stringf("%s.PIP.%s.%s", tile.c_str(), dest_wire.c_str(), source_wire.c_str()) << std::endl; } // Write out all the pips corresponding to a net void write_net(const NetInfo *net) @@ -184,6 +184,25 @@ struct NexusFasmWriter write_pip(p); blank(); } + // Find the CIBMUX output for a signal + WireId find_cibmux(const CellInfo *cell, IdString pin) + { + WireId cursor = ctx->getBelPinWire(cell->bel, pin); + if (cursor == WireId()) + return WireId(); + for (int i = 0; i < 10; i++) { + std::string cursor_name = IdString(ctx->wire_data(cursor).name).str(ctx); + if (cursor_name.find("JCIBMUXOUT") == 0) { + return cursor; + } + for (PipId pip : ctx->getPipsUphill(cursor)) + if (ctx->checkPipAvail(pip)) { + cursor = ctx->getPipSrcWire(pip); + break; + } + } + return WireId(); + } // Write out the mux config for a cell void write_cell_muxes(const CellInfo *cell) { @@ -206,8 +225,26 @@ struct NexusFasmWriter // Pins that must be explictly set to 1 rather than just left floating if ((pin_style & PINBIT_1) && (pin_mux == PINMUX_1)) write_bit(stringf("%sMUX.1", ctx->nameOf(port.first))); + // Handle CIB muxes - these must be set such that floating pins really are floating to VCC and not connected + // to another CIB signal + if ((pin_style & PINBIT_CIBMUX) && port.second.net == nullptr) { + WireId cibmuxout = find_cibmux(cell, port.first); + if (cibmuxout != WireId()) { + write_comment(stringf("CIBMUX for unused pin %s", ctx->nameOf(port.first))); + bool found = false; + for (PipId pip : ctx->getPipsUphill(cibmuxout)) { + if (ctx->checkPipAvail(pip) && ctx->checkWireAvail(ctx->getPipSrcWire(pip))) { + write_pip(pip); + found = true; + break; + } + } + NPNR_ASSERT(found); + } + } } } + // Write config for an OXIDE_COMB cell void write_comb(const CellInfo *cell) { @@ -244,10 +281,7 @@ struct NexusFasmWriter pop(); write_enum(cell, "REGDDR"); write_enum(cell, "SRMODE"); - write_enum(cell, "CLKMUX"); - write_enum(cell, "CEMUX"); - write_enum(cell, "LSRMUX"); - write_enum(cell, "GSR"); + write_cell_muxes(cell); pop(2); } // Write config for an SEIO33_CORE cell @@ -267,6 +301,7 @@ struct NexusFasmWriter const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS33").c_str())); write_ioattr(cell, "PULLMODE", "NONE"); + write_cell_muxes(cell); pop(2); } // Write config for an SEIO18_CORE cell @@ -287,6 +322,7 @@ struct NexusFasmWriter const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str())); write_ioattr(cell, "PULLMODE", "NONE"); + write_cell_muxes(cell); pop(3); } // Write config for an OSC_CORE cell @@ -302,6 +338,7 @@ struct NexusFasmWriter write_enum(cell, "LF_OUTPUT_EN"); write_enum(cell, "DEBUG_N", "DISABLED"); write_int_vector(stringf("HF_CLK_DIV[7:0]"), ctx->parse_lattice_param(cell, id_HF_CLK_DIV, 8, 0).intval, 8); + write_cell_muxes(cell); pop(2); } // Write out FASM for the whole design diff --git a/nexus/pack.cc b/nexus/pack.cc index b9472ed1..e9b4336d 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -321,7 +321,12 @@ struct NexusPacker { NetInfo *net = get_net_or_empty(cell, port); if (net == nullptr || net->driver.cell == nullptr) { - // Pin is disconnected, return its default value + // Pin is disconnected + // If a mux value exists already, honour it + CellPinMux exist_mux = ctx->get_cell_pinmux(cell, port); + if (exist_mux != PINMUX_SIG) + return exist_mux; + // Otherwise, look up the default value and use that CellPinStyle pin_style = ctx->get_cell_pin_style(cell, port); if ((pin_style & PINDEF_MASK) == PINDEF_0) return PINMUX_0; @@ -434,7 +439,7 @@ struct NexusPacker if (req_mux == PINMUX_INV) { // Pin is inverted. If there is a hard inverter; then use it - if ((pin_style & PINOPT_MASK) == PINOPT_INV) { + if (pin_style & PINOPT_INV) { uninvert_port(cell, port_name); ctx->set_cell_pinmux(cell, port_name, PINMUX_INV); } @@ -624,13 +629,30 @@ struct NexusPacker } } + void pack_constants() + { + // Make sure we have high and low nets available + get_const_net(id_VHI); + get_const_net(id_VLO); + // Iterate through cells + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + // Skip certain cells at this point + if (ci->type != id_LUT4 && ci->type != id_INV && ci->type != id_VHI && ci->type != id_VLO) + process_inv_constants(cell.second); + } + // Remove superfluous inverters and constant drivers + trim_design(); + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() { + pack_io(); pack_ffs(); + pack_constants(); pack_luts(); - pack_io(); } }; diff --git a/nexus/pins.cc b/nexus/pins.cc index 33015add..f9ddd5f5 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -48,15 +48,15 @@ static const std::unordered_map base_cell_pin_data }}, {id_SEIO18_CORE, { - {id_T, PINSTYLE_CE}, + {id_T, PINSTYLE_T}, {id_B, PINSTYLE_DEDI}, - {{}, PINSTYLE_INV_PU}, + {{}, PINSTYLE_PU}, }}, {id_SEIO33_CORE, { - {id_T, PINSTYLE_CE}, + {id_T, PINSTYLE_T}, {id_B, PINSTYLE_DEDI}, - {{}, PINSTYLE_INV_PU}, + {{}, PINSTYLE_PU}, }}, {id_OXIDE_EBR, {{id_CLKA, PINSTYLE_CLK}, {id_CLKB, PINSTYLE_CLK}, {id_CEA, PINSTYLE_CE}, {id_CEB, PINSTYLE_CE}, {id_CSA0, PINSTYLE_PU}, {id_CSA1, PINSTYLE_PU}, -- cgit v1.2.3 From 2df4d212c34abe8b6329817c530f2c7bfbac9f80 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 14 Oct 2020 11:55:04 +0100 Subject: nexus: Bel search function for DCC placement Signed-off-by: David Shah --- nexus/pack.cc | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index e9b4336d..96b48da9 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -24,6 +24,7 @@ #include "util.h" #include +#include NEXTPNR_NAMESPACE_BEGIN @@ -529,7 +530,7 @@ struct NexusPacker } } - BelId get_io_bel(CellInfo *ci) + BelId get_bel_attr(const CellInfo *ci) { if (!ci->attrs.count(id_BEL)) return BelId(); @@ -621,7 +622,7 @@ struct NexusPacker rename_port(ctx, ci, id_O, id_B); } // Get the IO bel - BelId bel = get_io_bel(ci); + BelId bel = get_bel_attr(ci); // Set the cell type to the bel type IdString type = ctx->getBelType(bel); NPNR_ASSERT(type != IdString()); @@ -645,6 +646,59 @@ struct NexusPacker trim_design(); } + // Using a BFS, search for bels of a given type either upstream or downstream of another cell + void find_connected_bels(const CellInfo *cell, IdString port, IdString dest_type, IdString dest_pin, int iter_limit, + std::vector &candidates) + { + int iter = 0; + std::queue visit; + std::unordered_set seen_wires; + std::unordered_set seen_bels; + + BelId bel = get_bel_attr(cell); + NPNR_ASSERT(bel != BelId()); + WireId start_wire = ctx->getBelPinWire(bel, port); + NPNR_ASSERT(start_wire != WireId()); + PortType dir = ctx->getBelPinType(bel, port); + + visit.push(start_wire); + + while (!visit.empty() && (iter++ < iter_limit)) { + WireId cursor = visit.front(); + // Check to see if we have reached a valid bel pin + for (auto bp : ctx->getWireBelPins(cursor)) { + if (ctx->getBelType(bp.bel) != dest_type) + continue; + if (dest_pin != IdString() && bp.pin != dest_pin) + continue; + if (seen_bels.count(bp.bel)) + continue; + seen_bels.insert(bp.bel); + candidates.push_back(bp.bel); + } + // Search in the appropriate direction up/downstream of the cursor + if (dir == PORT_OUT) { + for (PipId p : ctx->getPipsDownhill(cursor)) + if (ctx->checkPipAvail(p)) { + WireId dst = ctx->getPipDstWire(p); + if (seen_wires.count(dst)) + continue; + seen_wires.insert(dst); + visit.push(dst); + } + } else { + for (PipId p : ctx->getPipsUphill(cursor)) + if (ctx->checkPipAvail(p)) { + WireId src = ctx->getPipSrcWire(p); + if (seen_wires.count(src)) + continue; + seen_wires.insert(src); + visit.push(src); + } + } + } + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() -- cgit v1.2.3 From 7645354917b93417ebeef974f5ca8961c9a1a3c8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 15 Oct 2020 09:44:21 +0100 Subject: nexus: More global placement infrastructure Signed-off-by: David Shah --- nexus/pack.cc | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/nexus/pack.cc b/nexus/pack.cc index 96b48da9..7ba1ef7f 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -699,6 +699,83 @@ struct NexusPacker } } + // Find the nearest bel of a given type; matching a closure predicate + template BelId find_nearest_bel(const CellInfo *cell, IdString dest_type, Tpred predicate) + { + BelId origin = get_bel_attr(cell); + if (origin == BelId()) + return BelId(); + Loc origin_loc = ctx->getBelLocation(origin); + int best_distance = std::numeric_limits::max(); + BelId best_bel = BelId(); + + for (BelId bel : ctx->getBels()) { + if (ctx->getBelType(bel) != dest_type) + continue; + if (!predicate(bel)) + continue; + Loc bel_loc = ctx->getBelLocation(bel); + int dist = std::abs(origin_loc.x - bel_loc.x) + std::abs(origin_loc.y - bel_loc.y); + if (dist < best_distance) { + best_distance = dist; + best_bel = bel; + } + } + return best_bel; + } + + std::unordered_set used_bels; + + // Pre-place a primitive based on routeability first and distance second + BelId preplace_prim(CellInfo *cell, IdString pin, bool strict_routing) + { + std::vector routeability_candidates; + + NetInfo *pin_net = get_net_or_empty(cell, pin); + if (pin_net == nullptr) + return BelId(); + + CellInfo *pin_drv = pin_net->driver.cell; + if (pin_drv == nullptr) + return BelId(); + + // Check based on routeability + find_connected_bels(pin_drv, pin_net->driver.port, cell->type, pin, 25000, routeability_candidates); + + for (BelId cand : routeability_candidates) { + if (used_bels.count(cand)) + continue; + cell->attrs[id_BEL] = ctx->getBelName(cand).str(ctx); + used_bels.insert(cand); + return cand; + } + + // Unless in strict mode; check based on simple distance too + BelId nearest = find_nearest_bel(pin_drv, cell->type, [&](BelId bel) { return !used_bels.count(bel); }); + + if (nearest != BelId()) { + cell->attrs[id_BEL] = ctx->getBelName(nearest).str(ctx); + used_bels.insert(nearest); + return nearest; + } + + return BelId(); + } + + // Pre-place a singleton primitive; so decisions can be made on routeability downstream of it + void preplace_singleton(CellInfo *cell) + { + if (cell->attrs.count(id_BEL)) + return; + for (BelId bel : ctx->getBels()) { + if (ctx->getBelType(bel) != cell->type) + continue; + // Check that the bel really is a singleton... + NPNR_ASSERT(!cell->attrs.count(id_BEL)); + cell->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); + } + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() -- cgit v1.2.3 From 9affda5626e8207952076b8b12d2981e4bc8533e Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 15 Oct 2020 12:10:02 +0100 Subject: nexus: Build and embed chipdb automatically Signed-off-by: David Shah --- CMakeLists.txt | 1 + nexus/.gitignore | 1 + nexus/CMakeLists.txt | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/arch.cc | 19 +++++++----------- nexus/arch.h | 1 - nexus/family.cmake | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ nexus/main.cc | 2 -- 7 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 nexus/.gitignore create mode 100644 nexus/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index fa46956b..29f73f47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,6 +286,7 @@ endforeach (family) file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h) string(REGEX REPLACE "[^;]*/ice40/chipdb/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") string(REGEX REPLACE "[^;]*/ecp5/chipdb/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") +string(REGEX REPLACE "[^;]*nexus/chipdb/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") string(REGEX REPLACE "[^;]*/3rdparty[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") string(REGEX REPLACE "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") diff --git a/nexus/.gitignore b/nexus/.gitignore new file mode 100644 index 00000000..0cb37421 --- /dev/null +++ b/nexus/.gitignore @@ -0,0 +1 @@ +/chipdb/ diff --git a/nexus/CMakeLists.txt b/nexus/CMakeLists.txt new file mode 100644 index 00000000..30796cfc --- /dev/null +++ b/nexus/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.5) +project(chipdb-nexus NONE) + +set(ALL_NEXUS_FAMILIES LIFCL) + +# NOTE: Unlike iCE40 and ECP5; one database can cover all densities of a given family + +set(NEXUS_FAMILIES ${ALL_NEXUS_FAMILIES} CACHE STRING + "Include support for these Nexus families (available: ${ALL_NEXUS_FAMILIES})") +message(STATUS "Enabled Nexus families: ${NEXUS_FAMILIES}") + +if(DEFINED NEXUS_CHIPDB) + add_custom_target(chipdb-nexus-bbas ALL) +else() + # shared among all families + set(OXIDE_ROOT "" CACHE STRING + "prjoxide root folder") + message(STATUS "prjoxide root folder: ${OXIDE_ROOT}") + + set(all_device_bbas) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chipdb) + foreach(subfamily ${NEXUS_FAMILIES}) + if(NOT subfamily IN_LIST ALL_NEXUS_FAMILIES) + message(FATAL_ERROR "${subfamily} is not a supported Nexus family") + endif() + + set(family_bba chipdb/chipdb-${subfamily}.bba) + set(BBA_TOOL ${OXIDE_ROOT}/libprjoxide/target/release/oxide_bbaexport) + add_custom_command( + OUTPUT ${family_bba} + COMMAND + ${BBA_TOOL} ${subfamily} ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc ${family_bba}.new + # atomically update + COMMAND ${CMAKE_COMMAND} -E rename ${family_bba}.new ${family_bba} + DEPENDS + ${BBA_TOOL} + ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc + ${CMAKE_CURRENT_SOURCE_DIR}/bba_version.inc + ${PREVIOUS_CHIPDB_TARGET} + VERBATIM) + list(APPEND all_device_bbas ${family_bba}) + if(SERIALIZE_CHIPDBS) + set(PREVIOUS_CHIPDB_TARGET ${CMAKE_CURRENT_BINARY_DIR}/${family_bba}) + endif() + endforeach() + + add_custom_target(chipdb-nexus-bbas ALL DEPENDS ${all_device_bbas}) + + get_directory_property(has_parent PARENT_DIRECTORY) + if(has_parent) + set(NEXUS_CHIPDB ${CMAKE_CURRENT_BINARY_DIR}/chipdb PARENT_SCOPE) + # serialize chipdb build across multiple architectures + set(PREVIOUS_CHIPDB_TARGET chipdb-nexus-bbas PARENT_SCOPE) + else() + message(STATUS "Build nextpnr with -DNEXUS_CHIPDB=${CMAKE_CURRENT_BINARY_DIR}/chipdb") + endif() +endif() diff --git a/nexus/arch.cc b/nexus/arch.cc index 63977d6f..40efa62d 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -20,6 +20,7 @@ #include +#include "embed.h" #include "log.h" #include "nextpnr.h" #include "placer1.h" @@ -43,8 +44,6 @@ static std::tuple split_identifier_name(const std::string name.substr(second_slash + 1)); }; -static const DatabasePOD *get_chipdb(const RelPtr *ptr) { return ptr->get(); } - } // namespace // ----------------------------------------------------------------------- @@ -78,16 +77,12 @@ Arch::Arch(ArchArgs args) : args(args) log_error("Unknown device string '%s' (expected device name like 'LIFCL-40-8SG72C')\n", args.device.c_str()); package = args.device.substr(last_sep + 2, (package_end - (last_sep + 2)) + 1); rating = args.device.substr(package_end + 1); - // Load database (FIXME: baked-in databases too) - try { - blob_file.open(args.chipdb); - if (!blob_file.is_open()) - log_error("Unable to read chipdb %s\n", args.chipdb.c_str()); - const char *blob = reinterpret_cast(blob_file.data()); - db = get_chipdb(reinterpret_cast *>(blob)); - } catch (...) { - log_error("Unable to read chipdb %s\n", args.chipdb.c_str()); - } + // Load database + std::string chipdb = stringf("nexus/chipdb-%s.bin", family.c_str()); + auto db_ptr = reinterpret_cast *>(get_chipdb(chipdb)); + if (db_ptr == nullptr) + log_error("Failed to load chipdb '%s'\n", chipdb.c_str()); + db = db_ptr->get(); // Check database version and family if (db->version != bba_version) { log_error("Provided database version %d is %s than nextpnr version %d, please rebuild database/nextpnr.\n", diff --git a/nexus/arch.h b/nexus/arch.h index d5481bac..71114f62 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -820,7 +820,6 @@ const int bba_version = struct ArchArgs { - std::string chipdb; std::string device; }; diff --git a/nexus/family.cmake b/nexus/family.cmake index e69de29b..4ee65dbc 100644 --- a/nexus/family.cmake +++ b/nexus/family.cmake @@ -0,0 +1,53 @@ +add_subdirectory(${family}) +message(STATUS "Using Nexus chipdb: ${NEXUS_CHIPDB}") + +set(chipdb_sources) +set(chipdb_binaries) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${family}/chipdb) +foreach(subfamily ${NEXUS_FAMILIES}) + set(chipdb_bba ${NEXUS_CHIPDB}/chipdb-${subfamily}.bba) + set(chipdb_bin ${family}/chipdb/chipdb-${subfamily}.bin) + set(chipdb_cc ${family}/chipdb/chipdb-${subfamily}.cc) + if(BBASM_MODE STREQUAL "binary") + add_custom_command( + OUTPUT ${chipdb_bin} + COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${chipdb_bba} ${chipdb_bin} + DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba}) + list(APPEND chipdb_binaries ${chipdb_bin}) + elseif(BBASM_MODE STREQUAL "embed") + add_custom_command( + OUTPUT ${chipdb_cc} ${chipdb_bin} + COMMAND bbasm ${BBASM_ENDIAN_FLAG} --e ${chipdb_bba} ${chipdb_cc} ${chipdb_bin} + DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba}) + list(APPEND chipdb_sources ${chipdb_cc}) + list(APPEND chipdb_binaries ${chipdb_bin}) + elseif(BBASM_MODE STREQUAL "string") + add_custom_command( + OUTPUT ${chipdb_cc} + COMMAND bbasm ${BBASM_ENDIAN_FLAG} --c ${chipdb_bba} ${chipdb_cc} + DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba}) + list(APPEND chipdb_sources ${chipdb_cc}) + endif() +endforeach() +if(WIN32) + set(chipdb_rc ${CMAKE_CURRENT_BINARY_DIR}/${family}/resource/chipdb.rc) + list(APPEND chipdb_sources ${chipdb_rc}) + + file(WRITE ${chipdb_rc}) + foreach(subfamily ${NEXUS_FAMILIES}) + file(APPEND ${chipdb_rc} + "${family}/chipdb-${subfamily}.bin RCDATA \"${CMAKE_CURRENT_BINARY_DIR}/${family}/chipdb/chipdb-${subfamily}.bin\"") + endforeach() +endif() + +add_custom_target(chipdb-${family}-bins DEPENDS ${chipdb_sources} ${chipdb_binaries}) + +add_library(chipdb-${family} OBJECT ${NEXUS_CHIPDB} ${chipdb_sources}) +add_dependencies(chipdb-${family} chipdb-${family}-bins) +target_compile_options(chipdb-${family} PRIVATE -g0 -O0 -w) +target_compile_definitions(chipdb-${family} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family}) +target_include_directories(chipdb-${family} PRIVATE ${family}) + +foreach(family_target ${family_targets}) + target_sources(${family_target} PRIVATE $) +endforeach() diff --git a/nexus/main.cc b/nexus/main.cc index 83b22977..5b0ba94c 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -47,7 +47,6 @@ NexusCommandHandler::NexusCommandHandler(int argc, char **argv) : CommandHandler po::options_description NexusCommandHandler::getArchOptions() { po::options_description specific("Architecture specific options"); - specific.add_options()("chipdb", po::value(), "name of chip database binary"); specific.add_options()("device", po::value(), "device name"); specific.add_options()("fasm", po::value(), "fasm file to write"); specific.add_options()("pdc", po::value(), "physical constraints file"); @@ -68,7 +67,6 @@ void NexusCommandHandler::customBitstream(Context *ctx) std::unique_ptr NexusCommandHandler::createContext(std::unordered_map &values) { ArchArgs chipArgs; - chipArgs.chipdb = vm["chipdb"].as(); chipArgs.device = vm["device"].as(); return std::unique_ptr(new Context(chipArgs)); } -- cgit v1.2.3 From 901bf2bb1eeb97386fc22f6453240f61c0aa676b Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 15 Oct 2020 14:50:09 +0100 Subject: nexus: Update for new monolithic prjoxide Signed-off-by: David Shah --- nexus/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nexus/CMakeLists.txt b/nexus/CMakeLists.txt index 30796cfc..d58bd689 100644 --- a/nexus/CMakeLists.txt +++ b/nexus/CMakeLists.txt @@ -13,9 +13,9 @@ if(DEFINED NEXUS_CHIPDB) add_custom_target(chipdb-nexus-bbas ALL) else() # shared among all families - set(OXIDE_ROOT "" CACHE STRING - "prjoxide root folder") - message(STATUS "prjoxide root folder: ${OXIDE_ROOT}") + set(OXIDE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE STRING + "prjoxide install prefix") + message(STATUS "prjoxide install prefix: ${OXIDE_INSTALL_PREFIX}") set(all_device_bbas) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chipdb) @@ -25,15 +25,15 @@ else() endif() set(family_bba chipdb/chipdb-${subfamily}.bba) - set(BBA_TOOL ${OXIDE_ROOT}/libprjoxide/target/release/oxide_bbaexport) + set(PRJOXIDE_TOOL ${OXIDE_INSTALL_PREFIX}/bin/prjoxide) add_custom_command( OUTPUT ${family_bba} COMMAND - ${BBA_TOOL} ${subfamily} ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc ${family_bba}.new + ${PRJOXIDE_TOOL} bba-export ${subfamily} ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc ${family_bba}.new # atomically update COMMAND ${CMAKE_COMMAND} -E rename ${family_bba}.new ${family_bba} DEPENDS - ${BBA_TOOL} + ${PRJOXIDE_TOOL} ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc ${CMAKE_CURRENT_SOURCE_DIR}/bba_version.inc ${PREVIOUS_CHIPDB_TARGET} -- cgit v1.2.3 From c80144b7f08736ce769fd69b3f5a98c86b430d5b Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 15 Oct 2020 16:32:55 +0100 Subject: nexus: Generate FASM files that can be used standalone Signed-off-by: David Shah --- nexus/arch.cc | 8 +++++ nexus/arch.h | 2 +- nexus/constids.inc | 8 +++++ nexus/fasm.cc | 102 +++++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 113 insertions(+), 7 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 40efa62d..8005a130 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -77,6 +77,14 @@ Arch::Arch(ArchArgs args) : args(args) log_error("Unknown device string '%s' (expected device name like 'LIFCL-40-8SG72C')\n", args.device.c_str()); package = args.device.substr(last_sep + 2, (package_end - (last_sep + 2)) + 1); rating = args.device.substr(package_end + 1); + + // Check for 'ES' part + if (rating.size() > 1 && rating.substr(1) == "ES") { + variant = "ES"; + } else { + variant = ""; + } + // Load database std::string chipdb = stringf("nexus/chipdb-%s.bin", family.c_str()); auto db_ptr = reinterpret_cast *>(get_chipdb(chipdb)); diff --git a/nexus/arch.h b/nexus/arch.h index 71114f62..126d0a91 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -826,7 +826,7 @@ struct ArchArgs struct Arch : BaseCtx { ArchArgs args; - std::string family, device, package, speed, rating; + std::string family, device, package, speed, rating, variant; Arch(ArchArgs args); // ------------------------------------------------- diff --git a/nexus/constids.inc b/nexus/constids.inc index b4aa5077..3fc44207 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -155,3 +155,11 @@ X(IOPAD) X(PADDO) X(PADDI) X(PADDT) + +X(PREADD9_CORE) +X(MULT9_CORE) +X(MULT18_CORE) +X(REG18_CORE) +X(MULT18X36_CORE) +X(MULT36_CORE) +X(ACC54_CORE) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 3029f4dc..66747461 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -67,6 +67,13 @@ struct NexusFasmWriter out << name << std::endl; } } + // Write a FASM attribute + void write_attribute(const std::string &key, const std::string &value, bool str = true) + { + std::string qu = str ? "\"" : ""; + out << "{ " << key << "=" << qu << value << qu << " }" << std::endl; + last_was_blank = false; + } // Write a FASM comment void write_comment(const std::string &cmt) { out << "# " << cmt << std::endl; } // Write a FASM bitvector; optionally inverting the values in the process @@ -161,6 +168,12 @@ struct NexusFasmWriter std::string s = stringf("R%dC%d_%s", r, c, ctx->nameOf(ctx->bel_data(bel).name)); push(s); } + // Push a bel's group and name + void push_bel(BelId bel) + { + push_belgroup(bel); + fasm_ctx.back() += stringf(".%s", ctx->nameOf(ctx->bel_data(bel).name)); + } // Write out a pip in tile.dst.src format void write_pip(PipId pip) { @@ -284,12 +297,15 @@ struct NexusFasmWriter write_cell_muxes(cell); pop(2); } + + std::unordered_set used_io; + // Write config for an SEIO33_CORE cell void write_io33(const CellInfo *cell) { BelId bel = cell->bel; - push_belgroup(bel); - push_belname(bel); + used_io.insert(bel); + push_bel(bel); const NetInfo *t = get_net_or_empty(cell, id_T); auto tmux = ctx->get_cell_pinmux(cell, id_T); bool is_input = false, is_output = false; @@ -302,14 +318,14 @@ struct NexusFasmWriter write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS33").c_str())); write_ioattr(cell, "PULLMODE", "NONE"); write_cell_muxes(cell); - pop(2); + pop(); } // Write config for an SEIO18_CORE cell void write_io18(const CellInfo *cell) { BelId bel = cell->bel; - push_belgroup(bel); - push_belname(bel); + used_io.insert(bel); + push_bel(bel); push("SEIO18"); const NetInfo *t = get_net_or_empty(cell, id_T); auto tmux = ctx->get_cell_pinmux(cell, id_T); @@ -323,7 +339,7 @@ struct NexusFasmWriter write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str())); write_ioattr(cell, "PULLMODE", "NONE"); write_cell_muxes(cell); - pop(3); + pop(2); } // Write config for an OSC_CORE cell void write_osc(const CellInfo *cell) @@ -341,9 +357,79 @@ struct NexusFasmWriter write_cell_muxes(cell); pop(2); } + // Write out FASM for unused bels where needed + void write_unused() + { + write_comment("# Unused bels"); + + // DSP primitives are configured to a default mode; even if unused + static const std::unordered_map> dsp_defconf = { + {id_MULT9_CORE, + { + "GSR.ENABLED", + "MODE.NONE", + "RSTAMUX.RSTA", + "RSTPMUX.RSTP", + }}, + {id_PREADD9_CORE, + { + "GSR.ENABLED", + "MODE.NONE", + "RSTBMUX.RSTB", + "RSTCLMUX.RSTCL", + }}, + {id_REG18_CORE, + { + "GSR.ENABLED", + "MODE.NONE", + "RSTPMUX.RSTP", + }}, + {id_ACC54_CORE, + { + "ACCUBYPS.BYPASS", + "MODE.NONE", + }}, + }; + + for (BelId bel : ctx->getBels()) { + IdString type = ctx->getBelType(bel); + if (type == id_SEIO33_CORE && !used_io.count(bel)) { + push_bel(bel); + write_bit("BASE_TYPE.NONE"); + pop(); + blank(); + } else if (type == id_SEIO18_CORE && !used_io.count(bel)) { + push_bel(bel); + push("SEIO18"); + write_bit("BASE_TYPE.NONE"); + pop(2); + blank(); + } else if (dsp_defconf.count(type) && ctx->getBoundBelCell(bel) == nullptr) { + push_bel(bel); + for (const auto &cbit : dsp_defconf.at(type)) + write_bit(cbit); + pop(); + blank(); + } + } + } + // Write out placeholder bankref config + void write_bankcfg() + { + for (int i = 0; i < 8; i++) { + if (i >= 3 && i <= 5) + continue; // 1.8V banks, skip for now + write_bit(stringf("GLOBAL.BANK%d.VCC.3V3", i)); + } + blank(); + } // Write out FASM for the whole design void operator()() { + // Write device config + write_attribute("oxide.device", ctx->device); + write_attribute("oxide.device_variant", ctx->variant); + blank(); // Write routing for (auto n : sorted(ctx->nets)) { write_net(n.second); @@ -364,6 +450,10 @@ struct NexusFasmWriter write_osc(ci); blank(); } + // Write config for unused bels + write_unused(); + // Write bank config + write_bankcfg(); } }; } // namespace -- cgit v1.2.3 From 40441d83cde77baaa759fe5a7379047a97f6991b Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 16 Oct 2020 10:32:08 +0100 Subject: nexus: Promote and place global buffers Signed-off-by: David Shah --- nexus/constids.inc | 4 ++ nexus/pack.cc | 120 +++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 116 insertions(+), 8 deletions(-) diff --git a/nexus/constids.inc b/nexus/constids.inc index 3fc44207..c89c1ec8 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -163,3 +163,7 @@ X(REG18_CORE) X(MULT18X36_CORE) X(MULT36_CORE) X(ACC54_CORE) + +X(DCC) +X(CLKI) +X(CLKO) diff --git a/nexus/pack.cc b/nexus/pack.cc index 7ba1ef7f..33e0a11f 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -665,6 +665,7 @@ struct NexusPacker while (!visit.empty() && (iter++ < iter_limit)) { WireId cursor = visit.front(); + visit.pop(); // Check to see if we have reached a valid bel pin for (auto bp : ctx->getWireBelPins(cursor)) { if (ctx->getBelType(bp.bel) != dest_type) @@ -727,17 +728,20 @@ struct NexusPacker std::unordered_set used_bels; // Pre-place a primitive based on routeability first and distance second - BelId preplace_prim(CellInfo *cell, IdString pin, bool strict_routing) + bool preplace_prim(CellInfo *cell, IdString pin, bool strict_routing) { std::vector routeability_candidates; + if (cell->attrs.count(id_BEL)) + return false; + NetInfo *pin_net = get_net_or_empty(cell, pin); if (pin_net == nullptr) - return BelId(); + return false; CellInfo *pin_drv = pin_net->driver.cell; if (pin_drv == nullptr) - return BelId(); + return false; // Check based on routeability find_connected_bels(pin_drv, pin_net->driver.port, cell->type, pin, 25000, routeability_candidates); @@ -745,34 +749,132 @@ struct NexusPacker for (BelId cand : routeability_candidates) { if (used_bels.count(cand)) continue; + log_info(" constraining %s '%s' to bel '%s' based on dedicated routing\n", ctx->nameOf(cell), + ctx->nameOf(cell->type), ctx->nameOfBel(cand)); cell->attrs[id_BEL] = ctx->getBelName(cand).str(ctx); used_bels.insert(cand); - return cand; + return true; } // Unless in strict mode; check based on simple distance too BelId nearest = find_nearest_bel(pin_drv, cell->type, [&](BelId bel) { return !used_bels.count(bel); }); if (nearest != BelId()) { + log_info(" constraining %s '%s' to bel '%s'\n", ctx->nameOf(cell), ctx->nameOf(cell->type), + ctx->nameOfBel(nearest)); cell->attrs[id_BEL] = ctx->getBelName(nearest).str(ctx); used_bels.insert(nearest); - return nearest; + return true; } - return BelId(); + return false; } // Pre-place a singleton primitive; so decisions can be made on routeability downstream of it - void preplace_singleton(CellInfo *cell) + bool preplace_singleton(CellInfo *cell) { if (cell->attrs.count(id_BEL)) - return; + return false; + bool did_something = false; for (BelId bel : ctx->getBels()) { if (ctx->getBelType(bel) != cell->type) continue; // Check that the bel really is a singleton... NPNR_ASSERT(!cell->attrs.count(id_BEL)); cell->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); + log_info(" constraining %s '%s' to bel '%s'\n", ctx->nameOf(cell), ctx->nameOf(cell->type), + ctx->nameOfBel(bel)); + did_something = true; + } + return did_something; + } + + // Insert a buffer primitive in a signal; moving all users that match a predicate behind it + template + CellInfo *insert_buffer(NetInfo *net, IdString buffer_type, std::string name_postfix, IdString i, IdString o, + Tpred pred) + { + // Create the buffered net + NetInfo *buffered_net = ctx->createNet(ctx->id(stringf("%s$%s", ctx->nameOf(net), name_postfix.c_str()))); + // Create the buffer cell + CellInfo *buffer = ctx->createCell( + ctx->id(stringf("%s$drv_%s", ctx->nameOf(buffered_net), ctx->nameOf(buffer_type))), buffer_type); + buffer->addInput(i); + buffer->addOutput(o); + // Drive the buffered net with the buffer + connect_port(ctx, buffered_net, buffer, o); + // Filter users + std::vector remaining_users; + + for (auto &usr : net->users) { + if (pred(usr)) { + usr.cell->ports[usr.port].net = buffered_net; + buffered_net->users.push_back(usr); + } else { + remaining_users.push_back(usr); + } + } + + std::swap(net->users, remaining_users); + + // Connect buffer input to original net + connect_port(ctx, net, buffer, i); + + return buffer; + } + + // Insert global buffers + void promote_globals() + { + std::vector> clk_fanout; + int available_globals = 16; + for (auto net : sorted(ctx->nets)) { + NetInfo *ni = net.second; + // Skip undriven nets; and nets that are already global + if (ni->driver.cell == nullptr) + continue; + if (ni->driver.cell->type == id_DCC) { + --available_globals; + continue; + } + // Count the number of clock ports + int clk_count = 0; + for (const auto &usr : ni->users) { + auto port_style = ctx->get_cell_pin_style(usr.cell, usr.port); + if (port_style & PINGLB_CLK) + ++clk_count; + } + if (clk_count > 0) + clk_fanout.emplace_back(clk_count, ni->name); + } + if (available_globals <= 0) + return; + // Sort clocks by max fanout + std::sort(clk_fanout.begin(), clk_fanout.end(), std::greater>()); + log_info("Promoting globals...\n"); + // Promote the N highest fanout clocks + for (size_t i = 0; i < std::min(clk_fanout.size(), available_globals); i++) { + NetInfo *net = ctx->nets.at(clk_fanout.at(i).second).get(); + log_info(" promoting clock net '%s'\n", ctx->nameOf(net)); + insert_buffer(net, id_DCC, "glb_clk", id_CLKI, id_CLKO, [](const PortRef &port) { return true; }); + } + } + + // Place certain global cells + void place_globals() + { + // Keep running until we reach a fixed point + log_info("Placing globals...\n"); + bool did_something = true; + while (did_something) { + did_something = false; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == id_OSC_CORE) + did_something |= preplace_singleton(ci); + else if (ci->type == id_DCC) + did_something |= preplace_prim(ci, id_CLKI, false); + } } } @@ -784,6 +886,8 @@ struct NexusPacker pack_ffs(); pack_constants(); pack_luts(); + promote_globals(); + place_globals(); } }; -- cgit v1.2.3 From dfd6b6e39e56a2c2b10b051b9b54926e120f319e Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 16 Oct 2020 13:33:44 +0100 Subject: nexus: Add a simple global routing pass Signed-off-by: David Shah --- nexus/arch.cc | 3 ++ nexus/arch.h | 4 ++ nexus/global.cc | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 nexus/global.cc diff --git a/nexus/arch.cc b/nexus/arch.cc index 8005a130..3435755b 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -446,6 +446,9 @@ bool Arch::place() bool Arch::route() { assign_budget(getCtx(), true); + + route_globals(); + std::string router = str_or_default(settings, id("router"), defaultRouter); bool result; if (router == "router1") { diff --git a/nexus/arch.h b/nexus/arch.h index 126d0a91..60b4b166 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1319,6 +1319,10 @@ struct Arch : BaseCtx void assignArchInfo(); void assignCellInfo(CellInfo *cell); + // ------------------------------------------------- + // Arch-specific global routing + void route_globals(); + // ------------------------------------------------- std::vector getDecalGraphics(DecalId decal) const; diff --git a/nexus/global.cc b/nexus/global.cc new file mode 100644 index 00000000..2d183e95 --- /dev/null +++ b/nexus/global.cc @@ -0,0 +1,160 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 "log.h" +#include "nextpnr.h" +#include "util.h" + +#include + +NEXTPNR_NAMESPACE_BEGIN + +struct NexusGlobalRouter +{ + Context *ctx; + + NexusGlobalRouter(Context *ctx) : ctx(ctx){}; + + // When routing globals; we allow global->local for some tricky cases but never local->local + bool global_pip_filter(PipId pip) const + { + IdString dest_basename(ctx->wire_data(ctx->getPipDstWire(pip)).name); + const std::string &s = dest_basename.str(ctx); + if (s.size() > 2 && (s[0] == 'H' || s[0] == 'V') && s[1] == '0') + return false; + return true; + } + + // Dedicated backwards BFS routing for global networks + template + bool backwards_bfs_route(NetInfo *net, size_t user_idx, int iter_limit, bool strict, Tfilt pip_filter) + { + // Queue of wires to visit + std::queue visit; + // Wire -> upstream pip + std::unordered_map backtrace; + + // Lookup source and destination wires + WireId src = ctx->getNetinfoSourceWire(net); + WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(user_idx)); + + if (ctx->getBoundWireNet(src) != net) + ctx->bindWire(src, net, STRENGTH_LOCKED); + + if (src == dst) { + // Nothing more to do + return true; + } + + visit.push(dst); + backtrace[dst] = PipId(); + + int iter = 0; + + while (!visit.empty() && (iter++ < iter_limit)) { + WireId cursor = visit.front(); + visit.pop(); + // Search uphill pips + for (PipId pip : ctx->getPipsUphill(cursor)) { + // Skip pip if unavailable, and not because it's already used for this net + if (!ctx->checkPipAvail(pip) && ctx->getBoundPipNet(pip) != net) + continue; + WireId prev = ctx->getPipSrcWire(pip); + // Ditto for the upstream wire + if (!ctx->checkWireAvail(prev) && ctx->getBoundWireNet(prev) != net) + continue; + // Skip already visited wires + if (backtrace.count(prev)) + continue; + // Apply our custom pip filter + if (!pip_filter(pip)) + continue; + // Add to the queue + visit.push(prev); + backtrace[prev] = pip; + // Check if we are done yet + if (prev == src) + goto done; + } + if (false) { + done: + break; + } + } + + if (backtrace.count(src)) { + WireId cursor = src; + std::vector pips; + // Create a list of pips on the routed path + while (true) { + PipId pip = backtrace.at(cursor); + if (pip == PipId()) + break; + pips.push_back(pip); + cursor = ctx->getPipDstWire(pip); + } + // Reverse that list + std::reverse(pips.begin(), pips.end()); + // Bind pips until we hit already-bound routing + for (PipId pip : pips) { + WireId dst = ctx->getPipDstWire(pip); + if (ctx->getBoundWireNet(dst) == net) + break; + ctx->bindPip(pip, net, STRENGTH_LOCKED); + } + return true; + } else { + if (strict) + log_error("Failed to route net '%s' from %s to %s using dedicated routing.\n", ctx->nameOf(net), + ctx->nameOfWire(src), ctx->nameOfWire(dst)); + return false; + } + } + + void route_clk_net(NetInfo *net) + { + for (size_t i = 0; i < net->users.size(); i++) + backwards_bfs_route(net, i, 1000000, true, [&](PipId pip) { return global_pip_filter(pip); }); + log_info(" routed net '%s' using global resources\n", ctx->nameOf(net)); + } + + void operator()() + { + log_info("Routing globals...\n"); + for (auto net : sorted(ctx->nets)) { + NetInfo *ni = net.second; + CellInfo *drv = ni->driver.cell; + if (drv == nullptr) + continue; + if (drv->type == id_DCC) { + route_clk_net(ni); + continue; + } + } + } +}; + +void Arch::route_globals() +{ + NexusGlobalRouter glb_router(getCtx()); + glb_router(); +} + +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From cbf99d5e5390d8439722e0172067b687be5ac060 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 19 Oct 2020 13:31:21 +0100 Subject: nexus: LUTRAM support Signed-off-by: David Shah --- common/design_utils.cc | 7 +++ nexus/arch_place.cc | 10 +++++ nexus/constids.inc | 5 ++- nexus/fasm.cc | 13 ++++++ nexus/pack.cc | 119 +++++++++++++++++++++++++++++++++++++++++++++++-- nexus/pins.cc | 8 +++- 6 files changed, 157 insertions(+), 5 deletions(-) diff --git a/common/design_utils.cc b/common/design_utils.cc index dd866758..9478afb2 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -30,6 +30,13 @@ void replace_port(CellInfo *old_cell, IdString old_name, CellInfo *rep_cell, IdS if (!old_cell->ports.count(old_name)) return; PortInfo &old = old_cell->ports.at(old_name); + + // Create port on the replacement cell if it doesn't already exist + if (!rep_cell->ports.count(rep_name)) { + rep_cell->ports[rep_name].name = rep_name; + rep_cell->ports[rep_name].type = old.type; + } + PortInfo &rep = rep_cell->ports.at(rep_name); NPNR_ASSERT(old.type == rep.type); diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc index 7e50de29..feec75ad 100644 --- a/nexus/arch_place.cc +++ b/nexus/arch_place.cc @@ -33,6 +33,16 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const CellInfo *lut1 = lts.cells[(s << 3) | BEL_LUT1]; CellInfo *ff0 = lts.cells[(s << 3) | BEL_FF0]; CellInfo *ff1 = lts.cells[(s << 3) | BEL_FF1]; + + if (s == 2) { + CellInfo *ramw = lts.cells[(s << 3) | BEL_RAMW]; + // Nothing else in SLICEC can be used if the RAMW is used + if (ramw != nullptr) { + if (lut0 != nullptr || lut1 != nullptr || ff0 != nullptr || ff1 != nullptr) + return false; + } + } + if (lut0 != nullptr) { // Check for overuse of M signal if (lut0->lutInfo.mux2_used && ff0 != nullptr && ff0->ffInfo.m != nullptr) diff --git a/nexus/constids.inc b/nexus/constids.inc index c89c1ec8..625931f2 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -23,7 +23,7 @@ X(WAD0) X(WAD1) X(WAD2) X(WAD3) -X(WD) +X(WDI) X(WCK) X(WRE) @@ -167,3 +167,6 @@ X(ACC54_CORE) X(DCC) X(CLKI) X(CLKO) + +X(DPR16X4) +X(INITVAL) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 66747461..031cd9b3 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -298,6 +298,17 @@ struct NexusFasmWriter pop(2); } + // Write out config for an OXIDE_RAMW cell + void write_ramw(const CellInfo *cell) + { + BelId bel = cell->bel; + push_tile(bel.tile, id_PLC); + push("SLICEC"); + write_bit("MODE.RAMW"); + write_cell_muxes(cell); + pop(2); + } + std::unordered_set used_io; // Write config for an SEIO33_CORE cell @@ -442,6 +453,8 @@ struct NexusFasmWriter write_comb(ci); else if (ci->type == id_OXIDE_FF) write_ff(ci); + else if (ci->type == id_RAMW) + write_ramw(ci); else if (ci->type == id_SEIO33_CORE) write_io33(ci); else if (ci->type == id_SEIO18_CORE) diff --git a/nexus/pack.cc b/nexus/pack.cc index 33e0a11f..7dbef99b 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -878,11 +878,124 @@ struct NexusPacker } } + // Get a bus port name + IdString bus(const std::string &base, int i) { return ctx->id(stringf("%s[%d]", base.c_str(), i)); } + + IdString bus_flat(const std::string &base, int i) { return ctx->id(stringf("%s%d", base.c_str(), i)); } + + // Pack a LUTRAM into COMB and RAMW cells + void pack_lutram() + { + // Do this so we don't have an iterate-and-modfiy situation + std::vector lutrams; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type != id_DPR16X4) + continue; + lutrams.push_back(ci); + } + + // Port permutation vectors + IdString ramw_wdo[4] = {id_D1, id_C1, id_A1, id_B1}; + IdString ramw_wado[4] = {id_D0, id_B0, id_C0, id_A0}; + IdString comb0_rad[4] = {id_D, id_B, id_C, id_A}; + IdString comb1_rad[4] = {id_C, id_B, id_D, id_A}; + + for (CellInfo *ci : lutrams) { + // Create constituent cells + CellInfo *ramw = ctx->createCell(ctx->id(stringf("%s$lutram_ramw$", ctx->nameOf(ci))), id_RAMW); + std::vector combs; + for (int i = 0; i < 4; i++) + combs.push_back( + ctx->createCell(ctx->id(stringf("%s$lutram_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB)); + // Rewiring - external WCK and WRE + replace_port(ci, id_WCK, ramw, id_CLK); + replace_port(ci, id_WRE, ramw, id_LSR); + + // Internal WCK and WRE signals + ramw->addOutput(id_WCKO); + ramw->addOutput(id_WREO); + NetInfo *int_wck = ctx->createNet(ctx->id(stringf("%s$lutram_wck$", ctx->nameOf(ci)))); + NetInfo *int_wre = ctx->createNet(ctx->id(stringf("%s$lutram_wre$", ctx->nameOf(ci)))); + connect_port(ctx, int_wck, ramw, id_WCKO); + connect_port(ctx, int_wre, ramw, id_WREO); + + uint64_t initval = ctx->parse_lattice_param(ci, id_INITVAL, 64, 0).as_int64(); + + // Rewiring - buses + for (int i = 0; i < 4; i++) { + // Write address - external + replace_port(ci, bus("WAD", i), ramw, ramw_wado[i]); + // Write data - external + replace_port(ci, bus("DI", i), ramw, ramw_wdo[i]); + // Read data + replace_port(ci, bus("DO", i), combs[i], id_F); + // Read address + NetInfo *rad = get_net_or_empty(ci, bus("RAD", i)); + if (rad != nullptr) { + for (int j = 0; j < 4; j++) { + IdString port = (j % 2) ? comb1_rad[i] : comb0_rad[i]; + combs[j]->addInput(port); + connect_port(ctx, rad, combs[j], port); + } + disconnect_port(ctx, ci, bus("RAD", i)); + } + // Write address - internal + NetInfo *int_wad = ctx->createNet(ctx->id(stringf("%s$lutram_wad[%d]$", ctx->nameOf(ci), i))); + ramw->addOutput(bus_flat("WADO", i)); + connect_port(ctx, int_wad, ramw, bus_flat("WADO", i)); + for (int j = 0; j < 4; j++) { + combs[j]->addInput(bus_flat("WAD", i)); + connect_port(ctx, int_wad, combs[j], bus_flat("WAD", i)); + } + // Write data - internal + NetInfo *int_wd = ctx->createNet(ctx->id(stringf("%s$lutram_wd[%d]$", ctx->nameOf(ci), i))); + ramw->addOutput(bus_flat("WDO", i)); + connect_port(ctx, int_wd, ramw, bus_flat("WDO", i)); + combs[i]->addInput(id_WDI); + connect_port(ctx, int_wd, combs[i], id_WDI); + // Write clock and enable - internal + combs[i]->addInput(id_WCK); + combs[i]->addInput(id_WRE); + connect_port(ctx, int_wck, combs[i], id_WCK); + connect_port(ctx, int_wre, combs[i], id_WRE); + // Remap init val + uint64_t split_init = 0; + for (int j = 0; j < 16; j++) + if (initval & (1ULL << (4 * j + i))) + split_init |= (1 << j); + combs[i]->params[id_INIT] = Property(split_init, 16); + } + + // Setup relative constraints + combs[0]->constr_z = 0; + combs[0]->constr_abs_z = true; + for (int i = 1; i < 4; i++) { + combs[i]->constr_x = 0; + combs[i]->constr_y = 0; + combs[i]->constr_z = ((i / 2) << 3) | (i % 2); + combs[i]->constr_abs_z = true; + combs[i]->constr_parent = combs[0]; + combs[0]->constr_children.push_back(combs[i]); + } + + ramw->constr_x = 0; + ramw->constr_y = 0; + ramw->constr_z = (2 << 3) | Arch::BEL_RAMW; + ramw->constr_abs_z = true; + ramw->constr_parent = combs[0]; + combs[0]->constr_children.push_back(ramw); + // Remove now-packed cell + ctx->cells.erase(ci->name); + } + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() { pack_io(); + pack_lutram(); pack_ffs(); pack_constants(); pack_luts(); @@ -929,13 +1042,13 @@ void Arch::assignCellInfo(CellInfo *cell) cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); cell->ffInfo.di = get_net_or_empty(cell, id_DI); cell->ffInfo.m = get_net_or_empty(cell, id_M); - } else if (cell->type == ID_RAMW) { - cell->ffInfo.ctrlset.async = false; + } else if (cell->type == id_RAMW) { + cell->ffInfo.ctrlset.async = true; cell->ffInfo.ctrlset.regddr_en = false; cell->ffInfo.ctrlset.gsr_en = false; cell->ffInfo.ctrlset.clkmux = id(str_or_default(cell->params, id_CLKMUX, "CLK")).index; cell->ffInfo.ctrlset.cemux = ID_CE; - cell->ffInfo.ctrlset.lsrmux = id(str_or_default(cell->params, id_LSRMUX, "LSR")).index; + cell->ffInfo.ctrlset.lsrmux = ID_INV; cell->ffInfo.ctrlset.clk = get_net_or_empty(cell, id_CLK); cell->ffInfo.ctrlset.ce = nullptr; cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); diff --git a/nexus/pins.cc b/nexus/pins.cc index f9ddd5f5..1fa62b28 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -31,11 +31,12 @@ static const std::unordered_map base_cell_pin_data {id_WRE, PINSTYLE_DEDI}, {id_FCI, PINSTYLE_DEDI}, + {id_F1, PINSTYLE_DEDI}, {id_WAD0, PINSTYLE_DEDI}, {id_WAD1, PINSTYLE_DEDI}, {id_WAD2, PINSTYLE_DEDI}, {id_WAD3, PINSTYLE_DEDI}, - {id_WD, PINSTYLE_DEDI}, + {id_WDI, PINSTYLE_DEDI}, {{}, PINSTYLE_PU}, }}, @@ -46,6 +47,11 @@ static const std::unordered_map base_cell_pin_data {id_CE, PINSTYLE_CE}, {{}, PINSTYLE_DEDI}, }}, + {id_RAMW, + { + {id_CLK, PINSTYLE_CLK}, + {{}, PINSTYLE_DEDI}, + }}, {id_SEIO18_CORE, { {id_T, PINSTYLE_T}, -- cgit v1.2.3 From 1e4eb5ec391a26b1da8e3e7609ffc92768df1976 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 20 Oct 2020 09:49:40 +0100 Subject: nexus: Ignore some PDC commands Signed-off-by: David Shah --- nexus/pdc.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nexus/pdc.cc b/nexus/pdc.cc index 2622bfe1..189977e9 100644 --- a/nexus/pdc.cc +++ b/nexus/pdc.cc @@ -203,6 +203,10 @@ struct PDCParser return cmd_ldc_set_location(arguments); else if (cmd == "ldc_set_port") return cmd_ldc_set_port(arguments); + else if (cmd == "ldc_set_sysconfig" || cmd == "get_nets" || cmd == "create_clock") { + log_warning("%s is not yet supported!\n", cmd.c_str()); + return TCLValue(""); + } else log_error("Unsupported PDC command '%s'\n", cmd.c_str()); } -- cgit v1.2.3 From 70749b37d02b7efa677f231db32248743944fc3e Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 20 Oct 2020 09:50:42 +0100 Subject: nexus: Fix Python bindings Signed-off-by: David Shah --- nexus/arch_pybindings.cc | 4 +--- nexus/pdc.cc | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/nexus/arch_pybindings.cc b/nexus/arch_pybindings.cc index 8bee7713..caab8312 100644 --- a/nexus/arch_pybindings.cc +++ b/nexus/arch_pybindings.cc @@ -29,9 +29,7 @@ NEXTPNR_NAMESPACE_BEGIN void arch_wrap_python(py::module &m) { using namespace PythonConversion; - py::class_(m, "ArchArgs") - .def_readwrite("chipdb", &ArchArgs::chipdb) - .def_readwrite("device", &ArchArgs::device); + py::class_(m, "ArchArgs").def_readwrite("device", &ArchArgs::device); py::class_(m, "BelId").def_readwrite("index", &BelId::index).def_readwrite("tile", &BelId::tile); diff --git a/nexus/pdc.cc b/nexus/pdc.cc index 189977e9..3efab69a 100644 --- a/nexus/pdc.cc +++ b/nexus/pdc.cc @@ -206,8 +206,7 @@ struct PDCParser else if (cmd == "ldc_set_sysconfig" || cmd == "get_nets" || cmd == "create_clock") { log_warning("%s is not yet supported!\n", cmd.c_str()); return TCLValue(""); - } - else + } else log_error("Unsupported PDC command '%s'\n", cmd.c_str()); } -- cgit v1.2.3 From 8d1678f0ef757d095dd2b991b931612e10491e80 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 20 Oct 2020 10:28:09 +0100 Subject: nexus: Add stub GUI Signed-off-by: David Shah --- gui/nexus/family.cmake | 0 gui/nexus/mainwindow.cc | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ gui/nexus/mainwindow.h | 49 ++++++++++++++++++++++++++++ gui/nexus/nextpnr.qrc | 2 ++ nexus/arch.cc | 60 ++++++++++++++++++++++++++++++++-- nexus/archdefs.h | 10 ++++-- 6 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 gui/nexus/family.cmake create mode 100644 gui/nexus/mainwindow.cc create mode 100644 gui/nexus/mainwindow.h create mode 100644 gui/nexus/nextpnr.qrc diff --git a/gui/nexus/family.cmake b/gui/nexus/family.cmake new file mode 100644 index 00000000..e69de29b diff --git a/gui/nexus/mainwindow.cc b/gui/nexus/mainwindow.cc new file mode 100644 index 00000000..846284c4 --- /dev/null +++ b/gui/nexus/mainwindow.cc @@ -0,0 +1,87 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 "mainwindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "design_utils.h" +#include "log.h" + +static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } + +NEXTPNR_NAMESPACE_BEGIN + +MainWindow::MainWindow(std::unique_ptr context, CommandHandler *handler, QWidget *parent) + : BaseMainWindow(std::move(context), handler, parent) +{ + initMainResource(); + + std::string title = "nextpnr-nexus - [EMPTY]"; + setWindowTitle(title.c_str()); + + connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext); + + createMenu(); +} + +MainWindow::~MainWindow() {} + +void MainWindow::createMenu() +{ + // Add arch specific actions + + // Add actions in menus +} + +void MainWindow::new_proj() +{ + QStringList arch; + + // TODO: better device picker + arch.push_back("LIFCL-40-9BG400CES"); + arch.push_back("LIFCL-40-8BG72CES"); + + bool ok; + QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch, 0, false, &ok); + if (ok && !item.isEmpty()) { + ArchArgs chipArgs; + chipArgs.device = item.toStdString(); + ctx = std::unique_ptr(new Context(chipArgs)); + actionLoadJSON->setEnabled(true); + Q_EMIT contextChanged(ctx.get()); + } +} + +void MainWindow::newContext(Context *ctx) +{ + std::string title = "nextpnr-nexus - " + ctx->getChipName(); + setWindowTitle(title.c_str()); +} + +void MainWindow::onDisableActions() {} + +void MainWindow::onUpdateActions() {} + +NEXTPNR_NAMESPACE_END diff --git a/gui/nexus/mainwindow.h b/gui/nexus/mainwindow.h new file mode 100644 index 00000000..13556fa1 --- /dev/null +++ b/gui/nexus/mainwindow.h @@ -0,0 +1,49 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 MAINWINDOW_H +#define MAINWINDOW_H + +#include "../basewindow.h" + +NEXTPNR_NAMESPACE_BEGIN + +class MainWindow : public BaseMainWindow +{ + Q_OBJECT + + public: + explicit MainWindow(std::unique_ptr context, CommandHandler *handler, QWidget *parent = 0); + virtual ~MainWindow(); + + public: + void createMenu(); + + void onDisableActions() override; + void onUpdateActions() override; + + protected Q_SLOTS: + void new_proj() override; + + void newContext(Context *ctx); +}; + +NEXTPNR_NAMESPACE_END + +#endif // MAINWINDOW_H diff --git a/gui/nexus/nextpnr.qrc b/gui/nexus/nextpnr.qrc new file mode 100644 index 00000000..03585ec0 --- /dev/null +++ b/gui/nexus/nextpnr.qrc @@ -0,0 +1,2 @@ + + diff --git a/nexus/arch.cc b/nexus/arch.cc index 3435755b..b8973657 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -254,6 +254,21 @@ std::vector Arch::getBelPins(BelId bel) const return ret; } +std::vector> Arch::getBelAttrs(BelId bel) const +{ + std::vector> ret; + + ret.emplace_back(id("INDEX"), stringf("%d", bel.index)); + + ret.emplace_back(id("GRID_X"), stringf("%d", bel.tile % chip_info->width)); + ret.emplace_back(id("GRID_Y"), stringf("%d", bel.tile / chip_info->width)); + ret.emplace_back(id("BEL_Z"), stringf("%d", bel_data(bel).z)); + + ret.emplace_back(id("BEL_TYPE"), nameOf(getBelType(bel))); + + return ret; +} + // ----------------------------------------------------------------------- WireId Arch::getWireByName(IdString name) const @@ -334,19 +349,58 @@ std::vector> Arch::getPipAttrs(PipId pip) const // ----------------------------------------------------------------------- +namespace { +const float bel_ofs_x = 0.7, bel_ofs_y = 0.0375; +const float bel_sp_x = 0.1, bel_sp_y = 0.1; +const float bel_width = 0.075, bel_height = 0.075; +} // namespace + std::vector Arch::getDecalGraphics(DecalId decal) const { std::vector ret; + switch (decal.type) { + case DecalId::TYPE_BEL: { + auto style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; + if (decal.index != -1) { + int slice = (decal.index >> 3) & 0x3; + int bel = decal.index & 0x7; + float x1, x2, y1, y2; + if (bel == BEL_RAMW) { + x1 = bel_ofs_x; + y1 = bel_ofs_y + 2 * bel_sp_y * slice; + x2 = x1 + bel_sp_x + bel_width; + y2 = y1 + bel_height; + } else { + x1 = bel_ofs_x + bel_sp_x * (bel >> 1); + y1 = bel_ofs_y + 2 * bel_sp_y * slice + bel_sp_y * (bel & 0x1); + if (slice >= 2) + y1 += bel_sp_y * 1.5; + x2 = x1 + bel_width; + y2 = y1 + bel_height; + } + ret.emplace_back(GraphicElement::TYPE_BOX, style, x1, y1, x2, y2, 1); + } + break; + }; + default: + break; + } + return ret; } DecalXY Arch::getBelDecal(BelId bel) const { DecalXY decalxy; - decalxy.decal.index = -1; - decalxy.x = 0; - decalxy.y = 0; + decalxy.decal.type = DecalId::TYPE_BEL; + if (tile_is(bel, LOC_LOGIC)) + decalxy.decal.index = bel_data(bel).z; + else + decalxy.decal.index = -1; + decalxy.decal.active = (getBoundBelCell(bel) != nullptr); + decalxy.x = bel.tile % chip_info->width; + decalxy.y = bel.tile / chip_info->width; return decalxy; } diff --git a/nexus/archdefs.h b/nexus/archdefs.h index 8709cc9c..903386bf 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -136,8 +136,14 @@ struct DecalId int32_t index = -1; bool active = false; - bool operator==(const DecalId &other) const { return (type == other.type) && (index == other.index); } - bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); } + bool operator==(const DecalId &other) const + { + return (type == other.type) && (index == other.index) && (active == other.active); + } + bool operator!=(const DecalId &other) const + { + return (type != other.type) || (index != other.index) || (active != other.active); + } }; struct ArchNetInfo -- cgit v1.2.3 From 4503608c7c81dc04e837bce326deec0c3fcf7caa Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 13:45:13 +0100 Subject: nexus: Add packing rules for BRAM Signed-off-by: David Shah --- nexus/constids.inc | 33 +++++++++++++++++++++++++++++ nexus/pack.cc | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/nexus/constids.inc b/nexus/constids.inc index 625931f2..d571e9bb 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -170,3 +170,36 @@ X(CLKO) X(DPR16X4) X(INITVAL) + +X(DP16K) +X(PDP16K) +X(PDPSC16K) +X(SP16K) +X(FIFO16K) + +X(DP16K_MODE) +X(PDP16K_MODE) +X(PDPSC16K_MODE) +X(SP16K_MODE) +X(FIFO16K_MODE) + +X(DPSC512K) +X(PDPSC512K) +X(SP512K) + +X(DPSC512K_MODE) +X(PDPSC512K_MODE) +X(SP512K_MODE) + +X(WID) +X(CSDECODE_A) +X(CSDECODE_B) +X(CSDECODE_R) +X(CSDECODE_W) +X(CSDECODE) + +X(CLKW) +X(CLKR) +X(CEW) +X(CER) +X(RST) diff --git a/nexus/pack.cc b/nexus/pack.cc index 7dbef99b..fa2c78e8 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -990,11 +990,72 @@ struct NexusPacker } } + void convert_prims() + { + // Convert primitives from their non-CORE variant to their CORE variant + static const std::unordered_map prim_map = { + {id_OSCA, id_OSC_CORE}, {id_DP16K, id_DP16K_MODE}, {id_PDP16K, id_PDP16K_MODE}, + {id_PDPSC16K, id_PDPSC16K_MODE}, {id_SP16K, id_SP16K_MODE}, {id_FIFO16K, id_FIFO16K_MODE}, + }; + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (!prim_map.count(ci->type)) + continue; + prim_to_core(ci, prim_map.at(ci->type)); + } + } + + void add_bus_xform(XFormRule &rule, const std::string &o, const std::string &n, int width, int old_offset = 0, + int new_offset = 0) + { + for (int i = 0; i < width; i++) + rule.port_xform[bus_flat(o, i + old_offset)] = bus_flat(n, i + new_offset); + } + + void pack_bram() + { + std::unordered_map bram_rules; + bram_rules[id_DP16K_MODE].new_type = id_OXIDE_EBR; + bram_rules[id_DP16K_MODE].set_params.emplace_back(id_MODE, std::string("DP16K")); + bram_rules[id_DP16K_MODE].parse_params.emplace_back(id_CSDECODE_A, id_CSDECODE_A, 3, 7); + bram_rules[id_DP16K_MODE].parse_params.emplace_back(id_CSDECODE_B, id_CSDECODE_B, 3, 7); + // Pseudo dual port + bram_rules[id_PDP16K_MODE].new_type = id_OXIDE_EBR; + bram_rules[id_PDP16K_MODE].set_params.emplace_back(id_MODE, std::string("PDP16K")); + bram_rules[id_PDP16K_MODE].parse_params.emplace_back(id_CSDECODE_R, id_CSDECODE_R, 3, 7); + bram_rules[id_PDP16K_MODE].parse_params.emplace_back(id_CSDECODE_W, id_CSDECODE_W, 3, 7); + bram_rules[id_PDP16K_MODE].port_xform[id_CLKW] = id_CLKA; + bram_rules[id_PDP16K_MODE].port_xform[id_CLKR] = id_CLKB; + bram_rules[id_PDP16K_MODE].port_xform[id_CEW] = id_CEA; + bram_rules[id_PDP16K_MODE].port_xform[id_CER] = id_CEB; + bram_rules[id_PDP16K_MODE].port_multixform[id_RST] = {id_RSTA, id_RSTB}; + add_bus_xform(bram_rules[id_PDP16K_MODE], "ADW", "ADA", 14); + add_bus_xform(bram_rules[id_PDP16K_MODE], "ADR", "ADB", 14); + add_bus_xform(bram_rules[id_PDP16K_MODE], "CSW", "CSA", 3); + add_bus_xform(bram_rules[id_PDP16K_MODE], "CSR", "CSB", 3); + add_bus_xform(bram_rules[id_PDP16K_MODE], "DI", "DIA", 18, 0, 0); + add_bus_xform(bram_rules[id_PDP16K_MODE], "DI", "DIB", 18, 18, 0); + add_bus_xform(bram_rules[id_PDP16K_MODE], "DO", "DOB", 18, 0, 0); + add_bus_xform(bram_rules[id_PDP16K_MODE], "DO", "DOA", 18, 18, 0); + + // Pseudo dual port; single clock + bram_rules[id_PDPSC16K_MODE] = bram_rules[id_PDP16K_MODE]; + bram_rules[id_PDPSC16K_MODE].set_params.clear(); + bram_rules[id_PDPSC16K_MODE].set_params.emplace_back(id_MODE, std::string("PDPSC16K")); + bram_rules[id_PDPSC16K_MODE].port_multixform[id_CLK] = {id_CLKA, id_CLKB}; + + log_info("Packing BRAM...\n"); + generic_xform(bram_rules, true); + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() { pack_io(); + convert_prims(); + pack_bram(); pack_lutram(); pack_ffs(); pack_constants(); -- cgit v1.2.3 From 27ecaf3e88730f2e295345342cec864801dbd851 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 14:22:00 +0100 Subject: nexus: EBR FASM generation Signed-off-by: David Shah --- nexus/fasm.cc | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/pack.cc | 10 ++++++++ 2 files changed, 88 insertions(+) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 031cd9b3..60ff1aca 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -93,6 +93,16 @@ struct NexusFasmWriter bits[i] = (value & (1ULL << i)) != 0; write_vector(name, bits, invert); } + // Write an int vector param + void write_int_vector_param(const CellInfo *cell, const std::string &name, uint64_t defval, int width, + bool invert = false) + { + uint64_t value = int_or_default(cell->params, ctx->id(name), defval); + std::vector bits(width, false); + for (int i = 0; i < width; i++) + bits[i] = (value & (1ULL << i)) != 0; + write_vector(stringf("%s[%d:0]", name.c_str(), width - 1), bits, invert); + } // Look up an enum value in a cell's parameters and write it to the FASM in name.value format void write_enum(const CellInfo *cell, const std::string &name, const std::string &defval = "") { @@ -368,6 +378,72 @@ struct NexusFasmWriter write_cell_muxes(cell); pop(2); } + // Write config for an OXIDE_EBR cell + void write_bram(const CellInfo *cell) + { + // EBR configuration + BelId bel = cell->bel; + push_bel(bel); + int wid = int_or_default(cell->params, id_WID, 0); + std::string mode = str_or_default(cell->params, id_MODE, ""); + + write_bit(stringf("MODE.%s_MODE", mode.c_str())); + write_enum(cell, "GSR", "DISABLED"); + + write_int_vector("WID[10:0]", wid, 11); + + push(stringf("%s_MODE", mode.c_str())); + + if (mode == "DP16K") { + write_int_vector_param(cell, "CSDECODE_A", 7, 3); + write_int_vector_param(cell, "CSDECODE_B", 7, 3); + write_enum(cell, "ASYNC_RST_RELEASE_A"); + write_enum(cell, "ASYNC_RST_RELEASE_B"); + write_enum(cell, "DATA_WIDTH_A"); + write_enum(cell, "DATA_WIDTH_B"); + write_enum(cell, "OUTREG_A"); + write_enum(cell, "OUTREG_B"); + write_enum(cell, "RESETMODE_A"); + write_enum(cell, "RESETMODE_B"); + } else if (mode == "PDP16K" || mode == "PDPSC16K") { + write_int_vector_param(cell, "CSDECODE_W", 7, 3); + write_int_vector_param(cell, "CSDECODE_R", 7, 3); + write_enum(cell, "ASYNC_RST_RELEASE"); + write_enum(cell, "DATA_WIDTH_W"); + write_enum(cell, "DATA_WIDTH_R"); + write_enum(cell, "OUTREG"); + write_enum(cell, "RESETMODE"); + } + + pop(); + push("DP16K_MODE"); // muxes always use the DP16K perspective + write_cell_muxes(cell); + pop(2); + blank(); + + // EBR initialisation + if (wid > 0) { + push(stringf("IP_EBR_WID%d", wid)); + for (int i = 0; i < 64; i++) { + IdString param = ctx->id(stringf("INITVAL_%02X", i)); + if (!cell->params.count(param)) + continue; + auto &prop = cell->params.at(param); + std::string value; + if (prop.is_string) { + NPNR_ASSERT(prop.str.substr(0, 2) == "0x"); + // Lattice-style hex string + value = prop.str.substr(2); + value = stringf("320'h%s", value.c_str()); + } else { + // True Verilog bitvector + value = stringf("320'b%s", prop.str.c_str()); + } + write_bit(stringf("INITVAL_%02X[319:0] = %s", i, value.c_str())); + } + pop(); + } + } // Write out FASM for unused bels where needed void write_unused() { @@ -461,6 +537,8 @@ struct NexusFasmWriter write_io18(ci); else if (ci->type == id_OSC_CORE) write_osc(ci); + else if (ci->type == id_OXIDE_EBR) + write_bram(ci); blank(); } // Write config for unused bels diff --git a/nexus/pack.cc b/nexus/pack.cc index fa2c78e8..2cef0687 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1047,6 +1047,16 @@ struct NexusPacker log_info("Packing BRAM...\n"); generic_xform(bram_rules, true); + + int wid = 2; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type != id_OXIDE_EBR) + continue; + if (ci->params.count(id_WID)) + continue; + ci->params[id_WID] = wid++; + } } explicit NexusPacker(Context *ctx) : ctx(ctx) {} -- cgit v1.2.3 From e8e6316f887afa58c3d1476592ba0831353379a0 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 15:11:12 +0100 Subject: nexus: EBR fixes Signed-off-by: David Shah --- nexus/arch.h | 1 + nexus/constids.inc | 2 ++ nexus/fasm.cc | 9 +++++---- nexus/pack.cc | 2 ++ nexus/pins.cc | 15 +++++++-------- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 60b4b166..5924732b 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -796,6 +796,7 @@ enum CellPinStyle PINSTYLE_PU = 0x4022, // signals that float high and default high PINSTYLE_T = 0x4027, // PIO 'T' signal + PINSTYLE_ADLSB = 0x4017, // special case of the EBR address MSBs PINSTYLE_INV_PD = 0x0017, // invertible, pull down by default PINSTYLE_INV_PU = 0x4027, // invertible, pull up by default diff --git a/nexus/constids.inc b/nexus/constids.inc index d571e9bb..0c01b577 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -203,3 +203,5 @@ X(CLKR) X(CEW) X(CER) X(RST) + +X(WEAMUX) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 60ff1aca..fdab3bca 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -388,6 +388,7 @@ struct NexusFasmWriter std::string mode = str_or_default(cell->params, id_MODE, ""); write_bit(stringf("MODE.%s_MODE", mode.c_str())); + write_enum(cell, "INIT_DATA", "STATIC"); write_enum(cell, "GSR", "DISABLED"); write_int_vector("WID[10:0]", wid, 11); @@ -395,8 +396,8 @@ struct NexusFasmWriter push(stringf("%s_MODE", mode.c_str())); if (mode == "DP16K") { - write_int_vector_param(cell, "CSDECODE_A", 7, 3); - write_int_vector_param(cell, "CSDECODE_B", 7, 3); + write_int_vector_param(cell, "CSDECODE_A", 7, 3, true); + write_int_vector_param(cell, "CSDECODE_B", 7, 3, true); write_enum(cell, "ASYNC_RST_RELEASE_A"); write_enum(cell, "ASYNC_RST_RELEASE_B"); write_enum(cell, "DATA_WIDTH_A"); @@ -406,8 +407,8 @@ struct NexusFasmWriter write_enum(cell, "RESETMODE_A"); write_enum(cell, "RESETMODE_B"); } else if (mode == "PDP16K" || mode == "PDPSC16K") { - write_int_vector_param(cell, "CSDECODE_W", 7, 3); - write_int_vector_param(cell, "CSDECODE_R", 7, 3); + write_int_vector_param(cell, "CSDECODE_W", 7, 3, true); + write_int_vector_param(cell, "CSDECODE_R", 7, 3, true); write_enum(cell, "ASYNC_RST_RELEASE"); write_enum(cell, "DATA_WIDTH_W"); write_enum(cell, "DATA_WIDTH_R"); diff --git a/nexus/pack.cc b/nexus/pack.cc index 2cef0687..abe963ba 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1023,6 +1023,7 @@ struct NexusPacker // Pseudo dual port bram_rules[id_PDP16K_MODE].new_type = id_OXIDE_EBR; bram_rules[id_PDP16K_MODE].set_params.emplace_back(id_MODE, std::string("PDP16K")); + bram_rules[id_PDP16K_MODE].set_params.emplace_back(id_WEAMUX, std::string("1")); bram_rules[id_PDP16K_MODE].parse_params.emplace_back(id_CSDECODE_R, id_CSDECODE_R, 3, 7); bram_rules[id_PDP16K_MODE].parse_params.emplace_back(id_CSDECODE_W, id_CSDECODE_W, 3, 7); bram_rules[id_PDP16K_MODE].port_xform[id_CLKW] = id_CLKA; @@ -1043,6 +1044,7 @@ struct NexusPacker bram_rules[id_PDPSC16K_MODE] = bram_rules[id_PDP16K_MODE]; bram_rules[id_PDPSC16K_MODE].set_params.clear(); bram_rules[id_PDPSC16K_MODE].set_params.emplace_back(id_MODE, std::string("PDPSC16K")); + bram_rules[id_PDPSC16K_MODE].set_params.emplace_back(id_WEAMUX, std::string("1")); bram_rules[id_PDPSC16K_MODE].port_multixform[id_CLK] = {id_CLKA, id_CLKB}; log_info("Packing BRAM...\n"); diff --git a/nexus/pins.cc b/nexus/pins.cc index 1fa62b28..de92f2b6 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -64,14 +64,13 @@ static const std::unordered_map base_cell_pin_data {id_B, PINSTYLE_DEDI}, {{}, PINSTYLE_PU}, }}, - {id_OXIDE_EBR, {{id_CLKA, PINSTYLE_CLK}, {id_CLKB, PINSTYLE_CLK}, {id_CEA, PINSTYLE_CE}, - {id_CEB, PINSTYLE_CE}, {id_CSA0, PINSTYLE_PU}, {id_CSA1, PINSTYLE_PU}, - {id_CSA2, PINSTYLE_PU}, {id_CSB0, PINSTYLE_PU}, {id_CSB1, PINSTYLE_PU}, - {id_CSB2, PINSTYLE_PU}, {id_ADA0, PINSTYLE_INV_PD}, {id_ADA1, PINSTYLE_INV_PD}, - {id_ADA2, PINSTYLE_INV_PD}, {id_ADA2, PINSTYLE_INV_PD}, {id_ADA3, PINSTYLE_INV_PD}, - {id_ADB0, PINSTYLE_INV_PD}, {id_ADB1, PINSTYLE_INV_PD}, {id_WEA, PINSTYLE_INV_PD}, - {id_WEB, PINSTYLE_INV_PD}, {id_RSTA, PINSTYLE_INV_PD}, {id_RSTB, PINSTYLE_INV_PD}, - {{}, PINSTYLE_CIB}}}, + {id_OXIDE_EBR, + {{id_CLKA, PINSTYLE_CLK}, {id_CLKB, PINSTYLE_CLK}, {id_CEA, PINSTYLE_CE}, {id_CEB, PINSTYLE_CE}, + {id_CSA0, PINSTYLE_PU}, {id_CSA1, PINSTYLE_PU}, {id_CSA2, PINSTYLE_PU}, {id_CSB0, PINSTYLE_PU}, + {id_CSB1, PINSTYLE_PU}, {id_CSB2, PINSTYLE_PU}, {id_ADA0, PINSTYLE_ADLSB}, {id_ADA1, PINSTYLE_ADLSB}, + {id_ADA2, PINSTYLE_ADLSB}, {id_ADA2, PINSTYLE_ADLSB}, {id_ADA3, PINSTYLE_ADLSB}, {id_ADB0, PINSTYLE_ADLSB}, + {id_ADB1, PINSTYLE_ADLSB}, {id_WEA, PINSTYLE_INV_PD}, {id_WEB, PINSTYLE_INV_PD}, {id_RSTA, PINSTYLE_INV_PD}, + {id_RSTB, PINSTYLE_INV_PD}, {{}, PINSTYLE_CIB}}}, {id_OSC_CORE, { {id_HFOUTEN, PINSTYLE_PU}, -- cgit v1.2.3 From f749038959b589467ce0605cf60f68f18d7573cd Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 15:19:57 +0100 Subject: nexus: Improve placer config Signed-off-by: David Shah --- nexus/arch.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nexus/arch.cc b/nexus/arch.cc index b8973657..473c266f 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -483,6 +483,11 @@ bool Arch::place() cfg.ioBufTypes.insert(id_SEIO33_CORE); cfg.ioBufTypes.insert(id_SEIO18_CORE); cfg.ioBufTypes.insert(id_OSC_CORE); + cfg.cellGroups.emplace_back(); + cfg.cellGroups.back().insert(id_OXIDE_COMB); + cfg.cellGroups.back().insert(id_OXIDE_FF); + + cfg.beta = 0.7; cfg.criticalityExponent = 7; if (!placer_heap(getCtx(), cfg)) return false; -- cgit v1.2.3 From f8dca82a713d2d90d29c70357c5c276bed5d7862 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 16:02:58 +0100 Subject: nexus: Basic support for differential IO types Signed-off-by: David Shah --- nexus/arch.h | 31 +++++++++++++++++++++ nexus/fasm.cc | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- nexus/io.cc | 70 ++++++++++++++++++++++++++++++++++++++++++++++ nexus/pack.cc | 13 +++++++++ nexus/pins.cc | 6 ++++ 5 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 nexus/io.cc diff --git a/nexus/arch.h b/nexus/arch.h index 5924732b..85278272 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -813,6 +813,30 @@ enum CellPinMux PINMUX_INV = 3, }; +// This represents the various kinds of IO pins +enum IOStyle +{ + IOBANK_WR = 0x1, // needs wide range IO bank + IOBANK_HP = 0x2, // needs high perf IO bank + + IOMODE_REF = 0x10, // IO is referenced + IOMODE_DIFF = 0x20, // IO is true differential + IOMODE_PSEUDO_DIFF = 0x40, // IO is pseduo differential + + IOSTYLE_SE_WR = 0x01, // single ended, wide range + IOSTYLE_SE_HP = 0x02, // single ended, high perf + IOSTYLE_PD_WR = 0x41, // pseudo diff, wide range + + IOSTYLE_REF_HP = 0x12, // referenced high perf + IOSTYLE_DIFF_HP = 0x22, // differential high perf +}; + +struct IOTypeData +{ + IOStyle style; + int vcco; // required Vcco in 10mV +}; + // ----------------------------------------------------------------------- const int bba_version = @@ -1455,6 +1479,13 @@ struct Arch : BaseCtx const PadInfoPOD *get_bel_pad(BelId bel) const; std::string get_pad_functions(const PadInfoPOD *pad) const; + // ------------------------------------------------- + // Data about different IO standard, mostly used by bitgen + static const std::unordered_map io_types; + int get_io_type_vcc(const std::string &io_type) const; + bool is_io_type_diff(const std::string &io_type) const; + bool is_io_type_ref(const std::string &io_type) const; + // ------------------------------------------------- // List of IO constraints, used by PDC parser diff --git a/nexus/fasm.cc b/nexus/fasm.cc index fdab3bca..bd710ae4 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -125,6 +125,18 @@ struct NexusFasmWriter write_bit(stringf("%s.%s", name.c_str(), fnd->second.c_str())); } } + void write_ioattr_postfix(const CellInfo *cell, const std::string &name, const std::string &postfix, + const std::string &defval = "") + { + auto fnd = cell->attrs.find(ctx->id(name)); + if (fnd == cell->attrs.end()) { + if (!defval.empty()) + write_bit(stringf("%s_%s.%s", name.c_str(), postfix.c_str(), defval.c_str())); + } else { + write_bit(stringf("%s_%s.%s", name.c_str(), postfix.c_str(), fnd->second.c_str())); + } + } + // Gets the full name of a tile std::string tile_name(int loc, const PhysicalTileInfoPOD &tile) { @@ -321,6 +333,16 @@ struct NexusFasmWriter std::unordered_set used_io; + struct BankConfig + { + bool diff_used = false; + bool lvds_used = false; + bool slvs_used = false; + bool dphy_used = false; + }; + + std::map bank_cfg; + // Write config for an SEIO33_CORE cell void write_io33(const CellInfo *cell) { @@ -359,6 +381,54 @@ struct NexusFasmWriter const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str())); write_ioattr(cell, "PULLMODE", "NONE"); + pop(); + write_cell_muxes(cell); + pop(); + } + // Write config for an SEIO18_CORE cell + void write_diffio18(const CellInfo *cell) + { + BelId bel = cell->bel; + + Loc bel_loc = ctx->getBelLocation(bel); + for (int i = 0; i < 2; i++) { + // Mark both A and B pins as used + used_io.insert(ctx->getBelByLocation(Loc(bel_loc.x, bel_loc.y, i))); + } + push_belgroup(bel); + push("PIOA"); + push("DIFFIO18"); + + auto &bank = bank_cfg[ctx->get_bel_pad(ctx->getBelByLocation(Loc(bel_loc.x, bel_loc.y, 0)))->bank]; + + bank.diff_used = true; + + const NetInfo *t = get_net_or_empty(cell, id_T); + auto tmux = ctx->get_cell_pinmux(cell, id_T); + bool is_input = false, is_output = false; + if (tmux == PINMUX_0) { + is_output = true; + } else if (tmux == PINMUX_1 || t == nullptr) { + is_input = true; + } + + const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); + std::string type = str_or_default(cell->attrs, id_IO_TYPE, "LVDS"); + write_bit(stringf("BASE_TYPE.%s_%s", iodir, type.c_str())); + if (type == "LVDS") { + write_ioattr_postfix(cell, "DIFFDRIVE", "LVDS", "3P5"); + bank.lvds_used = true; + } else if (type == "SLVS") { + write_ioattr_postfix(cell, "DIFFDRIVE", "SLVS", "2P0"); + bank.slvs_used = true; + } else if (type == "MIPI_DPHY") { + write_ioattr_postfix(cell, "DIFFDRIVE", "MIPI_DPHY", "2P0"); + bank.dphy_used = true; + } + + write_ioattr(cell, "PULLMODE", "FAILSAFE"); + write_ioattr(cell, "DIFFRESISTOR"); + pop(); write_cell_muxes(cell); pop(2); } @@ -505,9 +575,20 @@ struct NexusFasmWriter void write_bankcfg() { for (int i = 0; i < 8; i++) { - if (i >= 3 && i <= 5) - continue; // 1.8V banks, skip for now - write_bit(stringf("GLOBAL.BANK%d.VCC.3V3", i)); + if (i >= 3 && i <= 5) { + // 1.8V banks + push(stringf("GLOBAL.BANK%d", i)); + auto &bank = bank_cfg[i]; + write_bit("DIFF_IO.ON", bank.diff_used); + write_bit("LVDS_IO.ON", bank.lvds_used); + write_bit("SLVS_IO.ON", bank.slvs_used); + write_bit("MIPI_DPHY_IO.ON", bank.dphy_used); + + pop(); + } else { + // 3.3V banks, this should eventually be set based on the bank config + write_bit(stringf("GLOBAL.BANK%d.VCC.3V3", i)); + } } blank(); } @@ -536,6 +617,8 @@ struct NexusFasmWriter write_io33(ci); else if (ci->type == id_SEIO18_CORE) write_io18(ci); + else if (ci->type == id_DIFFIO18_CORE) + write_diffio18(ci); else if (ci->type == id_OSC_CORE) write_osc(ci); else if (ci->type == id_OXIDE_EBR) diff --git a/nexus/io.cc b/nexus/io.cc new file mode 100644 index 00000000..fd5e584f --- /dev/null +++ b/nexus/io.cc @@ -0,0 +1,70 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 "log.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +const std::unordered_map Arch::io_types = { + {"LVCMOS33", {IOSTYLE_SE_WR, 330}}, {"LVCMOS25", {IOSTYLE_SE_WR, 250}}, + {"LVCMOS18", {IOSTYLE_SE_WR, 180}}, {"LVCMOS15", {IOSTYLE_SE_WR, 150}}, + {"LVCMOS12", {IOSTYLE_SE_WR, 120}}, {"LVCMOS10", {IOSTYLE_SE_WR, 120}}, + + {"LVCMOS33D", {IOSTYLE_PD_WR, 330}}, {"LVCMOS25D", {IOSTYLE_PD_WR, 250}}, + + {"LVCMOS18H", {IOSTYLE_SE_HP, 180}}, {"LVCMOS15H", {IOSTYLE_SE_HP, 150}}, + {"LVCMOS12H", {IOSTYLE_SE_HP, 120}}, {"LVCMOS10R", {IOSTYLE_SE_HP, 120}}, + {"LVCMOS10H", {IOSTYLE_SE_HP, 100}}, + + {"HSTL15_I", {IOSTYLE_REF_HP, 150}}, {"SSTL15_I", {IOSTYLE_REF_HP, 150}}, + {"SSTL15_II", {IOSTYLE_REF_HP, 150}}, {"SSTL135_I", {IOSTYLE_REF_HP, 135}}, + {"SSTL135_II", {IOSTYLE_REF_HP, 135}}, {"HSUL12", {IOSTYLE_REF_HP, 120}}, + + {"LVDS", {IOSTYLE_DIFF_HP, 180}}, {"SLVS", {IOSTYLE_DIFF_HP, 120}}, + {"MIPI_DPHY", {IOSTYLE_DIFF_HP, 120}}, {"HSUL12D", {IOSTYLE_DIFF_HP, 120}}, + + {"HSTL15D_I", {IOSTYLE_DIFF_HP, 150}}, {"SSTL15D_I", {IOSTYLE_DIFF_HP, 150}}, + {"SSTL15D_II", {IOSTYLE_DIFF_HP, 150}}, {"SSTL135D_I", {IOSTYLE_DIFF_HP, 135}}, + {"SSTL135D_II", {IOSTYLE_DIFF_HP, 135}}, {"HSUL12D", {IOSTYLE_DIFF_HP, 120}}, +}; + +int Arch::get_io_type_vcc(const std::string &io_type) const +{ + if (!io_types.count(io_type)) + log_error("IO type '%s' not supported.\n", io_type.c_str()); + return io_types.at(io_type).vcco; +} + +bool Arch::is_io_type_diff(const std::string &io_type) const +{ + if (!io_types.count(io_type)) + log_error("IO type '%s' not supported.\n", io_type.c_str()); + return io_types.at(io_type).style & IOMODE_DIFF; +} + +bool Arch::is_io_type_ref(const std::string &io_type) const +{ + if (!io_types.count(io_type)) + log_error("IO type '%s' not supported.\n", io_type.c_str()); + return io_types.at(io_type).style & IOMODE_REF; +} + +NEXTPNR_NAMESPACE_END diff --git a/nexus/pack.cc b/nexus/pack.cc index abe963ba..e12ce305 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -594,6 +594,19 @@ struct NexusPacker // Get IO type for reporting purposes std::string io_type = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33"); + if (ctx->is_io_type_diff(io_type)) { + // Convert from SEIO18 to DIFFIO18 + if (ctx->getBelType(bel) != id_SEIO18_CORE) + log_error("IO '%s' uses differential type '%s' but is placed on wide range pin '%s'\n", + ctx->nameOf(ci), io_type.c_str(), loc.c_str()); + Loc bel_loc = ctx->getBelLocation(bel); + if (bel_loc.z != 0) + log_error("IO '%s' uses differential type '%s' but is placed on 'B' side pin '%s'\n", + ctx->nameOf(ci), io_type.c_str(), loc.c_str()); + bel_loc.z = 2; + bel = ctx->getBelByLocation(bel_loc); + } + log_info("Constraining %s IO '%s' to pin %s (%s%sbel %s)\n", io_type.c_str(), ctx->nameOf(ci), loc.c_str(), func.c_str(), func.empty() ? "" : "; ", ctx->nameOfBel(bel)); ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); diff --git a/nexus/pins.cc b/nexus/pins.cc index de92f2b6..f2946657 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -58,6 +58,12 @@ static const std::unordered_map base_cell_pin_data {id_B, PINSTYLE_DEDI}, {{}, PINSTYLE_PU}, }}, + {id_DIFFIO18_CORE, + { + {id_T, PINSTYLE_T}, + {id_B, PINSTYLE_DEDI}, + {{}, PINSTYLE_PU}, + }}, {id_SEIO33_CORE, { {id_T, PINSTYLE_T}, -- cgit v1.2.3 From 5e90086d4f02910a973b743a42d8ae1853599ac1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 16:43:10 +0100 Subject: router2: Fix case where src and dst are the same Signed-off-by: David Shah --- common/router2.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/router2.cc b/common/router2.cc index 4dfd868b..15c97e52 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -774,8 +774,11 @@ struct Router2 if (dst == WireId() || ctx->getBoundWireNet(dst) == net) return true; // Skip routes where there is no routing (special cases) - if (!ad.routed) + if (!ad.routed) { + if ((src == dst) && ctx->getBoundWireNet(dst) != net) + ctx->bindWire(src, net, STRENGTH_WEAK); return true; + } WireId cursor = dst; -- cgit v1.2.3 From 00ff7c6cfe1a39bc1548776ae306ce065d48917b Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 16:44:22 +0100 Subject: nexus: Default to router2 for now Signed-off-by: David Shah --- nexus/arch.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 473c266f..7b4e2041 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -665,7 +665,7 @@ const std::vector Arch::availablePlacers = {"sa", }; -const std::string Arch::defaultRouter = "router1"; +const std::string Arch::defaultRouter = "router2"; const std::vector Arch::availableRouters = {"router1", "router2"}; NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From c89d830e1689116955c9257dd2933ff49eceeaba Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 16:54:49 +0100 Subject: nexus: Add WIDEFN9 support Signed-off-by: David Shah --- nexus/pack.cc | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/nexus/pack.cc b/nexus/pack.cc index e12ce305..ea3b4aca 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1074,6 +1074,52 @@ struct NexusPacker } } + void pack_widefn() + { + std::vector widefns; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type != id_WIDEFN9) + continue; + widefns.push_back(ci); + } + + for (CellInfo *ci : widefns) { + std::vector combs; + for (int i = 0; i < 2; i++) + combs.push_back( + ctx->createCell(ctx->id(stringf("%s$widefn_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB)); + + for (int i = 0; i < 2; i++) { + replace_port(ci, bus_flat("A", i), combs[i], id_A); + replace_port(ci, bus_flat("B", i), combs[i], id_B); + replace_port(ci, bus_flat("C", i), combs[i], id_C); + replace_port(ci, bus_flat("D", i), combs[i], id_D); + } + + replace_port(ci, id_SEL, combs[0], id_SEL); + replace_port(ci, id_Z, combs[0], id_OFX); + + NetInfo *f1 = ctx->createNet(ctx->id(stringf("%s$widefn_f1$", ctx->nameOf(ci)))); + combs[0]->addInput(id_F1); + combs[1]->addOutput(id_F); + connect_port(ctx, f1, combs[1], id_F); + connect_port(ctx, f1, combs[0], id_F1); + + combs[0]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT0, 16, 0); + combs[1]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT1, 16, 0); + + combs[1]->constr_parent = combs[0]; + combs[1]->constr_x = 0; + combs[1]->constr_y = 0; + combs[1]->constr_z = 1; + combs[1]->constr_abs_z = false; + combs[0]->constr_children.push_back(combs[1]); + + ctx->cells.erase(ci->name); + } + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() @@ -1082,6 +1128,7 @@ struct NexusPacker convert_prims(); pack_bram(); pack_lutram(); + pack_widefn(); pack_ffs(); pack_constants(); pack_luts(); -- cgit v1.2.3 From e6c28877735444c3bf7927a771cafd9f35bdac72 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 19:25:17 +0100 Subject: nexus: Basic support for carries Signed-off-by: David Shah --- nexus/arch_place.cc | 2 ++ nexus/constids.inc | 2 ++ nexus/fasm.cc | 6 ++-- nexus/pack.cc | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 3 deletions(-) diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc index feec75ad..35b14caf 100644 --- a/nexus/arch_place.cc +++ b/nexus/arch_place.cc @@ -59,6 +59,8 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const // If LUT1 is carry then LUT0 must be carry too if (lut1->lutInfo.is_carry && (lut0 == nullptr || !lut0->lutInfo.is_carry)) return false; + if (!lut1->lutInfo.is_carry && lut0 != nullptr && lut0->lutInfo.is_carry) + return false; } // Check for correct use of FF1 DI if (ff1 != nullptr && ff1->ffInfo.di != nullptr && (lut1 == nullptr || ff1->ffInfo.di != lut1->lutInfo.f)) diff --git a/nexus/constids.inc b/nexus/constids.inc index 0c01b577..8b1d2c89 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -205,3 +205,5 @@ X(CER) X(RST) X(WEAMUX) + +X(VCC_DRV) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index bd710ae4..72efa5da 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -22,6 +22,8 @@ #include "nextpnr.h" #include "util.h" +#include + NEXTPNR_NAMESPACE_BEGIN namespace { struct NexusFasmWriter @@ -291,12 +293,10 @@ struct NexusFasmWriter push(stringf("SLICE%c", slice)); if (cell->params.count(id_INIT)) write_int_vector(stringf("K%d.INIT[15:0]", k), int_or_default(cell->params, id_INIT, 0), 16); -#if 0 if (cell->lutInfo.is_carry) { write_bit("MODE.CCU2"); - write_enum(cell, "INJECT", "NO"); + write_enum(cell, "CCU2.INJECT", "NO"); } -#endif pop(2); } // Write config for an OXIDE_FF cell diff --git a/nexus/pack.cc b/nexus/pack.cc index ea3b4aca..8ba986b5 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -448,6 +448,14 @@ struct NexusPacker // Pin is tied to a constant // If there is a hard constant option; use it if ((pin_style & int(req_mux)) == req_mux) { + + if (cell->type == id_OXIDE_COMB) { + // Due to potentially overlapping routing, explicitly keep the one-driver + // until can correctly use the dedicated Vcc route + if (str_or_default(cell->params, id_MODE, "LOGIC") != "LOGIC") + continue; + } + disconnect_port(ctx, cell, port_name); ctx->set_cell_pinmux(cell, port_name, req_mux); } else if (port.second.net == nullptr) { @@ -1120,6 +1128,93 @@ struct NexusPacker } } + void pack_carries() + { + // Find root carry cells + log_info("Packing carries...\n"); + std::vector roots; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type != id_CCU2) + continue; + if (get_net_or_empty(ci, id_CIN) != nullptr) + continue; + roots.push_back(ci); + } + for (CellInfo *root : roots) { + CellInfo *ci = root; + CellInfo *constr_base = nullptr; + int idx = 0; + do { + if (ci->type != id_CCU2) + log_error("Found non-carry cell '%s' in carry chain!\n", ctx->nameOf(ci)); + // Split the carry into two COMB cells + std::vector combs; + for (int i = 0; i < 2; i++) + combs.push_back( + ctx->createCell(ctx->id(stringf("%s$ccu2_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB)); + // Rewire LUT ports + for (int i = 0; i < 2; i++) { + combs[i]->params[id_MODE] = std::string("CCU2"); + replace_port(ci, bus_flat("A", i), combs[i], id_A); + replace_port(ci, bus_flat("B", i), combs[i], id_B); + replace_port(ci, bus_flat("C", i), combs[i], id_C); + replace_port(ci, bus_flat("D", i), combs[i], id_D); + replace_port(ci, bus_flat("S", i), combs[i], id_F); + } + + // External carry chain + replace_port(ci, id_CIN, combs[0], id_FCI); + replace_port(ci, id_COUT, combs[1], id_FCO); + + // Copy parameters + if (ci->params.count(id_INJECT)) + combs[0]->params[id_INJECT] = ci->params[id_INJECT]; + combs[0]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT0, 16, 0); + combs[1]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT1, 16, 0); + + // Internal carry net between the two split COMB cells + NetInfo *int_cy = ctx->createNet(ctx->id(stringf("%s$widefn_int_cy$", ctx->nameOf(ci)))); + combs[0]->addOutput(id_FCO); + combs[1]->addInput(id_FCI); + connect_port(ctx, int_cy, combs[0], id_FCO); + connect_port(ctx, int_cy, combs[1], id_FCI); + + // Relative constraints + for (int i = 0; i < 2; i++) { + int z = (idx % 8); + combs[i]->constr_z = ((z / 2) << 3) | (z % 2); + combs[i]->constr_abs_z = true; + if (constr_base == nullptr) { + // This is the very first cell in the chain + constr_base = combs[i]; + } else { + combs[i]->constr_x = (idx / 8); + combs[i]->constr_y = 0; + combs[i]->constr_parent = constr_base; + constr_base->constr_children.push_back(combs[i]); + } + + ++idx; + } + + ctx->cells.erase(ci->name); + + // Find next cell in chain, if it exists + NetInfo *fco = get_net_or_empty(combs[1], id_FCO); + ci = nullptr; + if (fco != nullptr) { + if (fco->users.size() > 1) + log_error("Carry cell '%s' has multiple fanout on FCO\n", ctx->nameOf(combs[1])); + else if (fco->users.size() == 1) { + NPNR_ASSERT(fco->users.at(0).port == id_CIN); + ci = fco->users.at(0).cell; + } + } + } while (ci != nullptr); + } + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() @@ -1128,6 +1223,7 @@ struct NexusPacker convert_prims(); pack_bram(); pack_lutram(); + pack_carries(); pack_widefn(); pack_ffs(); pack_constants(); -- cgit v1.2.3 From 0a59cbb8ce59984b40bfaaa777a406e439d5fbdf Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 20:05:04 +0100 Subject: nexus: Use dedicated Vcc routing for OXIDE_COMB pins Signed-off-by: David Shah --- nexus/arch.h | 2 ++ nexus/bba_version.inc | 2 +- nexus/pack.cc | 26 +++++++++++++++----------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 85278272..c6064ea2 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -121,6 +121,7 @@ enum RelLocType : uint8_t REL_LOC_BRANCH_R = 4, REL_LOC_SPINE = 5, REL_LOC_HROW = 6, + REL_LOC_VCC = 7, }; enum ArcFlags @@ -421,6 +422,7 @@ inline bool chip_rel_loc_tile(const ChipInfoPOD *chip, int32_t base, const RelWi return true; } case REL_LOC_GLOBAL: + case REL_LOC_VCC: next = 0; return true; default: diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc index 1e8b3149..7f8f011e 100644 --- a/nexus/bba_version.inc +++ b/nexus/bba_version.inc @@ -1 +1 @@ -6 +7 diff --git a/nexus/pack.cc b/nexus/pack.cc index 8ba986b5..c085c5ee 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -292,6 +292,8 @@ struct NexusPacker continue; if (cell->ports.count(pin)) continue; + if (cell->type == id_OXIDE_COMB && pin == id_SEL) + continue; // doesn't always exist and not needed cell->ports[pin].name = pin; cell->ports[pin].type = dir; } @@ -313,7 +315,7 @@ struct NexusPacker NetInfo *new_net = ctx->createNet(ctx->id(stringf("$CONST_%s_NET_", type.c_str(ctx)))); CellInfo *new_cell = ctx->createCell(ctx->id(stringf("$CONST_%s_DRV_", type.c_str(ctx))), type); - new_cell->addInput(id_Z); + new_cell->addOutput(id_Z); connect_port(ctx, new_net, new_cell, id_Z); return new_net; } @@ -369,7 +371,7 @@ struct NexusPacker std::vector trim_nets; for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; - if (ci->type != id_INV && ci->type != id_VLO && ci->type != id_VHI) + if (ci->type != id_INV && ci->type != id_VLO && ci->type != id_VHI && ci->type != id_VCC_DRV) continue; NetInfo *z = get_net_or_empty(ci, id_Z); if (z == nullptr) { @@ -417,7 +419,7 @@ struct NexusPacker } } - NetInfo *gnd_net = nullptr, *vcc_net = nullptr; + NetInfo *gnd_net = nullptr, *vcc_net = nullptr, *dedi_vcc_net = nullptr; void process_inv_constants(CellInfo *cell) { @@ -449,11 +451,11 @@ struct NexusPacker // If there is a hard constant option; use it if ((pin_style & int(req_mux)) == req_mux) { - if (cell->type == id_OXIDE_COMB) { - // Due to potentially overlapping routing, explicitly keep the one-driver - // until can correctly use the dedicated Vcc route - if (str_or_default(cell->params, id_MODE, "LOGIC") != "LOGIC") - continue; + if ((cell->type == id_OXIDE_COMB) && (req_mux == PINMUX_1)) { + // We need to add a connection to the dedicated Vcc resource that can feed these cell ports + disconnect_port(ctx, cell, port_name); + connect_port(ctx, dedi_vcc_net, cell, port_name); + continue; } disconnect_port(ctx, cell, port_name); @@ -654,13 +656,15 @@ struct NexusPacker void pack_constants() { // Make sure we have high and low nets available - get_const_net(id_VHI); - get_const_net(id_VLO); + vcc_net = get_const_net(id_VHI); + gnd_net = get_const_net(id_VLO); + dedi_vcc_net = get_const_net(id_VCC_DRV); // Iterate through cells for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; // Skip certain cells at this point - if (ci->type != id_LUT4 && ci->type != id_INV && ci->type != id_VHI && ci->type != id_VLO) + if (ci->type != id_LUT4 && ci->type != id_INV && ci->type != id_VHI && ci->type != id_VLO && + ci->type != id_VCC_DRV) process_inv_constants(cell.second); } // Remove superfluous inverters and constant drivers -- cgit v1.2.3 From 3d4165616812acabd9b92c95d82f0c59aa9b698d Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 20:19:02 +0100 Subject: nexus: Default EBR DWS pins to 1 Signed-off-by: David Shah --- nexus/constids.inc | 6 ++++++ nexus/pins.cc | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/nexus/constids.inc b/nexus/constids.inc index 8b1d2c89..2ac2349c 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -204,6 +204,12 @@ X(CEW) X(CER) X(RST) +X(DWS0) +X(DWS1) +X(DWS2) +X(DWS3) +X(DWS4) + X(WEAMUX) X(VCC_DRV) diff --git a/nexus/pins.cc b/nexus/pins.cc index f2946657..e7be26f2 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -76,7 +76,8 @@ static const std::unordered_map base_cell_pin_data {id_CSB1, PINSTYLE_PU}, {id_CSB2, PINSTYLE_PU}, {id_ADA0, PINSTYLE_ADLSB}, {id_ADA1, PINSTYLE_ADLSB}, {id_ADA2, PINSTYLE_ADLSB}, {id_ADA2, PINSTYLE_ADLSB}, {id_ADA3, PINSTYLE_ADLSB}, {id_ADB0, PINSTYLE_ADLSB}, {id_ADB1, PINSTYLE_ADLSB}, {id_WEA, PINSTYLE_INV_PD}, {id_WEB, PINSTYLE_INV_PD}, {id_RSTA, PINSTYLE_INV_PD}, - {id_RSTB, PINSTYLE_INV_PD}, {{}, PINSTYLE_CIB}}}, + {id_RSTB, PINSTYLE_INV_PD}, {{id_DWS0}, PINSTYLE_PU}, {{id_DWS1}, PINSTYLE_PU}, {{id_DWS2}, PINSTYLE_PU}, + {{id_DWS3}, PINSTYLE_PU}, {{id_DWS4}, PINSTYLE_PU}, {{}, PINSTYLE_CIB}}}, {id_OSC_CORE, { {id_HFOUTEN, PINSTYLE_PU}, -- cgit v1.2.3 From f41b4045d47ee2632469398f86e2fb71eeb1d01f Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 5 Nov 2020 11:14:29 +0000 Subject: nexus: Add missing Q_MOC_RUN guard Signed-off-by: David Shah --- nexus/archdefs.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nexus/archdefs.h b/nexus/archdefs.h index 903386bf..cdf15825 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -46,7 +46,9 @@ struct DelayInfo return ret; } }; +// https://bugreports.qt.io/browse/QTBUG-80789 +#ifndef Q_MOC_RUN enum ConstIds { ID_NONE @@ -58,6 +60,7 @@ enum ConstIds #define X(t) static constexpr auto id_##t = IdString(ID_##t); #include "constids.inc" #undef X +#endif struct BelId { -- cgit v1.2.3 From 629a06b0ae20f93448881c2e534703e067f997c3 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 5 Nov 2020 11:44:34 +0000 Subject: nexus: Add error if device not specified Signed-off-by: David Shah --- nexus/main.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nexus/main.cc b/nexus/main.cc index 5b0ba94c..495793a3 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -67,6 +67,9 @@ void NexusCommandHandler::customBitstream(Context *ctx) std::unique_ptr NexusCommandHandler::createContext(std::unordered_map &values) { ArchArgs chipArgs; + if (!vm.count("device")) { + log_error("device must be specified on the command line (e.g. --device LIFCL-40-9BG400CES)\n"); + } chipArgs.device = vm["device"].as(); return std::unique_ptr(new Context(chipArgs)); } -- cgit v1.2.3 From 4e5ad7feac5b41876e3b09162cea8815fe0821bf Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 9 Nov 2020 11:43:54 +0000 Subject: nexus: Add timing structures to BBA Signed-off-by: David Shah --- nexus/arch.h | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- nexus/bba_version.inc | 2 +- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index c6064ea2..88660ca1 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -108,7 +108,7 @@ enum PipFlags NPNR_PACKED_STRUCT(struct PipInfoPOD { uint16_t from_wire, to_wire; uint16_t flags; - uint16_t padding; + uint16_t timing_class; int32_t tile_type; }); @@ -273,13 +273,61 @@ NPNR_PACKED_STRUCT(struct IdStringDBPOD { RelPtr> bba_id_strs; }); +// Timing structures are generally sorted using IdString indices as keys for fast binary searches +// All delays are integer picoseconds + +// Sort key: (from_port, to_port) for binary search by IdString +NPNR_PACKED_STRUCT(struct CellPropDelayPOD { + int32_t from_port; + int32_t to_port; + int32_t min_delay; + int32_t max_delay; +}); + +// Sort key: (sig_port, clock_port) for binary search by IdString +NPNR_PACKED_STRUCT(struct CellSetupHoldPOD { + int32_t sig_port; + int32_t clock_port; + int32_t min_setup; + int32_t max_setup; + int32_t min_hold; + int32_t max_hold; +}); + +// Sort key: (cell_type, cell_variant) for binary search by IdString +NPNR_PACKED_STRUCT(struct CellTimingPOD { + int32_t cell_type; + int32_t cell_variant; + int32_t num_prop_delays; + int32_t num_setup_holds; + RelPtr prop_delays; + RelPtr setup_holds; +}); + +NPNR_PACKED_STRUCT(struct PipTimingPOD { + int32_t min_delay; + int32_t max_delay; + int32_t min_fanout_adder; + int32_t max_fanout_adder; +}); + +NPNR_PACKED_STRUCT(struct SpeedGradePOD { + RelPtr name; + int32_t num_cell_types; + int32_t num_pip_classes; + RelPtr cell_types; + RelPtr pip_classes; +}); + NPNR_PACKED_STRUCT(struct DatabasePOD { uint32_t version; uint32_t num_chips; uint32_t num_loctypes; + uint32_t num_speed_grades; RelPtr family; RelPtr chips; RelPtr loctypes; + RelPtr speed_grades; RelPtr ids; }); diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc index 7f8f011e..45a4fb75 100644 --- a/nexus/bba_version.inc +++ b/nexus/bba_version.inc @@ -1 +1 @@ -7 +8 -- cgit v1.2.3 From 963fd175ad69c5468bfc486e789cf6c7ff85f57e Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 9 Nov 2020 14:53:11 +0000 Subject: nexus: Lookup speed grade and pip delays Signed-off-by: David Shah --- nexus/arch.cc | 21 +++++++++++++++++++++ nexus/arch.h | 11 ++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 7b4e2041..aff00d5d 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -137,6 +137,27 @@ Arch::Arch(ArchArgs args) : args(args) } log_error("Unknown package '%s'. Available package options:%s\n", package.c_str(), all_packages.c_str()); } + + // Validate and set up speed grade + + // Convert speed to speed grade (TODO: low power back bias mode too) + if (speed == "7") + speed = "10"; + else if (speed == "8") + speed = "11"; + else if (speed == "9") + speed = "12"; + + speed_grade = nullptr; + for (size_t i = 0; i < db->num_speed_grades; i++) { + auto &sg = db->speed_grades[i]; + if (sg.name.get() == speed) { + speed_grade = &sg; + break; + } + } + if (!speed_grade) + log_error("Unknown speed grade '%s'.\n", speed.c_str()); } // ----------------------------------------------------------------------- diff --git a/nexus/arch.h b/nexus/arch.h index 88660ca1..93272af8 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -307,6 +307,7 @@ NPNR_PACKED_STRUCT(struct CellTimingPOD { NPNR_PACKED_STRUCT(struct PipTimingPOD { int32_t min_delay; int32_t max_delay; + // fanout adder seemingly unused by nexus, reserved for future ECP5 etc support int32_t min_fanout_adder; int32_t max_fanout_adder; }); @@ -910,6 +911,7 @@ struct Arch : BaseCtx boost::iostreams::mapped_file_source blob_file; const DatabasePOD *db; const ChipInfoPOD *chip_info; + const SpeedGradePOD *speed_grade; int package_idx; @@ -1275,7 +1277,14 @@ struct Arch : BaseCtx WireId getPipDstWire(PipId pip) const { return canonical_wire(pip.tile, pip_data(pip).to_wire); } - DelayInfo getPipDelay(PipId pip) const { return getDelayFromNS(0.1 + (pip.index % 30) / 1000.0); } + DelayInfo getPipDelay(PipId pip) const + { + DelayInfo delay; + auto &cls = speed_grade->pip_classes[pip_data(pip).timing_class]; + delay.min_delay = std::max(0, cls.min_delay); + delay.max_delay = std::max(0, cls.max_delay); + return delay; + } UpDownhillPipRange getPipsDownhill(WireId wire) const { -- cgit v1.2.3 From fa9194e3e2212ef265b94b1be58da7c59d9a0bbf Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 9 Nov 2020 16:06:40 +0000 Subject: nexus: Add cell delay lookup Signed-off-by: David Shah --- nexus/arch.cc | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/arch.h | 6 +++++ nexus/archdefs.h | 2 ++ nexus/pack.cc | 2 ++ 4 files changed, 91 insertions(+) diff --git a/nexus/arch.cc b/nexus/arch.cc index aff00d5d..a25fc95e 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -435,11 +435,27 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { + if (cell->type == id_OXIDE_COMB) { + if (toPort == id_F) + return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); + } return false; } TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const { + auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; }; + if (cell->type == id_OXIDE_COMB) { + if (port == id_A || port == id_B || port == id_C || port == id_D) + return TMG_COMB_INPUT; + if (port == id_F) { + if (disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) && + disconnected(id_FCI)) + return TMG_IGNORE; + else + return TMG_COMB_OUTPUT; + } + } return TMG_IGNORE; } @@ -673,6 +689,71 @@ std::string Arch::get_pad_functions(const PadInfoPOD *pad) const // ----------------------------------------------------------------------- +// Helper for cell timing lookups +namespace { +template +int db_binary_search(const Tres *list, int count, Tgetter key_getter, Tkey key) +{ + if (count < 7) { + for (int i = 0; i < count; i++) { + if (key_getter(list[i]) == key) { + return i; + } + } + } else { + int b = 0, e = count - 1; + while (b <= e) { + int i = (b + e) / 2; + if (key_getter(list[i]) == key) { + return i; + } + if (key_getter(list[i]) > key) + e = i - 1; + else + b = i + 1; + } + } + return -1; +} +} // namespace + +int Arch::get_cell_timing_idx(IdString cell_type, IdString cell_variant) const +{ + return db_binary_search( + speed_grade->cell_types.get(), speed_grade->num_cell_types, + [](const CellTimingPOD &ct) { return std::make_pair(ct.cell_type, ct.cell_variant); }, + std::make_pair(cell_type.index, cell_variant.index)); +} + +bool Arch::lookup_cell_delay(int type_idx, IdString from_port, IdString to_port, DelayInfo &delay) const +{ + NPNR_ASSERT(type_idx != -1); + const auto &ct = speed_grade->cell_types[type_idx]; + int dly_idx = db_binary_search( + ct.prop_delays.get(), ct.num_prop_delays, + [](const CellPropDelayPOD &pd) { return std::make_pair(pd.from_port, pd.to_port); }, + std::make_pair(from_port.index, to_port.index)); + if (dly_idx == -1) + return false; + delay.min_delay = ct.prop_delays[dly_idx].min_delay; + delay.max_delay = ct.prop_delays[dly_idx].max_delay; + return true; +} + +void Arch::lookup_cell_setuphold(int type_idx, IdString from_port, IdString clock, DelayInfo &setup, + DelayInfo &hold) const +{ + NPNR_ASSERT(type_idx != -1); + const auto &ct = speed_grade->cell_types[type_idx]; + int dly_idx = db_binary_search( + ct.setup_holds.get(), ct.num_setup_holds, + [](const CellSetupHoldPOD &sh) { return std::make_pair(sh.sig_port, sh.clock_port); }, + std::make_pair(from_port.index, clock.index)); + NPNR_ASSERT(dly_idx != -1); +} + +// ----------------------------------------------------------------------- + #ifdef WITH_HEAP const std::string Arch::defaultPlacer = "heap"; #else diff --git a/nexus/arch.h b/nexus/arch.h index 93272af8..2d68ebeb 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1546,6 +1546,12 @@ struct Arch : BaseCtx bool is_io_type_ref(const std::string &io_type) const; // ------------------------------------------------- + // Cell timing lookup helpers + int get_cell_timing_idx(IdString cell_type, IdString cell_variant = IdString()) const; + bool lookup_cell_delay(int type_idx, IdString from_port, IdString to_port, DelayInfo &delay) const; + void lookup_cell_setuphold(int type_idx, IdString from_port, IdString clock, DelayInfo &setup, + DelayInfo &hold) const; + // ------------------------------------------------- // List of IO constraints, used by PDC parser std::unordered_map> io_attr; diff --git a/nexus/archdefs.h b/nexus/archdefs.h index cdf15825..8925c5f3 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -186,6 +186,8 @@ struct ArchCellInfo NetInfo *di, *m; } ffInfo; }; + + int tmg_index = -1; }; NEXTPNR_NAMESPACE_END diff --git a/nexus/pack.cc b/nexus/pack.cc index c085c5ee..5dcfd9e2 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1257,12 +1257,14 @@ void Arch::assignArchInfo() void Arch::assignCellInfo(CellInfo *cell) { + cell->tmg_index = -1; if (cell->type == id_OXIDE_COMB) { cell->lutInfo.is_memory = str_or_default(cell->params, id_MODE, "LOGIC") == "DPRAM"; cell->lutInfo.is_carry = str_or_default(cell->params, id_MODE, "LOGIC") == "CCU2"; cell->lutInfo.mux2_used = port_used(cell, id_OFX); cell->lutInfo.f = get_net_or_empty(cell, id_F); cell->lutInfo.ofx = get_net_or_empty(cell, id_OFX); + cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_LUT4); } else if (cell->type == id_OXIDE_FF) { cell->ffInfo.ctrlset.async = str_or_default(cell->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC"; cell->ffInfo.ctrlset.regddr_en = is_enabled(cell, id_REGDDR); -- cgit v1.2.3 From 6457b4ca7b90c59a886ed623d321504131ebeeb6 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 10 Nov 2020 10:09:08 +0000 Subject: nexus: Swap sort order to make some lookups easier Signed-off-by: David Shah --- nexus/arch.cc | 4 ++-- nexus/arch.h | 2 +- nexus/bba_version.inc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index a25fc95e..78e10e16 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -731,8 +731,8 @@ bool Arch::lookup_cell_delay(int type_idx, IdString from_port, IdString to_port, const auto &ct = speed_grade->cell_types[type_idx]; int dly_idx = db_binary_search( ct.prop_delays.get(), ct.num_prop_delays, - [](const CellPropDelayPOD &pd) { return std::make_pair(pd.from_port, pd.to_port); }, - std::make_pair(from_port.index, to_port.index)); + [](const CellPropDelayPOD &pd) { return std::make_pair(pd.to_port, pd.from_port); }, + std::make_pair(to_port.index, from_port.index)); if (dly_idx == -1) return false; delay.min_delay = ct.prop_delays[dly_idx].min_delay; diff --git a/nexus/arch.h b/nexus/arch.h index 2d68ebeb..cbe39316 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -276,7 +276,7 @@ NPNR_PACKED_STRUCT(struct IdStringDBPOD { // Timing structures are generally sorted using IdString indices as keys for fast binary searches // All delays are integer picoseconds -// Sort key: (from_port, to_port) for binary search by IdString +// Sort key: (to_port, from_port) for binary search by IdString NPNR_PACKED_STRUCT(struct CellPropDelayPOD { int32_t from_port; int32_t to_port; diff --git a/nexus/bba_version.inc b/nexus/bba_version.inc index 45a4fb75..ec635144 100644 --- a/nexus/bba_version.inc +++ b/nexus/bba_version.inc @@ -1 +1 @@ -8 +9 -- cgit v1.2.3 From 8c1f25cf31644c95e4ba0a1c2a2b212abd622167 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 10 Nov 2020 11:01:30 +0000 Subject: timing: Add a few more cell types Signed-off-by: David Shah --- nexus/arch.cc | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++---- nexus/arch.h | 9 ++++++ nexus/archdefs.h | 2 ++ nexus/constids.inc | 1 + nexus/pack.cc | 10 ++++++- 5 files changed, 97 insertions(+), 7 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 78e10e16..b6181011 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -435,9 +435,23 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { + auto lookup_port = [&](IdString p) { + auto fnd = cell->tmg_portmap.find(p); + return fnd == cell->tmg_portmap.end() ? p : fnd->second; + }; if (cell->type == id_OXIDE_COMB) { - if (toPort == id_F) - return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); + if (cell->lutInfo.is_carry) { + bool result = lookup_cell_delay(cell->tmg_index, lookup_port(fromPort), lookup_port(toPort), delay); + // Because CCU2 = 2x OXIDE_COMB + if (result && fromPort == id_FCI && toPort == id_FCO) { + delay.min_delay /= 2; + delay.max_delay /= 2; + } + return result; + } else { + if (toPort == id_F) + return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); + } } return false; } @@ -446,20 +460,44 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in { auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; }; if (cell->type == id_OXIDE_COMB) { - if (port == id_A || port == id_B || port == id_C || port == id_D) + if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_FCI) return TMG_COMB_INPUT; - if (port == id_F) { + if (port == id_F || port == id_OFX || port == id_FCO) { if (disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) && - disconnected(id_FCI)) + disconnected(id_FCI) && disconnected(id_SEL)) return TMG_IGNORE; else return TMG_COMB_OUTPUT; } + } else if (cell->type == id_OXIDE_FF) { + if (port == id_CLK) + return TMG_CLOCK_INPUT; + else if (port == id_Q) { + clockInfoCount = 1; + return TMG_REGISTER_OUTPUT; + } else { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } } return TMG_IGNORE; } -TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const { return {}; } +TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const +{ + TimingClockingInfo info; + if (cell->type == id_OXIDE_FF) { + info.edge = (cell->ffInfo.ctrlset.clkmux == ID_INV) ? FALLING_EDGE : RISING_EDGE; + info.clock_port = id_CLK; + if (port == id_Q) + NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, port, info.clockToQ)); + else + lookup_cell_setuphold(cell->tmg_index, port, id_CLK, info.setup, info.hold); + } else { + NPNR_ASSERT_FALSE("missing clocking info"); + } + return info; +} // ----------------------------------------------------------------------- @@ -750,6 +788,38 @@ void Arch::lookup_cell_setuphold(int type_idx, IdString from_port, IdString cloc [](const CellSetupHoldPOD &sh) { return std::make_pair(sh.sig_port, sh.clock_port); }, std::make_pair(from_port.index, clock.index)); NPNR_ASSERT(dly_idx != -1); + setup.min_delay = ct.setup_holds[dly_idx].min_setup; + setup.max_delay = ct.setup_holds[dly_idx].max_setup; + hold.min_delay = ct.setup_holds[dly_idx].min_hold; + hold.max_delay = ct.setup_holds[dly_idx].max_hold; +} + +void Arch::lookup_cell_setuphold_clock(int type_idx, IdString from_port, IdString &clock, DelayInfo &setup, + DelayInfo &hold) const +{ + NPNR_ASSERT(type_idx != -1); + const auto &ct = speed_grade->cell_types[type_idx]; + int dly_idx = db_binary_search( + ct.setup_holds.get(), ct.num_setup_holds, [](const CellSetupHoldPOD &sh) { return sh.sig_port; }, + from_port.index); + NPNR_ASSERT(dly_idx != -1); + clock = IdString(ct.setup_holds[dly_idx].clock_port); + setup.min_delay = ct.setup_holds[dly_idx].min_setup; + setup.max_delay = ct.setup_holds[dly_idx].max_setup; + hold.min_delay = ct.setup_holds[dly_idx].min_hold; + hold.max_delay = ct.setup_holds[dly_idx].max_hold; +} +void Arch::lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock, DelayInfo &delay) const +{ + NPNR_ASSERT(type_idx != -1); + const auto &ct = speed_grade->cell_types[type_idx]; + int dly_idx = db_binary_search( + ct.prop_delays.get(), ct.num_prop_delays, [](const CellPropDelayPOD &pd) { return pd.to_port; }, + to_port.index); + NPNR_ASSERT(dly_idx != -1); + clock = ct.prop_delays[dly_idx].from_port; + delay.min_delay = ct.prop_delays[dly_idx].min_delay; + delay.max_delay = ct.prop_delays[dly_idx].max_delay; } // ----------------------------------------------------------------------- diff --git a/nexus/arch.h b/nexus/arch.h index cbe39316..9b75a09e 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1547,10 +1547,19 @@ struct Arch : BaseCtx // ------------------------------------------------- // Cell timing lookup helpers + + // Given cell type and variant, get the index inside the speed grade timing data int get_cell_timing_idx(IdString cell_type, IdString cell_variant = IdString()) const; + // Return true and set delay if a comb path exists in a given cell timing index bool lookup_cell_delay(int type_idx, IdString from_port, IdString to_port, DelayInfo &delay) const; + // Get setup and hold time for a given cell timing index and signal/clock pair void lookup_cell_setuphold(int type_idx, IdString from_port, IdString clock, DelayInfo &setup, DelayInfo &hold) const; + // Get setup and hold time and associated clock for a given cell timing index and signal + void lookup_cell_setuphold_clock(int type_idx, IdString from_port, IdString &clock, DelayInfo &setup, + DelayInfo &hold) const; + // Similar to lookup_cell_delay but only needs the 'to' signal, intended for clk->out delays + void lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock, DelayInfo &delay) const; // ------------------------------------------------- // List of IO constraints, used by PDC parser diff --git a/nexus/archdefs.h b/nexus/archdefs.h index 8925c5f3..adc1342c 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -188,6 +188,8 @@ struct ArchCellInfo }; int tmg_index = -1; + // Map from cell/bel ports to logical timing ports + std::unordered_map tmg_portmap; }; NEXTPNR_NAMESPACE_END diff --git a/nexus/constids.inc b/nexus/constids.inc index 2ac2349c..b4d53265 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -90,6 +90,7 @@ X(CIN) X(COUT) X(S0) X(S1) +X(F0) X(CLKMUX) X(CEMUX) diff --git a/nexus/pack.cc b/nexus/pack.cc index 5dcfd9e2..481dcbf6 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1264,7 +1264,14 @@ void Arch::assignCellInfo(CellInfo *cell) cell->lutInfo.mux2_used = port_used(cell, id_OFX); cell->lutInfo.f = get_net_or_empty(cell, id_F); cell->lutInfo.ofx = get_net_or_empty(cell, id_OFX); - cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_LUT4); + cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, cell->lutInfo.is_carry ? id_CCU2 : id_LUT4); + if (cell->lutInfo.is_carry) { + cell->tmg_portmap[id_A] = id_A0; + cell->tmg_portmap[id_B] = id_B0; + cell->tmg_portmap[id_C] = id_C0; + cell->tmg_portmap[id_D] = id_D0; + cell->tmg_portmap[id_F] = id_F0; + } } else if (cell->type == id_OXIDE_FF) { cell->ffInfo.ctrlset.async = str_or_default(cell->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC"; cell->ffInfo.ctrlset.regddr_en = is_enabled(cell, id_REGDDR); @@ -1277,6 +1284,7 @@ void Arch::assignCellInfo(CellInfo *cell) cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); cell->ffInfo.di = get_net_or_empty(cell, id_DI); cell->ffInfo.m = get_net_or_empty(cell, id_M); + cell->tmg_index = get_cell_timing_idx(id_OXIDE_FF, id("PPP:SYNC")); } else if (cell->type == id_RAMW) { cell->ffInfo.ctrlset.async = true; cell->ffInfo.ctrlset.regddr_en = false; -- cgit v1.2.3 From 9b89a82573500118269515154265f811cef6191c Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 11 Nov 2020 11:31:14 +0000 Subject: nexus: Add LUTRAM and WIDEFN9 timing support Signed-off-by: David Shah --- nexus/arch.cc | 25 ++++++++++++++++++++++--- nexus/constids.inc | 1 + nexus/pack.cc | 11 ++++++++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index b6181011..5cd8ab9b 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -449,7 +449,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort } return result; } else { - if (toPort == id_F) + if (toPort == id_F || toPort == id_OFX) return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); } } @@ -460,11 +460,12 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in { auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; }; if (cell->type == id_OXIDE_COMB) { - if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_FCI) + if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_SEL || port == id_F1 || + port == id_FCI || port == id_WDI) return TMG_COMB_INPUT; if (port == id_F || port == id_OFX || port == id_FCO) { if (disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) && - disconnected(id_FCI) && disconnected(id_SEL)) + disconnected(id_FCI) && disconnected(id_SEL) && disconnected(id_WDI)) return TMG_IGNORE; else return TMG_COMB_OUTPUT; @@ -479,6 +480,17 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in clockInfoCount = 1; return TMG_REGISTER_INPUT; } + } else if (cell->type == id_RAMW) { + if (port == id_CLK) + return TMG_CLOCK_INPUT; + else if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3) { + clockInfoCount = 1; + return TMG_REGISTER_OUTPUT; + } else if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 || + port == id_D0 || port == id_D1) { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } } return TMG_IGNORE; } @@ -493,6 +505,13 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, port, info.clockToQ)); else lookup_cell_setuphold(cell->tmg_index, port, id_CLK, info.setup, info.hold); + } else if (cell->type == id_RAMW) { + info.edge = (cell->ffInfo.ctrlset.clkmux == ID_INV) ? FALLING_EDGE : RISING_EDGE; + info.clock_port = id_CLK; + if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3) + NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, port, info.clockToQ)); + else + lookup_cell_setuphold(cell->tmg_index, port, id_CLK, info.setup, info.hold); } else { NPNR_ASSERT_FALSE("missing clocking info"); } diff --git a/nexus/constids.inc b/nexus/constids.inc index b4d53265..a43104e2 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -171,6 +171,7 @@ X(CLKO) X(DPR16X4) X(INITVAL) +X(DPRAM) X(DP16K) X(PDP16K) diff --git a/nexus/pack.cc b/nexus/pack.cc index 481dcbf6..bfa45d38 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -990,6 +990,8 @@ struct NexusPacker if (initval & (1ULL << (4 * j + i))) split_init |= (1 << j); combs[i]->params[id_INIT] = Property(split_init, 16); + + combs[i]->params[id_MODE] = std::string("DPRAM"); } // Setup relative constraints @@ -1264,13 +1266,19 @@ void Arch::assignCellInfo(CellInfo *cell) cell->lutInfo.mux2_used = port_used(cell, id_OFX); cell->lutInfo.f = get_net_or_empty(cell, id_F); cell->lutInfo.ofx = get_net_or_empty(cell, id_OFX); - cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, cell->lutInfo.is_carry ? id_CCU2 : id_LUT4); if (cell->lutInfo.is_carry) { cell->tmg_portmap[id_A] = id_A0; cell->tmg_portmap[id_B] = id_B0; cell->tmg_portmap[id_C] = id_C0; cell->tmg_portmap[id_D] = id_D0; cell->tmg_portmap[id_F] = id_F0; + cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_CCU2); + } else if (cell->lutInfo.ofx != nullptr) { + cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_WIDEFN9); + } else if (cell->lutInfo.is_memory) { + cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_DPRAM); + } else { + cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_LUT4); } } else if (cell->type == id_OXIDE_FF) { cell->ffInfo.ctrlset.async = str_or_default(cell->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC"; @@ -1297,6 +1305,7 @@ void Arch::assignCellInfo(CellInfo *cell) cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); cell->ffInfo.di = nullptr; cell->ffInfo.m = nullptr; + cell->tmg_index = get_cell_timing_idx(id_RAMW); } } -- cgit v1.2.3 From 530d6ce9e9528e6067e679323694fbc1617586d8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 11 Nov 2020 13:42:23 +0000 Subject: nexus: Add EBR timing analysis Signed-off-by: David Shah --- nexus/arch.cc | 19 +++++++++++++++++++ nexus/pack.cc | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/nexus/arch.cc b/nexus/arch.cc index 5cd8ab9b..f4a88dfb 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -491,12 +491,23 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in clockInfoCount = 1; return TMG_REGISTER_INPUT; } + } else if (cell->type == id_OXIDE_EBR) { + if (port == id_DWS0 || port == id_DWS1 || port == id_DWS2 || port == id_DWS3 || port == id_DWS4) + return TMG_IGNORE; + if (port == id_CLKA || port == id_CLKB) + return TMG_CLOCK_INPUT; + clockInfoCount = 1; + return (cell->ports.at(port).type == PORT_IN) ? TMG_REGISTER_INPUT : TMG_REGISTER_OUTPUT; } return TMG_IGNORE; } TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const { + auto lookup_port = [&](IdString p) { + auto fnd = cell->tmg_portmap.find(p); + return fnd == cell->tmg_portmap.end() ? p : fnd->second; + }; TimingClockingInfo info; if (cell->type == id_OXIDE_FF) { info.edge = (cell->ffInfo.ctrlset.clkmux == ID_INV) ? FALLING_EDGE : RISING_EDGE; @@ -512,6 +523,14 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, port, info.clockToQ)); else lookup_cell_setuphold(cell->tmg_index, port, id_CLK, info.setup, info.hold); + } else if (cell->type == id_OXIDE_EBR) { + if (cell->ports.at(port).type == PORT_IN) { + lookup_cell_setuphold_clock(cell->tmg_index, lookup_port(port), info.clock_port, info.setup, info.hold); + } else { + lookup_cell_clock_out(cell->tmg_index, lookup_port(port), info.clock_port, info.clockToQ); + } + // Lookup edge based on inversion + info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE; } else { NPNR_ASSERT_FALSE("missing clocking info"); } diff --git a/nexus/pack.cc b/nexus/pack.cc index bfa45d38..260dcf19 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1306,6 +1306,25 @@ void Arch::assignCellInfo(CellInfo *cell) cell->ffInfo.di = nullptr; cell->ffInfo.m = nullptr; cell->tmg_index = get_cell_timing_idx(id_RAMW); + } else if (cell->type == id_OXIDE_EBR) { + // Strip off bus indices to get the timing ports + // as timing is generally word-wide + for (const auto &port : cell->ports) { + const std::string &name = port.first.str(this); + size_t idx_end = name.find_last_not_of("0123456789"); + std::string base = name.substr(0, idx_end + 1); + if (base == "ADA" || base == "ADB") { + // [4:0] and [13:5] have different timing + int idx = std::stoi(name.substr(idx_end + 1)); + cell->tmg_portmap[port.first] = id(base + ((idx >= 5) ? "_13_5" : "_4_0")); + } else { + // Just strip off bus index + cell->tmg_portmap[port.first] = id(base); + } + } + + cell->tmg_index = get_cell_timing_idx(id(str_or_default(cell->params, id_MODE, "DP16K") + "_MODE")); + NPNR_ASSERT(cell->tmg_index != -1); } } -- cgit v1.2.3 From a69c595802bb20517ced3a2a82a6dde4b8dc6a03 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 12 Nov 2020 11:08:13 +0000 Subject: router1: Fix same-source-dest case Signed-off-by: David Shah --- common/router1.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/router1.cc b/common/router1.cc index 946327d2..d2816c1e 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -469,6 +469,20 @@ struct Router1 } } + // special case + + if (src_wire == dst_wire) { + NetInfo *bound = ctx->getBoundWireNet(src_wire); + if (bound != nullptr) + NPNR_ASSERT(bound == net_info); + else { + ctx->bindWire(src_wire, net_info, STRENGTH_WEAK); + } + arc_to_wires[arc].insert(src_wire); + wire_to_arcs[src_wire].insert(arc); + return true; + } + // reset wire queue if (!queue.empty()) { -- cgit v1.2.3 From c7ad3cece64a7e3de7789c57d91d79cc361966e8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 12 Nov 2020 11:48:21 +0000 Subject: nexus: Tweak delay heuristics Signed-off-by: David Shah --- nexus/arch.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index f4a88dfb..6cc0da0f 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -545,19 +545,21 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const int dst_x = dst.tile % chip_info->width, dst_y = dst.tile / chip_info->width; int dist_x = std::abs(src_x - dst_x); int dist_y = std::abs(src_y - dst_y); - return 100 * dist_x + 100 * dist_y; + return 100 * dist_x + 100 * dist_y + 250; } delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const { if (net_info->driver.cell == nullptr || net_info->driver.cell->bel == BelId() || sink.cell->bel == BelId()) return 0; + if (sink.port == id_FCI) + return 0; int src_x = net_info->driver.cell->bel.tile % chip_info->width, src_y = net_info->driver.cell->bel.tile / chip_info->width; int dst_x = sink.cell->bel.tile % chip_info->width, dst_y = sink.cell->bel.tile / chip_info->width; int dist_x = std::abs(src_x - dst_x); int dist_y = std::abs(src_y - dst_y); - return 100 * dist_x + 100 * dist_y; + return 100 * dist_x + 100 * dist_y + 250; } bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } -- cgit v1.2.3 From 90608f2c898c179cb95fb469633867e7adc64fc4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 13 Nov 2020 10:06:53 +0000 Subject: nexus: Add some infrastructure for DSP packing Signed-off-by: David Shah --- common/design_utils.cc | 22 ++++++++ common/design_utils.h | 6 +++ nexus/pack.cc | 140 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 163 insertions(+), 5 deletions(-) diff --git a/common/design_utils.cc b/common/design_utils.cc index 9478afb2..7f339bac 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -164,4 +164,26 @@ void rename_net(Context *ctx, NetInfo *net, IdString new_name) net->name = new_name; } +std::vector create_bus(Context *ctx, IdString base_name, const std::string &postfix, int width) +{ + std::vector nets; + for (int i = 0; i < width; i++) + nets.push_back(ctx->createNet(ctx->id(stringf("%s/%s[%d]", base_name.c_str(ctx), postfix.c_str(), i)))); + return nets; +} + +void connect_bus(Context *ctx, CellInfo *cell, IdString port, std::vector &bus, PortType dir) +{ + for (int i = 0; i < int(bus.size()); i++) { + IdString p = ctx->id(stringf("%s%d", port.c_str(ctx), i)); + if (!cell->ports.count(p)) { + cell->ports[p].name = p; + cell->ports[p].type = dir; + } else { + NPNR_ASSERT(cell->ports.at(p).type == dir); + } + connect_port(ctx, bus.at(i), cell, p); + } +} + NEXTPNR_NAMESPACE_END diff --git a/common/design_utils.h b/common/design_utils.h index 301547c6..3a2245a7 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -104,6 +104,12 @@ void rename_port(Context *ctx, CellInfo *cell, IdString old_name, IdString new_n // Rename a net without invalidating pointers to it void rename_net(Context *ctx, NetInfo *net, IdString new_name); +// Create a bus of nets +std::vector create_bus(Context *ctx, IdString base_name, const std::string &postfix, int width); + +// Connect a bus of nets to a bus of ports +void connect_bus(Context *ctx, CellInfo *cell, IdString port, std::vector &bus, PortType dir); + void print_utilisation(const Context *ctx); NEXTPNR_NAMESPACE_END diff --git a/nexus/pack.cc b/nexus/pack.cc index 260dcf19..50278d48 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -264,11 +264,11 @@ struct NexusPacker std::unordered_map reference_bels; - void autocreate_ports(CellInfo *cell) + void autocreate_ports(CellInfo *cell, bool include_outputs = false) { - // Automatically create ports for all inputs of a cell; even if they were left off the instantiation - // so we can tie them to constants as appropriate - // This also checks for any cells that don't have corresponding bels + // Automatically create ports for all inputs, and maybe outputs, of a cell; even if they were left off the + // instantiation so we can tie them to constants as appropriate This also checks for any cells that don't have + // corresponding bels if (!reference_bels.count(cell->type)) { // We need to look up a corresponding bel to get the list of input ports @@ -288,7 +288,7 @@ struct NexusPacker BelId bel = reference_bels.at(cell->type); for (IdString pin : ctx->getBelPins(bel)) { PortType dir = ctx->getBelPinType(bel, pin); - if (dir != PORT_IN) + if (dir != PORT_IN && !include_outputs) continue; if (cell->ports.count(pin)) continue; @@ -1221,6 +1221,136 @@ struct NexusPacker } } + // Function to check if a wire is general routing; and therefore skipped for cascade purposes + bool is_general_routing(WireId wire) + { + std::string name = ctx->nameOf(ctx->wire_data(wire).name); + if (name.size() == 3 && (name.substr(0, 2) == "JF" || name.substr(0, 2) == "JQ")) + return false; + if (name.size() == 12 && (name.substr(0, 10) == "JCIBMUXOUT")) + return false; + return true; + } + + // Automatically generate cascade connections downstream of a cell + // using the temporary placement that we use solely to access the routing graph + void auto_cascade_cell(CellInfo *cell, BelId bel, const std::unordered_map &bel2cell) + { + for (auto port : sorted_ref(cell->ports)) { + // Skip if not an output, or being used already for something else + if (port.second.type != PORT_OUT || port.second.net != nullptr) + continue; + // Get the corresponding start wire + WireId start_wire = ctx->getBelPinWire(bel, port.first); + + // Skip if the start wire doesn't actually exist + if (start_wire == WireId()) + continue; + + // Standard BFS-type exploration + std::queue visit; + visit.push(start_wire); + int iter = 0; + const int iter_limit = 1000; + + while (!visit.empty() && (iter++ < iter_limit)) { + WireId cursor = visit.front(); + visit.pop(); + + // Check for downstream bel pins + bool found_active_pins = false; + for (auto bp : ctx->getWireBelPins(cursor)) { + auto fnd_cell = bel2cell.find(bp.bel); + // Always skip unused bels, and don't set found_active_pins + // so we can route through these if needed + if (fnd_cell == bel2cell.end()) + continue; + + found_active_pins = true; + + // Skip outputs + if (ctx->getBelPinType(bp.bel, bp.pin) != PORT_IN) + continue; + CellInfo *other_cell = fnd_cell->second; + // Skip pins that are already in use + if (get_net_or_empty(other_cell, bp.pin) != nullptr) + continue; + // Create the input if it doesn't exist + if (!other_cell->ports.count(bp.pin)) + other_cell->addInput(bp.pin); + // Make the connection + connect_ports(ctx, cell, port.first, other_cell, bp.pin); + } + + // By doing this we never attempt to route-through bels + // that are actually in use + if (found_active_pins) + continue; + + // Search downstream pips for wires to add to the queue + for (auto pip : ctx->getPipsDownhill(cursor)) + visit.push(ctx->getPipDstWire(pip)); + } + } + } + + // Insert all the cascade connections for a group of cells given the root + void auto_cascade_group(CellInfo *root) + { + + auto get_child_loc = [&](Loc base, const CellInfo *sub) { + Loc l = base; + l.x += sub->constr_x; + l.y += sub->constr_y; + l.z = sub->constr_abs_z ? sub->constr_z : (sub->constr_z + base.z); + return l; + }; + + // We first create a temporary placement so we can access the routing graph + bool found = false; + std::unordered_map bel2cell; + std::unordered_map cell2bel; + + for (BelId root_bel : ctx->getBels()) { + if (ctx->getBelType(root_bel) != root->type) + continue; + Loc root_loc = ctx->getBelLocation(root_bel); + found = true; + bel2cell.clear(); + cell2bel.clear(); + bel2cell[root_bel] = root; + cell2bel[root->name] = root_bel; + + for (auto child : root->constr_children) { + // Check that a valid placement exists for all children in the macro at this location + Loc c_loc = get_child_loc(root_loc, child); + BelId c_bel = ctx->getBelByLocation(c_loc); + if (c_bel == BelId()) { + found = false; + break; + } + if (ctx->getBelType(c_bel) != child->type) { + found = false; + break; + } + bel2cell[c_bel] = child; + cell2bel[child->name] = c_bel; + } + + if (found) + break; + } + + if (!found) + log_error("Failed to create temporary placement for cell '%s' of type '%s'\n", ctx->nameOf(root), + ctx->nameOf(root->type)); + + // Insert cascade connections from all cells in the macro + auto_cascade_cell(root, cell2bel.at(root->name), bel2cell); + for (auto child : root->constr_children) + auto_cascade_cell(child, cell2bel.at(child->name), bel2cell); + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() -- cgit v1.2.3 From 094bf419d4b7aa62a606b8c7bdbcfc1fc63cacf7 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 13 Nov 2020 13:44:10 +0000 Subject: nexus: Miscellaneous DSP infrastructure Signed-off-by: David Shah --- common/design_utils.cc | 24 ++++------------ common/design_utils.h | 10 +++---- nexus/constids.inc | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/pack.cc | 48 +++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 24 deletions(-) diff --git a/common/design_utils.cc b/common/design_utils.cc index 7f339bac..5227585f 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -164,25 +164,13 @@ void rename_net(Context *ctx, NetInfo *net, IdString new_name) net->name = new_name; } -std::vector create_bus(Context *ctx, IdString base_name, const std::string &postfix, int width) +void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, CellInfo *new_cell, + IdString new_name, int new_offset, int width, bool square_brackets) { - std::vector nets; - for (int i = 0; i < width; i++) - nets.push_back(ctx->createNet(ctx->id(stringf("%s/%s[%d]", base_name.c_str(ctx), postfix.c_str(), i)))); - return nets; -} - -void connect_bus(Context *ctx, CellInfo *cell, IdString port, std::vector &bus, PortType dir) -{ - for (int i = 0; i < int(bus.size()); i++) { - IdString p = ctx->id(stringf("%s%d", port.c_str(ctx), i)); - if (!cell->ports.count(p)) { - cell->ports[p].name = p; - cell->ports[p].type = dir; - } else { - NPNR_ASSERT(cell->ports.at(p).type == dir); - } - connect_port(ctx, bus.at(i), cell, p); + for (int i = 0; i < width; i++) { + IdString old_port = ctx->id(stringf(square_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); + IdString new_port = ctx->id(stringf(square_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); + replace_port(old_cell, old_port, new_cell, new_port); } } diff --git a/common/design_utils.h b/common/design_utils.h index 3a2245a7..2014e7ad 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -104,14 +104,12 @@ void rename_port(Context *ctx, CellInfo *cell, IdString old_name, IdString new_n // Rename a net without invalidating pointers to it void rename_net(Context *ctx, NetInfo *net, IdString new_name); -// Create a bus of nets -std::vector create_bus(Context *ctx, IdString base_name, const std::string &postfix, int width); - -// Connect a bus of nets to a bus of ports -void connect_bus(Context *ctx, CellInfo *cell, IdString port, std::vector &bus, PortType dir); - void print_utilisation(const Context *ctx); +// Disconnect a bus of nets (if connected) from old, and connect it to the new ports +void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, CellInfo *new_cell, + IdString new_name, int new_offset, int new_width, bool square_brackets = true); + NEXTPNR_NAMESPACE_END #endif diff --git a/nexus/constids.inc b/nexus/constids.inc index a43104e2..a996f0f8 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -165,6 +165,16 @@ X(MULT18X36_CORE) X(MULT36_CORE) X(ACC54_CORE) +X(PREADD9) +X(MULT9) +X(MULT18) +X(REG18) +X(M18X36) +X(MULT36) +X(ACC54) + +X(MULT9X9) + X(DCC) X(CLKI) X(CLKO) @@ -215,3 +225,70 @@ X(DWS4) X(WEAMUX) X(VCC_DRV) + +X(RSTCL) +X(CECL) +X(B2) +X(B3) +X(B4) +X(B5) +X(B6) +X(B7) +X(B8) +X(BSIGNED) +X(C2) +X(C3) +X(C4) +X(C5) +X(C6) +X(C7) +X(C8) +X(C9) + +X(RSTP) +X(CEP) +X(A2) +X(A3) +X(A4) +X(A5) +X(A6) +X(A7) +X(A8) +X(ASIGNED) + +X(SFTCTRL0) +X(SFTCTRL1) +X(SFTCTRL2) +X(SFTCTRL3) +X(ROUNDEN) + +X(LOAD) +X(M9ADDSUB1) +X(M9ADDSUB0) +X(ADDSUB1) +X(ADDSUB0) + +X(CEO) +X(RSTO) +X(CEC) +X(RSTC) +X(SIGNEDI) +X(CECIN) +X(CECTRL) +X(RSTCIN) +X(RSTCTRL) + +X(SIGNEDSTATIC_EN) +X(SUBSTRACT_EN) +X(CSIGNED) +X(BSIGNED_OPERAND_EN) +X(BYPASS_PREADD9) +X(REGBYPSBR0) +X(REGBYPSBR1) +X(REGBYPSBL) +X(SHIFTBR) +X(SHIFTBL) +X(PREADDCAS_EN) +X(SR_18BITSHIFT_EN) +X(OPC) +X(RESET) diff --git a/nexus/pack.cc b/nexus/pack.cc index 50278d48..d39a1c89 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1351,6 +1351,54 @@ struct NexusPacker auto_cascade_cell(child, cell2bel.at(child->name), bel2cell); } + // Create a DSP cell + CellInfo *create_dsp_cell(IdString base_name, IdString type, CellInfo *constr_base, int dx, int dz) + { + IdString name = ctx->id(stringf("%s/%s_x%d_z%d", ctx->nameOf(base_name), ctx->nameOf(type), dx, dz)); + CellInfo *cell = ctx->createCell(name, type); + if (constr_base != nullptr) { + // We might be constraining against an already-constrained cell + if (constr_base->constr_parent != nullptr) { + cell->constr_x = dx + constr_base->constr_x; + cell->constr_y = constr_base->constr_y; + cell->constr_z = dz + constr_base->constr_z; + cell->constr_abs_z = false; + cell->constr_parent = constr_base->constr_parent; + constr_base->constr_parent->constr_children.push_back(cell); + } else { + cell->constr_x = dx; + cell->constr_y = 0; + cell->constr_z = dz; + cell->constr_abs_z = false; + cell->constr_parent = constr_base; + constr_base->constr_children.push_back(cell); + } + } + // Setup some default parameters + if (type == id_PREADD9_CORE) { + cell->params[id_SIGNEDSTATIC_EN] = std::string("DISABLED"); + cell->params[id_BYPASS_PREADD9] = std::string("BYPASS"); + cell->params[id_CSIGNED] = std::string("DISABLED"); + cell->params[id_GSR] = std::string("DISABLED"); + cell->params[id_OPC] = std::string("INPUT_B_AS_PREADDER_OPERAND"); + cell->params[id_PREADDCAS_EN] = std::string("DISABLED"); + cell->params[id_PREADDCAS_EN] = std::string("DISABLED"); + cell->params[id_PREADDCAS_EN] = std::string("DISABLED"); + cell->params[id_REGBYPSBL] = std::string("REGISTER"); + cell->params[id_REGBYPSBR0] = std::string("BYPASS"); + cell->params[id_REGBYPSBR1] = std::string("BYPASS"); + cell->params[id_RESET] = std::string("SYNC"); + cell->params[id_SHIFTBL] = std::string("BYPASS"); + cell->params[id_SHIFTBR] = std::string("REGISTER"); + cell->params[id_SIGNEDSTATIC_EN] = std::string("DISABLED"); + cell->params[id_SR_18BITSHIFT_EN] = std::string("DISABLED"); + cell->params[id_SUBSTRACT_EN] = std::string("SUBTRACTION"); + } + return cell; + } + + void pack_dsps() { log_info("Packing DSPs...\n"); } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() -- cgit v1.2.3 From d9a19897c4f236906632e2d1c53bffebd3f9dacc Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 13 Nov 2020 14:17:40 +0000 Subject: nexus: More DSP primitive config Signed-off-by: David Shah --- nexus/constids.inc | 9 +++++++++ nexus/pack.cc | 16 ++++++++++++++++ nexus/pins.cc | 43 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/nexus/constids.inc b/nexus/constids.inc index a996f0f8..0309bdd3 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -292,3 +292,12 @@ X(PREADDCAS_EN) X(SR_18BITSHIFT_EN) X(OPC) X(RESET) + +X(ASIGNED_OPERAND_EN) +X(BYPASS_MULT9) +X(REGBYPSA1) +X(REGBYPSA2) +X(REGBYPSB) +X(SHIFTA) + +X(REGBYPS) diff --git a/nexus/pack.cc b/nexus/pack.cc index d39a1c89..811a7c3a 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1393,6 +1393,22 @@ struct NexusPacker cell->params[id_SIGNEDSTATIC_EN] = std::string("DISABLED"); cell->params[id_SR_18BITSHIFT_EN] = std::string("DISABLED"); cell->params[id_SUBSTRACT_EN] = std::string("SUBTRACTION"); + } else if (type == id_MULT9_CORE) { + cell->params[id_ASIGNED_OPERAND_EN] = std::string("DISABLED"); + cell->params[id_BYPASS_MULT9] = std::string("DISABLED"); + cell->params[id_GSR] = std::string("DISABLED"); + cell->params[id_REGBYPSA1] = std::string("DISABLED"); + cell->params[id_REGBYPSA2] = std::string("DISABLED"); + cell->params[id_REGBYPSB] = std::string("DISABLED"); + cell->params[id_RESET] = std::string("SYNC"); + cell->params[id_GSR] = std::string("DISABLED"); + cell->params[id_SHIFTA] = std::string("DISABLED"); + cell->params[id_SIGNEDSTATIC_EN] = std::string("DISABLED"); + cell->params[id_SR_18BITSHIFT_EN] = std::string("DISABLED"); + } else if (type == id_REG18_CORE) { + cell->params[id_GSR] = std::string("DISABLED"); + cell->params[id_REGBYPS] = std::string("BYPASS"); + cell->params[id_RESET] = std::string("SYNC"); } return cell; } diff --git a/nexus/pins.cc b/nexus/pins.cc index e7be26f2..134565ad 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -83,7 +83,48 @@ static const std::unordered_map base_cell_pin_data {id_HFOUTEN, PINSTYLE_PU}, {{}, PINSTYLE_CIB}, }}, -}; + {id_PREADD9_CORE, + { + {id_CLK, PINSTYLE_CLK}, {id_RSTCL, PINSTYLE_LSR}, {id_RSTB, PINSTYLE_LSR}, {id_CECL, PINSTYLE_CE}, + {id_CEB, PINSTYLE_CE}, + + {id_B0, PINSTYLE_CIB}, {id_B1, PINSTYLE_CIB}, {id_B2, PINSTYLE_CIB}, {id_B3, PINSTYLE_CIB}, + {id_B4, PINSTYLE_CIB}, {id_B5, PINSTYLE_CIB}, {id_B6, PINSTYLE_CIB}, {id_B7, PINSTYLE_CIB}, + {id_B8, PINSTYLE_CIB}, {id_BSIGNED, PINSTYLE_CIB}, + + {id_C0, PINSTYLE_CIB}, {id_C1, PINSTYLE_CIB}, {id_C2, PINSTYLE_CIB}, {id_C3, PINSTYLE_CIB}, + {id_C4, PINSTYLE_CIB}, {id_C5, PINSTYLE_CIB}, {id_C6, PINSTYLE_CIB}, {id_C7, PINSTYLE_CIB}, + {id_C8, PINSTYLE_CIB}, {id_C9, PINSTYLE_CIB}, + + {{}, PINSTYLE_DEDI}, + }}, + {id_MULT9_CORE, + { + {id_CLK, PINSTYLE_CLK}, + {id_RSTA, PINSTYLE_LSR}, + {id_RSTP, PINSTYLE_LSR}, + {id_CEA, PINSTYLE_CE}, + {id_CEP, PINSTYLE_CE}, + + {id_A0, PINSTYLE_CIB}, + {id_A1, PINSTYLE_CIB}, + {id_A2, PINSTYLE_CIB}, + {id_A3, PINSTYLE_CIB}, + {id_A4, PINSTYLE_CIB}, + {id_A5, PINSTYLE_CIB}, + {id_A6, PINSTYLE_CIB}, + {id_A7, PINSTYLE_CIB}, + {id_A8, PINSTYLE_CIB}, + {id_ASIGNED, PINSTYLE_CIB}, + + {{}, PINSTYLE_DEDI}, + }}, + {id_REG18_CORE, + { + {id_CLK, PINSTYLE_CLK}, + {id_RSTP, PINSTYLE_LSR}, + {id_CEP, PINSTYLE_CE}, + }}}; } // namespace void Arch::init_cell_pin_data() { cell_pins_db = base_cell_pin_data; } -- cgit v1.2.3 From 92031816257dbf76e79bce4dc9c4963824932c4d Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 13 Nov 2020 15:25:57 +0000 Subject: nexus: Support for unclocked 9x9 multiplies Signed-off-by: David Shah --- common/design_utils.cc | 8 ++--- common/design_utils.h | 4 +-- nexus/constids.inc | 2 ++ nexus/fasm.cc | 25 +++++++++++++++ nexus/pack.cc | 82 ++++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 102 insertions(+), 19 deletions(-) diff --git a/common/design_utils.cc b/common/design_utils.cc index 5227585f..6cd8f0f7 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -164,12 +164,12 @@ void rename_net(Context *ctx, NetInfo *net, IdString new_name) net->name = new_name; } -void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, CellInfo *new_cell, - IdString new_name, int new_offset, int width, bool square_brackets) +void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, + CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width) { for (int i = 0; i < width; i++) { - IdString old_port = ctx->id(stringf(square_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); - IdString new_port = ctx->id(stringf(square_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); + IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); + IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); replace_port(old_cell, old_port, new_cell, new_port); } } diff --git a/common/design_utils.h b/common/design_utils.h index 2014e7ad..9d4b0550 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -107,8 +107,8 @@ void rename_net(Context *ctx, NetInfo *net, IdString new_name); void print_utilisation(const Context *ctx); // Disconnect a bus of nets (if connected) from old, and connect it to the new ports -void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, CellInfo *new_cell, - IdString new_name, int new_offset, int new_width, bool square_brackets = true); +void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, + CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width); NEXTPNR_NAMESPACE_END diff --git a/nexus/constids.inc b/nexus/constids.inc index 0309bdd3..d5a878ea 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -301,3 +301,5 @@ X(REGBYPSB) X(SHIFTA) X(REGBYPS) + +X(PP) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 72efa5da..6aff3ae7 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -515,6 +515,27 @@ struct NexusFasmWriter pop(); } } + + bool is_mux_param(const std::string &key) + { + return (key.size() >= 3 && (key.compare(key.size() - 3, 3, "MUX") == 0)); + } + + // Write config for some kind of DSP cell + void write_dsp(const CellInfo *cell) + { + BelId bel = cell->bel; + push_bel(bel); + write_bit(stringf("MODE.%s", ctx->nameOf(cell->type))); + for (auto param : sorted_cref(cell->params)) { + const std::string ¶m_name = param.first.str(ctx); + if (is_mux_param(param_name)) + continue; + write_enum(cell, param_name); + } + write_cell_muxes(cell); + pop(); + } // Write out FASM for unused bels where needed void write_unused() { @@ -623,6 +644,10 @@ struct NexusFasmWriter write_osc(ci); else if (ci->type == id_OXIDE_EBR) write_bram(ci); + else if (ci->type == id_MULT9_CORE || ci->type == id_PREADD9_CORE || ci->type == id_MULT18_CORE || + ci->type == id_MULT18X36_CORE || ci->type == id_MULT36_CORE || ci->type == id_REG18_CORE || + ci->type == id_ACC54_CORE) + write_dsp(ci); blank(); } // Write config for unused bels diff --git a/nexus/pack.cc b/nexus/pack.cc index 811a7c3a..3a6b4796 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1226,10 +1226,10 @@ struct NexusPacker { std::string name = ctx->nameOf(ctx->wire_data(wire).name); if (name.size() == 3 && (name.substr(0, 2) == "JF" || name.substr(0, 2) == "JQ")) - return false; + return true; if (name.size() == 12 && (name.substr(0, 10) == "JCIBMUXOUT")) - return false; - return true; + return true; + return false; } // Automatically generate cascade connections downstream of a cell @@ -1247,9 +1247,14 @@ struct NexusPacker if (start_wire == WireId()) continue; + if (ctx->debug) + log_info(" searching cascade routing for wire %s:\n", ctx->nameOfWire(start_wire)); + // Standard BFS-type exploration std::queue visit; + std::unordered_set in_queue; visit.push(start_wire); + in_queue.insert(start_wire); int iter = 0; const int iter_limit = 1000; @@ -1257,6 +1262,9 @@ struct NexusPacker WireId cursor = visit.front(); visit.pop(); + if (ctx->debug) + log_info(" visit '%s'\n", ctx->nameOfWire(cursor)); + // Check for downstream bel pins bool found_active_pins = false; for (auto bp : ctx->getWireBelPins(cursor)) { @@ -1265,13 +1273,19 @@ struct NexusPacker // so we can route through these if needed if (fnd_cell == bel2cell.end()) continue; - - found_active_pins = true; - // Skip outputs if (ctx->getBelPinType(bp.bel, bp.pin) != PORT_IN) continue; + + if (ctx->debug) + log_info(" bel %s pin %s\n", ctx->nameOfBel(bp.bel), ctx->nameOf(bp.pin)); + + found_active_pins = true; CellInfo *other_cell = fnd_cell->second; + + if (other_cell == cell) + continue; + // Skip pins that are already in use if (get_net_or_empty(other_cell, bp.pin) != nullptr) continue; @@ -1280,6 +1294,9 @@ struct NexusPacker other_cell->addInput(bp.pin); // Make the connection connect_ports(ctx, cell, port.first, other_cell, bp.pin); + + if (ctx->debug) + log_info(" found %s.%s\n", ctx->nameOf(other_cell), ctx->nameOf(bp.pin)); } // By doing this we never attempt to route-through bels @@ -1288,8 +1305,16 @@ struct NexusPacker continue; // Search downstream pips for wires to add to the queue - for (auto pip : ctx->getPipsDownhill(cursor)) - visit.push(ctx->getPipDstWire(pip)); + for (auto pip : ctx->getPipsDownhill(cursor)) { + WireId dst = ctx->getPipDstWire(pip); + // Ignore general routing, as that isn't a useful cascade path + if (is_general_routing(dst)) + continue; + if (in_queue.count(dst)) + continue; + in_queue.insert(dst); + visit.push(dst); + } } } } @@ -1345,6 +1370,11 @@ struct NexusPacker log_error("Failed to create temporary placement for cell '%s' of type '%s'\n", ctx->nameOf(root), ctx->nameOf(root->type)); + // Create the necessary new ports + autocreate_ports(root, true); + for (auto child : root->constr_children) + autocreate_ports(child, true); + // Insert cascade connections from all cells in the macro auto_cascade_cell(root, cell2bel.at(root->name), bel2cell); for (auto child : root->constr_children) @@ -1395,11 +1425,11 @@ struct NexusPacker cell->params[id_SUBSTRACT_EN] = std::string("SUBTRACTION"); } else if (type == id_MULT9_CORE) { cell->params[id_ASIGNED_OPERAND_EN] = std::string("DISABLED"); - cell->params[id_BYPASS_MULT9] = std::string("DISABLED"); + cell->params[id_BYPASS_MULT9] = std::string("USED"); cell->params[id_GSR] = std::string("DISABLED"); - cell->params[id_REGBYPSA1] = std::string("DISABLED"); - cell->params[id_REGBYPSA2] = std::string("DISABLED"); - cell->params[id_REGBYPSB] = std::string("DISABLED"); + cell->params[id_REGBYPSA1] = std::string("BYPASS"); + cell->params[id_REGBYPSA2] = std::string("BYPASS"); + cell->params[id_REGBYPSB] = std::string("BYPASS"); cell->params[id_RESET] = std::string("SYNC"); cell->params[id_GSR] = std::string("DISABLED"); cell->params[id_SHIFTA] = std::string("DISABLED"); @@ -1413,13 +1443,39 @@ struct NexusPacker return cell; } - void pack_dsps() { log_info("Packing DSPs...\n"); } + void pack_dsps() + { + log_info("Packing DSPs...\n"); + std::vector to_remove; + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == id_MULT9X9) { + // MULT9X9: PREADD9 -> MULT9 -> REG18 + CellInfo *preadd9_0 = create_dsp_cell(ci->name, id_PREADD9_CORE, nullptr, 0, 0); + CellInfo *mult9_0 = create_dsp_cell(ci->name, id_MULT9_CORE, preadd9_0, 0, 2); + CellInfo *reg18_0 = create_dsp_cell(ci->name, id_REG18_CORE, preadd9_0, 2, 0); + replace_bus(ctx, ci, id_B, 0, true, preadd9_0, id_B, 0, false, 9); + replace_bus(ctx, ci, id_A, 0, true, mult9_0, id_A, 0, false, 9); + replace_bus(ctx, ci, id_Z, 0, true, reg18_0, id_PP, 0, false, 18); + auto_cascade_group(preadd9_0); + to_remove.push_back(ci); + } + } + + for (auto cell : to_remove) { + for (auto port : sorted_ref(cell->ports)) + disconnect_port(ctx, cell, port.first); + ctx->cells.erase(cell->name); + } + } explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() { pack_io(); + pack_dsps(); convert_prims(); pack_bram(); pack_lutram(); -- cgit v1.2.3 From 30c65931b2bb86b5dfd6140b35672f7db46c8d32 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 16 Nov 2020 09:07:25 +0000 Subject: nexus: Add support for clocked MULT9X9s Signed-off-by: David Shah --- common/design_utils.cc | 9 +++++++++ common/design_utils.h | 3 +++ nexus/constids.inc | 9 +++++++++ nexus/pack.cc | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/common/design_utils.cc b/common/design_utils.cc index 6cd8f0f7..4d1c9e53 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -174,4 +174,13 @@ void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_of } } +void copy_port(Context *ctx, CellInfo *old_cell, IdString old_name, CellInfo *new_cell, IdString new_name) +{ + if (!old_cell->ports.count(old_name)) + return; + new_cell->ports[new_name].name = new_name; + new_cell->ports[new_name].type = old_cell->ports.at(old_name).type; + connect_port(ctx, old_cell->ports.at(old_name).net, new_cell, new_name); +} + NEXTPNR_NAMESPACE_END diff --git a/common/design_utils.h b/common/design_utils.h index 9d4b0550..c93fe009 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -110,6 +110,9 @@ void print_utilisation(const Context *ctx); void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width); +// Copy a port from one cell to another +void copy_port(Context *ctx, CellInfo *old_cell, IdString old_name, CellInfo *new_cell, IdString new_name); + NEXTPNR_NAMESPACE_END #endif diff --git a/nexus/constids.inc b/nexus/constids.inc index d5a878ea..90416594 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -292,6 +292,7 @@ X(PREADDCAS_EN) X(SR_18BITSHIFT_EN) X(OPC) X(RESET) +X(RESETMODE) X(ASIGNED_OPERAND_EN) X(BYPASS_MULT9) @@ -303,3 +304,11 @@ X(SHIFTA) X(REGBYPS) X(PP) + +X(SIGNEDA) +X(SIGNEDB) +X(RSTOUT) +X(CEOUT) +X(REGINPUTA) +X(REGINPUTB) +X(REGOUTPUT) diff --git a/nexus/pack.cc b/nexus/pack.cc index 3a6b4796..0125378c 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1443,6 +1443,23 @@ struct NexusPacker return cell; } + void copy_global_dsp_params(CellInfo *orig, CellInfo *root) + { + if (root->params.count(id_GSR) && orig->params.count(id_GSR)) + root->params[id_GSR] = orig->params.at(id_GSR); + if (root->params.count(id_RESET) && orig->params.count(id_RESETMODE)) + root->params[id_RESET] = orig->params.at(id_RESETMODE); + for (auto child : root->constr_children) + copy_global_dsp_params(orig, child); + } + + void copy_param(CellInfo *orig, IdString orig_name, CellInfo *dst, IdString dst_name) + { + if (!orig->params.count(orig_name)) + return; + dst->params[dst_name] = orig->params[orig_name]; + } + void pack_dsps() { log_info("Packing DSPs...\n"); @@ -1458,6 +1475,25 @@ struct NexusPacker replace_bus(ctx, ci, id_B, 0, true, preadd9_0, id_B, 0, false, 9); replace_bus(ctx, ci, id_A, 0, true, mult9_0, id_A, 0, false, 9); replace_bus(ctx, ci, id_Z, 0, true, reg18_0, id_PP, 0, false, 18); + replace_port(ci, id_SIGNEDA, mult9_0, id_ASIGNED); + replace_port(ci, id_SIGNEDB, preadd9_0, id_BSIGNED); + + copy_port(ctx, ci, id_CLK, preadd9_0, id_CLK); + copy_port(ctx, ci, id_CLK, mult9_0, id_CLK); + copy_port(ctx, ci, id_CLK, reg18_0, id_CLK); + + replace_port(ci, id_CEA, mult9_0, id_CEA); + replace_port(ci, id_RSTA, mult9_0, id_RSTA); + replace_port(ci, id_CEB, preadd9_0, id_CEB); + replace_port(ci, id_RSTB, preadd9_0, id_RSTB); + replace_port(ci, id_CEOUT, reg18_0, id_CEP); + replace_port(ci, id_RSTOUT, reg18_0, id_RSTP); + + copy_param(ci, id_REGINPUTA, mult9_0, id_REGBYPSA1); + copy_param(ci, id_REGINPUTB, preadd9_0, id_REGBYPSBR0); + copy_param(ci, id_REGOUTPUT, reg18_0, id_REGBYPS); + + copy_global_dsp_params(ci, preadd9_0); auto_cascade_group(preadd9_0); to_remove.push_back(ci); } -- cgit v1.2.3 From d8e748bc5864f7937cf087cf08d7497bff0d4f6d Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 16 Nov 2020 13:04:43 +0000 Subject: nexus: Refactor DSP macro splitting to make it more generic Signed-off-by: David Shah --- common/design_utils.cc | 10 ++++ common/design_utils.h | 4 ++ nexus/constids.inc | 10 ++++ nexus/pack.cc | 139 ++++++++++++++++++++++++++++++++++++++----------- nexus/pins.cc | 19 +++++++ 5 files changed, 153 insertions(+), 29 deletions(-) diff --git a/common/design_utils.cc b/common/design_utils.cc index 4d1c9e53..16cc2710 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -183,4 +183,14 @@ void copy_port(Context *ctx, CellInfo *old_cell, IdString old_name, CellInfo *ne connect_port(ctx, old_cell->ports.at(old_name).net, new_cell, new_name); } +void copy_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, + CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width) +{ + for (int i = 0; i < width; i++) { + IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); + IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); + copy_port(ctx, old_cell, old_port, new_cell, new_port); + } +} + NEXTPNR_NAMESPACE_END diff --git a/common/design_utils.h b/common/design_utils.h index c93fe009..6f52eb0c 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -110,6 +110,10 @@ void print_utilisation(const Context *ctx); void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width); +// Copy a bus of nets (if connected) from old, and connect it to the new ports +void copy_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, + CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width); + // Copy a port from one cell to another void copy_port(Context *ctx, CellInfo *old_cell, IdString old_name, CellInfo *new_cell, IdString new_name); diff --git a/nexus/constids.inc b/nexus/constids.inc index 90416594..de3d27bc 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -312,3 +312,13 @@ X(CEOUT) X(REGINPUTA) X(REGINPUTB) X(REGOUTPUT) + +X(MULT18X18) +X(ROUNDBIT) +X(ROUNDHALFUP) +X(ROUNDRTZI) +X(SFTEN) + +X(MULT18X36) +X(MULT36X36H) +X(MULT36X36) diff --git a/nexus/pack.cc b/nexus/pack.cc index 0125378c..92035079 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1435,6 +1435,22 @@ struct NexusPacker cell->params[id_SHIFTA] = std::string("DISABLED"); cell->params[id_SIGNEDSTATIC_EN] = std::string("DISABLED"); cell->params[id_SR_18BITSHIFT_EN] = std::string("DISABLED"); + } else if (type == id_MULT18_CORE) { + cell->params[id_MULT18X18] = std::string("ENABLED"); + cell->params[id_ROUNDBIT] = std::string("ROUND_TO_BIT0"); + cell->params[id_ROUNDHALFUP] = std::string("DISABLED"); + cell->params[id_ROUNDRTZI] = std::string("ROUND_TO_ZERO"); + cell->params[id_SFTEN] = std::string("DISABLED"); + } else if (type == id_MULT18X36_CORE) { + cell->params[id_SFTEN] = std::string("DISABLED"); + cell->params[id_MULT18X36] = std::string("ENABLED"); + cell->params[id_MULT36] = std::string("DISABLED"); + cell->params[id_MULT36X36H] = std::string("USED_AS_LOWER_BIT_GENERATION"); + cell->params[id_ROUNDHALFUP] = std::string("DISABLED"); + cell->params[id_ROUNDRTZI] = std::string("ROUND_TO_ZERO"); + cell->params[id_ROUNDBIT] = std::string("ROUND_TO_BIT0"); + } else if (type == id_MULT36_CORE) { + cell->params[id_MULT36X36] = std::string("ENABLED"); } else if (type == id_REG18_CORE) { cell->params[id_GSR] = std::string("DISABLED"); cell->params[id_REGBYPS] = std::string("BYPASS"); @@ -1460,6 +1476,26 @@ struct NexusPacker dst->params[dst_name] = orig->params[orig_name]; } + struct DSPMacroType + { + int a_width; // width of 'A' input + int b_width; // width of 'B' input + int c_width; // width of 'C' input + int z_width; // width of 'Z' output + int N9x9; // number of 9x9 mult+preadds + int N18x18; // number of 18x18 mult + int N18x36; // number of 18x36 mult + bool has_preadd; // preadder is used + bool has_addsub; // post-multiply ALU addsub is used + }; + + const std::unordered_map dsp_types = { + {id_MULT9X9, {9, 9, 0, 18, 1, 0, 0, false, false}}, + {id_MULT18X18, {18, 18, 0, 36, 2, 1, 0, false, false}}, + {id_MULT18X36, {18, 36, 0, 54, 4, 2, 1, false, false}}, + {id_MULT36X36, {36, 36, 0, 72, 8, 4, 2, false, false}}, + }; + void pack_dsps() { log_info("Packing DSPs...\n"); @@ -1467,36 +1503,81 @@ struct NexusPacker for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; - if (ci->type == id_MULT9X9) { - // MULT9X9: PREADD9 -> MULT9 -> REG18 - CellInfo *preadd9_0 = create_dsp_cell(ci->name, id_PREADD9_CORE, nullptr, 0, 0); - CellInfo *mult9_0 = create_dsp_cell(ci->name, id_MULT9_CORE, preadd9_0, 0, 2); - CellInfo *reg18_0 = create_dsp_cell(ci->name, id_REG18_CORE, preadd9_0, 2, 0); - replace_bus(ctx, ci, id_B, 0, true, preadd9_0, id_B, 0, false, 9); - replace_bus(ctx, ci, id_A, 0, true, mult9_0, id_A, 0, false, 9); - replace_bus(ctx, ci, id_Z, 0, true, reg18_0, id_PP, 0, false, 18); - replace_port(ci, id_SIGNEDA, mult9_0, id_ASIGNED); - replace_port(ci, id_SIGNEDB, preadd9_0, id_BSIGNED); - - copy_port(ctx, ci, id_CLK, preadd9_0, id_CLK); - copy_port(ctx, ci, id_CLK, mult9_0, id_CLK); - copy_port(ctx, ci, id_CLK, reg18_0, id_CLK); - - replace_port(ci, id_CEA, mult9_0, id_CEA); - replace_port(ci, id_RSTA, mult9_0, id_RSTA); - replace_port(ci, id_CEB, preadd9_0, id_CEB); - replace_port(ci, id_RSTB, preadd9_0, id_RSTB); - replace_port(ci, id_CEOUT, reg18_0, id_CEP); - replace_port(ci, id_RSTOUT, reg18_0, id_RSTP); - - copy_param(ci, id_REGINPUTA, mult9_0, id_REGBYPSA1); - copy_param(ci, id_REGINPUTB, preadd9_0, id_REGBYPSBR0); - copy_param(ci, id_REGOUTPUT, reg18_0, id_REGBYPS); - - copy_global_dsp_params(ci, preadd9_0); - auto_cascade_group(preadd9_0); - to_remove.push_back(ci); + if (!dsp_types.count(ci->type)) + continue; + auto &mt = dsp_types.at(ci->type); + int Nreg18 = mt.z_width / 18; + + // Create consituent cells + std::vector preadd9(mt.N9x9), mult9(mt.N9x9), mult18(mt.N18x18), mult18x36(mt.N18x36), + reg18(Nreg18); + for (int i = 0; i < mt.N9x9; i++) { + preadd9[i] = create_dsp_cell(ci->name, id_PREADD9_CORE, preadd9[0], (i / 4) * 4 + (i / 2) % 2, (i % 2)); + mult9[i] = create_dsp_cell(ci->name, id_MULT9_CORE, preadd9[0], (i / 4) * 4 + (i / 2) % 2, (i % 2) + 2); } + for (int i = 0; i < mt.N18x18; i++) + mult18[i] = create_dsp_cell(ci->name, id_MULT18_CORE, preadd9[0], (i / 2) * 4 + i % 2, 4); + for (int i = 0; i < mt.N18x36; i++) + mult18x36[i] = create_dsp_cell(ci->name, id_MULT18X36_CORE, preadd9[0], (i * 4) + 2, 4); + for (int i = 0; i < Nreg18; i++) { + reg18[i] = create_dsp_cell(ci->name, id_REG18_CORE, preadd9[0], (i / 4) * 4 + 2, i % 4); + } + + // Configure the 9x9 preadd+multiply blocks + for (int i = 0; i < mt.N9x9; i++) { + // B input split across pre-adders + int b_start = (9 * i) % mt.b_width; + copy_bus(ctx, ci, id_B, b_start, true, preadd9[i], id_B, 0, false, 9); + // A input split across MULT9s + int a_start = 9 * (i % 2) + 18 * (i / 4); + copy_bus(ctx, ci, id_A, a_start, true, mult9[i], id_A, 0, false, 9); + // Connect control set signals + copy_port(ctx, ci, id_CLK, mult9[i], id_CLK); + copy_port(ctx, ci, id_CEA, mult9[i], id_CEA); + copy_port(ctx, ci, id_RSTA, mult9[i], id_RSTA); + copy_port(ctx, ci, id_CLK, preadd9[i], id_CLK); + copy_port(ctx, ci, id_CEB, preadd9[i], id_CEB); + copy_port(ctx, ci, id_RSTB, preadd9[i], id_RSTB); + // Copy register configuration + copy_param(ci, id_REGINPUTA, mult9[i], id_REGBYPSA1); + copy_param(ci, id_REGINPUTB, preadd9[i], id_REGBYPSBR0); + + // Connect up signedness for the most significant nonet + if ((b_start + 9) == mt.b_width) + copy_port(ctx, ci, id_BSIGNED, preadd9[i], id_SIGNEDB); + if ((a_start + 9) == mt.a_width) + copy_port(ctx, ci, id_ASIGNED, mult9[i], id_SIGNEDA); + } + + bool mult36_used = (mt.a_width >= 36) && (mt.b_width >= 36); + // Configure mult18x36s + for (int i = 0; i < mt.N18x36; i++) { + mult18x36[i]->params[id_MULT36] = mult36_used ? std::string("ENABLED") : std::string("DISABLED"); + mult18x36[i]->params[id_MULT36X36H] = (i == 1) ? std::string("USED_AS_HIGHER_BIT_GENERATION") + : std::string("USED_AS_LOWER_BIT_GENERATION"); + } + // Create final mult36 if needed + CellInfo *mult36 = nullptr; + if (mult36_used) { + mult36 = create_dsp_cell(ci->name, id_MULT36_CORE, preadd9[0], 6, 6); + } + + // Configure output registers + for (int i = 0; i < Nreg18; i++) { + // Output split across reg18s + replace_bus(ctx, ci, id_Z, i * 18, true, reg18[i], id_PP, 0, false, 18); + // Connect control set signals + copy_port(ctx, ci, id_CLK, reg18[i], id_CLK); + copy_port(ctx, ci, id_CEOUT, reg18[i], id_CEP); + copy_port(ctx, ci, id_RSTOUT, reg18[i], id_RSTP); + // Copy register configuration + copy_param(ci, id_REGOUTPUT, reg18[i], id_REGBYPS); + } + + // Misc finalisation + copy_global_dsp_params(ci, preadd9[0]); + auto_cascade_group(preadd9[0]); + to_remove.push_back(ci); } for (auto cell : to_remove) { diff --git a/nexus/pins.cc b/nexus/pins.cc index 134565ad..0cf5c3e2 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -124,6 +124,25 @@ static const std::unordered_map base_cell_pin_data {id_CLK, PINSTYLE_CLK}, {id_RSTP, PINSTYLE_LSR}, {id_CEP, PINSTYLE_CE}, + {{}, PINSTYLE_DEDI}, + }}, + {id_MULT18_CORE, + { + {id_SFTCTRL0, PINSTYLE_CIB}, + {id_SFTCTRL1, PINSTYLE_CIB}, + {id_SFTCTRL2, PINSTYLE_CIB}, + {id_SFTCTRL3, PINSTYLE_CIB}, + {id_ROUNDEN, PINSTYLE_CIB}, + {{}, PINSTYLE_DEDI}, + }}, + {id_MULT18X36_CORE, + { + {id_SFTCTRL0, PINSTYLE_CIB}, + {id_SFTCTRL1, PINSTYLE_CIB}, + {id_SFTCTRL2, PINSTYLE_CIB}, + {id_SFTCTRL3, PINSTYLE_CIB}, + {id_ROUNDEN, PINSTYLE_CIB}, + {{}, PINSTYLE_DEDI}, }}}; } // namespace -- cgit v1.2.3 From 160045a058dc6f5dea278f133f227697532f8cbb Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 16 Nov 2020 13:22:52 +0000 Subject: nexus: Fix validity checking when DSPs are used Signed-off-by: David Shah --- nexus/arch.h | 11 +++++++++-- nexus/arch_place.cc | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 9b75a09e..f7299cb7 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -979,7 +979,7 @@ struct Arch : BaseCtx cell->belStrength = strength; refreshUiBel(bel); - if (tile_is(bel, LOC_LOGIC)) + if (bel_tile_is(bel, LOC_LOGIC)) update_logic_bel(bel, cell); } @@ -988,7 +988,7 @@ struct Arch : BaseCtx NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(tileStatus[bel.tile].boundcells[bel.index] != nullptr); - if (tile_is(bel, LOC_LOGIC)) + if (bel_tile_is(bel, LOC_LOGIC)) update_logic_bel(bel, nullptr); tileStatus[bel.tile].boundcells[bel.index]->bel = BelId(); @@ -1492,6 +1492,13 @@ struct Arch : BaseCtx template bool tile_is(TId id, LocFlags lf) const { return tile_loc_flags(id) & lf; } + bool bel_tile_is(BelId bel, LocFlags lf) const + { + int32_t tile; + NPNR_ASSERT(rel_tile(bel.tile, bel_data(bel).rel_x, bel_data(bel).rel_y, tile)); + return chip_info->grid[tile].loc_flags & lf; + } + // ------------------------------------------------- enum LogicBelZ diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc index 35b14caf..0d141013 100644 --- a/nexus/arch_place.cc +++ b/nexus/arch_place.cc @@ -104,7 +104,7 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const bool Arch::isBelLocationValid(BelId bel) const { - if (tile_is(bel, LOC_LOGIC)) { + if (bel_tile_is(bel, LOC_LOGIC)) { LogicTileStatus *lts = tileStatus[bel.tile].lts; if (lts == nullptr) return true; -- cgit v1.2.3 From 6b5277638bc63b592e30acb4f51a4df3a8bd59d8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 16 Nov 2020 13:31:43 +0000 Subject: nexus: Fix slow routing around DSPs Signed-off-by: David Shah --- nexus/arch.cc | 24 ++++++++++++++++++++++++ nexus/arch.h | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/nexus/arch.cc b/nexus/arch.cc index 6cc0da0f..3a279f4a 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -582,6 +582,12 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const bb.y0 = std::min(bb.y0, y); bb.y1 = std::max(bb.y1, y); }; + + if (dsp_wires.count(src) || dsp_wires.count(dst)) { + bb.x0 -= 5; + bb.x1 += 5; + } + extend(dst_x, dst_y); return bb; @@ -617,10 +623,28 @@ bool Arch::place() return true; } +void Arch::pre_routing() +{ + for (auto cell : sorted(cells)) { + CellInfo *ci = cell.second; + if (ci->type == id_MULT9_CORE || ci->type == id_PREADD9_CORE || ci->type == id_MULT18_CORE || + ci->type == id_MULT18X36_CORE || ci->type == id_MULT36_CORE || ci->type == id_REG18_CORE || + ci->type == id_ACC54_CORE) { + for (auto port : sorted_ref(ci->ports)) { + WireId wire = getBelPinWire(ci->bel, port.first); + if (wire != WireId()) + dsp_wires.insert(wire); + } + } + } +} + bool Arch::route() { assign_budget(getCtx(), true); + pre_routing(); + route_globals(); std::string router = str_or_default(settings, id("router"), defaultRouter); diff --git a/nexus/arch.h b/nexus/arch.h index f7299cb7..debf66b0 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1367,6 +1367,10 @@ struct Arch : BaseCtx bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; ArcBounds getRouteBoundingBox(WireId src, WireId dst) const; + // for better DSP bounding boxes + void pre_routing(); + std::unordered_set dsp_wires; + // ------------------------------------------------- // Get the delay through a cell from one port to another, returning false -- cgit v1.2.3 From 54539b85191fc5292b1d342a0979f9f31a319998 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Nov 2020 15:13:00 +0000 Subject: nexus: Larger DSP tweaks Signed-off-by: David Shah --- nexus/fasm.cc | 8 +++++++- nexus/pins.cc | 16 ++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 6aff3ae7..5da809c6 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -526,11 +526,17 @@ struct NexusFasmWriter { BelId bel = cell->bel; push_bel(bel); - write_bit(stringf("MODE.%s", ctx->nameOf(cell->type))); + if (cell->type != id_MULT18_CORE && cell->type != id_MULT18X36_CORE && cell->type != id_MULT36_CORE) + write_bit(stringf("MODE.%s", ctx->nameOf(cell->type))); for (auto param : sorted_cref(cell->params)) { const std::string ¶m_name = param.first.str(ctx); if (is_mux_param(param_name)) continue; + if (param.first == id_ROUNDBIT) { + // currently unsupported in oxide, but appears rarely used + NPNR_ASSERT(param.second.as_string() == "ROUND_TO_BIT0"); + continue; + } write_enum(cell, param_name); } write_cell_muxes(cell); diff --git a/nexus/pins.cc b/nexus/pins.cc index 0cf5c3e2..78aa213e 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -128,19 +128,19 @@ static const std::unordered_map base_cell_pin_data }}, {id_MULT18_CORE, { - {id_SFTCTRL0, PINSTYLE_CIB}, - {id_SFTCTRL1, PINSTYLE_CIB}, - {id_SFTCTRL2, PINSTYLE_CIB}, - {id_SFTCTRL3, PINSTYLE_CIB}, + {id_SFTCTRL0, PINSTYLE_PU}, + {id_SFTCTRL1, PINSTYLE_PU}, + {id_SFTCTRL2, PINSTYLE_PU}, + {id_SFTCTRL3, PINSTYLE_PU}, {id_ROUNDEN, PINSTYLE_CIB}, {{}, PINSTYLE_DEDI}, }}, {id_MULT18X36_CORE, { - {id_SFTCTRL0, PINSTYLE_CIB}, - {id_SFTCTRL1, PINSTYLE_CIB}, - {id_SFTCTRL2, PINSTYLE_CIB}, - {id_SFTCTRL3, PINSTYLE_CIB}, + {id_SFTCTRL0, PINSTYLE_PU}, + {id_SFTCTRL1, PINSTYLE_PU}, + {id_SFTCTRL2, PINSTYLE_PU}, + {id_SFTCTRL3, PINSTYLE_PU}, {id_ROUNDEN, PINSTYLE_CIB}, {{}, PINSTYLE_DEDI}, }}}; -- cgit v1.2.3 From fcde8e2d56ec76df5a21e83660608188fdc1a7b9 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Nov 2020 15:16:32 +0000 Subject: nexus: Fix DSP signed ports Signed-off-by: David Shah --- nexus/pack.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index 92035079..383599a0 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1544,9 +1544,9 @@ struct NexusPacker // Connect up signedness for the most significant nonet if ((b_start + 9) == mt.b_width) - copy_port(ctx, ci, id_BSIGNED, preadd9[i], id_SIGNEDB); + copy_port(ctx, ci, id_SIGNEDB, preadd9[i], id_BSIGNED); if ((a_start + 9) == mt.a_width) - copy_port(ctx, ci, id_ASIGNED, mult9[i], id_SIGNEDA); + copy_port(ctx, ci, id_SIGNEDA, mult9[i], id_ASIGNED); } bool mult36_used = (mt.a_width >= 36) && (mt.b_width >= 36); -- cgit v1.2.3 From 91d746cfc83e3e78424ae079bc2bb36fa266788e Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Nov 2020 16:03:52 +0000 Subject: nexus: Add DSP pre-adder support Signed-off-by: David Shah --- nexus/constids.inc | 6 ++++++ nexus/pack.cc | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/nexus/constids.inc b/nexus/constids.inc index de3d27bc..f1f255ee 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -322,3 +322,9 @@ X(SFTEN) X(MULT18X36) X(MULT36X36H) X(MULT36X36) + +X(SIGNEDC) +X(REGINPUTC) + +X(MULTPREADD9X9) +X(MULTPREADD18X18) diff --git a/nexus/pack.cc b/nexus/pack.cc index 383599a0..e095afa6 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1412,8 +1412,6 @@ struct NexusPacker cell->params[id_GSR] = std::string("DISABLED"); cell->params[id_OPC] = std::string("INPUT_B_AS_PREADDER_OPERAND"); cell->params[id_PREADDCAS_EN] = std::string("DISABLED"); - cell->params[id_PREADDCAS_EN] = std::string("DISABLED"); - cell->params[id_PREADDCAS_EN] = std::string("DISABLED"); cell->params[id_REGBYPSBL] = std::string("REGISTER"); cell->params[id_REGBYPSBR0] = std::string("BYPASS"); cell->params[id_REGBYPSBR1] = std::string("BYPASS"); @@ -1494,6 +1492,8 @@ struct NexusPacker {id_MULT18X18, {18, 18, 0, 36, 2, 1, 0, false, false}}, {id_MULT18X36, {18, 36, 0, 54, 4, 2, 1, false, false}}, {id_MULT36X36, {36, 36, 0, 72, 8, 4, 2, false, false}}, + {id_MULTPREADD9X9, {9, 9, 9, 18, 1, 0, 0, true, false}}, + {id_MULTPREADD18X18, {18, 18, 18, 36, 2, 1, 0, true, false}}, }; void pack_dsps() @@ -1542,6 +1542,21 @@ struct NexusPacker copy_param(ci, id_REGINPUTA, mult9[i], id_REGBYPSA1); copy_param(ci, id_REGINPUTB, preadd9[i], id_REGBYPSBR0); + // Connect and configure pre-adder if it isn't bypassed + if (mt.has_preadd) { + copy_bus(ctx, ci, id_C, 9 * i, true, preadd9[i], id_C, 0, false, 9); + if (i == (mt.N9x9 - 1)) + copy_port(ctx, ci, id_SIGNEDC, preadd9[i], id_C9); + copy_param(ci, id_REGINPUTC, preadd9[i], id_REGBYPSBL); + copy_port(ctx, ci, id_CEC, preadd9[i], id_CECL); + copy_port(ctx, ci, id_RSTC, preadd9[i], id_RSTCL); + // Enable preadder + preadd9[i]->params[id_BYPASS_PREADD9] = std::string("USED"); + preadd9[i]->params[id_OPC] = std::string("INPUT_C_AS_PREADDER_OPERAND"); + if (i > 0) + preadd9[i]->params[id_PREADDCAS_EN] = std::string("ENABLED"); + } + // Connect up signedness for the most significant nonet if ((b_start + 9) == mt.b_width) copy_port(ctx, ci, id_SIGNEDB, preadd9[i], id_BSIGNED); -- cgit v1.2.3 From edd719c5c5d3aaf7892b4e950af9d9c8fec838f2 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Nov 2020 16:18:42 +0000 Subject: nexus: ACC54 definitions Signed-off-by: David Shah --- nexus/constids.inc | 34 ++++++++++++++++++++++++++++++++++ nexus/pack.cc | 32 ++++++++++++++++++++++++++++++++ nexus/pins.cc | 11 +++++++++++ 3 files changed, 77 insertions(+) diff --git a/nexus/constids.inc b/nexus/constids.inc index f1f255ee..cf19789c 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -328,3 +328,37 @@ X(REGINPUTC) X(MULTPREADD9X9) X(MULTPREADD18X18) + +X(REGPIPELINE) +X(REGADDSUB) +X(REGLOADC) +X(REGLOADC2) +X(REGCIN) + +X(ACC108CASCADE) +X(ACCUBYPS) +X(ACCUMODE) +X(ADDSUBSIGNREGBYPS1) +X(ADDSUBSIGNREGBYPS2) +X(ADDSUBSIGNREGBYPS3) +X(ADDSUB_CTRL) +X(CASCOUTREGBYPS) +X(CINREGBYPS1) +X(CINREGBYPS2) +X(CINREGBYPS3) +X(CONSTSEL) +X(CREGBYPS1) +X(CREGBYPS2) +X(CREGBYPS3) +X(DSPCASCADE) +X(LOADREGBYPS1) +X(LOADREGBYPS2) +X(LOADREGBYPS3) +X(M9ADDSUBREGBYPS1) +X(M9ADDSUBREGBYPS2) +X(M9ADDSUBREGBYPS3) +X(M9ADDSUB_CTRL) +X(OUTREGBYPS) +X(SIGN) +X(STATICOPCODE_EN) +X(PROGCONST) diff --git a/nexus/pack.cc b/nexus/pack.cc index e095afa6..48805267 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1453,6 +1453,38 @@ struct NexusPacker cell->params[id_GSR] = std::string("DISABLED"); cell->params[id_REGBYPS] = std::string("BYPASS"); cell->params[id_RESET] = std::string("SYNC"); + } else if (type == id_ACC54_CORE) { + cell->params[id_ACC108CASCADE] = std::string("BYPASSCASCADE"); + cell->params[id_ACCUBYPS] = std::string("USED"); + cell->params[id_ACCUMODE] = std::string("MODE7"); + cell->params[id_ADDSUBSIGNREGBYPS1] = std::string("REGISTER"); + cell->params[id_ADDSUBSIGNREGBYPS2] = std::string("REGISTER"); + cell->params[id_ADDSUBSIGNREGBYPS3] = std::string("BYPASS"); + cell->params[id_ADDSUB_CTRL] = std::string("ADD_ADD_CTRL_54_BIT_ADDER"); + cell->params[id_CASCOUTREGBYPS] = std::string("BYPASS"); + cell->params[id_CINREGBYPS1] = std::string("REGISTER"); + cell->params[id_CINREGBYPS2] = std::string("REGISTER"); + cell->params[id_CINREGBYPS3] = std::string("BYPASS"); + cell->params[id_CONSTSEL] = std::string("BYPASS"); + cell->params[id_CREGBYPS1] = std::string("REGISTER"); + cell->params[id_CREGBYPS2] = std::string("REGISTER"); + cell->params[id_CREGBYPS3] = std::string("BYPASS"); + cell->params[id_DSPCASCADE] = std::string("DISABLED"); + cell->params[id_GSR] = std::string("DISABLED"); + cell->params[id_LOADREGBYPS1] = std::string("REGISTER"); + cell->params[id_LOADREGBYPS2] = std::string("REGISTER"); + cell->params[id_LOADREGBYPS3] = std::string("BYPASS"); + cell->params[id_M9ADDSUBREGBYPS1] = std::string("REGISTER"); + cell->params[id_M9ADDSUBREGBYPS2] = std::string("REGISTER"); + cell->params[id_M9ADDSUBREGBYPS3] = std::string("BYPASS"); + cell->params[id_OUTREGBYPS] = std::string("REGISTER"); + cell->params[id_RESET] = std::string("SYNC"); + cell->params[id_ROUNDHALFUP] = std::string("DISABLED"); + cell->params[id_ROUNDRTZI] = std::string("ROUND_TO_ZERO"); + cell->params[id_ROUNDBIT] = std::string("ROUND_TO_BIT0"); + cell->params[id_SFTEN] = std::string("DISABLED"); + cell->params[id_SIGN] = std::string("DISABLED"); + cell->params[id_STATICOPCODE_EN] = std::string("DISABLED"); } return cell; } diff --git a/nexus/pins.cc b/nexus/pins.cc index 78aa213e..0587c032 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -143,6 +143,17 @@ static const std::unordered_map base_cell_pin_data {id_SFTCTRL3, PINSTYLE_PU}, {id_ROUNDEN, PINSTYLE_CIB}, {{}, PINSTYLE_DEDI}, + }}, + {id_ACC54_CORE, + { + {id_CLK, PINSTYLE_CLK}, {id_RSTC, PINSTYLE_LSR}, {id_CEC, PINSTYLE_CE}, + {id_SIGNEDI, PINSTYLE_CIB}, {id_RSTCTRL, PINSTYLE_LSR}, {id_CECTRL, PINSTYLE_CE}, + {id_RSTCIN, PINSTYLE_LSR}, {id_CECIN, PINSTYLE_CE}, {id_LOAD, PINSTYLE_CIB}, + {id_ADDSUB0, PINSTYLE_CIB}, {id_ADDSUB1, PINSTYLE_CIB}, {id_M9ADDSUB0, PINSTYLE_PU}, + {id_M9ADDSUB1, PINSTYLE_PU}, {id_ROUNDEN, PINSTYLE_CIB}, {id_RSTO, PINSTYLE_LSR}, + {id_CEO, PINSTYLE_CE}, {id_CIN, PINSTYLE_CIB}, {id_SFTCTRL0, PINSTYLE_PU}, + {id_SFTCTRL1, PINSTYLE_PU}, {id_SFTCTRL2, PINSTYLE_PU}, {id_SFTCTRL3, PINSTYLE_PU}, + {{}, PINSTYLE_DEDI}, }}}; } // namespace -- cgit v1.2.3 From f79552745494455267beb1a1cea0658e54ffebe7 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Nov 2020 17:16:33 +0000 Subject: nexus: Add MULTADDSUB18X18 support Signed-off-by: David Shah --- nexus/constids.inc | 13 +++++++++ nexus/pack.cc | 82 +++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/nexus/constids.inc b/nexus/constids.inc index cf19789c..9b12d197 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -362,3 +362,16 @@ X(OUTREGBYPS) X(SIGN) X(STATICOPCODE_EN) X(PROGCONST) + +X(MULTADDSUB18X18) +X(MULTADDSUB36X36) + +X(CEPIPE) +X(RSTPIPE) + +X(LOADC) +X(ADDSUB) +X(SIGNED) +X(SUM0) +X(SUM1) +X(CINPUT) diff --git a/nexus/pack.cc b/nexus/pack.cc index 48805267..eef1334b 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1457,27 +1457,28 @@ struct NexusPacker cell->params[id_ACC108CASCADE] = std::string("BYPASSCASCADE"); cell->params[id_ACCUBYPS] = std::string("USED"); cell->params[id_ACCUMODE] = std::string("MODE7"); - cell->params[id_ADDSUBSIGNREGBYPS1] = std::string("REGISTER"); - cell->params[id_ADDSUBSIGNREGBYPS2] = std::string("REGISTER"); + cell->params[id_ADDSUBSIGNREGBYPS1] = std::string("BYPASS"); + cell->params[id_ADDSUBSIGNREGBYPS2] = std::string("BYPASS"); cell->params[id_ADDSUBSIGNREGBYPS3] = std::string("BYPASS"); cell->params[id_ADDSUB_CTRL] = std::string("ADD_ADD_CTRL_54_BIT_ADDER"); cell->params[id_CASCOUTREGBYPS] = std::string("BYPASS"); - cell->params[id_CINREGBYPS1] = std::string("REGISTER"); - cell->params[id_CINREGBYPS2] = std::string("REGISTER"); + cell->params[id_CINREGBYPS1] = std::string("BYPASS"); + cell->params[id_CINREGBYPS2] = std::string("BYPASS"); cell->params[id_CINREGBYPS3] = std::string("BYPASS"); cell->params[id_CONSTSEL] = std::string("BYPASS"); - cell->params[id_CREGBYPS1] = std::string("REGISTER"); - cell->params[id_CREGBYPS2] = std::string("REGISTER"); + cell->params[id_CREGBYPS1] = std::string("BYPASS"); + cell->params[id_CREGBYPS2] = std::string("BYPASS"); cell->params[id_CREGBYPS3] = std::string("BYPASS"); cell->params[id_DSPCASCADE] = std::string("DISABLED"); cell->params[id_GSR] = std::string("DISABLED"); - cell->params[id_LOADREGBYPS1] = std::string("REGISTER"); - cell->params[id_LOADREGBYPS2] = std::string("REGISTER"); + cell->params[id_LOADREGBYPS1] = std::string("BYPASS"); + cell->params[id_LOADREGBYPS2] = std::string("BYPASS"); cell->params[id_LOADREGBYPS3] = std::string("BYPASS"); - cell->params[id_M9ADDSUBREGBYPS1] = std::string("REGISTER"); - cell->params[id_M9ADDSUBREGBYPS2] = std::string("REGISTER"); + cell->params[id_M9ADDSUBREGBYPS1] = std::string("BYPASS"); + cell->params[id_M9ADDSUBREGBYPS2] = std::string("BYPASS"); cell->params[id_M9ADDSUBREGBYPS3] = std::string("BYPASS"); - cell->params[id_OUTREGBYPS] = std::string("REGISTER"); + cell->params[id_M9ADDSUB_CTRL] = std::string("ADDITION"); + cell->params[id_OUTREGBYPS] = std::string("BYPASS"); cell->params[id_RESET] = std::string("SYNC"); cell->params[id_ROUNDHALFUP] = std::string("DISABLED"); cell->params[id_ROUNDRTZI] = std::string("ROUND_TO_ZERO"); @@ -1526,6 +1527,7 @@ struct NexusPacker {id_MULT36X36, {36, 36, 0, 72, 8, 4, 2, false, false}}, {id_MULTPREADD9X9, {9, 9, 9, 18, 1, 0, 0, true, false}}, {id_MULTPREADD18X18, {18, 18, 18, 36, 2, 1, 0, true, false}}, + {id_MULTADDSUB18X18, {18, 18, 54, 54, 2, 1, 0, false, true}}, }; void pack_dsps() @@ -1587,13 +1589,16 @@ struct NexusPacker preadd9[i]->params[id_OPC] = std::string("INPUT_C_AS_PREADDER_OPERAND"); if (i > 0) preadd9[i]->params[id_PREADDCAS_EN] = std::string("ENABLED"); + } else if (mt.has_addsub) { + // Connect only for routeability reasons + copy_bus(ctx, ci, id_C, 10 * i, true, preadd9[i], id_C, 0, false, 10); } // Connect up signedness for the most significant nonet if ((b_start + 9) == mt.b_width) - copy_port(ctx, ci, id_SIGNEDB, preadd9[i], id_BSIGNED); + copy_port(ctx, ci, mt.has_addsub ? id_SIGNED : id_SIGNEDB, preadd9[i], id_BSIGNED); if ((a_start + 9) == mt.a_width) - copy_port(ctx, ci, id_SIGNEDA, mult9[i], id_ASIGNED); + copy_port(ctx, ci, mt.has_addsub ? id_SIGNED : id_SIGNEDA, mult9[i], id_ASIGNED); } bool mult36_used = (mt.a_width >= 36) && (mt.b_width >= 36); @@ -1612,13 +1617,56 @@ struct NexusPacker // Configure output registers for (int i = 0; i < Nreg18; i++) { // Output split across reg18s - replace_bus(ctx, ci, id_Z, i * 18, true, reg18[i], id_PP, 0, false, 18); + if (!mt.has_addsub) + replace_bus(ctx, ci, id_Z, i * 18, true, reg18[i], id_PP, 0, false, 18); // Connect control set signals copy_port(ctx, ci, id_CLK, reg18[i], id_CLK); - copy_port(ctx, ci, id_CEOUT, reg18[i], id_CEP); - copy_port(ctx, ci, id_RSTOUT, reg18[i], id_RSTP); + copy_port(ctx, ci, mt.has_addsub ? id_CEPIPE : id_CEOUT, reg18[i], id_CEP); + copy_port(ctx, ci, mt.has_addsub ? id_RSTPIPE : id_RSTOUT, reg18[i], id_RSTP); // Copy register configuration - copy_param(ci, id_REGOUTPUT, reg18[i], id_REGBYPS); + copy_param(ci, mt.has_addsub ? id_REGPIPELINE : id_REGOUTPUT, reg18[i], id_REGBYPS); + } + + if (mt.has_addsub) { + // Create and configure ACC54s + int Nacc54 = mt.c_width / 54; + std::vector acc54(Nacc54); + for (int i = 0; i < Nacc54; i++) + acc54[i] = create_dsp_cell(ci->name, id_ACC54_CORE, preadd9[0], (i * 4) + 2, 5); + for (int i = 0; i < Nacc54; i++) { + // C addsub input + copy_bus(ctx, ci, id_C, 54 * i, true, acc54[i], id_CINPUT, 0, false, 54); + // Output + replace_bus(ctx, ci, id_Z, i * 54, true, acc54[i], id_SUM0, 0, false, 36); + replace_bus(ctx, ci, id_Z, i * 54 + 36, true, acc54[i], id_SUM1, 0, false, 18); + // Control set + copy_port(ctx, ci, id_CLK, acc54[i], id_CLK); + copy_port(ctx, ci, id_RSTCTRL, acc54[i], id_RSTCTRL); + copy_port(ctx, ci, id_CECTRL, acc54[i], id_CECTRL); + copy_port(ctx, ci, id_RSTCIN, acc54[i], id_RSTCIN); + copy_port(ctx, ci, id_CECIN, acc54[i], id_CECIN); + copy_port(ctx, ci, id_RSTOUT, acc54[i], id_RSTO); + copy_port(ctx, ci, id_CEOUT, acc54[i], id_CEO); + // Add/acc control + copy_port(ctx, ci, id_CIN, acc54[i], id_CIN); + copy_port(ctx, ci, id_SIGNED, acc54[i], id_SIGNEDI); + copy_port(ctx, ci, id_ADDSUB, acc54[i], id_ADDSUB0); + copy_port(ctx, ci, id_ADDSUB, acc54[i], id_ADDSUB1); + copy_port(ctx, ci, id_LOADC, acc54[i], id_LOAD); + // Configuration + copy_param(ci, id_REGINPUTC, acc54[i], id_CREGBYPS1); + copy_param(ci, id_REGADDSUB, acc54[i], id_ADDSUBSIGNREGBYPS1); + copy_param(ci, id_REGADDSUB, acc54[i], id_M9ADDSUBREGBYPS1); + copy_param(ci, id_REGLOADC, acc54[i], id_LOADREGBYPS1); + copy_param(ci, id_REGLOADC2, acc54[i], id_LOADREGBYPS2); + copy_param(ci, id_REGCIN, acc54[i], id_CINREGBYPS1); + + copy_param(ci, id_REGPIPELINE, acc54[i], id_CREGBYPS2); + copy_param(ci, id_REGPIPELINE, acc54[i], id_ADDSUBSIGNREGBYPS2); + copy_param(ci, id_REGPIPELINE, acc54[i], id_CINREGBYPS2); + copy_param(ci, id_REGPIPELINE, acc54[i], id_M9ADDSUBREGBYPS2); + copy_param(ci, id_REGOUTPUT, acc54[i], id_OUTREGBYPS); + } } // Misc finalisation -- cgit v1.2.3 From 5cf7f01169a009b0ac87d908683523bf0cfe1d37 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Nov 2020 19:49:54 +0000 Subject: nexus: Add MULTADDSUB36X36 Signed-off-by: David Shah --- nexus/pack.cc | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index eef1334b..3332f848 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1528,6 +1528,7 @@ struct NexusPacker {id_MULTPREADD9X9, {9, 9, 9, 18, 1, 0, 0, true, false}}, {id_MULTPREADD18X18, {18, 18, 18, 36, 2, 1, 0, true, false}}, {id_MULTADDSUB18X18, {18, 18, 54, 54, 2, 1, 0, false, true}}, + {id_MULTADDSUB36X36, {36, 36, 108, 108, 8, 4, 2, false, true}}, }; void pack_dsps() @@ -1554,7 +1555,10 @@ struct NexusPacker for (int i = 0; i < mt.N18x36; i++) mult18x36[i] = create_dsp_cell(ci->name, id_MULT18X36_CORE, preadd9[0], (i * 4) + 2, 4); for (int i = 0; i < Nreg18; i++) { - reg18[i] = create_dsp_cell(ci->name, id_REG18_CORE, preadd9[0], (i / 4) * 4 + 2, i % 4); + int idx = i; + if (mt.has_addsub && (i >= 4)) + idx += 2; + reg18[i] = create_dsp_cell(ci->name, id_REG18_CORE, preadd9[0], (idx / 4) * 4 + 2, idx % 4); } // Configure the 9x9 preadd+multiply blocks @@ -1591,7 +1595,7 @@ struct NexusPacker preadd9[i]->params[id_PREADDCAS_EN] = std::string("ENABLED"); } else if (mt.has_addsub) { // Connect only for routeability reasons - copy_bus(ctx, ci, id_C, 10 * i, true, preadd9[i], id_C, 0, false, 10); + copy_bus(ctx, ci, id_C, 10 * i + ((i >= 4) ? 14 : 0), true, preadd9[i], id_C, 0, false, 10); } // Connect up signedness for the most significant nonet @@ -1648,8 +1652,12 @@ struct NexusPacker copy_port(ctx, ci, id_RSTOUT, acc54[i], id_RSTO); copy_port(ctx, ci, id_CEOUT, acc54[i], id_CEO); // Add/acc control - copy_port(ctx, ci, id_CIN, acc54[i], id_CIN); - copy_port(ctx, ci, id_SIGNED, acc54[i], id_SIGNEDI); + if (i == 0) + copy_port(ctx, ci, id_CIN, acc54[i], id_CIN); + else + ctx->set_cell_pinmux(acc54[i], id_CIN, PINMUX_1); + if (i == (Nacc54 - 1)) + copy_port(ctx, ci, id_SIGNED, acc54[i], id_SIGNEDI); copy_port(ctx, ci, id_ADDSUB, acc54[i], id_ADDSUB0); copy_port(ctx, ci, id_ADDSUB, acc54[i], id_ADDSUB1); copy_port(ctx, ci, id_LOADC, acc54[i], id_LOAD); @@ -1666,6 +1674,15 @@ struct NexusPacker copy_param(ci, id_REGPIPELINE, acc54[i], id_CINREGBYPS2); copy_param(ci, id_REGPIPELINE, acc54[i], id_M9ADDSUBREGBYPS2); copy_param(ci, id_REGOUTPUT, acc54[i], id_OUTREGBYPS); + + if (i == 1) { + // Top ACC54 in a 108-bit config + acc54[i]->params[id_ACCUMODE] = std::string("MODE6"); + acc54[i]->params[id_ACC108CASCADE] = std::string("CASCADE2ACCU54TOFORMACCU108"); + } else if ((i == 0) && (Nacc54 == 2)) { + // Bottom ACC54 in a 108-bit config + acc54[i]->params[id_ACCUMODE] = std::string("MODE2"); + } } } -- cgit v1.2.3 From e3b3201d530c035e0c1f118e3752e16ca351311a Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 18 Nov 2020 10:17:27 +0000 Subject: nexus: Clocked MULTADDSUB36X36 fix Signed-off-by: David Shah --- nexus/pack.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nexus/pack.cc b/nexus/pack.cc index 3332f848..a410e2bd 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1651,6 +1651,8 @@ struct NexusPacker copy_port(ctx, ci, id_CECIN, acc54[i], id_CECIN); copy_port(ctx, ci, id_RSTOUT, acc54[i], id_RSTO); copy_port(ctx, ci, id_CEOUT, acc54[i], id_CEO); + copy_port(ctx, ci, id_RSTC, acc54[i], id_RSTC); + copy_port(ctx, ci, id_CEC, acc54[i], id_CEC); // Add/acc control if (i == 0) copy_port(ctx, ci, id_CIN, acc54[i], id_CIN); -- cgit v1.2.3 From 76543d05e7ef9b7bd87b7641ace08360d81dc023 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 18 Nov 2020 10:54:52 +0000 Subject: nexus: Tweak heuristics to improve routeability Signed-off-by: David Shah --- nexus/arch.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 3a279f4a..1ecf168d 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -545,7 +545,7 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const int dst_x = dst.tile % chip_info->width, dst_y = dst.tile / chip_info->width; int dist_x = std::abs(src_x - dst_x); int dist_y = std::abs(src_y - dst_y); - return 100 * dist_x + 100 * dist_y + 250; + return 75 * dist_x + 75 * dist_y + 200; } delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const { @@ -608,7 +608,7 @@ bool Arch::place() cfg.cellGroups.back().insert(id_OXIDE_COMB); cfg.cellGroups.back().insert(id_OXIDE_FF); - cfg.beta = 0.7; + cfg.beta = 0.6; cfg.criticalityExponent = 7; if (!placer_heap(getCtx(), cfg)) return false; -- cgit v1.2.3 From df3c6dfe3ec3740007d4dbd8ae6347e2af345c79 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 20 Nov 2020 10:27:04 +0000 Subject: nexus: Preliminary integration of DSP timing data Signed-off-by: David Shah --- nexus/arch.cc | 49 ++++++++++++++++++++++++++++++++++++++++++++++ nexus/arch.h | 4 ++++ nexus/pack.cc | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 111 insertions(+), 4 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 1ecf168d..d5e901eb 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -452,6 +452,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort if (toPort == id_F || toPort == id_OFX) return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); } + } else if (is_dsp_cell(cell)) { + if (fromPort == id_CLK) + return false; // don't include delays that are actually clock-to-out here + return lookup_cell_delay(cell->tmg_index, lookup_port(fromPort), lookup_port(toPort), delay); } return false; } @@ -459,6 +463,11 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const { auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; }; + auto lookup_port = [&](IdString p) { + auto fnd = cell->tmg_portmap.find(p); + return fnd == cell->tmg_portmap.end() ? p : fnd->second; + }; + clockInfoCount = 0; if (cell->type == id_OXIDE_COMB) { if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_SEL || port == id_F1 || port == id_FCI || port == id_WDI) @@ -498,6 +507,15 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_CLOCK_INPUT; clockInfoCount = 1; return (cell->ports.at(port).type == PORT_IN) ? TMG_REGISTER_INPUT : TMG_REGISTER_OUTPUT; + } else if (cell->type == id_MULT18_CORE || cell->type == id_MULT18X36_CORE || cell->type == id_MULT36_CORE) { + return (cell->ports.at(port).type == PORT_IN) ? TMG_COMB_INPUT : TMG_COMB_OUTPUT; + } else if (cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE) { + if (port == id_CLK) + return TMG_CLOCK_INPUT; + auto type = lookup_port_type(cell->tmg_index, lookup_port(port), cell->ports.at(port).type, id_CLK); + if (type == TMG_REGISTER_INPUT || type == TMG_REGISTER_OUTPUT) + clockInfoCount = 1; + return type; } return TMG_IGNORE; } @@ -531,6 +549,14 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } // Lookup edge based on inversion info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE; + } else if (cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE) { + info.clock_port = id_CLK; + if (cell->ports.at(port).type == PORT_IN) { + lookup_cell_setuphold(cell->tmg_index, lookup_port(port), id_CLK, info.setup, info.hold); + } else { + NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, lookup_port(port), info.clockToQ)); + } + info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE; } else { NPNR_ASSERT_FALSE("missing clocking info"); } @@ -819,6 +845,12 @@ int db_binary_search(const Tres *list, int count, Tgetter key_getter, Tkey key) } } // namespace +bool Arch::is_dsp_cell(const CellInfo *cell) const +{ + return cell->type == id_MULT18_CORE || cell->type == id_MULT18X36_CORE || cell->type == id_MULT36_CORE || + cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE; +} + int Arch::get_cell_timing_idx(IdString cell_type, IdString cell_variant) const { return db_binary_search( @@ -885,6 +917,23 @@ void Arch::lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock delay.min_delay = ct.prop_delays[dly_idx].min_delay; delay.max_delay = ct.prop_delays[dly_idx].max_delay; } +TimingPortClass Arch::lookup_port_type(int type_idx, IdString port, PortType dir, IdString clock) const +{ + if (dir == PORT_IN) { + NPNR_ASSERT(type_idx != -1); + const auto &ct = speed_grade->cell_types[type_idx]; + // If a setup-hold entry exists, then this is a register input + int sh_idx = db_binary_search( + ct.setup_holds.get(), ct.num_setup_holds, + [](const CellSetupHoldPOD &sh) { return std::make_pair(sh.sig_port, sh.clock_port); }, + std::make_pair(port.index, clock.index)); + return (sh_idx != -1) ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; + } else { + DelayInfo dly; + // If a clock-to-out entry exists, then this is a register output + return lookup_cell_delay(type_idx, clock, port, dly) ? TMG_REGISTER_OUTPUT : TMG_COMB_OUTPUT; + } +} // ----------------------------------------------------------------------- diff --git a/nexus/arch.h b/nexus/arch.h index debf66b0..1367161c 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1559,6 +1559,8 @@ struct Arch : BaseCtx // ------------------------------------------------- // Cell timing lookup helpers + bool is_dsp_cell(const CellInfo *cell) const; + // Given cell type and variant, get the index inside the speed grade timing data int get_cell_timing_idx(IdString cell_type, IdString cell_variant = IdString()) const; // Return true and set delay if a comb path exists in a given cell timing index @@ -1571,6 +1573,8 @@ struct Arch : BaseCtx DelayInfo &hold) const; // Similar to lookup_cell_delay but only needs the 'to' signal, intended for clk->out delays void lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock, DelayInfo &delay) const; + // Attempt to look up port type based on database + TimingPortClass lookup_port_type(int type_idx, IdString port, PortType dir, IdString clock) const; // ------------------------------------------------- // List of IO constraints, used by PDC parser diff --git a/nexus/pack.cc b/nexus/pack.cc index a410e2bd..ff5d1047 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -264,7 +264,7 @@ struct NexusPacker std::unordered_map reference_bels; - void autocreate_ports(CellInfo *cell, bool include_outputs = false) + void autocreate_ports(CellInfo *cell) { // Automatically create ports for all inputs, and maybe outputs, of a cell; even if they were left off the // instantiation so we can tie them to constants as appropriate This also checks for any cells that don't have @@ -288,7 +288,7 @@ struct NexusPacker BelId bel = reference_bels.at(cell->type); for (IdString pin : ctx->getBelPins(bel)) { PortType dir = ctx->getBelPinType(bel, pin); - if (dir != PORT_IN && !include_outputs) + if (dir != PORT_IN) continue; if (cell->ports.count(pin)) continue; @@ -1236,6 +1236,14 @@ struct NexusPacker // using the temporary placement that we use solely to access the routing graph void auto_cascade_cell(CellInfo *cell, BelId bel, const std::unordered_map &bel2cell) { + // Create outputs based on the actual bel + for (auto bp : ctx->getBelPins(bel)) { + if (ctx->getBelPinType(bel, bp) != PORT_OUT) + continue; + if (cell->ports.count(bp)) + continue; + cell->addOutput(bp); + } for (auto port : sorted_ref(cell->ports)) { // Skip if not an output, or being used already for something else if (port.second.type != PORT_OUT || port.second.net != nullptr) @@ -1371,9 +1379,9 @@ struct NexusPacker ctx->nameOf(root->type)); // Create the necessary new ports - autocreate_ports(root, true); + autocreate_ports(root); for (auto child : root->constr_children) - autocreate_ports(child, true); + autocreate_ports(child); // Insert cascade connections from all cells in the macro auto_cascade_cell(root, cell2bel.at(root->name), bel2cell); @@ -1738,6 +1746,12 @@ void Arch::assignArchInfo() } } +const std::vector dsp_bus_prefices = { + "M9ADDSUB", "ADDSUB", "SFTCTRL", "DSPIN", "CINPUT", "DSPOUT", "CASCOUT", "CASCIN", "PML72", "PMH72", "SUM1", + "SUM0", "BRS1", "BRS2", "BLS1", "BLS2", "BLSO", "BRSO", "PL18", "PH18", "PL36", "PH36", + "PL72", "PH72", "P72", "P36", "P18", "AS1", "AS2", "ARL", "ARH", "BRL", "BRH", + "AO", "BO", "AB", "AR", "BR", "PM", "PP", "A", "B", "C"}; + void Arch::assignCellInfo(CellInfo *cell) { cell->tmg_index = -1; @@ -1806,6 +1820,46 @@ void Arch::assignCellInfo(CellInfo *cell) cell->tmg_index = get_cell_timing_idx(id(str_or_default(cell->params, id_MODE, "DP16K") + "_MODE")); NPNR_ASSERT(cell->tmg_index != -1); + } else if (is_dsp_cell(cell)) { + // Strip off bus indices to get the timing ports + // as timing is generally word-wide + for (const auto &port : cell->ports) { + const std::string &name = port.first.str(this); + size_t idx_end = name.find_last_not_of("0123456789"); + if (idx_end == std::string::npos) + continue; + for (const auto &p : dsp_bus_prefices) { + if (name.size() > p.size() && name.substr(0, p.size()) == p && idx_end <= p.size()) { + cell->tmg_portmap[port.first] = id(p); + break; + } + } + } + // Build up the configuration string + std::set config; + for (const auto ¶m : cell->params) { + const std::string &name = param.first.str(this); + size_t byp_pos = name.find("REGBYPS"); + if (byp_pos != std::string::npos && param.second.str == "REGISTER") { + // Register enabled + config.insert(name.substr(0, byp_pos + 3) + name.substr(byp_pos + 7)); + } else if (param.first == id_BYPASS_PREADD9 && param.second.str == "BYPASS") { + // PREADD9 bypass + config.insert("BYPASS"); + } + } + std::string config_str; + for (const auto &cfg : config) { + if (!config_str.empty()) + config_str += ','; + config_str += cfg; + } + cell->tmg_index = get_cell_timing_idx(cell->type, id(config_str)); + if (cell->tmg_index == -1) { + log_warning("Unsupported timing config '%s' on %s cell '%s', falling back to default.\n", + config_str.c_str(), nameOf(cell->type), nameOf(cell)); + cell->tmg_index = get_cell_timing_idx(cell->type); + } } } -- cgit v1.2.3 From f84850064fc6747ea13faf41e4bae8b85be0ae5f Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 23 Nov 2020 14:37:15 +0000 Subject: nexus: Improve error handling in global router Signed-off-by: David Shah --- nexus/global.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nexus/global.cc b/nexus/global.cc index 2d183e95..f7abb399 100644 --- a/nexus/global.cc +++ b/nexus/global.cc @@ -55,6 +55,14 @@ struct NexusGlobalRouter WireId src = ctx->getNetinfoSourceWire(net); WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(user_idx)); + if (src == WireId()) + log_error("Net '%s' has an invalid source port %s.%s\n", ctx->nameOf(net), ctx->nameOf(net->driver.cell), + ctx->nameOf(net->driver.port)); + + if (dst == WireId()) + log_error("Net '%s' has an invalid sink port %s.%s\n", ctx->nameOf(net), + ctx->nameOf(net->users.at(user_idx).cell), ctx->nameOf(net->users.at(user_idx).port)); + if (ctx->getBoundWireNet(src) != net) ctx->bindWire(src, net, STRENGTH_LOCKED); -- cgit v1.2.3 From ca73e14cf9456d96f9cd6b04c0390c5fc5ee0973 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 23 Nov 2020 14:41:40 +0000 Subject: nexus: Add post-place LUTFF optimisation Signed-off-by: David Shah --- nexus/arch.cc | 28 +++++++-- nexus/arch.h | 23 ++++---- nexus/main.cc | 7 ++- nexus/post_place.cc | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 200 insertions(+), 19 deletions(-) create mode 100644 nexus/post_place.cc diff --git a/nexus/arch.cc b/nexus/arch.cc index d5e901eb..f5edaeb9 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -120,6 +120,21 @@ Arch::Arch(ArchArgs args) : args(args) for (size_t i = 0; i < chip_info->num_tiles; i++) { tileStatus[i].boundcells.resize(db->loctypes[chip_info->grid[i].loc_type].num_bels); } + // This structure is needed for a fast getBelByLocation because bels can have an offset + for (size_t i = 0; i < chip_info->num_tiles; i++) { + auto &loc = db->loctypes[chip_info->grid[i].loc_type]; + for (unsigned j = 0; j < loc.num_bels; j++) { + auto &bel = loc.bels[j]; + int rel_bel_tile; + if (!rel_tile(i, bel.rel_x, bel.rel_y, rel_bel_tile)) + continue; + auto &ts = tileStatus.at(rel_bel_tile); + if (int(ts.bels_by_z.size()) <= bel.z) + ts.bels_by_z.resize(bel.z + 1); + ts.bels_by_z[bel.z].tile = i; + ts.bels_by_z[bel.z].index = j; + } + } init_cell_pin_data(); // Validate and set up package package_idx = -1; @@ -609,13 +624,13 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const bb.y1 = std::max(bb.y1, y); }; + extend(dst_x, dst_y); + if (dsp_wires.count(src) || dsp_wires.count(dst)) { - bb.x0 -= 5; - bb.x1 += 5; + bb.x0 = std::max(0, bb.x0 - 6); + bb.x1 = std::min(chip_info->width, bb.x1 + 6); } - extend(dst_x, dst_y); - return bb; } @@ -634,7 +649,7 @@ bool Arch::place() cfg.cellGroups.back().insert(id_OXIDE_COMB); cfg.cellGroups.back().insert(id_OXIDE_FF); - cfg.beta = 0.6; + cfg.beta = 0.5; cfg.criticalityExponent = 7; if (!placer_heap(getCtx(), cfg)) return false; @@ -644,6 +659,9 @@ bool Arch::place() } else { log_error("Nexus architecture does not support placer '%s'\n", placer.c_str()); } + + post_place_opt(); + getCtx()->attrs[getCtx()->id("step")] = std::string("place"); archInfoToAttributes(); return true; diff --git a/nexus/arch.h b/nexus/arch.h index 1367161c..1d0d38b5 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -932,6 +932,7 @@ struct Arch : BaseCtx struct TileStatus { std::vector boundcells; + std::vector bels_by_z; LogicTileStatus *lts = nullptr; ~TileStatus() { delete lts; } }; @@ -1034,8 +1035,8 @@ struct Arch : BaseCtx { NPNR_ASSERT(bel != BelId()); Loc loc; - loc.x = bel.tile % chip_info->width; - loc.y = bel.tile / chip_info->width; + loc.x = bel.tile % chip_info->width + bel_data(bel).rel_x; + loc.y = bel.tile / chip_info->width + bel_data(bel).rel_y; loc.z = bel_data(bel).z; return loc; } @@ -1043,17 +1044,10 @@ struct Arch : BaseCtx BelId getBelByLocation(Loc loc) const { BelId ret; - auto &t = db->loctypes[chip_info->grid[loc.y * chip_info->width + loc.x].loc_type]; - if (loc.x >= 0 && loc.x < chip_info->width && loc.y >= 0 && loc.y < chip_info->height) { - for (size_t i = 0; i < t.num_bels; i++) { - if (t.bels[i].z == loc.z) { - ret.tile = loc.y * chip_info->width + loc.x; - ret.index = i; - break; - } - } - } - return ret; + auto &t = tileStatus.at(loc.y * chip_info->width + loc.x); + if (loc.z >= int(t.bels_by_z.size())) + return BelId(); + return t.bels_by_z.at(loc.z); } BelRange getBelsByTile(int x, int y) const; @@ -1400,6 +1394,9 @@ struct Arch : BaseCtx bool place(); bool route(); + // arch-specific post-placement optimisations + void post_place_opt(); + // ------------------------------------------------- // Assign architecure-specific arguments to nets and cells, which must be // called between packing or further diff --git a/nexus/main.cc b/nexus/main.cc index 495793a3..cced1b95 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -50,6 +50,8 @@ po::options_description NexusCommandHandler::getArchOptions() specific.add_options()("device", po::value(), "device name"); specific.add_options()("fasm", po::value(), "fasm file to write"); specific.add_options()("pdc", po::value(), "physical constraints file"); + specific.add_options()("no-post-place-opt", "disable post-place repacking (debugging use only)"); + return specific; } @@ -71,7 +73,10 @@ std::unique_ptr NexusCommandHandler::createContext(std::unordered_map(); - return std::unique_ptr(new Context(chipArgs)); + auto ctx = std::unique_ptr(new Context(chipArgs)); + if (vm.count("no-post-place-opt")) + ctx->settings[ctx->id("no_post_place_opt")] = Property::State::S1; + return ctx; } void NexusCommandHandler::customAfterLoad(Context *ctx) diff --git a/nexus/post_place.cc b/nexus/post_place.cc new file mode 100644 index 00000000..65676188 --- /dev/null +++ b/nexus/post_place.cc @@ -0,0 +1,161 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * 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 "design_utils.h" +#include "log.h" +#include "nextpnr.h" +#include "timing.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct NexusPostPlaceOpt +{ + Context *ctx; + NetCriticalityMap net_crit; + + NexusPostPlaceOpt(Context *ctx) : ctx(ctx){}; + + inline bool is_constrained(CellInfo *cell) + { + return cell->constr_parent != nullptr || !cell->constr_children.empty(); + } + + bool swap_cell_placement(CellInfo *cell, BelId new_bel) + { + if (is_constrained(cell)) + return false; + BelId oldBel = cell->bel; + CellInfo *other_cell = ctx->getBoundBelCell(new_bel); + if (other_cell != nullptr && (is_constrained(other_cell) || other_cell->belStrength > STRENGTH_WEAK)) { + return false; + } + + ctx->unbindBel(oldBel); + if (other_cell != nullptr) { + ctx->unbindBel(new_bel); + } + + ctx->bindBel(new_bel, cell, STRENGTH_WEAK); + + if (other_cell != nullptr) { + ctx->bindBel(oldBel, other_cell, STRENGTH_WEAK); + } + + if (!ctx->isBelLocationValid(new_bel) || ((other_cell != nullptr && !ctx->isBelLocationValid(oldBel)))) { + // New placement is not legal. + ctx->unbindBel(new_bel); + if (other_cell != nullptr) + ctx->unbindBel(oldBel); + + // Revert. + ctx->bindBel(oldBel, cell, STRENGTH_WEAK); + if (other_cell != nullptr) + ctx->bindBel(new_bel, other_cell, STRENGTH_WEAK); + return false; + } + + return true; + } + + int get_distance(BelId a, BelId b) + { + Loc la = ctx->getBelLocation(a); + Loc lb = ctx->getBelLocation(b); + return std::abs(la.x - lb.x) + std::abs(la.y - lb.y); + } + + BelId lut_to_ff(BelId lut) + { + Loc ff_loc = ctx->getBelLocation(lut); + ff_loc.z += (Arch::BEL_FF0 - Arch::BEL_LUT0); + return ctx->getBelByLocation(ff_loc); + } + + void opt_lutffs() + { + int moves_made = 0; + for (auto cell : sorted(ctx->cells)) { + // Search for FF cells + CellInfo *ff = cell.second; + if (ff->type != id_OXIDE_FF) + continue; + // Check M ('fabric') input net + NetInfo *m = get_net_or_empty(ff, id_M); + if (m == nullptr) + continue; + + // Ignore FFs that need both DI and M (PRLD mode) + if (get_net_or_empty(ff, id_DI) != nullptr) + continue; + + const auto &drv = m->driver; + // Skip if driver isn't a LUT/MUX2 + if (drv.cell == nullptr || drv.cell->type != id_OXIDE_COMB || (drv.port != id_F && drv.port != id_OFX)) + continue; + CellInfo *lut = drv.cell; + // Check distance to move isn't too far + if (get_distance(ff->bel, lut->bel) > lut_ff_radius) + continue; + // Find the bel we plan to move into + BelId dest_ff = lut_to_ff(lut->bel); + NPNR_ASSERT(dest_ff != BelId()); + NPNR_ASSERT(ctx->getBelType(dest_ff) == id_OXIDE_FF); + // Ended up in the ideal location by chance + if (dest_ff != ff->bel) { + // If dest_ff is already placed *and* using direct 'DI' input, don't touch it + CellInfo *dest_ff_cell = ctx->getBoundBelCell(dest_ff); + if (dest_ff_cell != nullptr && get_net_or_empty(dest_ff_cell, id_DI) != nullptr) + continue; + // Attempt the swap + bool swap_result = swap_cell_placement(ff, dest_ff); + if (!swap_result) + continue; + } + // Use direct interconnect + rename_port(ctx, ff, id_M, id_DI); + ff->params[id_SEL] = std::string("DL"); + ++moves_made; + continue; + } + log_info(" created %d direct LUT-FF pairs\n", moves_made); + } + + void operator()() + { + get_criticalities(ctx, &net_crit); + opt_lutffs(); + } + + // Configuration + const int lut_ff_radius = 2; + const int lut_lut_radius = 1; + const float lut_lut_crit = 0.85; +}; + +void Arch::post_place_opt() +{ + if (bool_or_default(settings, id("no_post_place_opt"))) + return; + log_info("Running post-place optimisations...\n"); + NexusPostPlaceOpt opt(getCtx()); + opt(); +} + +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From bb50b54d80c407854a2553b76e231bc5513b6295 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 25 Nov 2020 08:37:42 +0000 Subject: nexus: Add to CI Signed-off-by: David Shah --- .cirrus.yml | 2 +- .cirrus/Dockerfile.ubuntu16.04 | 12 +++++++++++- .cirrus/archcheck.sh | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index a493b04f..5221be46 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -5,7 +5,7 @@ task: memory: 20 dockerfile: .cirrus/Dockerfile.ubuntu16.04 - build_script: mkdir build && cd build && cmake .. -DARCH=all -DBUILD_TESTS=on && make -j $(nproc) + build_script: mkdir build && cd build && cmake .. -DARCH=all -DOXIDE_INSTALL_PREFIX=$HOME/.cargo -DBUILD_TESTS=on && make -j3 submodule_script: git submodule sync --recursive && git submodule update --init --recursive test_generic_script: cd build && ./nextpnr-generic-test test_ice40_script: cd build && ./nextpnr-ice40-test diff --git a/.cirrus/Dockerfile.ubuntu16.04 b/.cirrus/Dockerfile.ubuntu16.04 index 92ab2bae..e8de4ed9 100644 --- a/.cirrus/Dockerfile.ubuntu16.04 +++ b/.cirrus/Dockerfile.ubuntu16.04 @@ -9,7 +9,7 @@ RUN set -e -x ;\ build-essential autoconf cmake clang bison wget flex gperf \ libreadline-dev gawk tcl-dev libffi-dev graphviz xdot python3-dev \ libboost-all-dev qt5-default git libftdi-dev pkg-config libeigen3-dev \ - zlib1g-dev + zlib1g-dev curl RUN set -e -x ;\ mkdir -p /usr/local/src ;\ @@ -52,3 +52,13 @@ RUN set -e -x ;\ cmake . ;\ make -j $(nproc) ;\ make install + +RUN set -e -x ;\ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y ;\ + mkdir -p /usr/local/src ;\ + cd /usr/local/src ;\ + git clone --recursive https://github.com/daveshah1/prjoxide.git ;\ + cd prjoxide ;\ + git reset --hard bd5122fab12c1a517588365bd95cbfaa47eff25c ;\ + cd libprjoxide ;\ + PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide diff --git a/.cirrus/archcheck.sh b/.cirrus/archcheck.sh index fde19216..35f115d0 100755 --- a/.cirrus/archcheck.sh +++ b/.cirrus/archcheck.sh @@ -4,3 +4,4 @@ echo "Running archcheck!" ${BUILD_DIR}/nextpnr-ice40 --hx8k --package ct256 --test ${BUILD_DIR}/nextpnr-ice40 --up5k --package sg48 --test ${BUILD_DIR}/nextpnr-ecp5 --um5g-25k --package CABGA381 --test +${BUILD_DIR}/nextpnr-nexus --device LIFCL-40-9BG400CES --test -- cgit v1.2.3 From 567166aeceefc513c498c542873c1533bd73dfbe Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 26 Nov 2020 14:20:14 +0000 Subject: nexus: Fix db integrity check Signed-off-by: David Shah --- .cirrus/Dockerfile.ubuntu16.04 | 2 +- nexus/arch.cc | 23 ++++++----------------- nexus/arch.h | 2 +- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/.cirrus/Dockerfile.ubuntu16.04 b/.cirrus/Dockerfile.ubuntu16.04 index e8de4ed9..a62cf4c6 100644 --- a/.cirrus/Dockerfile.ubuntu16.04 +++ b/.cirrus/Dockerfile.ubuntu16.04 @@ -59,6 +59,6 @@ RUN set -e -x ;\ cd /usr/local/src ;\ git clone --recursive https://github.com/daveshah1/prjoxide.git ;\ cd prjoxide ;\ - git reset --hard bd5122fab12c1a517588365bd95cbfaa47eff25c ;\ + git reset --hard 72dbb7973f31a30c3b9d18f3bac97caaea9a7f33 ;\ cd libprjoxide ;\ PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide diff --git a/nexus/arch.cc b/nexus/arch.cc index f5edaeb9..f222a5ad 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -202,24 +202,13 @@ BelId Arch::getBelByName(IdString name) const return BelId(); } -BelRange Arch::getBelsByTile(int x, int y) const +std::vector Arch::getBelsByTile(int x, int y) const { - BelRange br; - NPNR_ASSERT(x >= 0 && x < chip_info->width); - NPNR_ASSERT(y >= 0 && y < chip_info->height); - br.b.cursor_tile = y * chip_info->width + x; - br.e.cursor_tile = y * chip_info->width + x; - br.b.cursor_index = 0; - br.e.cursor_index = db->loctypes[chip_info->grid[br.b.cursor_tile].loc_type].num_bels; - br.b.chip = chip_info; - br.b.db = db; - br.e.chip = chip_info; - br.e.db = db; - if (br.e.cursor_index == -1) - ++br.e.cursor_index; - else - ++br.e; - return br; + std::vector bels; + for (auto bel : tileStatus.at(y * chip_info->width + x).bels_by_z) + if (bel != BelId()) + bels.push_back(bel); + return bels; } WireId Arch::getBelPinWire(BelId bel, IdString pin) const diff --git a/nexus/arch.h b/nexus/arch.h index 1d0d38b5..d4d4799e 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1050,7 +1050,7 @@ struct Arch : BaseCtx return t.bels_by_z.at(loc.z); } - BelRange getBelsByTile(int x, int y) const; + std::vector getBelsByTile(int x, int y) const; bool getBelGlobalBuf(BelId bel) const { return false; } -- cgit v1.2.3 From 2fe8bebc6ce464afadef2403a8331031e16c5a5d Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 30 Nov 2020 08:59:04 +0000 Subject: nexus: Update docs and CMake Signed-off-by: David Shah --- .cirrus.yml | 2 +- CMakeLists.txt | 10 +++++++++- README.md | 16 +++++++++++++++- docs/nexus.md | 20 ++++++++++++++++++++ 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 docs/nexus.md diff --git a/.cirrus.yml b/.cirrus.yml index 5221be46..e24f3271 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -5,7 +5,7 @@ task: memory: 20 dockerfile: .cirrus/Dockerfile.ubuntu16.04 - build_script: mkdir build && cd build && cmake .. -DARCH=all -DOXIDE_INSTALL_PREFIX=$HOME/.cargo -DBUILD_TESTS=on && make -j3 + build_script: mkdir build && cd build && cmake .. -DARCH=all+alpha -DOXIDE_INSTALL_PREFIX=$HOME/.cargo -DBUILD_TESTS=on && make -j3 submodule_script: git submodule sync --recursive && git submodule update --init --recursive test_generic_script: cd build && ./nextpnr-generic-test test_ice40_script: cd build && ./nextpnr-ice40-test diff --git a/CMakeLists.txt b/CMakeLists.txt index 29f73f47..baddfb98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,8 @@ set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables") # List of families to build set(FAMILIES generic ice40 ecp5 nexus) +set(STABLE_FAMILIES generic ice40 ecp5) +set(EXPERIMENTAL_FAMILIES nexus) set(ARCH "" CACHE STRING "Architecture family for nextpnr build") set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES}) @@ -75,14 +77,20 @@ if (NOT ARCH) message(STATUS "Architecture needs to be set, set desired one with -DARCH=xxx") message(STATUS "Supported architectures are :") message(STATUS " all") + message(STATUS " all+alpha") foreach(item ${FAMILIES}) message(STATUS " ${item}") endforeach() message(FATAL_ERROR "Architecture setting is mandatory") endif () +if (ARCH STREQUAL "all+alpha") + SET(ARCH ${STABLE_FAMILIES} ${EXPERIMENTAL_FAMILIES}) +endif() + + if (ARCH STREQUAL "all") - SET(ARCH ${FAMILIES}) + SET(ARCH ${STABLE_FAMILIES}) endif() foreach(item ${ARCH}) diff --git a/README.md b/README.md index 6a70e1e3..699e0c55 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ tool. Currently nextpnr supports: * Lattice iCE40 devices supported by [Project IceStorm](http://www.clifford.at/icestorm/) * Lattice ECP5 devices supported by [Project Trellis](https://github.com/YosysHQ/prjtrellis) + * Lattice Nexus devices supported by [Project Oxide](https://github.com/daveshah1/prjoxide) * *(experimental)* a "generic" back-end for user-defined architectures There is some work in progress towards [support for Xilinx devices](https://github.com/daveshah1/nextpnr-xilinx/) but it is not upstream and not intended for end users at the present time. We hope to see more FPGA families supported in the future. We would love your help in developing this awesome new project! @@ -103,6 +104,19 @@ sudo make install - Examples of the ECP5 flow for a range of boards can be found in the [Project Trellis Examples](https://github.com/YosysHQ/prjtrellis/tree/master/examples). +### nextpnr-nexus + +For Nexus support, install [Project Oxide](https://github.com/daveshah1/prjoxide) to `$HOME/.cargo` or another location, which should be passed as `-DOXIDE_INSTALL_PREFIX=$HOME/.cargo` to CMake. Then build and install `nextpnr-nexus` using the following commands: + +``` +cmake . -DARCH=nexus -DOXIDE_INSTALL_PREFIX=$HOME/.cargo +make -j$(nproc) +sudo make install +``` + + - Examples of the Nexus flow for a range of boards can be found in the [Project Oxide Examples](https://github.com/daveshah1/prjoxide/tree/master/examples). + +Nexus support is currently experimental, and has only been tested with engineering sample silicon. ### nextpnr-generic @@ -126,7 +140,7 @@ make -j$(nproc) sudo make install ``` -To build every available architecture, use `-DARCH=all`. +To build every available stable architecture, use `-DARCH=all`. To include experimental arches (currently nexus), use `-DARCH=all+alpha`. Pre-generating chip databases ----------------------------- diff --git a/docs/nexus.md b/docs/nexus.md new file mode 100644 index 00000000..99ac926f --- /dev/null +++ b/docs/nexus.md @@ -0,0 +1,20 @@ +# nextpnr-nexus notes + +### Constraints + +Currently the following PDC constraint styles are supported for IO constraints: + +``` +ldc_set_location -site {G13} [get_ports gsrn] +ldc_set_port -iobuf {IO_TYPE=LVCMOS33} [get_ports {led[0]}] +``` + +Timing constraints are currently ignored, but should be expected to be supported soon. + +### Command Line + +A full device name is specified on the command line. It should be postfixed with 'ES' if using an engineering sample device to ensure correct use of the ES IDCODE. + +``` +--device LIFCL-40-9BG400CES +``` -- cgit v1.2.3