From eaae6b7dca43d90fabb7676c514d630b1575e9c4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 5 Jul 2018 13:32:13 +0200 Subject: [PATCH 01/31] ecp5: Begin planning data structures Signed-off-by: David Shah --- ecp5/arch.h | 639 ++++++++++++++++++++++++++++++++++++++++++++++ ecp5/archdefs.h | 126 +++++++++ ecp5/portpins.inc | 28 ++ 3 files changed, 793 insertions(+) create mode 100644 ecp5/arch.h create mode 100644 ecp5/archdefs.h create mode 100644 ecp5/portpins.inc diff --git a/ecp5/arch.h b/ecp5/arch.h new file mode 100644 index 00000000..c0dbc60b --- /dev/null +++ b/ecp5/arch.h @@ -0,0 +1,639 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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 + +NEXTPNR_NAMESPACE_BEGIN + +/**** Everything in this section must be kept in sync with chipdb.py ****/ + +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) + offset); } + + const T &operator[](size_t index) const { return get()[index]; } + + const T &operator*() const { return *(get()); } + + const T *operator->() const { return get(); } +}; + +NPNR_PACKED_STRUCT(struct BelWirePOD { + Location rel_wire_loc; + int32_t wire_index; + PortPin port; + }); + +NPNR_PACKED_STRUCT(struct BelInfoPOD { + RelPtr name; + BelType type; + int32_t num_bel_wires; + RelPtr bel_wires; + int8_t x, y, z; + int8_t padding_0; + }); + +NPNR_PACKED_STRUCT(struct BelPortPOD { + Location rel_bel_loc; + int32_t bel_index; + PortPin port; + }); + +NPNR_PACKED_STRUCT(struct PipInfoPOD { + Location rel_src_loc, rel_dst_loc; + int32_t src_idx, dst_idx; + int32_t delay; + Location rel_tile_loc; + int16_t tile_type; + int8_t pip_type; + int8_t padding_0; + }); + +NPNR_PACKED_STRUCT(struct PipLocatorPOD { + Location rel_loc; + int32_t index; +}); + +NPNR_PACKED_STRUCT(struct WireInfoPOD { + RelPtr name; + int32_t num_uphill, num_downhill; + RelPtr pips_uphill, pips_downhill; + + int32_t num_bels_downhill; + BelPortPOD bel_uphill; + RelPtr bels_downhill; + }); + +NPNR_PACKED_STRUCT(struct LocationTypePOD { + int32_t num_bels, num_wires, num_pips; + RelPtr bel_data; + RelPtr wire_data; + RelPtr pip_data; +}); + +NPNR_PACKED_STRUCT(struct ChipInfoPOD { + int32_t width, height; + int32_t num_location_types; + RelPtr locations; + RelPtr location_type; + }); + +#if defined(_MSC_VER) +extern const char *chipdb_blob_384; +extern const char *chipdb_blob_1k; +extern const char *chipdb_blob_5k; +extern const char *chipdb_blob_8k; +#else +extern const char chipdb_blob_384[]; +extern const char chipdb_blob_1k[]; +extern const char chipdb_blob_5k[]; +extern const char chipdb_blob_8k[]; +#endif + +/************************ End of chipdb section. ************************/ + +struct BelIterator +{ + int cursor; + + BelIterator operator++() + { + cursor++; + return *this; + } + BelIterator operator++(int) + { + BelIterator prior(*this); + cursor++; + return prior; + } + + bool operator!=(const BelIterator &other) const { return cursor != other.cursor; } + + bool operator==(const BelIterator &other) const { return cursor == other.cursor; } + + BelId operator*() const + { + BelId ret; + ret.index = cursor; + return ret; + } +}; + +struct BelRange +{ + BelIterator b, e; + BelIterator begin() const { return b; } + BelIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct BelPinIterator +{ + const BelPortPOD *ptr = nullptr; + + void operator++() { ptr++; } + bool operator!=(const BelPinIterator &other) const { return ptr != other.ptr; } + + BelPin operator*() const + { + BelPin ret; + ret.bel.index = ptr->bel_index; + ret.pin = ptr->port; + return ret; + } +}; + +struct BelPinRange +{ + BelPinIterator b, e; + BelPinIterator begin() const { return b; } + BelPinIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct WireIterator +{ + int cursor = -1; + + void operator++() { cursor++; } + bool operator!=(const WireIterator &other) const { return cursor != other.cursor; } + + WireId operator*() const + { + WireId ret; + ret.index = cursor; + return ret; + } +}; + +struct WireRange +{ + WireIterator b, e; + WireIterator begin() const { return b; } + WireIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct AllPipIterator +{ + int cursor = -1; + + void operator++() { cursor++; } + bool operator!=(const AllPipIterator &other) const { return cursor != other.cursor; } + + PipId operator*() const + { + PipId ret; + ret.index = cursor; + return ret; + } +}; + +struct AllPipRange +{ + AllPipIterator b, e; + AllPipIterator begin() const { return b; } + AllPipIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct PipIterator +{ + const int *cursor = nullptr; + + void operator++() { cursor++; } + bool operator!=(const PipIterator &other) const { return cursor != other.cursor; } + + PipId operator*() const + { + PipId ret; + ret.index = *cursor; + return ret; + } +}; + +struct PipRange +{ + PipIterator b, e; + PipIterator begin() const { return b; } + PipIterator end() const { return e; } +}; + +struct ArchArgs +{ + enum + { + NONE, + LP384, + LP1K, + LP8K, + HX1K, + HX8K, + UP5K + } type = NONE; + std::string package; +}; + +struct Arch : BaseCtx +{ + const ChipInfoPOD *chip_info; + const PackageInfoPOD *package_info; + + mutable std::unordered_map bel_by_name; + mutable std::unordered_map wire_by_name; + mutable std::unordered_map pip_by_name; + + std::vector bel_to_cell; + std::vector wire_to_net; + std::vector pip_to_net; + std::vector switches_locked; + + ArchArgs args; + Arch(ArchArgs args); + + std::string getChipName(); + + IdString archId() const { return id("ice40"); } + IdString archArgsToId(ArchArgs args) const; + + IdString belTypeToId(BelType type) const; + BelType belTypeFromId(IdString id) const; + + IdString portPinToId(PortPin type) const; + PortPin portPinFromId(IdString id) const; + + // ------------------------------------------------- + + BelId getBelByName(IdString name) const; + + IdString getBelName(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return id(chip_info->bel_data[bel.index].name.get()); + } + + uint32_t getBelChecksum(BelId bel) const { return bel.index; } + + void bindBel(BelId bel, IdString cell, PlaceStrength strength) + { + NPNR_ASSERT(bel != BelId()); + NPNR_ASSERT(bel_to_cell[bel.index] == IdString()); + bel_to_cell[bel.index] = cell; + cells[cell]->bel = bel; + cells[cell]->belStrength = strength; + } + + void unbindBel(BelId bel) + { + NPNR_ASSERT(bel != BelId()); + NPNR_ASSERT(bel_to_cell[bel.index] != IdString()); + cells[bel_to_cell[bel.index]]->bel = BelId(); + cells[bel_to_cell[bel.index]]->belStrength = STRENGTH_NONE; + bel_to_cell[bel.index] = IdString(); + } + + bool checkBelAvail(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return bel_to_cell[bel.index] == IdString(); + } + + IdString getBoundBelCell(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return bel_to_cell[bel.index]; + } + + IdString getConflictingBelCell(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return bel_to_cell[bel.index]; + } + + BelRange getBels() const + { + BelRange range; + range.b.cursor = 0; + range.e.cursor = chip_info->num_bels; + return range; + } + + BelRange getBelsByType(BelType type) const + { + BelRange range; +// FIXME +#if 0 + if (type == "TYPE_A") { + range.b.cursor = bels_type_a_begin; + range.e.cursor = bels_type_a_end; + } + ... +#endif + return range; + } + + BelRange getBelsAtSameTile(BelId bel) const; + + BelType getBelType(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return chip_info->bel_data[bel.index].type; + } + + WireId getWireBelPin(BelId bel, PortPin pin) const; + + BelPin getBelPinUphill(WireId wire) const + { + BelPin ret; + NPNR_ASSERT(wire != WireId()); + + if (chip_info->wire_data[wire.index].bel_uphill.bel_index >= 0) { + ret.bel.index = chip_info->wire_data[wire.index].bel_uphill.bel_index; + ret.pin = chip_info->wire_data[wire.index].bel_uphill.port; + } + + return ret; + } + + BelPinRange getBelPinsDownhill(WireId wire) const + { + BelPinRange range; + NPNR_ASSERT(wire != WireId()); + range.b.ptr = chip_info->wire_data[wire.index].bels_downhill.get(); + range.e.ptr = range.b.ptr + chip_info->wire_data[wire.index].num_bels_downhill; + return range; + } + + // ------------------------------------------------- + + WireId getWireByName(IdString name) const; + + IdString getWireName(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + return id(chip_info->wire_data[wire.index].name.get()); + } + + uint32_t getWireChecksum(WireId wire) const { return wire.index; } + + void bindWire(WireId wire, IdString net, PlaceStrength strength) + { + NPNR_ASSERT(wire != WireId()); + NPNR_ASSERT(wire_to_net[wire.index] == IdString()); + wire_to_net[wire.index] = net; + nets[net]->wires[wire].pip = PipId(); + nets[net]->wires[wire].strength = strength; + } + + void unbindWire(WireId wire) + { + NPNR_ASSERT(wire != WireId()); + NPNR_ASSERT(wire_to_net[wire.index] != IdString()); + + auto &net_wires = nets[wire_to_net[wire.index]]->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.index] = IdString(); + switches_locked[chip_info->pip_data[pip.index].switch_index] = IdString(); + } + + net_wires.erase(it); + wire_to_net[wire.index] = IdString(); + } + + bool checkWireAvail(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + return wire_to_net[wire.index] == IdString(); + } + + IdString getBoundWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + return wire_to_net[wire.index]; + } + + IdString getConflictingWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + return wire_to_net[wire.index]; + } + + WireRange getWires() const + { + WireRange range; + range.b.cursor = 0; + range.e.cursor = chip_info->num_wires; + return range; + } + + // ------------------------------------------------- + + PipId getPipByName(IdString name) const; + IdString getPipName(PipId pip) const; + + uint32_t getPipChecksum(PipId pip) const { return pip.index; } + + void bindPip(PipId pip, IdString net, PlaceStrength strength) + { + NPNR_ASSERT(pip != PipId()); + NPNR_ASSERT(pip_to_net[pip.index] == IdString()); + NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == IdString()); + + pip_to_net[pip.index] = net; + switches_locked[chip_info->pip_data[pip.index].switch_index] = net; + + WireId dst; + dst.index = chip_info->pip_data[pip.index].dst; + NPNR_ASSERT(wire_to_net[dst.index] == IdString()); + wire_to_net[dst.index] = net; + nets[net]->wires[dst].pip = pip; + nets[net]->wires[dst].strength = strength; + } + + void unbindPip(PipId pip) + { + NPNR_ASSERT(pip != PipId()); + NPNR_ASSERT(pip_to_net[pip.index] != IdString()); + NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != IdString()); + + WireId dst; + dst.index = chip_info->pip_data[pip.index].dst; + NPNR_ASSERT(wire_to_net[dst.index] != IdString()); + wire_to_net[dst.index] = IdString(); + nets[pip_to_net[pip.index]]->wires.erase(dst); + + pip_to_net[pip.index] = IdString(); + switches_locked[chip_info->pip_data[pip.index].switch_index] = IdString(); + } + + bool checkPipAvail(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + return switches_locked[chip_info->pip_data[pip.index].switch_index] == IdString(); + } + + IdString getBoundPipNet(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + return pip_to_net[pip.index]; + } + + IdString getConflictingPipNet(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + return switches_locked[chip_info->pip_data[pip.index].switch_index]; + } + + AllPipRange getPips() const + { + AllPipRange range; + range.b.cursor = 0; + range.e.cursor = chip_info->num_pips; + return range; + } + + WireId getPipSrcWire(PipId pip) const + { + WireId wire; + NPNR_ASSERT(pip != PipId()); + wire.index = chip_info->pip_data[pip.index].src; + return wire; + } + + WireId getPipDstWire(PipId pip) const + { + WireId wire; + NPNR_ASSERT(pip != PipId()); + wire.index = chip_info->pip_data[pip.index].dst; + return wire; + } + + DelayInfo getPipDelay(PipId pip) const + { + DelayInfo delay; + NPNR_ASSERT(pip != PipId()); + delay.delay = chip_info->pip_data[pip.index].delay; + return delay; + } + + PipRange getPipsDownhill(WireId wire) const + { + PipRange range; + NPNR_ASSERT(wire != WireId()); + range.b.cursor = chip_info->wire_data[wire.index].pips_downhill.get(); + range.e.cursor = range.b.cursor + chip_info->wire_data[wire.index].num_downhill; + return range; + } + + PipRange getPipsUphill(WireId wire) const + { + PipRange range; + NPNR_ASSERT(wire != WireId()); + range.b.cursor = chip_info->wire_data[wire.index].pips_uphill.get(); + range.e.cursor = range.b.cursor + chip_info->wire_data[wire.index].num_uphill; + return range; + } + + PipRange getWireAliases(WireId wire) const + { + PipRange range; + NPNR_ASSERT(wire != WireId()); + range.b.cursor = nullptr; + range.e.cursor = nullptr; + return range; + } + + BelId getPackagePinBel(const std::string &pin) const; + std::string getBelPackagePin(BelId bel) const; + + // ------------------------------------------------- + + void estimatePosition(BelId bel, int &x, int &y, bool &gb) const; + delay_t estimateDelay(WireId src, WireId dst) const; + delay_t getDelayEpsilon() const { return 20; } + delay_t getRipupDelayPenalty() const { return 200; } + float getDelayNS(delay_t v) const { return v * 0.001; } + uint32_t getDelayChecksum(delay_t v) const { return v; } + + // ------------------------------------------------- + + std::vector getFrameGraphics() const; + std::vector getBelGraphics(BelId bel) const; + std::vector getWireGraphics(WireId wire) const; + std::vector getPipGraphics(PipId pip) const; + + bool allGraphicsReload = false; + bool frameGraphicsReload = false; + std::unordered_set belGraphicsReload; + std::unordered_set wireGraphicsReload; + std::unordered_set pipGraphicsReload; + + // ------------------------------------------------- + + // Get the delay through a cell from one port to another, returning false + // if no path exists + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const; + // Get the associated clock to a port, or empty if the port is combinational + IdString getPortClock(const CellInfo *cell, IdString port) const; + // Return true if a port is a clock + bool isClockPort(const CellInfo *cell, IdString port) const; + // Return true if a port is a net + 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; + + // Helper function for above + bool logicCellsCompatible(const std::vector &cells) const; + + IdString id_glb_buf_out; + IdString id_icestorm_lc, id_sb_io, id_sb_gb; + IdString id_cen, id_clk, id_sr; + IdString id_i0, id_i1, id_i2, id_i3; + IdString id_dff_en, id_neg_clk; +}; + +NEXTPNR_NAMESPACE_END diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h new file mode 100644 index 00000000..f185da72 --- /dev/null +++ b/ecp5/archdefs.h @@ -0,0 +1,126 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Clifford Wolf + * + * 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 delay = 0; + + delay_t raiseDelay() const { return delay; } + delay_t fallDelay() const { return delay; } + delay_t avgDelay() const { return delay; } + + DelayInfo operator+(const DelayInfo &other) const + { + DelayInfo ret; + ret.delay = this->delay + other.delay; + return ret; + } +}; + +// ----------------------------------------------------------------------- + +enum BelType : int32_t +{ + TYPE_NONE, + TYPE_TRELLIS_SLICE, + TYPE_TRELLIS_IO +}; + +enum PortPin : int32_t +{ + PIN_NONE, +#define X(t) PIN_##t, +#include "portpins.inc" +#undef X + PIN_MAXIDX +}; + +NPNR_PACKED_STRUCT( +struct Location { + int16_t x = -1, y = -1; + bool operator==(const Location &other) const { return x == other.x && y == other.y; } + bool operator!=(const Location &other) const { return x != other.x || y == other.y; } +} +); + +struct BelId +{ + Location location; + int32_t index = -1; + + bool operator==(const BelId &other) const { return index == other.index && location == other.location; } + bool operator!=(const BelId &other) const { return index != other.index || location != other.location; } +}; + +struct WireId +{ + Location location; + int32_t index = -1; + + bool operator==(const WireId &other) const { return index == other.index && location == other.location; } + bool operator!=(const WireId &other) const { return index != other.index || location != other.location; } +}; + +struct PipId +{ + Location location; + int32_t index = -1; + + bool operator==(const WireId &other) const { return index == other.index && location == other.location; } + bool operator!=(const WireId &other) const { return index != other.index || location != other.location; } +}; + +NEXTPNR_NAMESPACE_END + +namespace std { +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept { return hash()(bel.index); } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept + { + return hash()(wire.index); + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept { return hash()(pip.index); } +}; + +template <> struct hash : hash +{ +}; + +template <> struct hash : hash +{ +}; +} // namespace std diff --git a/ecp5/portpins.inc b/ecp5/portpins.inc new file mode 100644 index 00000000..cfe0a349 --- /dev/null +++ b/ecp5/portpins.inc @@ -0,0 +1,28 @@ +X(A0) +X(B0) +X(C0) +X(D0) +X(A1) +X(B1) +X(C1) +X(D1) +X(M0) +X(M1) +X(FCI) +X(FXA) +X(FXB) +X(CLK) +X(LSR) +X(CE) +X(F0) +X(Q0) +X(F1) +X(Q1) +X(FCO) +X(OFX0) +X(OFX1) + +X(I) +X(O) +X(T) +X(B) From c4af52dd5b3830905e2b9e8f7135f886882841ba Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 5 Jul 2018 20:59:11 +0200 Subject: [PATCH 02/31] ecp5: Working on arch implementation Signed-off-by: David Shah --- CMakeLists.txt | 2 +- common/pybindings.cc | 8 +- ecp5/arch.h | 231 ++++++++++++++++++++++++--------------- ecp5/archdefs.h | 8 +- ecp5/family.cmake | 0 ice40/arch_pybindings.cc | 8 +- 6 files changed, 153 insertions(+), 104 deletions(-) create mode 100644 ecp5/family.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index dc4e2a4f..f4586dfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ option(BUILD_PYTHON "Build Python Integration" ON) option(BUILD_TESTS "Build GUI" OFF) # List of families to build -set(FAMILIES generic ice40) +set(FAMILIES generic ice40 ecp5) set(ARCH "" CACHE STRING "Architecture family for nextpnr build") set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES}) diff --git a/common/pybindings.cc b/common/pybindings.cc index 1a0eba8f..061dfc47 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -110,13 +110,13 @@ BOOST_PYTHON_MODULE(MODULE_NAME) readwrite_wrapper, pass_through>::def_wrap(ci_cls, "belStrength"); readonly_wrapper>::def_wrap(ci_cls, - "pins"); + "pins"); auto pi_cls = class_>("PortInfo", no_init); readwrite_wrapper, conv_from_str>::def_wrap(pi_cls, "name"); readonly_wrapper>::def_wrap(pi_cls, - "net"); + "net"); readwrite_wrapper, pass_through>::def_wrap(pi_cls, "type"); @@ -131,11 +131,11 @@ BOOST_PYTHON_MODULE(MODULE_NAME) readonly_wrapper>::def_wrap( ni_cls, "users"); readonly_wrapper>::def_wrap(ni_cls, - "wires"); + "wires"); auto pr_cls = class_>("PortRef", no_init); readonly_wrapper>::def_wrap(pr_cls, - "cell"); + "cell"); readwrite_wrapper, conv_from_str>::def_wrap(pr_cls, "port"); readwrite_wrapper, diff --git a/ecp5/arch.h b/ecp5/arch.h index c0dbc60b..98c4cd76 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -44,35 +44,35 @@ template struct RelPtr }; NPNR_PACKED_STRUCT(struct BelWirePOD { - Location rel_wire_loc; - int32_t wire_index; - PortPin port; - }); + Location rel_wire_loc; + int32_t wire_index; + PortPin port; +}); NPNR_PACKED_STRUCT(struct BelInfoPOD { - RelPtr name; - BelType type; - int32_t num_bel_wires; - RelPtr bel_wires; - int8_t x, y, z; - int8_t padding_0; - }); + RelPtr name; + BelType type; + int32_t num_bel_wires; + RelPtr bel_wires; + int8_t x, y, z; + int8_t padding_0; +}); NPNR_PACKED_STRUCT(struct BelPortPOD { - Location rel_bel_loc; - int32_t bel_index; - PortPin port; - }); + Location rel_bel_loc; + int32_t bel_index; + PortPin port; +}); NPNR_PACKED_STRUCT(struct PipInfoPOD { - Location rel_src_loc, rel_dst_loc; - int32_t src_idx, dst_idx; - int32_t delay; - Location rel_tile_loc; - int16_t tile_type; - int8_t pip_type; - int8_t padding_0; - }); + Location rel_src_loc, rel_dst_loc; + int32_t src_idx, dst_idx; + int32_t delay; + Location rel_tile_loc; + int16_t tile_type; + int8_t pip_type; + int8_t padding_0; +}); NPNR_PACKED_STRUCT(struct PipLocatorPOD { Location rel_loc; @@ -80,28 +80,28 @@ NPNR_PACKED_STRUCT(struct PipLocatorPOD { }); NPNR_PACKED_STRUCT(struct WireInfoPOD { - RelPtr name; - int32_t num_uphill, num_downhill; - RelPtr pips_uphill, pips_downhill; + RelPtr name; + int32_t num_uphill, num_downhill; + RelPtr pips_uphill, pips_downhill; - int32_t num_bels_downhill; - BelPortPOD bel_uphill; - RelPtr bels_downhill; - }); + int32_t num_bels_downhill; + BelPortPOD bel_uphill; + RelPtr bels_downhill; +}); NPNR_PACKED_STRUCT(struct LocationTypePOD { - int32_t num_bels, num_wires, num_pips; - RelPtr bel_data; - RelPtr wire_data; - RelPtr pip_data; + int32_t num_bels, num_wires, num_pips; + RelPtr bel_data; + RelPtr wire_data; + RelPtr pip_data; }); NPNR_PACKED_STRUCT(struct ChipInfoPOD { - int32_t width, height; - int32_t num_location_types; - RelPtr locations; - RelPtr location_type; - }); + int32_t width, height; + int32_t num_location_types; + RelPtr locations; + RelPtr location_type; +}); #if defined(_MSC_VER) extern const char *chipdb_blob_384; @@ -119,11 +119,17 @@ extern const char chipdb_blob_8k[]; struct BelIterator { - int cursor; + const ChipInfoPOD *chip; + int cursor_index; + int cursor_tile; BelIterator operator++() { - cursor++; + cursor_index++; + while (cursor_index >= ci->locations[ci->location_type[cursor_tile]]->num_bels) { + cursor_index = 0; + cursor_tile++; + } return *this; } BelIterator operator++(int) @@ -133,14 +139,22 @@ struct BelIterator return prior; } - bool operator!=(const BelIterator &other) const { return cursor != other.cursor; } + 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 == other.cursor; } + bool operator==(const BelIterator &other) const + { + return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; + } BelId operator*() const { BelId ret; - ret.index = cursor; + ret.location.x = cursor_tile % chip->width; + ret.location.y = cursor_tile / chip->width; + ret.index = cursor_index; return ret; } }; @@ -157,7 +171,7 @@ struct BelRange struct BelPinIterator { const BelPortPOD *ptr = nullptr; - + const Location wire_loc; void operator++() { ptr++; } bool operator!=(const BelPinIterator &other) const { return ptr != other.ptr; } @@ -165,6 +179,7 @@ struct BelPinIterator { BelPin ret; ret.bel.index = ptr->bel_index; + ret.bel.location = wire_loc + ptr->rel_bel_loc; ret.pin = ptr->port; return ret; } @@ -181,15 +196,42 @@ struct BelPinRange struct WireIterator { - int cursor = -1; + const ChipInfoPOD *chip; + int cursor_index; + int cursor_tile; - void operator++() { cursor++; } - bool operator!=(const WireIterator &other) const { return cursor != other.cursor; } + WireIterator operator++() + { + cursor_index++; + while (cursor_index >= ci->locations[ci->location_type[cursor_tile]]->num_wires) { + cursor_index = 0; + cursor_tile++; + } + return *this; + } + WireIterator operator++(int) + { + WireIterator prior(*this); + cursor++; + 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.index = cursor; + ret.location.x = cursor_tile % chip->width; + ret.location.y = cursor_tile / chip->width; + ret.index = cursor_index; return ret; } }; @@ -205,15 +247,42 @@ struct WireRange struct AllPipIterator { - int cursor = -1; + const ChipInfoPOD *chip; + int cursor_index; + int cursor_tile; - void operator++() { cursor++; } - bool operator!=(const AllPipIterator &other) const { return cursor != other.cursor; } + AllPipIterator operator++() + { + cursor_index++; + while (cursor_index >= ci->locations[ci->location_type[cursor_tile]]->num_pips) { + cursor_index = 0; + cursor_tile++; + } + return *this; + } + AllPipIterator operator++(int) + { + WireIterator prior(*this); + cursor++; + 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.index = cursor; + ret.location.x = cursor_tile % chip->width; + ret.location.y = cursor_tile / chip->width; + ret.index = cursor_index; return ret; } }; @@ -229,7 +298,9 @@ struct AllPipRange struct PipIterator { - const int *cursor = nullptr; + + const PipLocatorPOD *cursor = nullptr; + Location wire_loc; void operator++() { cursor++; } bool operator!=(const PipIterator &other) const { return cursor != other.cursor; } @@ -237,7 +308,8 @@ struct PipIterator PipId operator*() const { PipId ret; - ret.index = *cursor; + ret.index = cursor->index; + ret.location = wire_loc + cursor->location; return ret; } }; @@ -254,14 +326,12 @@ struct ArchArgs enum { NONE, - LP384, - LP1K, - LP8K, - HX1K, - HX8K, - UP5K + LFE5U_25F, + LFE5U_45F, + LFE5U_85F, } type = NONE; std::string package; + int speed = 6; }; struct Arch : BaseCtx @@ -273,17 +343,17 @@ struct Arch : BaseCtx mutable std::unordered_map wire_by_name; mutable std::unordered_map pip_by_name; - std::vector bel_to_cell; - std::vector wire_to_net; - std::vector pip_to_net; - std::vector switches_locked; + std::unordered_map bel_to_cell; + std::unordered_map wire_to_net; + std::unordered_map pip_to_net; + std::unordered_map switches_locked; ArchArgs args; Arch(ArchArgs args); std::string getChipName(); - IdString archId() const { return id("ice40"); } + IdString archId() const { return id("ecp5"); } IdString archArgsToId(ArchArgs args) const; IdString belTypeToId(BelType type) const; @@ -307,8 +377,8 @@ struct Arch : BaseCtx void bindBel(BelId bel, IdString cell, PlaceStrength strength) { NPNR_ASSERT(bel != BelId()); - NPNR_ASSERT(bel_to_cell[bel.index] == IdString()); - bel_to_cell[bel.index] = cell; + NPNR_ASSERT(bel_to_cell[bel] == IdString()); + bel_to_cell[bel] = cell; cells[cell]->bel = bel; cells[cell]->belStrength = strength; } @@ -331,13 +401,13 @@ struct Arch : BaseCtx IdString getBoundBelCell(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return bel_to_cell[bel.index]; + return bel_to_cell.at(bel); } IdString getConflictingBelCell(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return bel_to_cell[bel.index]; + return bel_to_cell.at(bel); } BelRange getBels() const @@ -613,27 +683,6 @@ struct Arch : BaseCtx bool isClockPort(const CellInfo *cell, IdString port) const; // Return true if a port is a net 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; - - // Helper function for above - bool logicCellsCompatible(const std::vector &cells) const; - - IdString id_glb_buf_out; - IdString id_icestorm_lc, id_sb_io, id_sb_gb; - IdString id_cen, id_clk, id_sr; - IdString id_i0, id_i1, id_i2, id_i3; - IdString id_dff_en, id_neg_clk; }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index f185da72..0af48753 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -60,13 +60,13 @@ enum PortPin : int32_t PIN_MAXIDX }; -NPNR_PACKED_STRUCT( -struct Location { +NPNR_PACKED_STRUCT(struct Location { int16_t x = -1, y = -1; bool operator==(const Location &other) const { return x == other.x && y == other.y; } bool operator!=(const Location &other) const { return x != other.x || y == other.y; } -} -); +}); + +Location operator+(const Location &a, const Location &b) { return Location{a.x + b.x, a.y + b.y};} struct BelId { diff --git a/ecp5/family.cmake b/ecp5/family.cmake new file mode 100644 index 00000000..e69de29b diff --git a/ice40/arch_pybindings.cc b/ice40/arch_pybindings.cc index 67a37983..ac8c189a 100644 --- a/ice40/arch_pybindings.cc +++ b/ice40/arch_pybindings.cc @@ -75,7 +75,7 @@ void arch_wrap_python() fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingBelCell"); fn_wrapper_0a>::def_wrap(ctx_cls, - "getBels"); + "getBels"); fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getBelsAtSameTile"); @@ -139,15 +139,15 @@ void arch_wrap_python() fn_wrapper_0a>::def_wrap( ctx_cls, "getChipName"); fn_wrapper_0a>::def_wrap(ctx_cls, - "archId"); + "archId"); typedef std::unordered_map> CellMap; typedef std::unordered_map> NetMap; readonly_wrapper>::def_wrap(ctx_cls, - "cells"); + "cells"); readonly_wrapper>::def_wrap(ctx_cls, - "nets"); + "nets"); WRAP_RANGE(Bel, conv_to_str); WRAP_RANGE(Wire, conv_to_str); WRAP_RANGE(AllPip, conv_to_str); From 7862d1b84b9f770a97f1650cf4a1eb2e02b6389f Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 6 Jul 2018 12:15:07 +0200 Subject: [PATCH 03/31] ecp5: Implementing core arch.h functions Signed-off-by: David Shah --- ecp5/arch.cc | 386 ++++++++++++++++++++++++++++++++++++++++ ecp5/arch.h | 199 +++++++++++++-------- ecp5/arch_pybindings.cc | 32 ++++ ecp5/arch_pybindings.h | 31 ++++ ecp5/archdefs.h | 46 ++++- ecp5/main.cc | 139 +++++++++++++++ 6 files changed, 746 insertions(+), 87 deletions(-) create mode 100644 ecp5/arch.cc create mode 100644 ecp5/arch_pybindings.cc create mode 100644 ecp5/arch_pybindings.h create mode 100644 ecp5/main.cc diff --git a/ecp5/arch.cc b/ecp5/arch.cc new file mode 100644 index 00000000..41c73cf8 --- /dev/null +++ b/ecp5/arch.cc @@ -0,0 +1,386 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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 +#include "log.h" +#include "nextpnr.h" +#include "util.h" +NEXTPNR_NAMESPACE_BEGIN + +// ----------------------------------------------------------------------- + +IdString Arch::belTypeToId(BelType type) const +{ + if (type == TYPE_ICESTORM_LC) + return id("ICESTORM_LC"); + if (type == TYPE_ICESTORM_RAM) + return id("ICESTORM_RAM"); + if (type == TYPE_SB_IO) + return id("SB_IO"); + if (type == TYPE_SB_GB) + return id("SB_GB"); + if (type == TYPE_ICESTORM_PLL) + return id("ICESTORM_PLL"); + if (type == TYPE_SB_WARMBOOT) + return id("SB_WARMBOOT"); + if (type == TYPE_SB_MAC16) + return id("SB_MAC16"); + if (type == TYPE_ICESTORM_HFOSC) + return id("ICESTORM_HFOSC"); + if (type == TYPE_ICESTORM_LFOSC) + return id("ICESTORM_LFOSC"); + if (type == TYPE_SB_I2C) + return id("SB_I2C"); + if (type == TYPE_SB_SPI) + return id("SB_SPI"); + if (type == TYPE_IO_I3C) + return id("IO_I3C"); + if (type == TYPE_SB_LEDDA_IP) + return id("SB_LEDDA_IP"); + if (type == TYPE_SB_RGBA_DRV) + return id("SB_RGBA_DRV"); + if (type == TYPE_ICESTORM_SPRAM) + return id("ICESTORM_SPRAM"); + return IdString(); +} + +BelType Arch::belTypeFromId(IdString type) const +{ + if (type == id("ICESTORM_LC")) + return TYPE_ICESTORM_LC; + if (type == id("ICESTORM_RAM")) + return TYPE_ICESTORM_RAM; + if (type == id("SB_IO")) + return TYPE_SB_IO; + if (type == id("SB_GB")) + return TYPE_SB_GB; + if (type == id("ICESTORM_PLL")) + return TYPE_ICESTORM_PLL; + if (type == id("SB_WARMBOOT")) + return TYPE_SB_WARMBOOT; + if (type == id("SB_MAC16")) + return TYPE_SB_MAC16; + if (type == id("ICESTORM_HFOSC")) + return TYPE_ICESTORM_HFOSC; + if (type == id("ICESTORM_LFOSC")) + return TYPE_ICESTORM_LFOSC; + if (type == id("SB_I2C")) + return TYPE_SB_I2C; + if (type == id("SB_SPI")) + return TYPE_SB_SPI; + if (type == id("IO_I3C")) + return TYPE_IO_I3C; + if (type == id("SB_LEDDA_IP")) + return TYPE_SB_LEDDA_IP; + if (type == id("SB_RGBA_DRV")) + return TYPE_SB_RGBA_DRV; + if (type == id("ICESTORM_SPRAM")) + return TYPE_ICESTORM_SPRAM; + return TYPE_NONE; +} + +// ----------------------------------------------------------------------- + +void IdString::initialize_arch(const BaseCtx *ctx) +{ +#define X(t) initialize_add(ctx, #t, PIN_##t); +#include "portpins.inc" +#undef X +} + +IdString Arch::portPinToId(PortPin type) const +{ + IdString ret; + if (type > 0 && type < PIN_MAXIDX) + ret.index = type; + return ret; +} + +PortPin Arch::portPinFromId(IdString type) const +{ + if (type.index > 0 && type.index < PIN_MAXIDX) + return PortPin(type.index); + return PIN_NONE; +} + +// ----------------------------------------------------------------------- + +static const ChipInfoPOD *get_chip_info(const RelPtr *ptr) { return ptr->get(); } + +#if defined(_MSC_VER) +void load_chipdb(); +#endif + +Arch::Arch(ArchArgs args) : args(args) +{ +#if defined(_MSC_VER) + load_chipdb(); +#endif + +#ifdef ICE40_HX1K_ONLY + if (args.type == ArchArgs::HX1K) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_1k)); + } else { + log_error("Unsupported iCE40 chip type.\n"); + } +#else + if (args.type == ArchArgs::LP384) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_384)); + } else if (args.type == ArchArgs::LP1K || args.type == ArchArgs::HX1K) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_1k)); + } else if (args.type == ArchArgs::UP5K) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_5k)); + } else if (args.type == ArchArgs::LP8K || args.type == ArchArgs::HX8K) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_8k)); + } else { + log_error("Unsupported iCE40 chip type.\n"); + } +#endif + + package_info = nullptr; + for (int i = 0; i < chip_info->num_packages; i++) { + if (chip_info->packages_data[i].name.get() == args.package) { + package_info = &(chip_info->packages_data[i]); + break; + } + } + if (package_info == nullptr) + log_error("Unsupported package '%s'.\n", args.package.c_str()); + + bel_to_cell.resize(chip_info->num_bels); + wire_to_net.resize(chip_info->num_wires); + pip_to_net.resize(chip_info->num_pips); + switches_locked.resize(chip_info->num_switches); + + // Initialise regularly used IDStrings for performance + id_glb_buf_out = id("GLOBAL_BUFFER_OUTPUT"); + id_icestorm_lc = id("ICESTORM_LC"); + id_sb_io = id("SB_IO"); + id_sb_gb = id("SB_GB"); + id_cen = id("CEN"); + id_clk = id("CLK"); + id_sr = id("SR"); + id_i0 = id("I0"); + id_i1 = id("I1"); + id_i2 = id("I2"); + id_i3 = id("I3"); + id_dff_en = id("DFF_ENABLE"); + id_neg_clk = id("NEG_CLK"); +} + +// ----------------------------------------------------------------------- + +std::string Arch::getChipName() +{ +#ifdef ICE40_HX1K_ONLY + if (args.type == ArchArgs::HX1K) { + return "Lattice LP1K"; + } else { + log_error("Unsupported iCE40 chip type.\n"); + } +#else + if (args.type == ArchArgs::LP384) { + return "Lattice LP384"; + } else if (args.type == ArchArgs::LP1K) { + return "Lattice LP1K"; + } else if (args.type == ArchArgs::HX1K) { + return "Lattice HX1K"; + } else if (args.type == ArchArgs::UP5K) { + return "Lattice UP5K"; + } else if (args.type == ArchArgs::LP8K) { + return "Lattice LP8K"; + } else if (args.type == ArchArgs::HX8K) { + return "Lattice HX8K"; + } else { + log_error("Unknown chip\n"); + } +#endif +} + +// ----------------------------------------------------------------------- + +IdString Arch::archArgsToId(ArchArgs args) const +{ + if (args.type == ArchArgs::LP384) + return id("lp384"); + if (args.type == ArchArgs::LP1K) + return id("lp1k"); + if (args.type == ArchArgs::HX1K) + return id("hx1k"); + if (args.type == ArchArgs::UP5K) + return id("up5k"); + if (args.type == ArchArgs::LP8K) + return id("lp8k"); + if (args.type == ArchArgs::HX8K) + return id("hx8k"); + return IdString(); +} + +// ----------------------------------------------------------------------- + +BelId Arch::getBelByName(IdString name) const +{ + BelId ret; + + if (bel_by_name.empty()) { + for (int i = 0; i < chip_info->num_bels; i++) + bel_by_name[id(chip_info->bel_data[i].name.get())] = i; + } + + auto it = bel_by_name.find(name); + if (it != bel_by_name.end()) + ret.index = it->second; + + return ret; +} + +BelRange Arch::getBelsAtSameTile(BelId bel) const +{ + BelRange br; + NPNR_ASSERT(bel != BelId()); + // This requires Bels at the same tile are consecutive + int x = chip_info->bel_data[bel.index].x; + int y = chip_info->bel_data[bel.index].y; + int start = bel.index, end = bel.index; + while (start >= 0 && chip_info->bel_data[start].x == x && chip_info->bel_data[start].y == y) + start--; + start++; + br.b.cursor = start; + while (end < chip_info->num_bels && chip_info->bel_data[end].x == x && chip_info->bel_data[end].y == y) + end++; + br.e.cursor = end; + return br; +} + +WireId Arch::getWireBelPin(BelId bel, PortPin pin) const +{ + WireId ret; + + NPNR_ASSERT(bel != BelId()); + + int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires; + const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get(); + + for (int i = 0; i < num_bel_wires; i++) + if (bel_wires[i].port == pin) { + ret.index = bel_wires[i].wire_index; + break; + } + + return ret; +} + +// ----------------------------------------------------------------------- + +WireId Arch::getWireByName(IdString name) const +{ + WireId ret; + + if (wire_by_name.empty()) { + for (int i = 0; i < chip_info->num_wires; i++) + wire_by_name[id(chip_info->wire_data[i].name.get())] = i; + } + + auto it = wire_by_name.find(name); + if (it != wire_by_name.end()) + ret.index = it->second; + + return ret; +} + +// ----------------------------------------------------------------------- + +PipId Arch::getPipByName(IdString name) const +{ + PipId ret; + + if (pip_by_name.empty()) { + for (int i = 0; i < chip_info->num_pips; i++) { + PipId pip; + pip.index = i; + pip_by_name[getPipName(pip)] = i; + } + } + + auto it = pip_by_name.find(name); + if (it != pip_by_name.end()) + ret.index = it->second; + + return ret; +} + +IdString Arch::getPipName(PipId pip) const +{ + NPNR_ASSERT(pip != PipId()); + + int x = chip_info->pip_data[pip.index].x; + int y = chip_info->pip_data[pip.index].y; + + std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get(); + std::replace(src_name.begin(), src_name.end(), '/', '.'); + + std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get(); + std::replace(dst_name.begin(), dst_name.end(), '/', '.'); + + return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name); +} + +// ----------------------------------------------------------------------- + +BelId Arch::getPackagePinBel(const std::string &pin) const { return BelId(); } + +std::string Arch::getBelPackagePin(BelId bel) const { return ""; } +// ----------------------------------------------------------------------- + +void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const {} + +delay_t Arch::estimateDelay(WireId src, WireId dst) const { return 1; } + +// ----------------------------------------------------------------------- + +std::vector Arch::getFrameGraphics() const +{ + std::vector ret; + + return ret; +} + +std::vector Arch::getBelGraphics(BelId bel) const +{ + std::vector ret; + + return ret; +} + +std::vector Arch::getWireGraphics(WireId wire) const +{ + std::vector ret; + // FIXME + return ret; +} + +std::vector Arch::getPipGraphics(PipId pip) const +{ + std::vector ret; + // FIXME + return ret; +}; + +NEXTPNR_NAMESPACE_END diff --git a/ecp5/arch.h b/ecp5/arch.h index 98c4cd76..85a3e44c 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -44,7 +44,7 @@ template struct RelPtr }; NPNR_PACKED_STRUCT(struct BelWirePOD { - Location rel_wire_loc; + LocationPOD rel_wire_loc; int32_t wire_index; PortPin port; }); @@ -59,23 +59,23 @@ NPNR_PACKED_STRUCT(struct BelInfoPOD { }); NPNR_PACKED_STRUCT(struct BelPortPOD { - Location rel_bel_loc; + LocationPOD rel_bel_loc; int32_t bel_index; PortPin port; }); NPNR_PACKED_STRUCT(struct PipInfoPOD { - Location rel_src_loc, rel_dst_loc; + LocationPOD rel_src_loc, rel_dst_loc; int32_t src_idx, dst_idx; int32_t delay; - Location rel_tile_loc; + LocationPOD rel_tile_loc; int16_t tile_type; int8_t pip_type; int8_t padding_0; }); NPNR_PACKED_STRUCT(struct PipLocatorPOD { - Location rel_loc; + LocationPOD rel_loc; int32_t index; }); @@ -98,21 +98,20 @@ NPNR_PACKED_STRUCT(struct LocationTypePOD { NPNR_PACKED_STRUCT(struct ChipInfoPOD { int32_t width, height; + int32_t num_tiles; int32_t num_location_types; RelPtr locations; RelPtr location_type; }); #if defined(_MSC_VER) -extern const char *chipdb_blob_384; -extern const char *chipdb_blob_1k; -extern const char *chipdb_blob_5k; -extern const char *chipdb_blob_8k; +extern const char *chipdb_blob_25k; +extern const char *chipdb_blob_45k; +extern const char *chipdb_blob_85k; #else -extern const char chipdb_blob_384[]; -extern const char chipdb_blob_1k[]; -extern const char chipdb_blob_5k[]; -extern const char chipdb_blob_8k[]; +extern const char chipdb_blob_25k[]; +extern const char chipdb_blob_45k[]; +extern const char chipdb_blob_85k[]; #endif /************************ End of chipdb section. ************************/ @@ -126,7 +125,8 @@ struct BelIterator BelIterator operator++() { cursor_index++; - while (cursor_index >= ci->locations[ci->location_type[cursor_tile]]->num_bels) { + while (cursor_tile < chip->num_tiles && + cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_bels) { cursor_index = 0; cursor_tile++; } @@ -135,7 +135,7 @@ struct BelIterator BelIterator operator++(int) { BelIterator prior(*this); - cursor++; + ++(*this); return prior; } @@ -171,7 +171,7 @@ struct BelRange struct BelPinIterator { const BelPortPOD *ptr = nullptr; - const Location wire_loc; + Location wire_loc; void operator++() { ptr++; } bool operator!=(const BelPinIterator &other) const { return ptr != other.ptr; } @@ -203,7 +203,8 @@ struct WireIterator WireIterator operator++() { cursor_index++; - while (cursor_index >= ci->locations[ci->location_type[cursor_tile]]->num_wires) { + while (cursor_tile < chip->num_tiles && + cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_wires) { cursor_index = 0; cursor_tile++; } @@ -212,7 +213,7 @@ struct WireIterator WireIterator operator++(int) { WireIterator prior(*this); - cursor++; + ++(*this); return prior; } @@ -254,7 +255,8 @@ struct AllPipIterator AllPipIterator operator++() { cursor_index++; - while (cursor_index >= ci->locations[ci->location_type[cursor_tile]]->num_pips) { + while (cursor_tile < chip->num_tiles && + cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_pips) { cursor_index = 0; cursor_tile++; } @@ -262,8 +264,8 @@ struct AllPipIterator } AllPipIterator operator++(int) { - WireIterator prior(*this); - cursor++; + AllPipIterator prior(*this); + ++(*this); return prior; } @@ -309,7 +311,7 @@ struct PipIterator { PipId ret; ret.index = cursor->index; - ret.location = wire_loc + cursor->location; + ret.location = wire_loc + cursor->rel_loc; return ret; } }; @@ -337,7 +339,6 @@ struct ArchArgs struct Arch : BaseCtx { const ChipInfoPOD *chip_info; - const PackageInfoPOD *package_info; mutable std::unordered_map bel_by_name; mutable std::unordered_map wire_by_name; @@ -366,10 +367,15 @@ struct Arch : BaseCtx BelId getBelByName(IdString name) const; + template const LocationTypePOD *locInfo(Id &id) const + { + return &(chip_info->locations[chip_info->location_type[id.location.y * chip_info->width + id.location.x]]); + } + IdString getBelName(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return id(chip_info->bel_data[bel.index].name.get()); + return id(locInfo(bel)->bel_data[bel.index].name.get()); } uint32_t getBelChecksum(BelId bel) const { return bel.index; } @@ -386,35 +392,46 @@ struct Arch : BaseCtx void unbindBel(BelId bel) { NPNR_ASSERT(bel != BelId()); - NPNR_ASSERT(bel_to_cell[bel.index] != IdString()); - cells[bel_to_cell[bel.index]]->bel = BelId(); - cells[bel_to_cell[bel.index]]->belStrength = STRENGTH_NONE; - bel_to_cell[bel.index] = IdString(); + NPNR_ASSERT(bel_to_cell[bel] != IdString()); + cells[bel_to_cell[bel]]->bel = BelId(); + cells[bel_to_cell[bel]]->belStrength = STRENGTH_NONE; + bel_to_cell[bel] = IdString(); } bool checkBelAvail(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return bel_to_cell[bel.index] == IdString(); + return bel_to_cell.find(bel) == bel_to_cell.end() || bel_to_cell.at(bel) == IdString(); } IdString getBoundBelCell(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return bel_to_cell.at(bel); + if (bel_to_cell.find(bel) == bel_to_cell.end()) + return IdString(); + else + return bel_to_cell.at(bel); } IdString getConflictingBelCell(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return bel_to_cell.at(bel); + if (bel_to_cell.find(bel) == bel_to_cell.end()) + return IdString(); + else + return bel_to_cell.at(bel); } BelRange getBels() const { BelRange range; - range.b.cursor = 0; - range.e.cursor = chip_info->num_bels; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.chip = chip_info; + ++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; return range; } @@ -437,7 +454,7 @@ struct Arch : BaseCtx BelType getBelType(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return chip_info->bel_data[bel.index].type; + return locInfo(bel)->bel_data[bel.index].type; } WireId getWireBelPin(BelId bel, PortPin pin) const; @@ -447,9 +464,10 @@ struct Arch : BaseCtx BelPin ret; NPNR_ASSERT(wire != WireId()); - if (chip_info->wire_data[wire.index].bel_uphill.bel_index >= 0) { - ret.bel.index = chip_info->wire_data[wire.index].bel_uphill.bel_index; - ret.pin = chip_info->wire_data[wire.index].bel_uphill.port; + if (locInfo(wire)->wire_data[wire.index].bel_uphill.bel_index >= 0) { + ret.bel.index = locInfo(wire)->wire_data[wire.index].bel_uphill.bel_index; + ret.bel.location = wire.location + locInfo(wire)->wire_data[wire.index].bel_uphill.rel_bel_loc; + ret.pin = locInfo(wire)->wire_data[wire.index].bel_uphill.port; } return ret; @@ -459,8 +477,10 @@ struct Arch : BaseCtx { BelPinRange range; NPNR_ASSERT(wire != WireId()); - range.b.ptr = chip_info->wire_data[wire.index].bels_downhill.get(); - range.e.ptr = range.b.ptr + chip_info->wire_data[wire.index].num_bels_downhill; + range.b.ptr = locInfo(wire)->wire_data[wire.index].bels_downhill.get(); + range.b.wire_loc = wire.location; + range.e.ptr = range.b.ptr + locInfo(wire)->wire_data[wire.index].num_bels_downhill; + range.e.wire_loc = wire.location; return range; } @@ -471,7 +491,7 @@ struct Arch : BaseCtx IdString getWireName(WireId wire) const { NPNR_ASSERT(wire != WireId()); - return id(chip_info->wire_data[wire.index].name.get()); + return id(locInfo(wire)->wire_data[wire.index].name.get()); } uint32_t getWireChecksum(WireId wire) const { return wire.index; } @@ -479,8 +499,8 @@ struct Arch : BaseCtx void bindWire(WireId wire, IdString net, PlaceStrength strength) { NPNR_ASSERT(wire != WireId()); - NPNR_ASSERT(wire_to_net[wire.index] == IdString()); - wire_to_net[wire.index] = net; + NPNR_ASSERT(wire_to_net[wire] == IdString()); + wire_to_net[wire] = net; nets[net]->wires[wire].pip = PipId(); nets[net]->wires[wire].strength = strength; } @@ -488,45 +508,55 @@ struct Arch : BaseCtx void unbindWire(WireId wire) { NPNR_ASSERT(wire != WireId()); - NPNR_ASSERT(wire_to_net[wire.index] != IdString()); + NPNR_ASSERT(wire_to_net[wire] != IdString()); - auto &net_wires = nets[wire_to_net[wire.index]]->wires; + auto &net_wires = nets[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.index] = IdString(); - switches_locked[chip_info->pip_data[pip.index].switch_index] = IdString(); + pip_to_net[pip] = IdString(); } net_wires.erase(it); - wire_to_net[wire.index] = IdString(); + wire_to_net[wire] = IdString(); } bool checkWireAvail(WireId wire) const { NPNR_ASSERT(wire != WireId()); - return wire_to_net[wire.index] == IdString(); + return wire_to_net.find(wire) == wire_to_net.end() || wire_to_net.at(wire) == IdString(); } IdString getBoundWireNet(WireId wire) const { NPNR_ASSERT(wire != WireId()); - return wire_to_net[wire.index]; + if (wire_to_net.find(wire) == wire_to_net.end()) + return IdString(); + else + return wire_to_net.at(wire); } IdString getConflictingWireNet(WireId wire) const { NPNR_ASSERT(wire != WireId()); - return wire_to_net[wire.index]; + if (wire_to_net.find(wire) == wire_to_net.end()) + return IdString(); + else + return wire_to_net.at(wire); } WireRange getWires() const { WireRange range; - range.b.cursor = 0; - range.e.cursor = chip_info->num_wires; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.chip = chip_info; + ++range.b; //-1 and then ++ deals with the case of no wries in the first tile + range.e.cursor_tile = chip_info->width * chip_info->height; + range.e.cursor_index = 0; + range.e.chip = chip_info; return range; } @@ -540,16 +570,15 @@ struct Arch : BaseCtx void bindPip(PipId pip, IdString net, PlaceStrength strength) { NPNR_ASSERT(pip != PipId()); - NPNR_ASSERT(pip_to_net[pip.index] == IdString()); - NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == IdString()); + NPNR_ASSERT(pip_to_net[pip] == IdString()); - pip_to_net[pip.index] = net; - switches_locked[chip_info->pip_data[pip.index].switch_index] = net; + pip_to_net[pip] = net; WireId dst; - dst.index = chip_info->pip_data[pip.index].dst; - NPNR_ASSERT(wire_to_net[dst.index] == IdString()); - wire_to_net[dst.index] = net; + dst.index = locInfo(pip)->pip_data[pip.index].dst_idx; + dst.location = pip.location + locInfo(pip)->pip_data[pip.index].rel_dst_loc; + NPNR_ASSERT(wire_to_net[dst] == IdString()); + wire_to_net[dst] = net; nets[net]->wires[dst].pip = pip; nets[net]->wires[dst].strength = strength; } @@ -557,42 +586,52 @@ struct Arch : BaseCtx void unbindPip(PipId pip) { NPNR_ASSERT(pip != PipId()); - NPNR_ASSERT(pip_to_net[pip.index] != IdString()); - NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != IdString()); + NPNR_ASSERT(pip_to_net[pip] != IdString()); WireId dst; - dst.index = chip_info->pip_data[pip.index].dst; - NPNR_ASSERT(wire_to_net[dst.index] != IdString()); - wire_to_net[dst.index] = IdString(); - nets[pip_to_net[pip.index]]->wires.erase(dst); + dst.index = locInfo(pip)->pip_data[pip.index].dst_idx; + dst.location = pip.location + locInfo(pip)->pip_data[pip.index].rel_dst_loc; + NPNR_ASSERT(wire_to_net[dst] != IdString()); + wire_to_net[dst] = IdString(); + nets[pip_to_net[pip]]->wires.erase(dst); - pip_to_net[pip.index] = IdString(); - switches_locked[chip_info->pip_data[pip.index].switch_index] = IdString(); + pip_to_net[pip] = IdString(); } bool checkPipAvail(PipId pip) const { NPNR_ASSERT(pip != PipId()); - return switches_locked[chip_info->pip_data[pip.index].switch_index] == IdString(); + return pip_to_net.find(pip) == pip_to_net.end() || pip_to_net.at(pip) == IdString(); } IdString getBoundPipNet(PipId pip) const { NPNR_ASSERT(pip != PipId()); - return pip_to_net[pip.index]; + if (pip_to_net.find(pip) == pip_to_net.end()) + return IdString(); + else + return pip_to_net.at(pip); } IdString getConflictingPipNet(PipId pip) const { NPNR_ASSERT(pip != PipId()); - return switches_locked[chip_info->pip_data[pip.index].switch_index]; + if (pip_to_net.find(pip) == pip_to_net.end()) + return IdString(); + else + return pip_to_net.at(pip); } AllPipRange getPips() const { AllPipRange range; - range.b.cursor = 0; - range.e.cursor = chip_info->num_pips; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.chip = chip_info; + ++range.b; //-1 and then ++ deals with the case of no wries in the first tile + range.e.cursor_tile = chip_info->width * chip_info->height; + range.e.cursor_index = 0; + range.e.chip = chip_info; return range; } @@ -600,7 +639,8 @@ struct Arch : BaseCtx { WireId wire; NPNR_ASSERT(pip != PipId()); - wire.index = chip_info->pip_data[pip.index].src; + wire.index = locInfo(pip)->pip_data[pip.index].src_idx; + wire.location = pip.location + locInfo(pip)->pip_data[pip.index].rel_src_loc; return wire; } @@ -608,7 +648,8 @@ struct Arch : BaseCtx { WireId wire; NPNR_ASSERT(pip != PipId()); - wire.index = chip_info->pip_data[pip.index].dst; + wire.index = locInfo(pip)->pip_data[pip.index].dst_idx; + wire.location = pip.location + locInfo(pip)->pip_data[pip.index].rel_dst_loc; return wire; } @@ -616,7 +657,7 @@ struct Arch : BaseCtx { DelayInfo delay; NPNR_ASSERT(pip != PipId()); - delay.delay = chip_info->pip_data[pip.index].delay; + delay.delay = locInfo(pip)->pip_data[pip.index].delay; return delay; } @@ -624,8 +665,8 @@ struct Arch : BaseCtx { PipRange range; NPNR_ASSERT(wire != WireId()); - range.b.cursor = chip_info->wire_data[wire.index].pips_downhill.get(); - range.e.cursor = range.b.cursor + chip_info->wire_data[wire.index].num_downhill; + range.b.cursor = locInfo(wire)->wire_data[wire.index].pips_downhill.get(); + range.e.cursor = range.b.cursor + locInfo(wire)->wire_data[wire.index].num_downhill; return range; } @@ -633,8 +674,8 @@ struct Arch : BaseCtx { PipRange range; NPNR_ASSERT(wire != WireId()); - range.b.cursor = chip_info->wire_data[wire.index].pips_uphill.get(); - range.e.cursor = range.b.cursor + chip_info->wire_data[wire.index].num_uphill; + range.b.cursor = locInfo(wire)->wire_data[wire.index].pips_uphill.get(); + range.e.cursor = range.b.cursor + locInfo(wire)->wire_data[wire.index].num_uphill; return range; } diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc new file mode 100644 index 00000000..8310c3a1 --- /dev/null +++ b/ecp5/arch_pybindings.cc @@ -0,0 +1,32 @@ +/* + * 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 "nextpnr.h" +#include "pybindings.h" + +NEXTPNR_NAMESPACE_BEGIN + +void arch_wrap_python() {} + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/ecp5/arch_pybindings.h b/ecp5/arch_pybindings.h new file mode 100644 index 00000000..f7f07529 --- /dev/null +++ b/ecp5/arch_pybindings.h @@ -0,0 +1,31 @@ +/* + * 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 ARCH_PYBINDINGS_H +#define ARCH_PYBINDINGS_H +#ifndef NO_PYTHON + +#include "nextpnr.h" +#include "pybindings.h" + +NEXTPNR_NAMESPACE_BEGIN + +NEXTPNR_NAMESPACE_END +#endif +#endif diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index 0af48753..f913e445 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -60,13 +60,21 @@ enum PortPin : int32_t PIN_MAXIDX }; -NPNR_PACKED_STRUCT(struct Location { +NPNR_PACKED_STRUCT(struct LocationPOD { int16_t x, y; }); + +struct Location +{ int16_t x = -1, y = -1; + Location() : x(-1), y(-1){}; + Location(int16_t x, int16_t y) : x(x), y(y){}; + Location(const LocationPOD &pod) : x(pod.x), y(pod.y){}; + Location(const Location &loc) : x(loc.x), y(loc.y){}; + bool operator==(const Location &other) const { return x == other.x && y == other.y; } bool operator!=(const Location &other) const { return x != other.x || y == other.y; } -}); +}; -Location operator+(const Location &a, const Location &b) { return Location{a.x + b.x, a.y + b.y};} +Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); } struct BelId { @@ -91,29 +99,51 @@ struct PipId Location location; int32_t index = -1; - bool operator==(const WireId &other) const { return index == other.index && location == other.location; } - bool operator!=(const WireId &other) const { return index != other.index || location != other.location; } + bool operator==(const PipId &other) const { return index == other.index && location == other.location; } + bool operator!=(const PipId &other) const { return index != other.index || location != other.location; } }; NEXTPNR_NAMESPACE_END namespace std { +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Location &loc) const noexcept + { + std::size_t seed = std::hash()(loc.x); + seed ^= std::hash()(loc.y) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } +}; + template <> struct hash { - std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept { return hash()(bel.index); } + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept + { + std::size_t seed = std::hash()(bel.location); + seed ^= std::hash()(bel.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } }; template <> struct hash { std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept { - return hash()(wire.index); + std::size_t seed = std::hash()(wire.location); + seed ^= std::hash()(wire.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; } }; template <> struct hash { - std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept { return hash()(pip.index); } + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept + { + std::size_t seed = std::hash()(pip.location); + seed ^= std::hash()(pip.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } }; template <> struct hash : hash diff --git a/ecp5/main.cc b/ecp5/main.cc new file mode 100644 index 00000000..d025d8d4 --- /dev/null +++ b/ecp5/main.cc @@ -0,0 +1,139 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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 + +#ifndef NO_GUI +#include +#include "application.h" +#include "mainwindow.h" +#endif +#ifndef NO_PYTHON +#include "pybindings.h" +#endif +#include +#include +#include +#include "log.h" +#include "nextpnr.h" +#include "version.h" + +USING_NEXTPNR_NAMESPACE + +int main(int argc, char *argv[]) +{ + try { + + namespace po = boost::program_options; + int rc = 0; + + log_files.push_back(stdout); + + po::options_description options("Allowed options"); + options.add_options()("help,h", "show help"); + options.add_options()("verbose,v", "verbose output"); + options.add_options()("force,f", "keep running after errors"); +#ifndef NO_GUI + options.add_options()("gui", "start gui"); +#endif + + po::positional_options_description pos; +#ifndef NO_PYTHON + options.add_options()("run", po::value>(), "python file to execute"); + pos.add("run", -1); +#endif + options.add_options()("version,V", "show version"); + + po::variables_map vm; + try { + po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run(); + + po::store(parsed, vm); + + po::notify(vm); + } + + catch (std::exception &e) { + std::cout << e.what() << "\n"; + return 1; + } + + if (vm.count("help") || argc == 1) { + std::cout << boost::filesystem::basename(argv[0]) + << " -- Next Generation Place and Route (git " + "sha1 " GIT_COMMIT_HASH_STR ")\n"; + std::cout << "\n"; + std::cout << options << "\n"; + return argc != 1; + } + + if (vm.count("version")) { + std::cout << boost::filesystem::basename(argv[0]) + << " -- Next Generation Place and Route (git " + "sha1 " GIT_COMMIT_HASH_STR ")\n"; + return 1; + } + + Context ctx(ArchArgs{}); + + if (vm.count("verbose")) { + ctx.verbose = true; + } + + if (vm.count("force")) { + ctx.force = true; + } + + if (vm.count("seed")) { + ctx.rngseed(vm["seed"].as()); + } + +#ifndef NO_PYTHON + if (vm.count("run")) { + init_python(argv[0], true); + python_export_global("ctx", ctx); + + std::vector files = vm["run"].as>(); + for (auto filename : files) + execute_python_file(filename.c_str()); + + deinit_python(); + } +#endif + +#ifndef NO_GUI + if (vm.count("gui")) { + Application a(argc, argv); + MainWindow w; + w.show(); + + rc = a.exec(); + } +#endif + return rc; + } catch (log_execution_error_exception) { +#if defined(_MSC_VER) + _exit(EXIT_FAILURE); +#else + _Exit(EXIT_FAILURE); +#endif + } +} + +#endif From 074df03c593f03ba93ac19804c551e22a404d8d6 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 6 Jul 2018 12:20:15 +0200 Subject: [PATCH 04/31] ecp5: Add stub for UI Signed-off-by: David Shah --- gui/ecp5/mainwindow.cc | 50 ++++++++++++++++++++++++++++++++++++++++++ gui/ecp5/mainwindow.h | 46 ++++++++++++++++++++++++++++++++++++++ gui/ecp5/nextpnr.qrc | 2 ++ 3 files changed, 98 insertions(+) create mode 100644 gui/ecp5/mainwindow.cc create mode 100644 gui/ecp5/mainwindow.h create mode 100644 gui/ecp5/nextpnr.qrc diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc new file mode 100644 index 00000000..6cc3dd15 --- /dev/null +++ b/gui/ecp5/mainwindow.cc @@ -0,0 +1,50 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "mainwindow.h" + +static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } + +NEXTPNR_NAMESPACE_BEGIN + +MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent) +{ + initMainResource(); + + std::string title = "nextpnr-ecp5 - [EMPTY]"; + setWindowTitle(title.c_str()); + + createMenu(); +} + +MainWindow::~MainWindow() {} + +void MainWindow::createMenu() +{ + QMenu *menu_Custom = new QMenu("&Generic", menuBar); + menuBar->addAction(menu_Custom->menuAction()); +} + +void MainWindow::new_proj() {} + +void MainWindow::open_proj() {} + +bool MainWindow::save_proj() { return false; } + +NEXTPNR_NAMESPACE_END diff --git a/gui/ecp5/mainwindow.h b/gui/ecp5/mainwindow.h new file mode 100644 index 00000000..fd9812cd --- /dev/null +++ b/gui/ecp5/mainwindow.h @@ -0,0 +1,46 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "../basewindow.h" + +NEXTPNR_NAMESPACE_BEGIN + +class MainWindow : public BaseMainWindow +{ + Q_OBJECT + + public: + explicit MainWindow(QWidget *parent = 0); + virtual ~MainWindow(); + + public: + void createMenu(); + + protected Q_SLOTS: + virtual void new_proj(); + virtual void open_proj(); + virtual bool save_proj(); +}; + +NEXTPNR_NAMESPACE_END + +#endif // MAINWINDOW_H diff --git a/gui/ecp5/nextpnr.qrc b/gui/ecp5/nextpnr.qrc new file mode 100644 index 00000000..03585ec0 --- /dev/null +++ b/gui/ecp5/nextpnr.qrc @@ -0,0 +1,2 @@ + + From 83303bae5aec42ef411478dc2841b28aeecfd5e0 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 6 Jul 2018 14:02:37 +0200 Subject: [PATCH 05/31] ecp5: Implementing (at least stubs) most of arch.cc Signed-off-by: David Shah --- ecp5/arch.cc | 285 ++++++++++++++-------------------------- ecp5/arch.h | 24 +++- ecp5/arch_pybindings.h | 44 +++++++ ecp5/archdefs.h | 2 +- ecp5/place_legaliser.cc | 26 ++++ ecp5/place_legaliser.h | 31 +++++ 6 files changed, 223 insertions(+), 189 deletions(-) create mode 100644 ecp5/place_legaliser.cc create mode 100644 ecp5/place_legaliser.h diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 41c73cf8..fe10d415 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -2,6 +2,7 @@ * 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 @@ -19,80 +20,41 @@ #include #include +#include #include "log.h" #include "nextpnr.h" #include "util.h" + NEXTPNR_NAMESPACE_BEGIN +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)); +}; + // ----------------------------------------------------------------------- IdString Arch::belTypeToId(BelType type) const { - if (type == TYPE_ICESTORM_LC) - return id("ICESTORM_LC"); - if (type == TYPE_ICESTORM_RAM) - return id("ICESTORM_RAM"); - if (type == TYPE_SB_IO) - return id("SB_IO"); - if (type == TYPE_SB_GB) - return id("SB_GB"); - if (type == TYPE_ICESTORM_PLL) - return id("ICESTORM_PLL"); - if (type == TYPE_SB_WARMBOOT) - return id("SB_WARMBOOT"); - if (type == TYPE_SB_MAC16) - return id("SB_MAC16"); - if (type == TYPE_ICESTORM_HFOSC) - return id("ICESTORM_HFOSC"); - if (type == TYPE_ICESTORM_LFOSC) - return id("ICESTORM_LFOSC"); - if (type == TYPE_SB_I2C) - return id("SB_I2C"); - if (type == TYPE_SB_SPI) - return id("SB_SPI"); - if (type == TYPE_IO_I3C) - return id("IO_I3C"); - if (type == TYPE_SB_LEDDA_IP) - return id("SB_LEDDA_IP"); - if (type == TYPE_SB_RGBA_DRV) - return id("SB_RGBA_DRV"); - if (type == TYPE_ICESTORM_SPRAM) - return id("ICESTORM_SPRAM"); + if (type == TYPE_TRELLIS_SLICE) + return id("TRELLIS_SLICE"); + if (type == TYPE_TRELLIS_IO) + return id("TRELLIS_IO"); return IdString(); } BelType Arch::belTypeFromId(IdString type) const { - if (type == id("ICESTORM_LC")) - return TYPE_ICESTORM_LC; - if (type == id("ICESTORM_RAM")) - return TYPE_ICESTORM_RAM; - if (type == id("SB_IO")) - return TYPE_SB_IO; - if (type == id("SB_GB")) - return TYPE_SB_GB; - if (type == id("ICESTORM_PLL")) - return TYPE_ICESTORM_PLL; - if (type == id("SB_WARMBOOT")) - return TYPE_SB_WARMBOOT; - if (type == id("SB_MAC16")) - return TYPE_SB_MAC16; - if (type == id("ICESTORM_HFOSC")) - return TYPE_ICESTORM_HFOSC; - if (type == id("ICESTORM_LFOSC")) - return TYPE_ICESTORM_LFOSC; - if (type == id("SB_I2C")) - return TYPE_SB_I2C; - if (type == id("SB_SPI")) - return TYPE_SB_SPI; - if (type == id("IO_I3C")) - return TYPE_IO_I3C; - if (type == id("SB_LEDDA_IP")) - return TYPE_SB_LEDDA_IP; - if (type == id("SB_RGBA_DRV")) - return TYPE_SB_RGBA_DRV; - if (type == id("ICESTORM_SPRAM")) - return TYPE_ICESTORM_SPRAM; + if (type == id("TRELLIS_SLICE")) + return TYPE_TRELLIS_SLICE; + if (type == id("TRELLIS_IO")) + return TYPE_TRELLIS_IO; return TYPE_NONE; } @@ -101,7 +63,9 @@ BelType Arch::belTypeFromId(IdString type) const void IdString::initialize_arch(const BaseCtx *ctx) { #define X(t) initialize_add(ctx, #t, PIN_##t); + #include "portpins.inc" + #undef X } @@ -134,102 +98,43 @@ Arch::Arch(ArchArgs args) : args(args) load_chipdb(); #endif -#ifdef ICE40_HX1K_ONLY - if (args.type == ArchArgs::HX1K) { - chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_1k)); + if (args.type == ArchArgs::LFE5U_25F) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_25k)); + } else if (args.type == ArchArgs::LFE5U_45F) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_45k)); + } else if (args.type == ArchArgs::LFE5U_85F) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_85k)); } else { - log_error("Unsupported iCE40 chip type.\n"); + log_error("Unsupported ECP5 chip type.\n"); } -#else - if (args.type == ArchArgs::LP384) { - chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_384)); - } else if (args.type == ArchArgs::LP1K || args.type == ArchArgs::HX1K) { - chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_1k)); - } else if (args.type == ArchArgs::UP5K) { - chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_5k)); - } else if (args.type == ArchArgs::LP8K || args.type == ArchArgs::HX8K) { - chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_8k)); - } else { - log_error("Unsupported iCE40 chip type.\n"); - } -#endif - - package_info = nullptr; - for (int i = 0; i < chip_info->num_packages; i++) { - if (chip_info->packages_data[i].name.get() == args.package) { - package_info = &(chip_info->packages_data[i]); - break; - } - } - if (package_info == nullptr) - log_error("Unsupported package '%s'.\n", args.package.c_str()); - - bel_to_cell.resize(chip_info->num_bels); - wire_to_net.resize(chip_info->num_wires); - pip_to_net.resize(chip_info->num_pips); - switches_locked.resize(chip_info->num_switches); - - // Initialise regularly used IDStrings for performance - id_glb_buf_out = id("GLOBAL_BUFFER_OUTPUT"); - id_icestorm_lc = id("ICESTORM_LC"); - id_sb_io = id("SB_IO"); - id_sb_gb = id("SB_GB"); - id_cen = id("CEN"); - id_clk = id("CLK"); - id_sr = id("SR"); - id_i0 = id("I0"); - id_i1 = id("I1"); - id_i2 = id("I2"); - id_i3 = id("I3"); - id_dff_en = id("DFF_ENABLE"); - id_neg_clk = id("NEG_CLK"); } // ----------------------------------------------------------------------- std::string Arch::getChipName() { -#ifdef ICE40_HX1K_ONLY - if (args.type == ArchArgs::HX1K) { - return "Lattice LP1K"; - } else { - log_error("Unsupported iCE40 chip type.\n"); - } -#else - if (args.type == ArchArgs::LP384) { - return "Lattice LP384"; - } else if (args.type == ArchArgs::LP1K) { - return "Lattice LP1K"; - } else if (args.type == ArchArgs::HX1K) { - return "Lattice HX1K"; - } else if (args.type == ArchArgs::UP5K) { - return "Lattice UP5K"; - } else if (args.type == ArchArgs::LP8K) { - return "Lattice LP8K"; - } else if (args.type == ArchArgs::HX8K) { - return "Lattice HX8K"; + + if (args.type == ArchArgs::LFE5U_25F) { + return "Lattice LFE5U-25F"; + } else if (args.type == ArchArgs::LFE5U_45F) { + return "Lattice LFE5U-45F"; + } else if (args.type == ArchArgs::LFE5U_85F) { + return "Lattice LFE5U-85F"; } else { log_error("Unknown chip\n"); } -#endif } // ----------------------------------------------------------------------- IdString Arch::archArgsToId(ArchArgs args) const { - if (args.type == ArchArgs::LP384) - return id("lp384"); - if (args.type == ArchArgs::LP1K) - return id("lp1k"); - if (args.type == ArchArgs::HX1K) - return id("hx1k"); - if (args.type == ArchArgs::UP5K) - return id("up5k"); - if (args.type == ArchArgs::LP8K) - return id("lp8k"); - if (args.type == ArchArgs::HX8K) - return id("hx8k"); + if (args.type == ArchArgs::LFE5U_25F) + return id("lfe5u_25f"); + if (args.type == ArchArgs::LFE5U_45F) + return id("lfe5u_45f"); + if (args.type == ArchArgs::LFE5U_85F) + return id("lfe5u_85f"); return IdString(); } @@ -238,16 +143,23 @@ IdString Arch::archArgsToId(ArchArgs args) const BelId Arch::getBelByName(IdString name) const { BelId ret; - - if (bel_by_name.empty()) { - for (int i = 0; i < chip_info->num_bels; i++) - bel_by_name[id(chip_info->bel_data[i].name.get())] = i; - } - auto it = bel_by_name.find(name); if (it != bel_by_name.end()) - ret.index = it->second; + return it->second; + Location loc; + std::string basename; + std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); + ret.location = loc; + const LocationTypePOD *loci = locInfo(ret); + for (int i = 0; i < loci->num_bels; i++) { + if (std::strcmp(loci->bel_data[i].name.get(), basename.c_str()) == 0) { + ret.index = i; + break; + } + } + if (ret.index >= 0) + bel_by_name[name] = ret; return ret; } @@ -255,17 +167,10 @@ BelRange Arch::getBelsAtSameTile(BelId bel) const { BelRange br; NPNR_ASSERT(bel != BelId()); - // This requires Bels at the same tile are consecutive - int x = chip_info->bel_data[bel.index].x; - int y = chip_info->bel_data[bel.index].y; - int start = bel.index, end = bel.index; - while (start >= 0 && chip_info->bel_data[start].x == x && chip_info->bel_data[start].y == y) - start--; - start++; - br.b.cursor = start; - while (end < chip_info->num_bels && chip_info->bel_data[end].x == x && chip_info->bel_data[end].y == y) - end++; - br.e.cursor = end; + br.b.cursor_tile = bel.location.y * chip_info->width + bel.location.x; + br.e.cursor_tile = bel.location.y * chip_info->width + bel.location.x; + br.b.cursor_index = 0; + br.e.cursor_index = locInfo(bel)->num_bels; return br; } @@ -275,11 +180,11 @@ WireId Arch::getWireBelPin(BelId bel, PortPin pin) const NPNR_ASSERT(bel != BelId()); - int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires; - const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get(); - + int num_bel_wires = locInfo(bel)->bel_data[bel.index].num_bel_wires; + const BelWirePOD *bel_wires = locInfo(bel)->bel_data[bel.index].bel_wires.get(); for (int i = 0; i < num_bel_wires; i++) if (bel_wires[i].port == pin) { + ret.location = bel.location + bel_wires[i].rel_wire_loc; ret.index = bel_wires[i].wire_index; break; } @@ -292,16 +197,23 @@ WireId Arch::getWireBelPin(BelId bel, PortPin pin) const WireId Arch::getWireByName(IdString name) const { WireId ret; - - if (wire_by_name.empty()) { - for (int i = 0; i < chip_info->num_wires; i++) - wire_by_name[id(chip_info->wire_data[i].name.get())] = i; - } - auto it = wire_by_name.find(name); if (it != wire_by_name.end()) - ret.index = it->second; + return it->second; + Location loc; + std::string basename; + std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); + ret.location = loc; + const LocationTypePOD *loci = locInfo(ret); + for (int i = 0; i < loci->num_wires; i++) { + if (std::strcmp(loci->wire_data[i].name.get(), basename.c_str()) == 0) { + ret.index = i; + break; + } + } + if (ret.index >= 0) + wire_by_name[name] = ret; return ret; } @@ -309,34 +221,35 @@ WireId Arch::getWireByName(IdString name) const PipId Arch::getPipByName(IdString name) const { - PipId ret; - - if (pip_by_name.empty()) { - for (int i = 0; i < chip_info->num_pips; i++) { - PipId pip; - pip.index = i; - pip_by_name[getPipName(pip)] = i; - } - } - auto it = pip_by_name.find(name); if (it != pip_by_name.end()) - ret.index = it->second; + return it->second; - return ret; + PipId ret; + Location loc; + std::string basename; + std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); + const LocationTypePOD *loci = locInfo(ret); + for (int i = 0; i < loci->num_pips; i++) { + PipId curr; + curr.location = loc; + curr.index = i; + pip_by_name[getPipName(curr)] = curr; + } + return pip_by_name[name]; } IdString Arch::getPipName(PipId pip) const { NPNR_ASSERT(pip != PipId()); - int x = chip_info->pip_data[pip.index].x; - int y = chip_info->pip_data[pip.index].y; + int x = pip.location.x; + int y = pip.location.y; - std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get(); + std::string src_name = getWireName(getPipSrcWire(pip)).str(this); std::replace(src_name.begin(), src_name.end(), '/', '.'); - std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get(); + std::string dst_name = getWireName(getPipDstWire(pip)).str(this); std::replace(dst_name.begin(), dst_name.end(), '/', '.'); return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name); @@ -383,4 +296,10 @@ std::vector Arch::getPipGraphics(PipId pip) const return ret; }; +// ----------------------------------------------------------------------- + +bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } + +bool Arch::isBelLocationValid(BelId bel) const { return true; } + NEXTPNR_NAMESPACE_END diff --git a/ecp5/arch.h b/ecp5/arch.h index 85a3e44c..91e9fdcd 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -2,6 +2,7 @@ * 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 @@ -21,6 +22,8 @@ #error Include "arch.h" via "nextpnr.h" only. #endif +#include + NEXTPNR_NAMESPACE_BEGIN /**** Everything in this section must be kept in sync with chipdb.py ****/ @@ -340,9 +343,9 @@ struct Arch : BaseCtx { const ChipInfoPOD *chip_info; - mutable std::unordered_map bel_by_name; - mutable std::unordered_map wire_by_name; - mutable std::unordered_map pip_by_name; + mutable std::unordered_map bel_by_name; + mutable std::unordered_map wire_by_name; + mutable std::unordered_map pip_by_name; std::unordered_map bel_to_cell; std::unordered_map wire_to_net; @@ -375,7 +378,9 @@ struct Arch : BaseCtx IdString getBelName(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return id(locInfo(bel)->bel_data[bel.index].name.get()); + std::stringstream name; + name << "X" << bel.location.x << "/Y" << bel.location.y << "/" << locInfo(bel)->bel_data[bel.index].name.get(); + return id(name.str()); } uint32_t getBelChecksum(BelId bel) const { return bel.index; } @@ -491,7 +496,11 @@ struct Arch : BaseCtx IdString getWireName(WireId wire) const { NPNR_ASSERT(wire != WireId()); - return id(locInfo(wire)->wire_data[wire.index].name.get()); + + std::stringstream name; + name << "X" << wire.location.x << "/Y" << wire.location.y << "/" + << locInfo(wire)->wire_data[wire.index].name.get(); + return id(name.str()); } uint32_t getWireChecksum(WireId wire) const { return wire.index; } @@ -724,6 +733,11 @@ struct Arch : BaseCtx bool isClockPort(const CellInfo *cell, IdString port) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; + + // ------------------------------------------------- + // Placement validity checks + bool isValidBelForCell(CellInfo *cell, BelId bel) const; + bool isBelLocationValid(BelId bel) const; }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/arch_pybindings.h b/ecp5/arch_pybindings.h index f7f07529..a5044f29 100644 --- a/ecp5/arch_pybindings.h +++ b/ecp5/arch_pybindings.h @@ -26,6 +26,50 @@ 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 +{ + BelType from_str(Context *ctx, std::string name) { return ctx->belTypeFromId(ctx->id(name)); } + + std::string to_str(Context *ctx, BelType typ) { return ctx->belTypeToId(typ).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) { 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) { return ctx->getPipName(id).str(ctx); } +}; + +template <> struct string_converter +{ + PortPin from_str(Context *ctx, std::string name) { return ctx->portPinFromId(ctx->id(name)); } + + std::string to_str(Context *ctx, PortPin id) { return ctx->portPinToId(id).str(ctx); } +}; + +} // namespace PythonConversion + NEXTPNR_NAMESPACE_END #endif #endif diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index f913e445..b05ac22c 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -74,7 +74,7 @@ struct Location bool operator!=(const Location &other) const { return x != other.x || y == other.y; } }; -Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); } +inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); } struct BelId { diff --git a/ecp5/place_legaliser.cc b/ecp5/place_legaliser.cc new file mode 100644 index 00000000..0d23f15b --- /dev/null +++ b/ecp5/place_legaliser.cc @@ -0,0 +1,26 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * 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. + * + */ + +#include "place_legaliser.h" + +NEXTPNR_NAMESPACE_BEGIN + +bool legalise_design(Context *ctx) { return true; } + +NEXTPNR_NAMESPACE_END diff --git a/ecp5/place_legaliser.h b/ecp5/place_legaliser.h new file mode 100644 index 00000000..5f4df6aa --- /dev/null +++ b/ecp5/place_legaliser.h @@ -0,0 +1,31 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * 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 PLACE_LEGALISER_H +#define PLACE_LEGALISER_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +bool legalise_design(Context *ctx); + +NEXTPNR_NAMESPACE_END + +#endif From fdd13edff0b9c8263f251a3d2c2bbfeb31ad8bee Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 7 Jul 2018 15:29:02 +0200 Subject: [PATCH 06/31] ecp5: Starting to develop a Trellis importer Signed-off-by: David Shah --- ecp5/.gitignore | 2 + ecp5/arch.h | 2 - ecp5/trellis_import.py | 120 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 ecp5/.gitignore create mode 100755 ecp5/trellis_import.py diff --git a/ecp5/.gitignore b/ecp5/.gitignore new file mode 100644 index 00000000..c20c2ab7 --- /dev/null +++ b/ecp5/.gitignore @@ -0,0 +1,2 @@ +__pycache__ + diff --git a/ecp5/arch.h b/ecp5/arch.h index 91e9fdcd..53385acf 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -57,8 +57,6 @@ NPNR_PACKED_STRUCT(struct BelInfoPOD { BelType type; int32_t num_bel_wires; RelPtr bel_wires; - int8_t x, y, z; - int8_t padding_0; }); NPNR_PACKED_STRUCT(struct BelPortPOD { diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py new file mode 100755 index 00000000..59027d8a --- /dev/null +++ b/ecp5/trellis_import.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +import pytrellis +import database + +location_types = dict() +type_at_location = dict() +tiletype_names = dict() + + +def is_global(loc): + return loc.x == -2 and loc.y == -2 + + +# Get the index for a tiletype +def get_tiletype_index(name): + if name in tiletype_names: + return tiletype_names[name] + idx = len(tiletype_names) + tiletype_names[name] = idx + return idx + + +loc_wire_indices = dict() +loc_wires = dict() + +# Import all wire names at all locations +def import_location_wires(rg, x, y): + loc_wire_indices[x, y] = dict() + loc_wires[x, y] = list() + rtile = rg.tiles[pytrellis.Location(x, y)] + for wire in rtile.wires: + name = rg.to_str(wire.key()) + idx = len(loc_wires[x, y]) + loc_wires[x, y].append(name) + loc_wire_indices[x, y][name] = idx + + +# Take a RoutingId from Trellis and make into a (relx, rely, name) tuple +def resolve_wirename(rg, rid, cur_x, cur_y): + if is_global(rid.loc): + return (cur_x, cur_y, rg.to_str(rid.id)) + else: + x = rid.loc.x + y = rid.loc.y + widx = loc_wire_indices[x, y][rg.to_str(rid.id)] + return (x - cur_x, y - cur_y, widx) + + +loc_arc_indices = dict() # Map RoutingId index to nextpnr index +loc_arcs = dict() + + +# Import all arc indices at a location +def index_location_arcs(rg, x, y): + loc_arc_indices[x, y] = dict() + loc_arcs[x, y] = list() + rtile = rg.tiles[pytrellis.Location(x, y)] + for arc in rtile.arcs: + idx = len(loc_arcs) + trid = arc.key() + loc_arcs[x, y].append(trid) + loc_arc_indices[x, y][trid] = idx + +# Import a location, deduplicating if appropriate +def import_location(rg, x, y): + rtile = rg.tiles[pytrellis.Location(x, y)] + arcs = [] # (src, dst, configurable, tiletype) + wires = [] # (name, uphill, downhill, belpin_uphill, belpins_downhill) + bels = [] # (name, (pin, wire)) + for name in loc_wires[x, y]: + w = rtile.wires[rg.ident(name)] + arcs_uphill = [] + arcs_downhill = [] + for uh in w.uphill: + arcidx = loc_arc_indices[uh.loc.x, uh.loc.y][uh.id] + arcs_uphill.append((uh.loc.x - x, uh.loc.y - y, arcidx)) + for dh in w.downhill: + arcidx = loc_arc_indices[dh.loc.x, dh.loc.y][dh.id] + arcs_downhill.append((dh.loc.x - x, dh.loc.y - y, arcidx)) + # TODO: Bel pins + wires.append((name, tuple(arcs_downhill), tuple(arcs_uphill), tuple(), tuple())) + + for arcidx in loc_arcs[x, y]: + a = rtile.arcs[arcidx] + source_wire = resolve_wirename(rg, a.source, x, y) + dest_wire = resolve_wirename(rg, a.sink, x, y) + arcs.append((source_wire, dest_wire, a.configurable, get_tiletype_index(rg.to_str(a.tiletype)))) + + tile_data = (tuple(wires), tuple(arcs), tuple(bels)) + if tile_data in location_types: + type_at_location[x, y] = location_types[tile_data] + else: + idx = len(location_types) + location_types[tile_data] = idx + type_at_location[x, y] = idx + +def main(): + pytrellis.load_database(database.get_db_root()) + print("Initialising chip...") + chip = pytrellis.Chip("LFE5U-45F") + print("Building routing graph...") + rg = chip.get_routing_graph() + max_row = chip.get_max_row() + max_col = chip.get_max_col() + print("Indexing wires...") + for y in range(0, max_row+1): + for x in range(0, max_col+1): + import_location_wires(rg, x, y) + print("Indexing arcs...") + for y in range(0, max_row+1): + for x in range(0, max_col+1): + index_location_arcs(rg, x, y) + print("Importing tiles...") + for y in range(0, max_row+1): + for x in range(0, max_col+1): + print(" At R{}C{}".format(y, x)) + import_location(rg, x, y) + +if __name__ == "__main__": + main() From 12b0f7f162b80a43fba81a0948b4ea7ef7b6b0c6 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 7 Jul 2018 17:11:46 +0200 Subject: [PATCH 07/31] ecp5: Adding Bels to import script Signed-off-by: David Shah --- ecp5/trellis_import.py | 128 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 10 deletions(-) diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 59027d8a..2081cda9 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -23,16 +23,25 @@ def get_tiletype_index(name): loc_wire_indices = dict() loc_wires = dict() +loc_bels = dict() +wire_bel_pins_uphill = dict() +wire_bel_pins_downhill = dict() + + # Import all wire names at all locations def import_location_wires(rg, x, y): loc_wire_indices[x, y] = dict() loc_wires[x, y] = list() + wire_bel_pins_uphill[x, y] = list() + wire_bel_pins_downhill[x, y] = list() rtile = rg.tiles[pytrellis.Location(x, y)] for wire in rtile.wires: name = rg.to_str(wire.key()) idx = len(loc_wires[x, y]) loc_wires[x, y].append(name) loc_wire_indices[x, y][name] = idx + wire_bel_pins_uphill[x, y].append([]) + wire_bel_pins_downhill[x, y].append([]) # Take a RoutingId from Trellis and make into a (relx, rely, name) tuple @@ -61,24 +70,117 @@ def index_location_arcs(rg, x, y): loc_arcs[x, y].append(trid) loc_arc_indices[x, y][trid] = idx + +def add_bel_input(bel_x, bel_y, bel_idx, bel_pin, wire_x, wire_y, wire_name): + loc_bels[bel_x, bel_y][bel_idx][2].append((bel_pin, (wire_x, wire_y, loc_wire_indices[wire_x, wire_y][wire_name]))) + wire_bel_pins_downhill[wire_x, wire_y][loc_wire_indices[wire_x, wire_y][wire_name]].append(( + (bel_x, bel_y, bel_idx), bel_pin)) + + +def add_bel_output(bel_x, bel_y, bel_idx, bel_pin, wire_x, wire_y, wire_name): + loc_bels[bel_x, bel_y][bel_idx][2].append((bel_pin, (wire_x, wire_y, loc_wire_indices[wire_x, wire_y][wire_name]))) + wire_bel_pins_uphill[wire_x, wire_y][loc_wire_indices[wire_x, wire_y][wire_name]].append(( + (bel_x, bel_y, bel_idx), bel_pin)) + + +def add_slice(x, y, z): + idx = len(loc_bels[x, y]) + l = ("A", "B", "C", "D")[z] + name = "SLICE" + l + loc_bels[x, y].append((name, "SLICE", [])) + lc0 = z * 2 + lc1 = z * 2 + 1 + add_bel_input(x, y, idx, "A0", x, y, "A{}_SLICE".format(lc0)) + add_bel_input(x, y, idx, "B0", x, y, "B{}_SLICE".format(lc0)) + add_bel_input(x, y, idx, "C0", x, y, "C{}_SLICE".format(lc0)) + add_bel_input(x, y, idx, "D0", x, y, "D{}_SLICE".format(lc0)) + add_bel_input(x, y, idx, "M0", x, y, "M{}_SLICE".format(lc0)) + + add_bel_input(x, y, idx, "A1", x, y, "A{}_SLICE".format(lc1)) + add_bel_input(x, y, idx, "B1", x, y, "B{}_SLICE".format(lc1)) + add_bel_input(x, y, idx, "C1", x, y, "C{}_SLICE".format(lc1)) + add_bel_input(x, y, idx, "D1", x, y, "D{}_SLICE".format(lc1)) + add_bel_input(x, y, idx, "M1", x, y, "M{}_SLICE".format(lc1)) + + add_bel_input(x, y, idx, "FCI", x, y, "FCI{}_SLICE".format(l if z > 0 else "")) + add_bel_input(x, y, idx, "FXA", x, y, "FXA{}_SLICE".format(l)) + add_bel_input(x, y, idx, "FXB", x, y, "FXB{}_SLICE".format(l)) + + add_bel_input(x, y, idx, "CLK", x, y, "CLK{}_SLICE".format(z)) + add_bel_input(x, y, idx, "LSR", x, y, "LSR{}_SLICE".format(z)) + add_bel_input(x, y, idx, "CE", x, y, "CE{}_SLICE".format(z)) + + add_bel_output(x, y, idx, "F0", x, y, "F{}_SLICE".format(lc0)) + add_bel_output(x, y, idx, "Q0", x, y, "Q{}_SLICE".format(lc0)) + + add_bel_output(x, y, idx, "F1", x, y, "F{}_SLICE".format(lc1)) + add_bel_output(x, y, idx, "Q1", x, y, "Q{}_SLICE".format(lc1)) + + add_bel_output(x, y, idx, "FCO", x, y, "FCO{}_SLICE".format(l if z < 3 else "")) + + +def add_pio(x, y, z): + idx = len(loc_bels[x, y]) + l = ("A", "B", "C", "D")[z] + name = "PIO" + l + loc_bels[x, y].append((name, "PIO", [])) + add_bel_input(x, y, idx, "I", x, y, "PADDO{}_PIO".format(l)) + add_bel_input(x, y, idx, "T", x, y, "PADDT{}_PIO".format(l)) + add_bel_output(x, y, idx, "O", x, y, "JPADDI{}_PIO".format(l)) + + +def add_bels(chip, x, y): + loc_bels[x, y] = [] + tiles = chip.get_tiles_by_position(y, x) + num_slices = 0 + num_pios = 0 + for tile in tiles: + tt = tile.info.type + if tt == "PLC2": + num_slices = 4 + elif "PICL0" in tt or "PICR0" in tt: + num_pios = 4 + elif "PIOT0" in tt or "PIOB0" in tt: + num_pios = 2 + for i in range(num_slices): + add_slice(x, y, i) + for i in range(num_pios): + add_pio(x, y, i) + + # Import a location, deduplicating if appropriate def import_location(rg, x, y): rtile = rg.tiles[pytrellis.Location(x, y)] arcs = [] # (src, dst, configurable, tiletype) wires = [] # (name, uphill, downhill, belpin_uphill, belpins_downhill) - bels = [] # (name, (pin, wire)) + bels = [] # (name, [(pin, wire)]) for name in loc_wires[x, y]: w = rtile.wires[rg.ident(name)] arcs_uphill = [] arcs_downhill = [] + belpins_uphill = [] + belpins_downhill = [] for uh in w.uphill: arcidx = loc_arc_indices[uh.loc.x, uh.loc.y][uh.id] arcs_uphill.append((uh.loc.x - x, uh.loc.y - y, arcidx)) for dh in w.downhill: arcidx = loc_arc_indices[dh.loc.x, dh.loc.y][dh.id] arcs_downhill.append((dh.loc.x - x, dh.loc.y - y, arcidx)) - # TODO: Bel pins - wires.append((name, tuple(arcs_downhill), tuple(arcs_uphill), tuple(), tuple())) + for bp in wire_bel_pins_uphill[x, y][loc_wire_indices[x, y][name]]: + bel, pin = bp + bel_x, bel_y, bel_idx = bel + belpins_uphill.append(((bel_x - x, bel_y - y, bel_idx), pin)) + for bp in wire_bel_pins_downhill[x, y][loc_wire_indices[x, y][name]]: + bel, pin = bp + bel_x, bel_y, bel_idx = bel + belpins_downhill.append(((bel_x - x, bel_y - y, bel_idx), pin)) + assert len(belpins_uphill) <= 1 + wires.append((name, tuple(arcs_downhill), tuple(arcs_uphill), tuple(belpins_uphill), tuple(belpins_downhill))) + + for bel in loc_bels[x, y]: + name, beltype, pins = bel + xformed_pins = tuple((p[0], (p[1][0] - x, p[1][1] - y, p[1][2])) for p in pins) + bels.append((name, beltype, xformed_pins)) for arcidx in loc_arcs[x, y]: a = rtile.arcs[arcidx] @@ -94,27 +196,33 @@ def import_location(rg, x, y): location_types[tile_data] = idx type_at_location[x, y] = idx + def main(): pytrellis.load_database(database.get_db_root()) print("Initialising chip...") - chip = pytrellis.Chip("LFE5U-45F") + chip = pytrellis.Chip("LFE5U-25F") print("Building routing graph...") rg = chip.get_routing_graph() max_row = chip.get_max_row() max_col = chip.get_max_col() print("Indexing wires...") - for y in range(0, max_row+1): - for x in range(0, max_col+1): + for y in range(0, max_row + 1): + for x in range(0, max_col + 1): import_location_wires(rg, x, y) print("Indexing arcs...") - for y in range(0, max_row+1): - for x in range(0, max_col+1): + for y in range(0, max_row + 1): + for x in range(0, max_col + 1): index_location_arcs(rg, x, y) + print("Adding bels...") + for y in range(0, max_row + 1): + for x in range(0, max_col + 1): + add_bels(chip, x, y) print("Importing tiles...") - for y in range(0, max_row+1): - for x in range(0, max_col+1): + for y in range(0, max_row + 1): + for x in range(0, max_col + 1): print(" At R{}C{}".format(y, x)) import_location(rg, x, y) + if __name__ == "__main__": main() From ee45f5790978f7f776bb559707826802335918b8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 7 Jul 2018 17:37:33 +0200 Subject: [PATCH 08/31] ecp5: Starting to add BBA to importer Signed-off-by: David Shah --- ecp5/arch.h | 1 - ecp5/trellis_import.py | 313 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+), 1 deletion(-) diff --git a/ecp5/arch.h b/ecp5/arch.h index 53385acf..b9cedd2c 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -69,7 +69,6 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD { LocationPOD rel_src_loc, rel_dst_loc; int32_t src_idx, dst_idx; int32_t delay; - LocationPOD rel_tile_loc; int16_t tile_type; int8_t pip_type; int8_t padding_0; diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 2081cda9..88420c6f 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -197,6 +197,319 @@ def import_location(rg, x, y): type_at_location[x, y] = idx +class BinaryBlobAssembler: + def __init__(self, cname, endianness, nodebug=False): + assert endianness in ["le", "be"] + self.cname = cname + self.endianness = endianness + self.finalized = False + self.data = bytearray() + self.comments = dict() + self.labels = dict() + self.exports = set() + self.labels_byaddr = dict() + self.ltypes_byaddr = dict() + self.strings = dict() + self.refs = dict() + self.nodebug = nodebug + + def l(self, name, ltype=None, export=False): + assert not self.finalized + assert name not in self.labels + assert len(self.data) not in self.labels_byaddr + self.labels[name] = len(self.data) + if ltype is not None: + self.ltypes_byaddr[len(self.data)] = ltype + self.labels_byaddr[len(self.data)] = name + if export: + assert ltype is not None + self.exports.add(len(self.data)) + + def r(self, name, comment): + assert not self.finalized + assert len(self.data) % 4 == 0 + assert len(self.data) not in self.refs + if self.nodebug: + comment = None + if name is not None: + self.refs[len(self.data)] = (name, comment) + self.data.append(0) + self.data.append(0) + self.data.append(0) + self.data.append(0) + if (name is None) and (comment is not None): + self.comments[len(self.data)] = comment + " (null reference)" + + def s(self, s, comment): + assert not self.finalized + if self.nodebug: + comment = None + if s not in self.strings: + index = len(self.strings) + self.strings[s] = index + else: + index = self.strings[s] + if comment is not None: + self.r("str%d" % index, '%s: "%s"' % (comment, s)) + else: + self.r("str%d" % index, None) + + def u8(self, v, comment): + assert not self.finalized + if self.nodebug: + comment = None + self.data.append(v) + if comment is not None: + self.comments[len(self.data)] = comment + + def u16(self, v, comment): + assert not self.finalized + assert len(self.data) % 2 == 0 + if self.nodebug: + comment = None + if self.endianness == "le": + self.data.append(v & 255) + self.data.append((v >> 8) & 255) + elif self.endianness == "be": + self.data.append((v >> 8) & 255) + self.data.append(v & 255) + else: + assert 0 + if comment is not None: + self.comments[len(self.data)] = comment + + def s16(self, v, comment): + assert not self.finalized + assert len(self.data) % 2 == 0 + if self.nodebug: + comment = None + c2val = (~v + 1) if v < 0 else v + if self.endianness == "le": + self.data.append(c2val & 255) + self.data.append((c2val >> 8) & 255) + elif self.endianness == "be": + self.data.append((c2val >> 8) & 255) + self.data.append(c2val & 255) + else: + assert 0 + if comment is not None: + self.comments[len(self.data)] = comment + + def u32(self, v, comment): + assert not self.finalized + assert len(self.data) % 4 == 0 + if self.nodebug: + comment = None + if self.endianness == "le": + self.data.append(v & 255) + self.data.append((v >> 8) & 255) + self.data.append((v >> 16) & 255) + self.data.append((v >> 24) & 255) + elif self.endianness == "be": + self.data.append((v >> 24) & 255) + self.data.append((v >> 16) & 255) + self.data.append((v >> 8) & 255) + self.data.append(v & 255) + else: + assert 0 + if comment is not None: + self.comments[len(self.data)] = comment + + def finalize(self): + assert not self.finalized + for s, index in self.strings.items(): + self.l("str%d" % index, "char") + for c in s: + self.data.append(ord(c)) + self.data.append(0) + self.finalized = True + cursor = 0 + while cursor < len(self.data): + if cursor in self.refs: + v = self.labels[self.refs[cursor][0]] - cursor + if self.endianness == "le": + self.data[cursor + 0] = (v & 255) + self.data[cursor + 1] = ((v >> 8) & 255) + self.data[cursor + 2] = ((v >> 16) & 255) + self.data[cursor + 3] = ((v >> 24) & 255) + elif self.endianness == "be": + self.data[cursor + 0] = ((v >> 24) & 255) + self.data[cursor + 1] = ((v >> 16) & 255) + self.data[cursor + 2] = ((v >> 8) & 255) + self.data[cursor + 3] = (v & 255) + else: + assert 0 + cursor += 4 + else: + cursor += 1 + + def write_verbose_c(self, f, ctype="const unsigned char"): + assert self.finalized + print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f) + cursor = 0 + bytecnt = 0 + while cursor < len(self.data): + if cursor in self.comments: + if bytecnt == 0: + print(" ", end="", file=f) + print(" // %s" % self.comments[cursor], file=f) + bytecnt = 0 + if cursor in self.labels_byaddr: + if bytecnt != 0: + print(file=f) + if cursor in self.exports: + print("#define %s ((%s*)(%s+%d))" % ( + self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f) + else: + print(" // [%d] %s" % (cursor, self.labels_byaddr[cursor]), file=f) + bytecnt = 0 + if cursor in self.refs: + if bytecnt != 0: + print(file=f) + print(" ", end="", file=f) + print(" %-4s" % ("%d," % self.data[cursor + 0]), end="", file=f) + print(" %-4s" % ("%d," % self.data[cursor + 1]), end="", file=f) + print(" %-4s" % ("%d," % self.data[cursor + 2]), end="", file=f) + print(" %-4s" % ("%d," % self.data[cursor + 3]), end="", file=f) + print(" // [%d] %s (reference to %s)" % (cursor, self.refs[cursor][1], self.refs[cursor][0]), file=f) + bytecnt = 0 + cursor += 4 + else: + if bytecnt == 0: + print(" ", end="", file=f) + print(" %-4s" % ("%d," % self.data[cursor]), end=("" if bytecnt < 15 else "\n"), file=f) + bytecnt = (bytecnt + 1) & 15 + cursor += 1 + if bytecnt != 0: + print(file=f) + print("};", file=f) + + def write_compact_c(self, f, ctype="const unsigned char"): + assert self.finalized + print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f) + column = 0 + for v in self.data: + if column == 0: + print(" ", end="", file=f) + column += 2 + s = "%d," % v + print(s, end="", file=f) + column += len(s) + if column > 75: + print(file=f) + column = 0 + if column != 0: + print(file=f) + for cursor in self.exports: + print("#define %s ((%s*)(%s+%d))" % ( + self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f) + print("};", file=f) + + def write_uint64_c(self, f, ctype="const uint64_t"): + assert self.finalized + print("%s %s[%d] = {" % (ctype, self.cname, (len(self.data) + 7) // 8), file=f) + column = 0 + for i in range((len(self.data) + 7) // 8): + v0 = self.data[8 * i + 0] if 8 * i + 0 < len(self.data) else 0 + v1 = self.data[8 * i + 1] if 8 * i + 1 < len(self.data) else 0 + v2 = self.data[8 * i + 2] if 8 * i + 2 < len(self.data) else 0 + v3 = self.data[8 * i + 3] if 8 * i + 3 < len(self.data) else 0 + v4 = self.data[8 * i + 4] if 8 * i + 4 < len(self.data) else 0 + v5 = self.data[8 * i + 5] if 8 * i + 5 < len(self.data) else 0 + v6 = self.data[8 * i + 6] if 8 * i + 6 < len(self.data) else 0 + v7 = self.data[8 * i + 7] if 8 * i + 7 < len(self.data) else 0 + if self.endianness == "le": + v = v0 << 0 + v |= v1 << 8 + v |= v2 << 16 + v |= v3 << 24 + v |= v4 << 32 + v |= v5 << 40 + v |= v6 << 48 + v |= v7 << 56 + elif self.endianness == "be": + v = v7 << 0 + v |= v6 << 8 + v |= v5 << 16 + v |= v4 << 24 + v |= v3 << 32 + v |= v2 << 40 + v |= v1 << 48 + v |= v0 << 56 + else: + assert 0 + if column == 3: + print(" 0x%016x," % v, file=f) + column = 0 + else: + if column == 0: + print(" ", end="", file=f) + print(" 0x%016x," % v, end="", file=f) + column += 1 + if column != 0: + print("", file=f) + print("};", file=f) + + def write_string_c(self, f, ctype="const char"): + assert self.finalized + assert self.data[len(self.data) - 1] == 0 + print("%s %s[%d] =" % (ctype, self.cname, len(self.data)), file=f) + print(" \"", end="", file=f) + column = 0 + for i in range(len(self.data) - 1): + if (self.data[i] < 32) or (self.data[i] > 126): + print("\\%03o" % self.data[i], end="", file=f) + column += 4 + elif self.data[i] == ord('"') or self.data[i] == ord('\\'): + print("\\" + chr(self.data[i]), end="", file=f) + column += 2 + else: + print(chr(self.data[i]), end="", file=f) + column += 1 + if column > 70 and (i != len(self.data) - 2): + print("\"\n \"", end="", file=f) + column = 0 + print("\";", file=f) + + def write_binary(self, f): + assert self.finalized + assert self.data[len(self.data) - 1] == 0 + f.buffer.write(self.data) + + +def write_database(bba): + def write_loc(x, y, sym_name): + bba.s16(x, "%s_x" % sym_name) + bba.s16(y, "%s_y" % sym_name) + + for loctype, idx in sorted(location_types.items(), key=lambda x: x[1]): + wires, arcs, bels = loctype + bba.l("loc%d_pips" % idx, "PipInfoPOD") + for arc in arcs: + src_wire, dst_wire, configurable, tile_type = arc + write_loc(src_wire[0], src_wire[1], "src") + write_loc(dst_wire[0], dst_wire[1], "dst") + bba.u32(src_wire[2], "src_idx") + bba.u32(dst_wire[2], "dst_idx") + bba.u32(1, "delay") # TODO:delay + bba.u16(tile_type, "tile_type") + bba.u8(1 if not configurable else 0, "pip_type") + bba.u8(0, "padding") + for wire_idx in range(len(wires)): + wire = wires[wire_idx] + name, downpips, uppips, downbels, upbels = wire + if len(downpips) > 0: + bba.l("loc%d_wire%d_downpips" % (idx, wire_idx), "PipLocatorPOD") + for dp in downpips: + write_loc(dp[0], dp[1], "rel_loc") + bba.u32(dp[2], "idx") + if len(uppips) > 0: + bba.l("loc%d_wire%d_uppips" % (idx, wire_idx), "PipLocatorPOD") + for up in uppips: + write_loc(up[0], up[1], "rel_loc") + bba.u32(up[2], "idx") + + def main(): pytrellis.load_database(database.get_db_root()) print("Initialising chip...") From 6f90c3df6118a857b24ee3ef031b54ef8aa8b7a8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 7 Jul 2018 19:58:42 +0200 Subject: [PATCH 09/31] ecp5: Adding complete binary blob writer to Trellis importer Signed-off-by: David Shah --- ecp5/.gitignore | 2 +- ecp5/trellis_import.py | 188 ++++++++++++++++++++++++++++++++++------- 2 files changed, 158 insertions(+), 32 deletions(-) diff --git a/ecp5/.gitignore b/ecp5/.gitignore index c20c2ab7..3249a7bb 100644 --- a/ecp5/.gitignore +++ b/ecp5/.gitignore @@ -1,2 +1,2 @@ __pycache__ - +chipdbs/ diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 88420c6f..ff86098b 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -1,11 +1,21 @@ #!/usr/bin/env python3 import pytrellis import database +import argparse location_types = dict() type_at_location = dict() tiletype_names = dict() +parser = argparse.ArgumentParser(description="import ECP5 routing and bels from Project Trellis") +group = parser.add_mutually_exclusive_group() +group.add_argument("-b", "--binary", action="store_true") +group.add_argument("-c", "--c_file", action="store_true") +parser.add_argument("device", type=str, help="target device") +parser.add_argument("outfile", type=argparse.FileType('w'), help="output filename") +parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc") +args = parser.parse_args() + def is_global(loc): return loc.x == -2 and loc.y == -2 @@ -20,6 +30,8 @@ def get_tiletype_index(name): return idx +portpins = dict() + loc_wire_indices = dict() loc_wires = dict() @@ -65,19 +77,21 @@ def index_location_arcs(rg, x, y): loc_arcs[x, y] = list() rtile = rg.tiles[pytrellis.Location(x, y)] for arc in rtile.arcs: - idx = len(loc_arcs) + idx = len(loc_arcs[x, y]) trid = arc.key() loc_arcs[x, y].append(trid) loc_arc_indices[x, y][trid] = idx def add_bel_input(bel_x, bel_y, bel_idx, bel_pin, wire_x, wire_y, wire_name): + bel_pin = portpins[bel_pin] loc_bels[bel_x, bel_y][bel_idx][2].append((bel_pin, (wire_x, wire_y, loc_wire_indices[wire_x, wire_y][wire_name]))) wire_bel_pins_downhill[wire_x, wire_y][loc_wire_indices[wire_x, wire_y][wire_name]].append(( (bel_x, bel_y, bel_idx), bel_pin)) def add_bel_output(bel_x, bel_y, bel_idx, bel_pin, wire_x, wire_y, wire_name): + bel_pin = portpins[bel_pin] loc_bels[bel_x, bel_y][bel_idx][2].append((bel_pin, (wire_x, wire_y, loc_wire_indices[wire_x, wire_y][wire_name]))) wire_bel_pins_uphill[wire_x, wire_y][loc_wire_indices[wire_x, wire_y][wire_name]].append(( (bel_x, bel_y, bel_idx), bel_pin)) @@ -359,7 +373,7 @@ class BinaryBlobAssembler: print(file=f) if cursor in self.exports: print("#define %s ((%s*)(%s+%d))" % ( - self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f) + self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f) else: print(" // [%d] %s" % (cursor, self.labels_byaddr[cursor]), file=f) bytecnt = 0 @@ -402,7 +416,7 @@ class BinaryBlobAssembler: print(file=f) for cursor in self.exports: print("#define %s ((%s*)(%s+%d))" % ( - self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f) + self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f) print("};", file=f) def write_uint64_c(self, f, ctype="const uint64_t"): @@ -477,43 +491,139 @@ class BinaryBlobAssembler: f.buffer.write(self.data) -def write_database(bba): +bel_types = { + "NONE": 0, + "SLICE": 1, + "PIO": 2 +} + +def write_database(dev_name, endianness): def write_loc(x, y, sym_name): - bba.s16(x, "%s_x" % sym_name) - bba.s16(y, "%s_y" % sym_name) + bba.s16(x, "%s.x" % sym_name) + bba.s16(y, "%s.y" % sym_name) + + bba = BinaryBlobAssembler("chipdb_blob_%s" % dev_name, endianness) + bba.r("chip_info", "chip_info") for loctype, idx in sorted(location_types.items(), key=lambda x: x[1]): wires, arcs, bels = loctype - bba.l("loc%d_pips" % idx, "PipInfoPOD") - for arc in arcs: - src_wire, dst_wire, configurable, tile_type = arc - write_loc(src_wire[0], src_wire[1], "src") - write_loc(dst_wire[0], dst_wire[1], "dst") - bba.u32(src_wire[2], "src_idx") - bba.u32(dst_wire[2], "dst_idx") - bba.u32(1, "delay") # TODO:delay - bba.u16(tile_type, "tile_type") - bba.u8(1 if not configurable else 0, "pip_type") - bba.u8(0, "padding") - for wire_idx in range(len(wires)): - wire = wires[wire_idx] - name, downpips, uppips, downbels, upbels = wire - if len(downpips) > 0: - bba.l("loc%d_wire%d_downpips" % (idx, wire_idx), "PipLocatorPOD") - for dp in downpips: - write_loc(dp[0], dp[1], "rel_loc") - bba.u32(dp[2], "idx") - if len(uppips) > 0: - bba.l("loc%d_wire%d_uppips" % (idx, wire_idx), "PipLocatorPOD") - for up in uppips: - write_loc(up[0], up[1], "rel_loc") - bba.u32(up[2], "idx") + if len(arcs) > 0: + bba.l("loc%d_pips" % idx, "PipInfoPOD") + for arc in arcs: + src_wire, dst_wire, configurable, tile_type = arc + write_loc(src_wire[0], src_wire[1], "src") + write_loc(dst_wire[0], dst_wire[1], "dst") + bba.u32(src_wire[2], "src_idx") + bba.u32(dst_wire[2], "dst_idx") + bba.u32(1, "delay") # TODO:delay + bba.u16(tile_type, "tile_type") + bba.u8(1 if not configurable else 0, "pip_type") + bba.u8(0, "padding") + if len(wires) > 0: + for wire_idx in range(len(wires)): + wire = wires[wire_idx] + name, downpips, uppips, downbels, upbels = wire + if len(downpips) > 0: + bba.l("loc%d_wire%d_downpips" % (idx, wire_idx), "PipLocatorPOD") + for dp in downpips: + write_loc(dp[0], dp[1], "rel_loc") + bba.u32(dp[2], "index") + if len(uppips) > 0: + bba.l("loc%d_wire%d_uppips" % (idx, wire_idx), "PipLocatorPOD") + for up in uppips: + write_loc(up[0], up[1], "rel_loc") + bba.u32(up[2], "index") + if len(downbels) > 0: + bba.l("loc%d_wire%d_downbels" % (idx, wire_idx), "BelPortPOD") + for db in downbels: + bel, pin = db + write_loc(bel[0], bel[1], "rel_bel_loc") + bba.u32(bel[2], "bel_index") + bba.u32(pin, "port") + bba.l("loc%d_wires" % idx, "WireInfoPOD") + for wire_idx in range(len(wires)): + wire = wires[wire_idx] + name, downpips, uppips, downbels, upbels = wire + bba.s(name, "name") + bba.u32(len(uppips), "num_uphill") + bba.u32(len(downpips), "num_downhill") + bba.r("loc%d_wire%d_uppips" % (idx, wire_idx) if len(uppips) > 0 else None, "pips_uphill") + bba.r("loc%d_wire%d_downpips" % (idx, wire_idx) if len(downpips) > 0 else None, "pips_downhill") + bba.u32(len(downbels), "num_bels_downhill") + if len(upbels) == 1: + bel, pin = upbels[0] + write_loc(bel[0], bel[1], "uphill_bel_loc") + bba.u32(bel[2], "uphill_bel_idx") + bba.u32(pin, "uphill_bel_pin") + else: + write_loc(-1, -1, "bel_uphill.rel_bel_loc") + bba.u32(0xFFFFFFFF, "bel_uphill.bel_index") + bba.u32(0, "bel_uphill.port") + bba.r("loc%d_wire%d_downbels" % (idx, wire_idx) if len(downbels) > 0 else None, "bels_downhill") + if len(bels) > 0: + for bel_idx in range(len(bels)): + bel, beltype, pins = bels[bel_idx] + bba.l("loc%d_bel%d_wires" % (idx, bel_idx), "BelPortPOD") + for pin in pins: + port, wire = pin + write_loc(wire[0], wire[1], "rel_wire_loc") + bba.u32(wire[2], "wire_index") + bba.u32(port, "port") + bba.l("loc%d_bels" % idx, "BelInfoPOD") + for bel_idx in range(len(bels)): + bel, beltype, pins = bels[bel_idx] + bba.s(bel, "name") + bba.u32(bel_types[beltype], "type") + bba.u32(len(pins), "num_bel_wires") + bba.r("loc%d_bel%d_wires" % (idx, bel_idx), "bel_wires") + bba.l("locations", "LocationTypePOD") + for loctype, idx in sorted(location_types.items(), key=lambda x: x[1]): + wires, arcs, bels = loctype + bba.u32(len(bels), "num_bels") + bba.u32(len(wires), "num_wires") + bba.u32(len(arcs), "num_pips") + bba.r("loc%d_bels" % idx if len(bels) > 0 else None, "bel_data") + bba.r("loc%d_wires" % idx if len(wires) > 0 else None, "wire_data") + bba.r("loc%d_pips" % idx if len(arcs) > 0 else None, "pips_data") + + bba.l("location_types", "int32_t") + for y in range(0, max_row+1): + for x in range(0, max_col+1): + bba.u32(type_at_location[x, y], "loctype") + + bba.l("chip_info") + bba.u32(max_col + 1, "width") + bba.u32(max_row + 1, "height") + bba.u32((max_col + 1) * (max_row + 1), "num_tiles") + bba.u32(len(location_types), "num_location_types") + bba.r("locations", "locations") + bba.r("location_types", "location_type") + bba.finalize() + return bba + +dev_names = {"LFE5U-25F": "25k", "LFE5U-45F": "45k", "LFE5U-85F": "85k"} def main(): + global max_row, max_col pytrellis.load_database(database.get_db_root()) + args = parser.parse_args() + + # Read port pin file + with open(args.portspins) as f: + for line in f: + line = line.replace("(", " ") + line = line.replace(")", " ") + line = line.split() + if len(line) == 0: + continue + assert len(line) == 2 + assert line[0] == "X" + idx = len(portpins) + 1 + portpins[line[1]] = idx + print("Initialising chip...") - chip = pytrellis.Chip("LFE5U-25F") + chip = pytrellis.Chip(args.device) print("Building routing graph...") rg = chip.get_routing_graph() max_row = chip.get_max_row() @@ -535,7 +645,23 @@ def main(): for x in range(0, max_col + 1): print(" At R{}C{}".format(y, x)) import_location(rg, x, y) + print("{} unique location types".format(len(location_types))) + bba = write_database(dev_names[args.device], "le") + if args.c_file: + print('#include "nextpnr.h"', file=args.outfile) + print('NEXTPNR_NAMESPACE_BEGIN', file=args.outfile) + + + if args.binary: + bba.write_binary(args.outfile) + + if args.c_file: + bba.write_string_c(args.outfile) + + if args.c_file: + print('NEXTPNR_NAMESPACE_END', file=args.outfile) + if __name__ == "__main__": main() From bb683d71d698481f472df26747c5332d49d933c1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 11:15:30 +0200 Subject: [PATCH 10/31] ecp5: Add 25k database Signed-off-by: David Shah --- .gitignore | 1 + ecp5/arch.cc | 25 ++++++++++++++++++++++++- ecp5/family.cmake | 41 +++++++++++++++++++++++++++++++++++++++++ ecp5/main.cc | 7 +++++-- ecp5/trellis_import.py | 6 +++--- 5 files changed, 74 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index f308b34a..6ca9e19f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /objs/ /nextpnr-generic* /nextpnr-ice40* +/nextpnr-ecp5* cmake-build-*/ Makefile cmake_install.cmake diff --git a/ecp5/arch.cc b/ecp5/arch.cc index fe10d415..a200e102 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -92,12 +92,20 @@ static const ChipInfoPOD *get_chip_info(const RelPtr *ptr) { return void load_chipdb(); #endif +#define LFE5U_25F_ONLY + Arch::Arch(ArchArgs args) : args(args) { #if defined(_MSC_VER) load_chipdb(); #endif - +#ifdef LFE5U_25F_ONLY + if (args.type == ArchArgs::LFE5U_25F) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_25k)); + } else { + log_error("Unsupported ECP5 chip type.\n"); + } +#else if (args.type == ArchArgs::LFE5U_25F) { chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_25k)); } else if (args.type == ArchArgs::LFE5U_45F) { @@ -107,6 +115,8 @@ Arch::Arch(ArchArgs args) : args(args) } else { log_error("Unsupported ECP5 chip type.\n"); } +#endif + } // ----------------------------------------------------------------------- @@ -302,4 +312,17 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } bool Arch::isBelLocationValid(BelId bel) const { return true; } +// ----------------------------------------------------------------------- + + +bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const +{ + return false; +} + +IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return IdString(); } + +bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } + + NEXTPNR_NAMESPACE_END diff --git a/ecp5/family.cmake b/ecp5/family.cmake index e69de29b..7f528886 100644 --- a/ecp5/family.cmake +++ b/ecp5/family.cmake @@ -0,0 +1,41 @@ + +set(devices 25k) + +set(DB_PY ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/trellis_import.py) + +file(MAKE_DIRECTORY ecp5/chipdbs/) +add_library(ecp5_chipdb OBJECT ecp5/chipdbs/) +target_compile_definitions(ecp5_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family}) +target_include_directories(ecp5_chipdb PRIVATE ${family}/) +set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=${TRELLIS_ROOT}/libtrellis:${TRELLIS_ROOT}/util/common") +if (MSVC) + target_sources(ecp5_chipdb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resource/embed.cc) + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resources/chipdb.rc PROPERTIES LANGUAGE RC) + foreach (dev ${devices}) + set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bin) + set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc) + add_custom_command(OUTPUT ${DEV_CC_DB} + COMMAND ${ENV_CMD} python3 ${DB_PY} -b -p ${DEV_PORTS_INC} ${dev} ${DEV_CC_DB} + DEPENDS ${DB_PY} + ) + target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB}) + set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE) + foreach (target ${family_targets}) + target_sources(${target} PRIVATE $ ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resource/chipdb.rc) + endforeach (target) + endforeach (dev) +else() + target_compile_options(ecp5_chipdb PRIVATE -g0 -O0 -w) + foreach (dev ${devices}) + set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.cc) + set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc) + add_custom_command(OUTPUT ${DEV_CC_DB} + COMMAND ${ENV_CMD} python3 ${DB_PY} -c -p ${DEV_PORTS_INC} ${dev} ${DEV_CC_DB} + DEPENDS ${DB_PY} + ) + target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB}) + foreach (target ${family_targets}) + target_sources(${target} PRIVATE $) + endforeach (target) + endforeach (dev) +endif() diff --git a/ecp5/main.cc b/ecp5/main.cc index d025d8d4..55e835e3 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -89,8 +89,11 @@ int main(int argc, char *argv[]) "sha1 " GIT_COMMIT_HASH_STR ")\n"; return 1; } - - Context ctx(ArchArgs{}); + ArchArgs args; + args.type = ArchArgs::LFE5U_25F; + args.package = "CABGA381"; + args.speed = 6; + Context ctx(args); if (vm.count("verbose")) { ctx.verbose = true; diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index ff86098b..aabcffb1 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -602,7 +602,7 @@ def write_database(dev_name, endianness): bba.finalize() return bba -dev_names = {"LFE5U-25F": "25k", "LFE5U-45F": "45k", "LFE5U-85F": "85k"} +dev_names = {"25k": "LFE5U-25F", "45k": "LFE5U-45F", "85k": "LFE5U-85F"} def main(): global max_row, max_col @@ -623,7 +623,7 @@ def main(): portpins[line[1]] = idx print("Initialising chip...") - chip = pytrellis.Chip(args.device) + chip = pytrellis.Chip(dev_names[args.device]) print("Building routing graph...") rg = chip.get_routing_graph() max_row = chip.get_max_row() @@ -646,7 +646,7 @@ def main(): print(" At R{}C{}".format(y, x)) import_location(rg, x, y) print("{} unique location types".format(len(location_types))) - bba = write_database(dev_names[args.device], "le") + bba = write_database(args.device, "le") if args.c_file: From 738b410bf84d5508942014a14344593ac99ce3b7 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 11:20:04 +0200 Subject: [PATCH 11/31] cmake: Only add ECP5 target when TRELLIS_ROOT specified Signed-off-by: David Shah --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4586dfa..e9d25abc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,9 @@ option(BUILD_PYTHON "Build Python Integration" ON) option(BUILD_TESTS "Build GUI" OFF) # List of families to build +if(DEFINED TRELLIS_ROOT) set(FAMILIES generic ice40 ecp5) + set(ARCH "" CACHE STRING "Architecture family for nextpnr build") set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES}) From c33aa259ad822d9c4ce3f46922504a70e730704c Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 12:35:27 +0200 Subject: [PATCH 12/31] ecp5: Adding a simple prepacked synth script Signed-off-by: David Shah --- ecp5/synth/.gitignore | 0 ecp5/synth/blinky.v | 16 ++++++++++ ecp5/synth/blinky.ys | 9 ++++++ ecp5/synth/cells.v | 39 +++++++++++++++++++++++ ecp5/synth/simple_map.v | 68 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+) create mode 100644 ecp5/synth/.gitignore create mode 100644 ecp5/synth/blinky.v create mode 100644 ecp5/synth/blinky.ys create mode 100644 ecp5/synth/cells.v create mode 100644 ecp5/synth/simple_map.v diff --git a/ecp5/synth/.gitignore b/ecp5/synth/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/ecp5/synth/blinky.v b/ecp5/synth/blinky.v new file mode 100644 index 00000000..aba58801 --- /dev/null +++ b/ecp5/synth/blinky.v @@ -0,0 +1,16 @@ +module top(input clk_pin, output [3:0] led_pin); + + wire clk; + wire [3:0] led; + + TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk)); + TRELLIS_IO #(.DIR("OUTPUT")) led_buf [3:0] (.B(led_pin), .I(led)); + + reg [25:0] ctr = 0; + + always@(posedge clk) + ctr <= ctr + 1'b1; + + assign led = ctr[25:22]; + +endmodule diff --git a/ecp5/synth/blinky.ys b/ecp5/synth/blinky.ys new file mode 100644 index 00000000..c0b74636 --- /dev/null +++ b/ecp5/synth/blinky.ys @@ -0,0 +1,9 @@ +read_verilog blinky.v +read_verilog -lib cells.v +synth -top top +abc -lut 4 +techmap -map simple_map.v +splitnets +opt_clean +stat +write_json blinky.json diff --git a/ecp5/synth/cells.v b/ecp5/synth/cells.v new file mode 100644 index 00000000..2435713a --- /dev/null +++ b/ecp5/synth/cells.v @@ -0,0 +1,39 @@ +(* blackbox *) +module TRELLIS_SLICE( + input A0, B0, C0, D0, + input A1, B1, C1, D1, + input M0, M1, + input FCI, FXA, FXB, + input CLK, LSR, CE, + output F0, Q0, + output F1, Q1, + output FCO, OFX0, OFX1 +); + +parameter MODE = "LOGIC"; +parameter GSR = "ENABLED"; +parameter SRMODE = "LSR_OVER_CE"; +parameter CEMUX = "1"; +parameter CLKMUX = "CLK"; +parameter LSRMUX = "LSR"; +parameter LUT0_INITVAL = 16'h0000; +parameter LUT1_INITVAL = 16'h0000; +parameter REG0_SD = "0"; +parameter REG1_SD = "0"; +parameter REG0_REGSET = "RESET"; +parameter REG1_REGSET = "RESET"; +parameter CCU2_INJECT1_0 = "NO"; +parameter CCU2_INJECT1_1 = "NO"; + +endmodule + +(* blackbox *) +module TRELLIS_IO( + inout B, + input I, + input T, + output O, +); +parameter DIR = "INPUT"; + +endmodule diff --git a/ecp5/synth/simple_map.v b/ecp5/synth/simple_map.v new file mode 100644 index 00000000..4a50fd01 --- /dev/null +++ b/ecp5/synth/simple_map.v @@ -0,0 +1,68 @@ +module \$_DFF_P_ (input D, C, output Q); + TRELLIS_SLICE #( + .MODE("LOGIC"), + .CLKMUX("CLK"), + .CEMUX("1"), + .REG0_SD("0"), + .REG0_REGSET("RESET"), + .SRMODE("LSR_OVER_CE"), + .GSR("DISABLED") + ) _TECHMAP_REPLACE_ ( + .CLK(C), + .M0(D), + .Q0(Q) + ); +endmodule + +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + TRELLIS_SLICE #( + .MODE("LOGIC"), + .LUT0_INITVAL(LUT) + ) _TECHMAP_REPLACE_ ( + .A0(A[0]), + .F0(Y) + ); + end + if (WIDTH == 2) begin + TRELLIS_SLICE #( + .MODE("LOGIC"), + .LUT0_INITVAL(LUT) + ) _TECHMAP_REPLACE_ ( + .A0(A[0]), + .B0(A[1]), + .F0(Y) + ); + end + if (WIDTH == 3) begin + TRELLIS_SLICE #( + .MODE("LOGIC"), + .LUT0_INITVAL(LUT) + ) _TECHMAP_REPLACE_ ( + .A0(A[0]), + .B0(A[1]), + .C0(A[2]), + .F0(Y) + ); + end + if (WIDTH == 4) begin + TRELLIS_SLICE #( + .MODE("LOGIC"), + .LUT0_INITVAL(LUT) + ) _TECHMAP_REPLACE_ ( + .A0(A[0]), + .B0(A[1]), + .C0(A[2]), + .D0(A[3]), + .F0(Y) + ); + end + endgenerate +endmodule From 93f379a488ae9f4c103c6d61cad49fba8012bc5a Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 12:41:11 +0200 Subject: [PATCH 13/31] ecp5: Adding JSON input option (not working yet) Signed-off-by: David Shah --- ecp5/main.cc | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ecp5/main.cc b/ecp5/main.cc index 55e835e3..a9b02537 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -30,10 +30,18 @@ #include #include #include +#include #include "log.h" #include "nextpnr.h" #include "version.h" +#include "place_sa.h" +#include "route.h" +#include "design_utils.h" +#include "timing.h" +#include "jsonparse.h" + + USING_NEXTPNR_NAMESPACE int main(int argc, char *argv[]) @@ -52,6 +60,8 @@ int main(int argc, char *argv[]) #ifndef NO_GUI options.add_options()("gui", "start gui"); #endif + options.add_options()("json", po::value(), "JSON design file to ingest"); + options.add_options()("seed", po::value(), "seed value for random number generator"); po::positional_options_description pos; #ifndef NO_PYTHON @@ -107,6 +117,31 @@ int main(int argc, char *argv[]) ctx.rngseed(vm["seed"].as()); } + if (vm.count("json")) { + std::string filename = vm["json"].as(); + std::ifstream f(filename); + if (!parse_json_file(f, filename, &ctx)) + log_error("Loading design failed.\n"); + + //if (!pack_design(&ctx) && !ctx.force) + // log_error("Packing design failed.\n"); + if (vm.count("freq")) + ctx.target_freq = vm["freq"].as() * 1e6; + assign_budget(&ctx); + ctx.check(); + print_utilisation(&ctx); + ctx.timing_driven = true; + if (vm.count("no-tmdriv")) + ctx.timing_driven = false; + + if (!place_design_sa(&ctx) && !ctx.force) + log_error("Placing design failed.\n"); + ctx.check(); + if (!route_design(&ctx) && !ctx.force) + log_error("Routing design failed.\n"); + + } + #ifndef NO_PYTHON if (vm.count("run")) { init_python(argv[0], true); From 59cb1600d9995eb52af41caea7bc364dcf47ea35 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 12:48:25 +0200 Subject: [PATCH 14/31] ecp5: Fixing arch bugs Signed-off-by: David Shah --- ecp5/arch.cc | 6 +++++- ecp5/archdefs.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index a200e102..6708c339 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -272,7 +272,11 @@ BelId Arch::getPackagePinBel(const std::string &pin) const { return BelId(); } std::string Arch::getBelPackagePin(BelId bel) const { return ""; } // ----------------------------------------------------------------------- -void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const {} +void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const { + x = bel.location.x; + y = bel.location.y; + gb = false; +} delay_t Arch::estimateDelay(WireId src, WireId dst) const { return 1; } diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index b05ac22c..4facc786 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -71,7 +71,7 @@ struct Location Location(const Location &loc) : x(loc.x), y(loc.y){}; bool operator==(const Location &other) const { return x == other.x && y == other.y; } - bool operator!=(const Location &other) const { return x != other.x || y == other.y; } + bool operator!=(const Location &other) const { return x != other.x || y != other.y; } }; inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); } From 49f39b8d5635e607764f12c31d2f18a0f79b1969 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 12:56:43 +0200 Subject: [PATCH 15/31] ecp5: Place design working, router now segfaults due to db issue Signed-off-by: David Shah --- CMakeLists.txt | 1 + ecp5/arch.cc | 6 +-- ecp5/main.cc | 13 +++---- ecp5/pack.cc | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ ecp5/pack.h | 31 ++++++++++++++++ 5 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 ecp5/pack.cc create mode 100644 ecp5/pack.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e9d25abc..32063af6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,6 +219,7 @@ endforeach (family) file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h) string(REGEX REPLACE "[^;]*/ice40/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") +string(REGEX REPLACE "[^;]*/ecp5/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") string(REGEX REPLACE "[^;]*/3rdparty[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") string(REGEX REPLACE "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 6708c339..e80ad829 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -116,7 +116,6 @@ Arch::Arch(ArchArgs args) : args(args) log_error("Unsupported ECP5 chip type.\n"); } #endif - } // ----------------------------------------------------------------------- @@ -272,7 +271,8 @@ BelId Arch::getPackagePinBel(const std::string &pin) const { return BelId(); } std::string Arch::getBelPackagePin(BelId bel) const { return ""; } // ----------------------------------------------------------------------- -void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const { +void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const +{ x = bel.location.x; y = bel.location.y; gb = false; @@ -318,7 +318,6 @@ bool Arch::isBelLocationValid(BelId bel) const { return true; } // ----------------------------------------------------------------------- - bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const { return false; @@ -328,5 +327,4 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } - NEXTPNR_NAMESPACE_END diff --git a/ecp5/main.cc b/ecp5/main.cc index a9b02537..245d1cc3 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -29,18 +29,18 @@ #endif #include #include -#include #include +#include #include "log.h" #include "nextpnr.h" #include "version.h" +#include "design_utils.h" +#include "jsonparse.h" +#include "pack.h" #include "place_sa.h" #include "route.h" -#include "design_utils.h" #include "timing.h" -#include "jsonparse.h" - USING_NEXTPNR_NAMESPACE @@ -123,8 +123,8 @@ int main(int argc, char *argv[]) if (!parse_json_file(f, filename, &ctx)) log_error("Loading design failed.\n"); - //if (!pack_design(&ctx) && !ctx.force) - // log_error("Packing design failed.\n"); + if (!pack_design(&ctx) && !ctx.force) + log_error("Packing design failed.\n"); if (vm.count("freq")) ctx.target_freq = vm["freq"].as() * 1e6; assign_budget(&ctx); @@ -139,7 +139,6 @@ int main(int argc, char *argv[]) ctx.check(); if (!route_design(&ctx) && !ctx.force) log_error("Routing design failed.\n"); - } #ifndef NO_PYTHON diff --git a/ecp5/pack.cc b/ecp5/pack.cc new file mode 100644 index 00000000..7f54c231 --- /dev/null +++ b/ecp5/pack.cc @@ -0,0 +1,99 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * 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. + * + */ + +#include "pack.h" +#include +#include +#include +#include "design_utils.h" +#include "log.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) +{ + return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") || + cell->type == ctx->id("$nextpnr_iobuf"); +} + +static bool is_trellis_io(const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); } + +// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated +void pack_io(Context *ctx) +{ + std::unordered_set packed_cells; + std::vector> new_cells; + log_info("Packing IOs..\n"); + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_nextpnr_iob(ctx, ci)) { + CellInfo *trio = nullptr; + if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + trio = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_trellis_io, ctx->id("B"), true, ci); + + } else if (ci->type == ctx->id("$nextpnr_obuf")) { + trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci); + } + if (trio != nullptr) { + // Trivial case, TRELLIS_IO used. Just destroy the net and the + // iobuf + log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx), + ci->type.c_str(ctx), ci->name.c_str(ctx)); + NetInfo *net = trio->ports.at(ctx->id("B")).net; + if (net != nullptr) { + ctx->nets.erase(net->name); + trio->ports.at(ctx->id("B")).net = nullptr; + } + if (ci->type == ctx->id("$nextpnr_iobuf")) { + NetInfo *net2 = ci->ports.at(ctx->id("I")).net; + if (net2 != nullptr) { + ctx->nets.erase(net2->name); + } + } + } else { + log_error("TRELLIS_IO required on all top level IOs...\n"); + } + packed_cells.insert(ci->name); + std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin())); + } + } + for (auto pcell : packed_cells) { + ctx->cells.erase(pcell); + } + for (auto &ncell : new_cells) { + ctx->cells[ncell->name] = std::move(ncell); + } +} + +// Main pack function +bool pack_design(Context *ctx) +{ + try { + log_break(); + pack_io(ctx); + log_info("Checksum: 0x%08x\n", ctx->checksum()); + return true; + } catch (log_execution_error_exception) { + return false; + } +} + +NEXTPNR_NAMESPACE_END diff --git a/ecp5/pack.h b/ecp5/pack.h new file mode 100644 index 00000000..cc051a41 --- /dev/null +++ b/ecp5/pack.h @@ -0,0 +1,31 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * 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 PACK_H +#define PACK_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +bool pack_design(Context *ctx); + +NEXTPNR_NAMESPACE_END + +#endif // ROUTE_H From 417913fd8509a44edf8421184c46d15d6763908b Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 13:29:13 +0200 Subject: [PATCH 16/31] ecp5: Architecture fixes Signed-off-by: David Shah --- ecp5/arch.h | 4 ++++ ecp5/trellis_import.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ecp5/arch.h b/ecp5/arch.h index b9cedd2c..029caef0 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -672,7 +672,9 @@ struct Arch : BaseCtx PipRange range; NPNR_ASSERT(wire != WireId()); range.b.cursor = locInfo(wire)->wire_data[wire.index].pips_downhill.get(); + range.b.wire_loc = wire.location; range.e.cursor = range.b.cursor + locInfo(wire)->wire_data[wire.index].num_downhill; + range.e.wire_loc = wire.location; return range; } @@ -681,7 +683,9 @@ struct Arch : BaseCtx PipRange range; NPNR_ASSERT(wire != WireId()); range.b.cursor = locInfo(wire)->wire_data[wire.index].pips_uphill.get(); + range.b.wire_loc = wire.location; range.e.cursor = range.b.cursor + locInfo(wire)->wire_data[wire.index].num_uphill; + range.e.wire_loc = wire.location; return range; } diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index aabcffb1..62aa4d81 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -297,7 +297,7 @@ class BinaryBlobAssembler: assert len(self.data) % 2 == 0 if self.nodebug: comment = None - c2val = (~v + 1) if v < 0 else v + c2val = (((-v) ^ 0xffff) + 1) if v < 0 else v if self.endianness == "le": self.data.append(c2val & 255) self.data.append((c2val >> 8) & 255) From 981522b10ead3b3c2cbc5f9f270b9fae9320395b Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 14:24:32 +0200 Subject: [PATCH 17/31] ecp5: Blinky example places and routes Signed-off-by: David Shah --- ecp5/arch.cc | 8 +++++++- ecp5/synth/wire.v | 11 +++++++++++ ecp5/synth/wire.ys | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 ecp5/synth/wire.v create mode 100644 ecp5/synth/wire.ys diff --git a/ecp5/arch.cc b/ecp5/arch.cc index e80ad829..d0dc63f0 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -218,11 +218,14 @@ WireId Arch::getWireByName(IdString name) const for (int i = 0; i < loci->num_wires; i++) { if (std::strcmp(loci->wire_data[i].name.get(), basename.c_str()) == 0) { ret.index = i; + ret.location = loc; break; } } if (ret.index >= 0) wire_by_name[name] = ret; + else + ret.location = Location(); return ret; } @@ -278,7 +281,10 @@ void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const gb = false; } -delay_t Arch::estimateDelay(WireId src, WireId dst) const { return 1; } +delay_t Arch::estimateDelay(WireId src, WireId dst) const +{ + return abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y); +} // ----------------------------------------------------------------------- diff --git a/ecp5/synth/wire.v b/ecp5/synth/wire.v new file mode 100644 index 00000000..2af68ed2 --- /dev/null +++ b/ecp5/synth/wire.v @@ -0,0 +1,11 @@ +module top(input a_pin, output [3:0] led_pin); + + wire a; + wire [3:0] led; + + TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a)); + TRELLIS_IO #(.DIR("OUTPUT")) led_buf [3:0] (.B(led_pin), .I(led)); + + //assign led[0] = !a; + always @(posedge a) led[0] <= !led[0]; +endmodule diff --git a/ecp5/synth/wire.ys b/ecp5/synth/wire.ys new file mode 100644 index 00000000..f916588b --- /dev/null +++ b/ecp5/synth/wire.ys @@ -0,0 +1,9 @@ +read_verilog wire.v +read_verilog -lib cells.v +synth -top top +abc -lut 4 +techmap -map simple_map.v +splitnets +opt_clean +stat +write_json wire.json From 863c22620b58d1043a32cdc22b23c954afcccc10 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 15:29:53 +0200 Subject: [PATCH 18/31] ecp5: Dump routing as a sanity check Signed-off-by: David Shah --- ecp5/main.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ecp5/main.cc b/ecp5/main.cc index 245d1cc3..c07e08de 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -139,6 +139,14 @@ int main(int argc, char *argv[]) ctx.check(); if (!route_design(&ctx) && !ctx.force) log_error("Routing design failed.\n"); + + // TEST BEGIN + for (auto pip : ctx.getPips()) { + if (!ctx.checkPipAvail(pip)) { + std::cout << ctx.getWireName(ctx.getPipSrcWire(pip)).str(&ctx) << " -> " << ctx.getWireName(ctx.getPipDstWire(pip)).str(&ctx) << std::endl; + } + } + // TEST END } #ifndef NO_PYTHON From 6cc6113d5a5e3a6463f3e3923efee613acc50579 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 17:24:35 +0200 Subject: [PATCH 19/31] ecp5: Link libtrellis library to ecp5 binary Signed-off-by: David Shah --- ecp5/family.cmake | 8 ++++++++ ecp5/main.cc | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/ecp5/family.cmake b/ecp5/family.cmake index 7f528886..190a3345 100644 --- a/ecp5/family.cmake +++ b/ecp5/family.cmake @@ -39,3 +39,11 @@ else() endforeach (target) endforeach (dev) endif() + +find_library(TRELLIS_LIB trellis PATHS ${TRELLIS_ROOT}/libtrellis) + +foreach (target ${family_targets}) + target_compile_definitions(${target} PRIVATE TRELLIS_ROOT="${TRELLIS_ROOT}") + target_include_directories(${target} PRIVATE ${TRELLIS_ROOT}/libtrellis/include) + target_link_libraries(${target} PRIVATE ${TRELLIS_LIB}) +endforeach (target) diff --git a/ecp5/main.cc b/ecp5/main.cc index c07e08de..c16e23bb 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -31,6 +31,9 @@ #include #include #include + +#include "Database.hpp" + #include "log.h" #include "nextpnr.h" #include "version.h" @@ -99,6 +102,9 @@ int main(int argc, char *argv[]) "sha1 " GIT_COMMIT_HASH_STR ")\n"; return 1; } + + Trellis::load_database(TRELLIS_ROOT"/database"); + ArchArgs args; args.type = ArchArgs::LFE5U_25F; args.package = "CABGA381"; From 54f06fdf72307579a6808a051f81a6d67e89f0ff Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 18:25:53 +0200 Subject: [PATCH 20/31] ecp5: Adding tiletypes to database Signed-off-by: David Shah --- ecp5/arch.h | 6 ++++++ ecp5/main.cc | 10 ++++++++-- ecp5/trellis_import.py | 5 +++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ecp5/arch.h b/ecp5/arch.h index 029caef0..219a960e 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -102,6 +102,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { int32_t num_location_types; RelPtr locations; RelPtr location_type; + RelPtr> tiletype_names; }); #if defined(_MSC_VER) @@ -698,6 +699,11 @@ struct Arch : BaseCtx return range; } + std::string getPipTiletype(PipId pip) const + { + return chip_info->tiletype_names[locInfo(pip)->pip_data[pip.index].tile_type].get(); + } + BelId getPackagePinBel(const std::string &pin) const; std::string getBelPackagePin(BelId bel) const; diff --git a/ecp5/main.cc b/ecp5/main.cc index c16e23bb..6456a11e 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -33,6 +33,8 @@ #include #include "Database.hpp" +#include "Chip.hpp" +#include "Tile.hpp" #include "log.h" #include "nextpnr.h" @@ -103,7 +105,7 @@ int main(int argc, char *argv[]) return 1; } - Trellis::load_database(TRELLIS_ROOT"/database"); + Trellis::load_database(TRELLIS_ROOT "/database"); ArchArgs args; args.type = ArchArgs::LFE5U_25F; @@ -147,9 +149,13 @@ int main(int argc, char *argv[]) log_error("Routing design failed.\n"); // TEST BEGIN + Trellis::Chip c("LFE5U-25F"); for (auto pip : ctx.getPips()) { if (!ctx.checkPipAvail(pip)) { - std::cout << ctx.getWireName(ctx.getPipSrcWire(pip)).str(&ctx) << " -> " << ctx.getWireName(ctx.getPipDstWire(pip)).str(&ctx) << std::endl; + auto tile = c.get_tile_by_position_and_type(pip.location.y, pip.location.x, ctx.getPipTiletype(pip)); + std::cout << ctx.getWireName(ctx.getPipSrcWire(pip)).str(&ctx) << " -> " + << ctx.getWireName(ctx.getPipDstWire(pip)).str(&ctx) << " [in tile " + << tile->info.name << "]" << std::endl; } } // TEST END diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 62aa4d81..6ff929f7 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -592,6 +592,10 @@ def write_database(dev_name, endianness): for x in range(0, max_col+1): bba.u32(type_at_location[x, y], "loctype") + bba.l("tiletype_names", "RelPtr") + for tt in tiletype_names: + bba.s(tt, "name") + bba.l("chip_info") bba.u32(max_col + 1, "width") bba.u32(max_row + 1, "height") @@ -599,6 +603,7 @@ def write_database(dev_name, endianness): bba.u32(len(location_types), "num_location_types") bba.r("locations", "locations") bba.r("location_types", "location_type") + bba.r("tiletype_names", "tiletype_names") bba.finalize() return bba From de82ecad592a98a7061bfccb62d3df40d50b1354 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 8 Jul 2018 18:36:59 +0200 Subject: [PATCH 21/31] ecp5: Make target device 45k on account of current hardware access Signed-off-by: David Shah --- ecp5/arch.cc | 8 ++++---- ecp5/family.cmake | 2 +- ecp5/main.cc | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index d0dc63f0..47546ccc 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -92,16 +92,16 @@ static const ChipInfoPOD *get_chip_info(const RelPtr *ptr) { return void load_chipdb(); #endif -#define LFE5U_25F_ONLY +#define LFE5U_45F_ONLY Arch::Arch(ArchArgs args) : args(args) { #if defined(_MSC_VER) load_chipdb(); #endif -#ifdef LFE5U_25F_ONLY - if (args.type == ArchArgs::LFE5U_25F) { - chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_25k)); +#ifdef LFE5U_45F_ONLY + if (args.type == ArchArgs::LFE5U_45F) { + chip_info = get_chip_info(reinterpret_cast *>(chipdb_blob_45k)); } else { log_error("Unsupported ECP5 chip type.\n"); } diff --git a/ecp5/family.cmake b/ecp5/family.cmake index 190a3345..f58cdbb2 100644 --- a/ecp5/family.cmake +++ b/ecp5/family.cmake @@ -1,5 +1,5 @@ -set(devices 25k) +set(devices 45k) set(DB_PY ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/trellis_import.py) diff --git a/ecp5/main.cc b/ecp5/main.cc index 6456a11e..ae229d51 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -108,7 +108,7 @@ int main(int argc, char *argv[]) Trellis::load_database(TRELLIS_ROOT "/database"); ArchArgs args; - args.type = ArchArgs::LFE5U_25F; + args.type = ArchArgs::LFE5U_45F; args.package = "CABGA381"; args.speed = 6; Context ctx(args); @@ -149,7 +149,7 @@ int main(int argc, char *argv[]) log_error("Routing design failed.\n"); // TEST BEGIN - Trellis::Chip c("LFE5U-25F"); + Trellis::Chip c("LFE5U-45F"); for (auto pip : ctx.getPips()) { if (!ctx.checkPipAvail(pip)) { auto tile = c.get_tile_by_position_and_type(pip.location.y, pip.location.x, ctx.getPipTiletype(pip)); From bad926bcc3e4d77bcb76d54eff689a91233c71fb Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 9 Jul 2018 12:02:31 +0200 Subject: [PATCH 22/31] ecp5: Adding bitstream gen for pips and LUT init Signed-off-by: David Shah --- ecp5/arch.cc | 6 +- ecp5/arch.h | 2 + ecp5/bitstream.cc | 135 ++++++++++++++++++++++++++++++++++++++++++ ecp5/bitstream.h | 32 ++++++++++ ecp5/main.cc | 30 ++++++---- ecp5/synth/.gitignore | 2 + 6 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 ecp5/bitstream.cc create mode 100644 ecp5/bitstream.h diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 47546ccc..7217af78 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -124,11 +124,11 @@ std::string Arch::getChipName() { if (args.type == ArchArgs::LFE5U_25F) { - return "Lattice LFE5U-25F"; + return "LFE5U-25F"; } else if (args.type == ArchArgs::LFE5U_45F) { - return "Lattice LFE5U-45F"; + return "LFE5U-45F"; } else if (args.type == ArchArgs::LFE5U_85F) { - return "Lattice LFE5U-85F"; + return "LFE5U-85F"; } else { log_error("Unknown chip\n"); } diff --git a/ecp5/arch.h b/ecp5/arch.h index 219a960e..cc63eeaa 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -704,6 +704,8 @@ struct Arch : BaseCtx return chip_info->tiletype_names[locInfo(pip)->pip_data[pip.index].tile_type].get(); } + int8_t getPipType(PipId pip) const { return locInfo(pip)->pip_data[pip.index].pip_type; } + BelId getPackagePinBel(const std::string &pin) const; std::string getBelPackagePin(BelId bel) const; diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc new file mode 100644 index 00000000..e44892ed --- /dev/null +++ b/ecp5/bitstream.cc @@ -0,0 +1,135 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * 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. + * + */ + +#include "bitstream.h" + +// From Project Trellis +#include "BitDatabase.hpp" +#include "Bitstream.hpp" +#include "Chip.hpp" +#include "ChipConfig.hpp" +#include "Tile.hpp" +#include "TileConfig.hpp" + +#include +#include + +#include "log.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +// Convert an absolute wire name to a relative Trellis one +static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) +{ + std::string basename = ctx->locInfo(wire)->wire_data[wire.index].name.get(); + std::string prefix2 = basename.substr(0, 2); + if (prefix2 == "G_" || prefix2 == "L_" || prefix2 == "R_") + return basename; + if (loc == wire.location) + return basename; + std::string rel_prefix; + if (wire.location.y < loc.y) + rel_prefix += "N" + to_string(loc.y - wire.location.y); + if (wire.location.y > loc.y) + rel_prefix += "S" + to_string(wire.location.y - loc.y); + if (wire.location.x > loc.x) + rel_prefix += "E" + to_string(wire.location.x - loc.x); + if (wire.location.x < loc.x) + rel_prefix += "W" + to_string(loc.x - wire.location.x); + return rel_prefix + "_" + basename; +} + +static std::vector int_to_bitvector(int val, int size) +{ + std::vector bv; + for (int i = 0; i < size; i++) { + bv.push_back((val & (1 << i)) != 0); + } + return bv; +} + +void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file, + std::string bitstream_file) +{ + Trellis::Chip empty_chip(ctx->getChipName()); + Trellis::ChipConfig cc; + if (!base_config_file.empty()) { + std::ifstream config_file(base_config_file); + if (!config_file) { + log_error("failed to open base config file '%s'\n", base_config_file.c_str()); + } + std::string str((std::istreambuf_iterator(config_file)), std::istreambuf_iterator()); + cc = Trellis::ChipConfig::from_string(str); + } else { + cc.chip_name = ctx->getChipName(); + // TODO: .bit metadata + } + + // Add all set, configurable pips to the config + for (auto pip : ctx->getPips()) { + if (ctx->getBoundPipNet(pip) != IdString()) { + if (ctx->getPipType(pip) == 0) { // ignore fixed pips + auto tile = empty_chip.get_tile_by_position_and_type(pip.location.y, pip.location.x, + ctx->getPipTiletype(pip)); + std::string source = get_trellis_wirename(ctx, pip.location, ctx->getPipSrcWire(pip)); + std::string sink = get_trellis_wirename(ctx, pip.location, ctx->getPipDstWire(pip)); + cc.tiles[tile->info.name].add_arc(sink, source); + } + } + } + + // Set all bankref tiles to 3.3V (TODO) + for (const auto &tile : empty_chip.tiles) { + std::string type = tile.second->info.type; + if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") { + cc.tiles[type].add_enum("BANK.VCCIO", "3V3"); + } + } + + // Configure slices + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->bel == BelId()) { + log_warning("found unplaced cell '%s' during bitstream gen\n", ci->name.c_str(ctx)); + } + BelId bel = ci->bel; + if (ci->type == ctx->id("TRELLIS_SLICE")) { + auto tile = empty_chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, "PLC2"); + std::string tname = tile->info.name; + std::string slice = ctx->locInfo(bel)->bel_data[bel.index].name.get(); + int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL")); + int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL")); + cc.tiles[tname].add_word(slice + ".K0.INIT", int_to_bitvector(lut0_init, 16)); + cc.tiles[tname].add_word(slice + ".K1.INIT", int_to_bitvector(lut1_init, 16)); + } + } + + // Configure chip + Trellis::Chip cfg_chip = cc.to_chip(); + if (!bitstream_file.empty()) { + Trellis::Bitstream::serialise_chip(cfg_chip).write_bit_py(bitstream_file); + } + if (!text_config_file.empty()) { + std::ofstream out_config(text_config_file); + out_config << cc.to_string(); + } +} + +NEXTPNR_NAMESPACE_END diff --git a/ecp5/bitstream.h b/ecp5/bitstream.h new file mode 100644 index 00000000..62617470 --- /dev/null +++ b/ecp5/bitstream.h @@ -0,0 +1,32 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * 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 BITSTREAM_H +#define BITSTREAM_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +void write_bitstream(Context *ctx, std::string base_config_file = "", std::string text_config_file = "", + std::string bitstream_file = ""); + +NEXTPNR_NAMESPACE_END + +#endif // BITSTREAM_H diff --git a/ecp5/main.cc b/ecp5/main.cc index ae229d51..caa28563 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -32,14 +32,15 @@ #include #include -#include "Database.hpp" #include "Chip.hpp" +#include "Database.hpp" #include "Tile.hpp" #include "log.h" #include "nextpnr.h" #include "version.h" +#include "bitstream.h" #include "design_utils.h" #include "jsonparse.h" #include "pack.h" @@ -68,6 +69,10 @@ int main(int argc, char *argv[]) options.add_options()("json", po::value(), "JSON design file to ingest"); options.add_options()("seed", po::value(), "seed value for random number generator"); + options.add_options()("basecfg", po::value(), "base chip configuration in Trellis text format"); + options.add_options()("bit", po::value(), "bitstream file to write"); + options.add_options()("textcfg", po::value(), "textual configuration in Trellis format to write"); + po::positional_options_description pos; #ifndef NO_PYTHON options.add_options()("run", po::value>(), "python file to execute"); @@ -148,17 +153,18 @@ int main(int argc, char *argv[]) if (!route_design(&ctx) && !ctx.force) log_error("Routing design failed.\n"); - // TEST BEGIN - Trellis::Chip c("LFE5U-45F"); - for (auto pip : ctx.getPips()) { - if (!ctx.checkPipAvail(pip)) { - auto tile = c.get_tile_by_position_and_type(pip.location.y, pip.location.x, ctx.getPipTiletype(pip)); - std::cout << ctx.getWireName(ctx.getPipSrcWire(pip)).str(&ctx) << " -> " - << ctx.getWireName(ctx.getPipDstWire(pip)).str(&ctx) << " [in tile " - << tile->info.name << "]" << std::endl; - } - } - // TEST END + std::string basecfg; + if (vm.count("basecfg")) + basecfg = vm["basecfg"].as(); + + std::string bitstream; + if (vm.count("bit")) + bitstream = vm["bit"].as(); + + std::string textcfg; + if (vm.count("textcfg")) + textcfg = vm["textcfg"].as(); + write_bitstream(&ctx, basecfg, textcfg, bitstream); } #ifndef NO_PYTHON diff --git a/ecp5/synth/.gitignore b/ecp5/synth/.gitignore index e69de29b..23844244 100644 --- a/ecp5/synth/.gitignore +++ b/ecp5/synth/.gitignore @@ -0,0 +1,2 @@ +*.config +*.bit From b397dd80712005e4c71b492e27d6af35e6bdc1e9 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 9 Jul 2018 12:55:56 +0200 Subject: [PATCH 23/31] ecp5: Adding bitstream gen for slice config Signed-off-by: David Shah --- common/util.h | 12 ++++++++++++ ecp5/bitstream.cc | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/common/util.h b/common/util.h index 60eb35af..b492b98c 100644 --- a/common/util.h +++ b/common/util.h @@ -39,6 +39,18 @@ ValueType get_or_default(const Container &ct, const KeyType &key, ValueType def return found->second; }; +// Get a value from a map-style container, returning default if value is not +// found (forces string) +template +std::string str_or_default(const Container &ct, const KeyType &key, std::string def = "") +{ + auto found = ct.find(key); + if (found == ct.end()) + return def; + else + return found->second; +}; + // Get a value from a map-style container, converting to int, and returning // default if value is not found template int int_or_default(const Container &ct, const KeyType &key, int def = 0) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index e44892ed..5f9294c2 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -118,6 +118,20 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL")); cc.tiles[tname].add_word(slice + ".K0.INIT", int_to_bitvector(lut0_init, 16)); cc.tiles[tname].add_word(slice + ".K1.INIT", int_to_bitvector(lut1_init, 16)); + cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, ctx->id("MODE"), "LOGIC")); + cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); + cc.tiles[tname].add_enum(slice + ".REG0.SD", str_or_default(ci->params, ctx->id("REG0_SD"), "0")); + cc.tiles[tname].add_enum(slice + ".REG1.SD", str_or_default(ci->params, ctx->id("REG1_SD"), "0")); + cc.tiles[tname].add_enum(slice + ".REG0.REGSET", + str_or_default(ci->params, ctx->id("REG0_REGSET"), "RESET")); + cc.tiles[tname].add_enum(slice + ".REG1.REGSET", + str_or_default(ci->params, ctx->id("REG1_REGSET"), "RESET")); + cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, ctx->id("CEMUX"), "1")); + // TODO: CLKMUX, CEMUX, carry + } else if (ci->type == ctx->id("TRELLIS_IO")) { + // TODO: IO config + } else { + NPNR_ASSERT_FALSE("unsupported cell type"); } } From 29d65bd368fa32f7ea13515902df752d30ec4f39 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 10 Jul 2018 11:24:30 +0200 Subject: [PATCH 24/31] ecp5: Working on bitstream gen Signed-off-by: David Shah --- CMakeLists.txt | 2 +- common/util.h | 1 + ecp5/bitstream.cc | 106 +++++++- ecp5/synth/.gitignore | 1 - ecp5/synth/ulx3s.v | 16 ++ ecp5/synth/ulx3s.ys | 9 + ecp5/synth/ulx3s_empty.config | 453 ++++++++++++++++++++++++++++++++++ 7 files changed, 585 insertions(+), 3 deletions(-) create mode 100644 ecp5/synth/ulx3s.v create mode 100644 ecp5/synth/ulx3s.ys create mode 100644 ecp5/synth/ulx3s_empty.config diff --git a/CMakeLists.txt b/CMakeLists.txt index 32063af6..f9dc10ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /W4 /wd4100 /wd4244 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996 /wd4127") else() set(CMAKE_CXX_FLAGS_DEBUG "-Wall -fPIC -ggdb") -set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O3 -g") +set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O0 -ggdb") endif() set(CMAKE_DEFIN) diff --git a/common/util.h b/common/util.h index b492b98c..8f361dc8 100644 --- a/common/util.h +++ b/common/util.h @@ -96,6 +96,7 @@ inline const NetInfo *get_net_or_empty(const CellInfo *cell, const IdString port else return nullptr; }; + NEXTPNR_NAMESPACE_END #endif diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 5f9294c2..04bbc24f 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -33,6 +33,8 @@ #include "log.h" #include "util.h" +#define fmt_str(x) (static_cast(std::ostringstream() << x).str()) + NEXTPNR_NAMESPACE_BEGIN // Convert an absolute wire name to a relative Trellis one @@ -65,11 +67,97 @@ static std::vector int_to_bitvector(int val, int size) return bv; } +// Get the PIO tile corresponding to a PIO bel +static std::string get_pio_tile(Context *ctx, Trellis::Chip &chip, BelId bel) +{ + static const std::set pioabcd_l = {"PICL1", "PICL1_DQS0", "PICL1_DQS3"}; + static const std::set pioabcd_r = {"PICR1", "PICR1_DQS0", "PICR1_DQS3"}; + static const std::set pioa_b = {"PICB0", "EFB0_PICB0", "EFB2_PICB0"}; + static const std::set piob_b = {"PICB1", "EFB1_PICB1", "EFB3_PICB1"}; + + std::string pio_name = ctx->locInfo(bel)->bel_data[bel.index].name.get(); + if (bel.location.y == 0) { + if (pio_name == "PIOA") { + return chip.get_tile_by_position_and_type(0, bel.location.x, "PIOT0")->info.name; + } else if (pio_name == "PIOB") { + return chip.get_tile_by_position_and_type(0, bel.location.x + 1, "PIOT1")->info.name; + } else { + NPNR_ASSERT_FALSE("bad PIO location"); + } + } else if (bel.location.y == ctx->chip_info->height - 1) { + if (pio_name == "PIOA") { + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pioa_b)->info.name; + } else if (pio_name == "PIOB") { + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, piob_b)->info.name; + } else { + NPNR_ASSERT_FALSE("bad PIO location"); + } + } else if (bel.location.x == 0) { + return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_l)->info.name; + } else if (bel.location.x == ctx->chip_info->width - 1) { + return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_r)->info.name; + } else { + NPNR_ASSERT_FALSE("bad PIO location"); + } +} + +// Get the PIC tile corresponding to a PIO bel +static std::string get_pic_tile(Context *ctx, Trellis::Chip &chip, BelId bel) +{ + static const std::set picab_l = {"PICL0", "PICL0_DQS2"}; + static const std::set piccd_l = {"PICL2", "PICL2_DQS1", "MIB_CIB_LR"}; + static const std::set picab_r = {"PICR0", "PICR0_DQS2"}; + static const std::set piccd_r = {"PICR2", "PICR2_DQS1", "MIB_CIB_LR_A"}; + + static const std::set pica_b = {"PICB0", "EFB0_PICB0", "EFB2_PICB0"}; + static const std::set picb_b = {"PICB1", "EFB1_PICB1", "EFB3_PICB1"}; + + std::string pio_name = ctx->locInfo(bel)->bel_data[bel.index].name.get(); + if (bel.location.y == 0) { + if (pio_name == "PIOA") { + return chip.get_tile_by_position_and_type(1, bel.location.x, "PICT0")->info.name; + } else if (pio_name == "PIOB") { + return chip.get_tile_by_position_and_type(1, bel.location.x + 1, "PICT1")->info.name; + } else { + NPNR_ASSERT_FALSE("bad PIO location"); + } + } else if (bel.location.y == ctx->chip_info->height - 1) { + if (pio_name == "PIOA") { + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pica_b)->info.name; + } else if (pio_name == "PIOB") { + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, picb_b)->info.name; + } else { + NPNR_ASSERT_FALSE("bad PIO location"); + } + } else if (bel.location.x == 0) { + if (pio_name == "PIOA" || pio_name == "PIOB") { + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_l)->info.name; + } else if (pio_name == "PIOC" || pio_name == "PIOD") { + return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_l)->info.name; + } else { + NPNR_ASSERT_FALSE("bad PIO location"); + } + } else if (bel.location.x == ctx->chip_info->width - 1) { + if (pio_name == "PIOA" || pio_name == "PIOB") { + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_r)->info.name; + } else if (pio_name == "PIOC" || pio_name == "PIOD") { + return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_r)->info.name; + } else { + NPNR_ASSERT_FALSE("bad PIO location"); + } + } else { + NPNR_ASSERT_FALSE("bad PIO location"); + } +} + void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file, std::string bitstream_file) { Trellis::Chip empty_chip(ctx->getChipName()); Trellis::ChipConfig cc; + + std::set cib_tiles = {"CIB", "CIB_LR", "CIB_LR_S", "CIB_EFB0", "CIB_EFB1"}; + if (!base_config_file.empty()) { std::ifstream config_file(base_config_file); if (!config_file) { @@ -129,7 +217,23 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, ctx->id("CEMUX"), "1")); // TODO: CLKMUX, CEMUX, carry } else if (ci->type == ctx->id("TRELLIS_IO")) { - // TODO: IO config + std::string pio = ctx->locInfo(bel)->bel_data[bel.index].name.get(); + std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33"); + std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT"); + std::string pio_tile = get_pio_tile(ctx, empty_chip, bel); + std::string pic_tile = get_pic_tile(ctx, empty_chip, bel); + cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype); + cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype); + if (dir != "INPUT" && (ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr)) { + // Tie tristate low if unconnected for outputs or bidir + std::string jpt = fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/JPADDT" << pio.back()); + WireId jpt_wire = ctx->getWireByName(ctx->id(jpt)); + PipId jpt_pip = *ctx->getPipsUphill(jpt_wire).begin(); + WireId cib_wire = ctx->getPipSrcWire(jpt_pip); + std::string cib_tile = empty_chip.get_tile_by_position_and_type(cib_wire.location.y, cib_wire.location.x, cib_tiles)->info.name; + std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get(); + cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0"); + } } else { NPNR_ASSERT_FALSE("unsupported cell type"); } diff --git a/ecp5/synth/.gitignore b/ecp5/synth/.gitignore index 23844244..5b3bf578 100644 --- a/ecp5/synth/.gitignore +++ b/ecp5/synth/.gitignore @@ -1,2 +1 @@ -*.config *.bit diff --git a/ecp5/synth/ulx3s.v b/ecp5/synth/ulx3s.v new file mode 100644 index 00000000..7f0786f5 --- /dev/null +++ b/ecp5/synth/ulx3s.v @@ -0,0 +1,16 @@ +module top(input a_pin, output led_pin, output gpio0_pin); + + wire a; + wire led; + wire gpio0; + (* BEL="X90/Y65/PIOB" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a)); + (* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led), .T(t)); + (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0), .T(t)); + assign led = !a; + wire t; + TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'h0000)) gnd (.F0(t)); + TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0)); +endmodule diff --git a/ecp5/synth/ulx3s.ys b/ecp5/synth/ulx3s.ys new file mode 100644 index 00000000..d741c985 --- /dev/null +++ b/ecp5/synth/ulx3s.ys @@ -0,0 +1,9 @@ +read_verilog ulx3s.v +read_verilog -lib cells.v +synth -top top +abc -lut 4 +techmap -map simple_map.v +splitnets +opt_clean +stat +write_json ulx3s.json diff --git a/ecp5/synth/ulx3s_empty.config b/ecp5/synth/ulx3s_empty.config new file mode 100644 index 00000000..8b641943 --- /dev/null +++ b/ecp5/synth/ulx3s_empty.config @@ -0,0 +1,453 @@ +.device LFE5U-45F + +.comment Lattice Semiconductor Corporation Bitstream +.comment Version: Diamond (64-bit) 3.10.0.111.2 +.comment Bitstream Status: Final Version 10.25 +.comment Design name: wire_impl1.ncd +.comment Architecture: sa5p00 +.comment Part: LFE5U-45F-6CABGA381 +.comment Date: Sun Jul 8 15:46:42 2018 +.comment Rows: 9470 +.comment Cols: 846 +.comment Bits: 8011620 +.comment Readback: Off +.comment Security: Off +.comment Bitstream CRC: 0x66BA + +.tile CIB_R10C3:PVT_COUNT2 +unknown: F2B0 +unknown: F3B0 +unknown: F5B0 +unknown: F11B0 +unknown: F13B0 + +.tile CIB_R5C1:CIB_PLL1 +enum: CIB.JA3MUX 0 +enum: CIB.JB3MUX 0 + + +.tile CIB_R5C89:CIB_PLL1 +enum: CIB.JA3MUX 0 +enum: CIB.JB3MUX 0 + + +.tile CIB_R70C3:CIB_PLL3 +enum: CIB.JA3MUX 0 +enum: CIB.JB3MUX 0 + + +.tile CIB_R70C42:VCIB_DCU0 +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C43:VCIB_DCUA +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C44:VCIB_DCUB +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C45:VCIB_DCUC +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C46:VCIB_DCUD +enum: CIB.JA1MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C47:VCIB_DCUF +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C48:VCIB_DCU3 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C49:VCIB_DCU2 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C50:VCIB_DCUG +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C51:VCIB_DCUH +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C52:VCIB_DCUI +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C53:VCIB_DCU1 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 + + +.tile CIB_R70C69:VCIB_DCU0 +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C6:CIB_EFB0 +enum: CIB.JB3MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C70:VCIB_DCUA +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C71:VCIB_DCUB +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C72:VCIB_DCUC +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C73:VCIB_DCUD +enum: CIB.JA1MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C74:VCIB_DCUF +enum: CIB.JA1MUX 0 +enum: CIB.JA3MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC2MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C75:VCIB_DCU3 +enum: CIB.JA5MUX 0 +enum: CIB.JA7MUX 0 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JC0MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC6MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C76:VCIB_DCU2 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C77:VCIB_DCUG +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C78:VCIB_DCUH +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C79:VCIB_DCUI +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB7MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD6MUX 0 + + +.tile CIB_R70C7:CIB_EFB1 +enum: CIB.JA3MUX 0 +enum: CIB.JA4MUX 0 +enum: CIB.JA5MUX 0 +enum: CIB.JA6MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB4MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JB6MUX 0 +enum: CIB.JC3MUX 0 +enum: CIB.JC4MUX 0 +enum: CIB.JC5MUX 0 +enum: CIB.JD3MUX 0 +enum: CIB.JD4MUX 0 +enum: CIB.JD5MUX 0 + + +.tile CIB_R70C80:VCIB_DCU1 +enum: CIB.JB1MUX 0 +enum: CIB.JB3MUX 0 +enum: CIB.JB5MUX 0 +enum: CIB.JD0MUX 0 +enum: CIB.JD2MUX 0 + + +.tile CIB_R70C87:CIB_PLL3 +enum: CIB.JA3MUX 0 +enum: CIB.JB3MUX 0 + + +.tile MIB_R10C40:CMUX_UL_0 +arc: G_DCS0CLK0 G_VPFN0000 + + +.tile MIB_R10C41:CMUX_UR_0 +arc: G_DCS0CLK1 G_VPFN0000 + + +.tile MIB_R58C40:CMUX_LL_0 +arc: G_DCS1CLK0 G_VPFN0000 + + +.tile MIB_R58C41:CMUX_LR_0 +arc: G_DCS1CLK1 G_VPFN0000 + + +.tile MIB_R71C4:EFB0_PICB0 +unknown: F54B1 +unknown: F56B1 +unknown: F82B1 +unknown: F94B1 + +.tile MIB_R71C3:BANKREF8 +unknown: F18B0 + From 98cdb6082d1f916bd47676c3e7f1feab6b585216 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 10 Jul 2018 11:57:58 +0200 Subject: [PATCH 25/31] ecp5: Bitstream progress Signed-off-by: David Shah --- ecp5/bitstream.cc | 2 +- ecp5/synth/cells.v | 2 +- ecp5/synth/simple_map.v | 6 +++--- ecp5/synth/ulx3s.v | 9 ++++----- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 04bbc24f..f4f175bb 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -187,7 +187,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex for (const auto &tile : empty_chip.tiles) { std::string type = tile.second->info.type; if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") { - cc.tiles[type].add_enum("BANK.VCCIO", "3V3"); + cc.tiles[tile.first].add_enum("BANK.VCCIO", "3V3"); } } diff --git a/ecp5/synth/cells.v b/ecp5/synth/cells.v index 2435713a..d2c6d560 100644 --- a/ecp5/synth/cells.v +++ b/ecp5/synth/cells.v @@ -27,7 +27,7 @@ parameter CCU2_INJECT1_1 = "NO"; endmodule -(* blackbox *) +(* blackbox *) (* keep *) module TRELLIS_IO( inout B, input I, diff --git a/ecp5/synth/simple_map.v b/ecp5/synth/simple_map.v index 4a50fd01..550fa92c 100644 --- a/ecp5/synth/simple_map.v +++ b/ecp5/synth/simple_map.v @@ -25,7 +25,7 @@ module \$lut (A, Y); if (WIDTH == 1) begin TRELLIS_SLICE #( .MODE("LOGIC"), - .LUT0_INITVAL(LUT) + .LUT0_INITVAL({8{LUT[1:0]}}) ) _TECHMAP_REPLACE_ ( .A0(A[0]), .F0(Y) @@ -34,7 +34,7 @@ module \$lut (A, Y); if (WIDTH == 2) begin TRELLIS_SLICE #( .MODE("LOGIC"), - .LUT0_INITVAL(LUT) + .LUT0_INITVAL({4{LUT[3:0]}}) ) _TECHMAP_REPLACE_ ( .A0(A[0]), .B0(A[1]), @@ -44,7 +44,7 @@ module \$lut (A, Y); if (WIDTH == 3) begin TRELLIS_SLICE #( .MODE("LOGIC"), - .LUT0_INITVAL(LUT) + .LUT0_INITVAL({2{LUT[7:0]}}) ) _TECHMAP_REPLACE_ ( .A0(A[0]), .B0(A[1]), diff --git a/ecp5/synth/ulx3s.v b/ecp5/synth/ulx3s.v index 7f0786f5..25535e35 100644 --- a/ecp5/synth/ulx3s.v +++ b/ecp5/synth/ulx3s.v @@ -3,14 +3,13 @@ module top(input a_pin, output led_pin, output gpio0_pin); wire a; wire led; wire gpio0; - (* BEL="X90/Y65/PIOB" *) (* IO_TYPE="LVCMOS33" *) + (* BEL="X6/Y0/PIOB" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a)); (* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led), .T(t)); + TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led)); (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0), .T(t)); + TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0)); assign led = !a; - wire t; - TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'h0000)) gnd (.F0(t)); + TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0)); endmodule From 1830c9372e9bb959cb886c8271d64778550a7ebb Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 10 Jul 2018 12:31:58 +0200 Subject: [PATCH 26/31] ecp5: *** Blinky working *** Signed-off-by: David Shah --- CMakeLists.txt | 2 +- ecp5/bitstream.cc | 3 +++ ecp5/synth/blinky.v | 22 ++++++++++++++++++++-- ecp5/synth/ulx3s.v | 11 +++++++---- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9dc10ec..32063af6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /W4 /wd4100 /wd4244 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996 /wd4127") else() set(CMAKE_CXX_FLAGS_DEBUG "-Wall -fPIC -ggdb") -set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O0 -ggdb") +set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O3 -g") endif() set(CMAKE_DEFIN) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index f4f175bb..0e8d4aa4 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -234,6 +234,9 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get(); cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0"); } + if (dir == "INPUT") { + cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON"); + } } else { NPNR_ASSERT_FALSE("unsupported cell type"); } diff --git a/ecp5/synth/blinky.v b/ecp5/synth/blinky.v index aba58801..bda627cc 100644 --- a/ecp5/synth/blinky.v +++ b/ecp5/synth/blinky.v @@ -1,10 +1,25 @@ -module top(input clk_pin, output [3:0] led_pin); +module top(input clk_pin, output [3:0] led_pin, output gpio0_pin); wire clk; wire [3:0] led; + wire gpio0; + + (* BEL="X0/Y35/PIOA" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk)); - TRELLIS_IO #(.DIR("OUTPUT")) led_buf [3:0] (.B(led_pin), .I(led)); + + (* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0])); + (* BEL="X0/Y23/PIOD" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1])); + (* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2])); + (* BEL="X0/Y26/PIOC" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3])); + + + (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0)); reg [25:0] ctr = 0; @@ -13,4 +28,7 @@ module top(input clk_pin, output [3:0] led_pin); assign led = ctr[25:22]; + // Tie GPIO0, keep board from rebooting + TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0)); + endmodule diff --git a/ecp5/synth/ulx3s.v b/ecp5/synth/ulx3s.v index 25535e35..486366fa 100644 --- a/ecp5/synth/ulx3s.v +++ b/ecp5/synth/ulx3s.v @@ -1,15 +1,18 @@ -module top(input a_pin, output led_pin, output gpio0_pin); +module top(input a_pin, output led_pin, output led2_pin, output gpio0_pin); wire a; - wire led; + wire led, led2; wire gpio0; - (* BEL="X6/Y0/PIOB" *) (* IO_TYPE="LVCMOS33" *) + (* BEL="X90/Y65/PIOB" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a)); (* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led)); + (* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led2_buf (.B(led2_pin), .I(led2)); (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0)); - assign led = !a; + assign led = a; + assign led2 = !a; TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0)); endmodule From 9a2e8caf1c40ede0922a9c492e5c774ec66e61d3 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 10 Jul 2018 14:02:01 +0200 Subject: [PATCH 27/31] ecp5: Buttons working Signed-off-by: David Shah --- ecp5/synth/blinky.v | 15 ++++++++++++--- ecp5/synth/ulx3s.v | 2 +- ecp5/trellis_import.py | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ecp5/synth/blinky.v b/ecp5/synth/blinky.v index bda627cc..11ba5e4c 100644 --- a/ecp5/synth/blinky.v +++ b/ecp5/synth/blinky.v @@ -1,7 +1,7 @@ module top(input clk_pin, output [3:0] led_pin, output gpio0_pin); wire clk; - wire [3:0] led; + wire [7:0] led; wire gpio0; @@ -17,16 +17,25 @@ module top(input clk_pin, output [3:0] led_pin, output gpio0_pin); (* BEL="X0/Y26/PIOC" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3])); + (* BEL="X0/Y26/PIOB" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4])); + (* BEL="X0/Y32/PIOD" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5])); + (* BEL="X0/Y26/PIOD" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6])); + (* BEL="X0/Y29/PIOD" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7])); + (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0)); - reg [25:0] ctr = 0; + reg [27:0] ctr = 0; always@(posedge clk) ctr <= ctr + 1'b1; - assign led = ctr[25:22]; + assign led = ctr[27:20]; // Tie GPIO0, keep board from rebooting TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0)); diff --git a/ecp5/synth/ulx3s.v b/ecp5/synth/ulx3s.v index 486366fa..08f6e65b 100644 --- a/ecp5/synth/ulx3s.v +++ b/ecp5/synth/ulx3s.v @@ -3,7 +3,7 @@ module top(input a_pin, output led_pin, output led2_pin, output gpio0_pin); wire a; wire led, led2; wire gpio0; - (* BEL="X90/Y65/PIOB" *) (* IO_TYPE="LVCMOS33" *) + (* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a)); (* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led)); diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 6ff929f7..60e48844 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -154,7 +154,7 @@ def add_bels(chip, x, y): num_slices = 4 elif "PICL0" in tt or "PICR0" in tt: num_pios = 4 - elif "PIOT0" in tt or "PIOB0" in tt: + elif "PIOT0" in tt or ("PICB0" in tt and "SPICB" not in tt): num_pios = 2 for i in range(num_slices): add_slice(x, y, i) From 610adfef7ea72493de7a6ff2de727b208cd88920 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 10 Jul 2018 14:21:37 +0200 Subject: [PATCH 28/31] ecp5: Make blinky more fancy Signed-off-by: David Shah --- ecp5/synth/blinky.v | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/ecp5/synth/blinky.v b/ecp5/synth/blinky.v index 11ba5e4c..5327ab35 100644 --- a/ecp5/synth/blinky.v +++ b/ecp5/synth/blinky.v @@ -30,12 +30,32 @@ module top(input clk_pin, output [3:0] led_pin, output gpio0_pin); (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0)); - reg [27:0] ctr = 0; + localparam ctr_width = 28; + reg [ctr_width-1:0] ctr = 0; always@(posedge clk) ctr <= ctr + 1'b1; - assign led = ctr[27:20]; + reg [9:0] brightness [0:7]; + + reg [7:0] led_reg; + + genvar i; + generate + for (i = 0; i < 8; i=i+1) begin + always @ (posedge clk) begin + if (ctr[ctr_width-1 : ctr_width-3] > i) + brightness[i] <= 10'h3FF; + else if (ctr[ctr_width-1 : ctr_width-3] == i) + brightness[i] <= ctr[ctr_width-4:ctr_width-13]; + else + brightness[i] <= 0; + led_reg[i] <= ctr[9:0] < brightness[i]; + end + end + endgenerate + + assign led = led_reg; // Tie GPIO0, keep board from rebooting TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0)); From eac5a6dbd829b82117f814ed99f6959c82973935 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 11 Jul 2018 10:09:02 +0200 Subject: [PATCH 29/31] ecp5: Blinky tweaks Signed-off-by: David Shah --- ecp5/synth/blinky.v | 34 ++++++++++++++++++++++++---------- ecp5/synth/ulx3s_empty.config | 14 -------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ecp5/synth/blinky.v b/ecp5/synth/blinky.v index 5327ab35..ac7c6ea3 100644 --- a/ecp5/synth/blinky.v +++ b/ecp5/synth/blinky.v @@ -1,13 +1,16 @@ -module top(input clk_pin, output [3:0] led_pin, output gpio0_pin); +module top(input clk_pin, input btn_pin, output [3:0] led_pin, output gpio0_pin); wire clk; wire [7:0] led; - + wire btn; wire gpio0; (* BEL="X0/Y35/PIOA" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk)); + (* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *) + TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn)); + (* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0])); (* BEL="X0/Y23/PIOD" *) (* IO_TYPE="LVCMOS33" *) @@ -30,27 +33,38 @@ module top(input clk_pin, output [3:0] led_pin, output gpio0_pin); (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *) TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0)); - localparam ctr_width = 28; + localparam ctr_width = 24; + localparam ctr_max = 2**ctr_width - 1; reg [ctr_width-1:0] ctr = 0; + reg [9:0] pwm_ctr = 0; + reg dir = 0; - always@(posedge clk) - ctr <= ctr + 1'b1; + always@(posedge clk) begin + ctr <= btn ? ctr : (dir ? ctr - 1'b1 : ctr + 1'b1); + if (ctr[ctr_width-1 : ctr_width-3] == 0 && dir == 1) + dir <= 1'b0; + else if (ctr[ctr_width-1 : ctr_width-3] == 7 && dir == 0) + dir <= 1'b1; + pwm_ctr <= pwm_ctr + 1'b1; + end reg [9:0] brightness [0:7]; - + localparam bright_max = 2**10 - 1; reg [7:0] led_reg; genvar i; generate for (i = 0; i < 8; i=i+1) begin always @ (posedge clk) begin - if (ctr[ctr_width-1 : ctr_width-3] > i) - brightness[i] <= 10'h3FF; - else if (ctr[ctr_width-1 : ctr_width-3] == i) + if (ctr[ctr_width-1 : ctr_width-3] == i) + brightness[i] <= bright_max; + else if (ctr[ctr_width-1 : ctr_width-3] == (i - 1)) brightness[i] <= ctr[ctr_width-4:ctr_width-13]; + else if (ctr[ctr_width-1 : ctr_width-3] == (i + 1)) + brightness[i] <= bright_max - ctr[ctr_width-4:ctr_width-13]; else brightness[i] <= 0; - led_reg[i] <= ctr[9:0] < brightness[i]; + led_reg[i] <= pwm_ctr < brightness[i]; end end endgenerate diff --git a/ecp5/synth/ulx3s_empty.config b/ecp5/synth/ulx3s_empty.config index 8b641943..815e7f0d 100644 --- a/ecp5/synth/ulx3s_empty.config +++ b/ecp5/synth/ulx3s_empty.config @@ -1,19 +1,5 @@ .device LFE5U-45F -.comment Lattice Semiconductor Corporation Bitstream -.comment Version: Diamond (64-bit) 3.10.0.111.2 -.comment Bitstream Status: Final Version 10.25 -.comment Design name: wire_impl1.ncd -.comment Architecture: sa5p00 -.comment Part: LFE5U-45F-6CABGA381 -.comment Date: Sun Jul 8 15:46:42 2018 -.comment Rows: 9470 -.comment Cols: 846 -.comment Bits: 8011620 -.comment Readback: Off -.comment Security: Off -.comment Bitstream CRC: 0x66BA - .tile CIB_R10C3:PVT_COUNT2 unknown: F2B0 unknown: F3B0 From 6601adf76074e793501b52cf0f0ad0ec1f049855 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 11 Jul 2018 10:44:06 +0200 Subject: [PATCH 30/31] cmake: Post-rebase ecp5 fixes Signed-off-by: David Shah --- CMakeLists.txt | 1 - gui/ecp5/family.cmake | 0 2 files changed, 1 deletion(-) create mode 100644 gui/ecp5/family.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 32063af6..cbcdf67d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ option(BUILD_PYTHON "Build Python Integration" ON) option(BUILD_TESTS "Build GUI" OFF) # List of families to build -if(DEFINED TRELLIS_ROOT) set(FAMILIES generic ice40 ecp5) set(ARCH "" CACHE STRING "Architecture family for nextpnr build") diff --git a/gui/ecp5/family.cmake b/gui/ecp5/family.cmake new file mode 100644 index 00000000..e69de29b From bcc63091fb30ac15c9f98bfc3b3a41d921af0bd4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 11 Jul 2018 11:08:12 +0200 Subject: [PATCH 31/31] ecp5: New libtrellis tile lookup API Signed-off-by: David Shah --- ecp5/bitstream.cc | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 0e8d4aa4..a04a4250 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -78,24 +78,24 @@ static std::string get_pio_tile(Context *ctx, Trellis::Chip &chip, BelId bel) std::string pio_name = ctx->locInfo(bel)->bel_data[bel.index].name.get(); if (bel.location.y == 0) { if (pio_name == "PIOA") { - return chip.get_tile_by_position_and_type(0, bel.location.x, "PIOT0")->info.name; + return chip.get_tile_by_position_and_type(0, bel.location.x, "PIOT0"); } else if (pio_name == "PIOB") { - return chip.get_tile_by_position_and_type(0, bel.location.x + 1, "PIOT1")->info.name; + return chip.get_tile_by_position_and_type(0, bel.location.x + 1, "PIOT1"); } else { NPNR_ASSERT_FALSE("bad PIO location"); } } else if (bel.location.y == ctx->chip_info->height - 1) { if (pio_name == "PIOA") { - return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pioa_b)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pioa_b); } else if (pio_name == "PIOB") { - return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, piob_b)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, piob_b); } else { NPNR_ASSERT_FALSE("bad PIO location"); } } else if (bel.location.x == 0) { - return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_l)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_l); } else if (bel.location.x == ctx->chip_info->width - 1) { - return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_r)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_r); } else { NPNR_ASSERT_FALSE("bad PIO location"); } @@ -115,33 +115,33 @@ static std::string get_pic_tile(Context *ctx, Trellis::Chip &chip, BelId bel) std::string pio_name = ctx->locInfo(bel)->bel_data[bel.index].name.get(); if (bel.location.y == 0) { if (pio_name == "PIOA") { - return chip.get_tile_by_position_and_type(1, bel.location.x, "PICT0")->info.name; + return chip.get_tile_by_position_and_type(1, bel.location.x, "PICT0"); } else if (pio_name == "PIOB") { - return chip.get_tile_by_position_and_type(1, bel.location.x + 1, "PICT1")->info.name; + return chip.get_tile_by_position_and_type(1, bel.location.x + 1, "PICT1"); } else { NPNR_ASSERT_FALSE("bad PIO location"); } } else if (bel.location.y == ctx->chip_info->height - 1) { if (pio_name == "PIOA") { - return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pica_b)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pica_b); } else if (pio_name == "PIOB") { - return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, picb_b)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, picb_b); } else { NPNR_ASSERT_FALSE("bad PIO location"); } } else if (bel.location.x == 0) { if (pio_name == "PIOA" || pio_name == "PIOB") { - return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_l)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_l); } else if (pio_name == "PIOC" || pio_name == "PIOD") { - return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_l)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_l); } else { NPNR_ASSERT_FALSE("bad PIO location"); } } else if (bel.location.x == ctx->chip_info->width - 1) { if (pio_name == "PIOA" || pio_name == "PIOB") { - return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_r)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_r); } else if (pio_name == "PIOC" || pio_name == "PIOD") { - return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_r)->info.name; + return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_r); } else { NPNR_ASSERT_FALSE("bad PIO location"); } @@ -174,11 +174,11 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex for (auto pip : ctx->getPips()) { if (ctx->getBoundPipNet(pip) != IdString()) { if (ctx->getPipType(pip) == 0) { // ignore fixed pips - auto tile = empty_chip.get_tile_by_position_and_type(pip.location.y, pip.location.x, + std::string tile = empty_chip.get_tile_by_position_and_type(pip.location.y, pip.location.x, ctx->getPipTiletype(pip)); std::string source = get_trellis_wirename(ctx, pip.location, ctx->getPipSrcWire(pip)); std::string sink = get_trellis_wirename(ctx, pip.location, ctx->getPipDstWire(pip)); - cc.tiles[tile->info.name].add_arc(sink, source); + cc.tiles[tile].add_arc(sink, source); } } } @@ -199,8 +199,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } BelId bel = ci->bel; if (ci->type == ctx->id("TRELLIS_SLICE")) { - auto tile = empty_chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, "PLC2"); - std::string tname = tile->info.name; + std::string tname = empty_chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, "PLC2"); std::string slice = ctx->locInfo(bel)->bel_data[bel.index].name.get(); int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL")); int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL")); @@ -230,7 +229,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex WireId jpt_wire = ctx->getWireByName(ctx->id(jpt)); PipId jpt_pip = *ctx->getPipsUphill(jpt_wire).begin(); WireId cib_wire = ctx->getPipSrcWire(jpt_pip); - std::string cib_tile = empty_chip.get_tile_by_position_and_type(cib_wire.location.y, cib_wire.location.x, cib_tiles)->info.name; + std::string cib_tile = empty_chip.get_tile_by_position_and_type(cib_wire.location.y, cib_wire.location.x, cib_tiles); std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get(); cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0"); }