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 --- nexus/arch.h | 172 ++++++++++++++++++++++++++++++++++++++++++ nexus/archdefs.h | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/constids.inc | 0 nexus/family.cmake | 0 4 files changed, 387 insertions(+) create mode 100644 nexus/arch.h create mode 100644 nexus/archdefs.h create mode 100644 nexus/constids.inc create mode 100644 nexus/family.cmake (limited to 'nexus') 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 (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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 (limited to 'nexus') 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(-) (limited to 'nexus') 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 (limited to 'nexus') 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 (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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 (limited to 'nexus') 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 (limited to 'nexus') 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(+) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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 --- nexus/arch.cc | 3 +++ nexus/arch.h | 5 +++-- nexus/constids.inc | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(+) (limited to 'nexus') 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 --- nexus/arch_pybindings.cc | 76 +++++++++++++++++++++++++++++++++++++ nexus/arch_pybindings.h | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 nexus/arch_pybindings.cc create mode 100644 nexus/arch_pybindings.h (limited to 'nexus') 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(-) (limited to 'nexus') 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 (limited to 'nexus') 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 --- nexus/arch.cc | 6 ------ nexus/arch.h | 46 +++++++++++++++++++++++++++++++++++++++++++++- nexus/arch_place.cc | 28 +++++++++++++++++----------- nexus/archdefs.h | 1 - nexus/pack.cc | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 19 deletions(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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 (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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 --- nexus/pack.cc | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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 --- nexus/arch.h | 30 ++++++++++++++++++------------ nexus/fasm.cc | 24 ++++++++++++++++++++++++ nexus/pins.cc | 2 +- 3 files changed, 43 insertions(+), 13 deletions(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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 --- nexus/.gitignore | 1 + nexus/CMakeLists.txt | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/arch.cc | 19 +++++++----------- nexus/arch.h | 1 - nexus/family.cmake | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ nexus/main.cc | 2 -- 6 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 nexus/.gitignore create mode 100644 nexus/CMakeLists.txt (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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 (limited to 'nexus') 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 --- nexus/arch_place.cc | 10 +++++ nexus/constids.inc | 5 ++- nexus/fasm.cc | 13 ++++++ nexus/pack.cc | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++-- nexus/pins.cc | 8 +++- 5 files changed, 150 insertions(+), 5 deletions(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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 --- nexus/arch.cc | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- nexus/archdefs.h | 10 ++++++++-- 2 files changed, 65 insertions(+), 5 deletions(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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 (limited to 'nexus') 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 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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 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(-) (limited to 'nexus') 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 --- nexus/pack.cc | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 5 deletions(-) (limited to 'nexus') 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 --- nexus/constids.inc | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ nexus/pack.cc | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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 --- nexus/constids.inc | 2 ++ nexus/fasm.cc | 25 +++++++++++++++++ nexus/pack.cc | 82 +++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 96 insertions(+), 13 deletions(-) (limited to 'nexus') 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 --- nexus/constids.inc | 9 +++++++++ nexus/pack.cc | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'nexus') 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 --- nexus/constids.inc | 10 ++++ nexus/pack.cc | 139 ++++++++++++++++++++++++++++++++++++++++++----------- nexus/pins.cc | 19 ++++++++ 3 files changed, 139 insertions(+), 29 deletions(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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(-) (limited to 'nexus') 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(-) (limited to 'nexus') 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(+) (limited to 'nexus') 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 (limited to 'nexus') 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 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 --- nexus/arch.cc | 23 ++++++----------------- nexus/arch.h | 2 +- 2 files changed, 7 insertions(+), 18 deletions(-) (limited to 'nexus') 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