Merge branch 'ecp5' into 'master'
Adding ECP5 support See merge request SymbioticEDA/nextpnr!5
This commit is contained in:
commit
a70f5ce109
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
|||||||
/objs/
|
/objs/
|
||||||
/nextpnr-generic*
|
/nextpnr-generic*
|
||||||
/nextpnr-ice40*
|
/nextpnr-ice40*
|
||||||
|
/nextpnr-ecp5*
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
Makefile
|
Makefile
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
|
@ -7,7 +7,8 @@ option(BUILD_PYTHON "Build Python Integration" ON)
|
|||||||
option(BUILD_TESTS "Build GUI" OFF)
|
option(BUILD_TESTS "Build GUI" OFF)
|
||||||
|
|
||||||
# List of families to build
|
# List of families to build
|
||||||
set(FAMILIES generic ice40)
|
set(FAMILIES generic ice40 ecp5)
|
||||||
|
|
||||||
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
|
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
|
||||||
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
|
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
|
||||||
|
|
||||||
@ -217,6 +218,7 @@ endforeach (family)
|
|||||||
|
|
||||||
file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h)
|
file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h)
|
||||||
string(REGEX REPLACE "[^;]*/ice40/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
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 "[^;]*/3rdparty[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||||
string(REGEX REPLACE "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
string(REGEX REPLACE "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||||
|
|
||||||
|
@ -39,6 +39,18 @@ ValueType get_or_default(const Container &ct, const KeyType &key, ValueType def
|
|||||||
return found->second;
|
return found->second;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get a value from a map-style container, returning default if value is not
|
||||||
|
// found (forces string)
|
||||||
|
template <typename Container, typename KeyType>
|
||||||
|
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
|
// Get a value from a map-style container, converting to int, and returning
|
||||||
// default if value is not found
|
// default if value is not found
|
||||||
template <typename Container, typename KeyType> int int_or_default(const Container &ct, const KeyType &key, int def = 0)
|
template <typename Container, typename KeyType> int int_or_default(const Container &ct, const KeyType &key, int def = 0)
|
||||||
@ -84,6 +96,7 @@ inline const NetInfo *get_net_or_empty(const CellInfo *cell, const IdString port
|
|||||||
else
|
else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
2
ecp5/.gitignore
vendored
Normal file
2
ecp5/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
__pycache__
|
||||||
|
chipdbs/
|
336
ecp5/arch.cc
Normal file
336
ecp5/arch.cc
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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 <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include "log.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
static std::tuple<int, int, std::string> 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_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("TRELLIS_SLICE"))
|
||||||
|
return TYPE_TRELLIS_SLICE;
|
||||||
|
if (type == id("TRELLIS_IO"))
|
||||||
|
return TYPE_TRELLIS_IO;
|
||||||
|
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<ChipInfoPOD> *ptr) { return ptr->get(); }
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
void load_chipdb();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LFE5U_45F_ONLY
|
||||||
|
|
||||||
|
Arch::Arch(ArchArgs args) : args(args)
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
load_chipdb();
|
||||||
|
#endif
|
||||||
|
#ifdef LFE5U_45F_ONLY
|
||||||
|
if (args.type == ArchArgs::LFE5U_45F) {
|
||||||
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_45k));
|
||||||
|
} else {
|
||||||
|
log_error("Unsupported ECP5 chip type.\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (args.type == ArchArgs::LFE5U_25F) {
|
||||||
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_25k));
|
||||||
|
} else if (args.type == ArchArgs::LFE5U_45F) {
|
||||||
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_45k));
|
||||||
|
} else if (args.type == ArchArgs::LFE5U_85F) {
|
||||||
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_85k));
|
||||||
|
} else {
|
||||||
|
log_error("Unsupported ECP5 chip type.\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
std::string Arch::getChipName()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (args.type == ArchArgs::LFE5U_25F) {
|
||||||
|
return "LFE5U-25F";
|
||||||
|
} else if (args.type == ArchArgs::LFE5U_45F) {
|
||||||
|
return "LFE5U-45F";
|
||||||
|
} else if (args.type == ArchArgs::LFE5U_85F) {
|
||||||
|
return "LFE5U-85F";
|
||||||
|
} else {
|
||||||
|
log_error("Unknown chip\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
IdString Arch::archArgsToId(ArchArgs args) const
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
BelId Arch::getBelByName(IdString name) const
|
||||||
|
{
|
||||||
|
BelId ret;
|
||||||
|
auto it = bel_by_name.find(name);
|
||||||
|
if (it != bel_by_name.end())
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
BelRange Arch::getBelsAtSameTile(BelId bel) const
|
||||||
|
{
|
||||||
|
BelRange br;
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId Arch::getWireBelPin(BelId bel, PortPin pin) const
|
||||||
|
{
|
||||||
|
WireId ret;
|
||||||
|
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
WireId Arch::getWireByName(IdString name) const
|
||||||
|
{
|
||||||
|
WireId ret;
|
||||||
|
auto it = wire_by_name.find(name);
|
||||||
|
if (it != wire_by_name.end())
|
||||||
|
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;
|
||||||
|
ret.location = loc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret.index >= 0)
|
||||||
|
wire_by_name[name] = ret;
|
||||||
|
else
|
||||||
|
ret.location = Location();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PipId Arch::getPipByName(IdString name) const
|
||||||
|
{
|
||||||
|
auto it = pip_by_name.find(name);
|
||||||
|
if (it != pip_by_name.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
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 = pip.location.x;
|
||||||
|
int y = pip.location.y;
|
||||||
|
|
||||||
|
std::string src_name = getWireName(getPipSrcWire(pip)).str(this);
|
||||||
|
std::replace(src_name.begin(), src_name.end(), '/', '.');
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
x = bel.location.x;
|
||||||
|
y = bel.location.y;
|
||||||
|
gb = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||||
|
{
|
||||||
|
return abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
std::vector<GraphicElement> Arch::getFrameGraphics() const
|
||||||
|
{
|
||||||
|
std::vector<GraphicElement> ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GraphicElement> Arch::getBelGraphics(BelId bel) const
|
||||||
|
{
|
||||||
|
std::vector<GraphicElement> ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GraphicElement> Arch::getWireGraphics(WireId wire) const
|
||||||
|
{
|
||||||
|
std::vector<GraphicElement> ret;
|
||||||
|
// FIXME
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GraphicElement> Arch::getPipGraphics(PipId pip) const
|
||||||
|
{
|
||||||
|
std::vector<GraphicElement> ret;
|
||||||
|
// FIXME
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
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
|
752
ecp5/arch.h
Normal file
752
ecp5/arch.h
Normal file
@ -0,0 +1,752 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NEXTPNR_H
|
||||||
|
#error Include "arch.h" via "nextpnr.h" only.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/**** Everything in this section must be kept in sync with chipdb.py ****/
|
||||||
|
|
||||||
|
template <typename T> struct RelPtr
|
||||||
|
{
|
||||||
|
int32_t offset;
|
||||||
|
|
||||||
|
// void set(const T *ptr) {
|
||||||
|
// offset = reinterpret_cast<const char*>(ptr) -
|
||||||
|
// reinterpret_cast<const char*>(this);
|
||||||
|
// }
|
||||||
|
|
||||||
|
const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(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 {
|
||||||
|
LocationPOD rel_wire_loc;
|
||||||
|
int32_t wire_index;
|
||||||
|
PortPin port;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct BelInfoPOD {
|
||||||
|
RelPtr<char> name;
|
||||||
|
BelType type;
|
||||||
|
int32_t num_bel_wires;
|
||||||
|
RelPtr<BelWirePOD> bel_wires;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct BelPortPOD {
|
||||||
|
LocationPOD rel_bel_loc;
|
||||||
|
int32_t bel_index;
|
||||||
|
PortPin port;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct PipInfoPOD {
|
||||||
|
LocationPOD rel_src_loc, rel_dst_loc;
|
||||||
|
int32_t src_idx, dst_idx;
|
||||||
|
int32_t delay;
|
||||||
|
int16_t tile_type;
|
||||||
|
int8_t pip_type;
|
||||||
|
int8_t padding_0;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct PipLocatorPOD {
|
||||||
|
LocationPOD rel_loc;
|
||||||
|
int32_t index;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct WireInfoPOD {
|
||||||
|
RelPtr<char> name;
|
||||||
|
int32_t num_uphill, num_downhill;
|
||||||
|
RelPtr<PipLocatorPOD> pips_uphill, pips_downhill;
|
||||||
|
|
||||||
|
int32_t num_bels_downhill;
|
||||||
|
BelPortPOD bel_uphill;
|
||||||
|
RelPtr<BelPortPOD> bels_downhill;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct LocationTypePOD {
|
||||||
|
int32_t num_bels, num_wires, num_pips;
|
||||||
|
RelPtr<BelInfoPOD> bel_data;
|
||||||
|
RelPtr<WireInfoPOD> wire_data;
|
||||||
|
RelPtr<PipInfoPOD> pip_data;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
|
||||||
|
int32_t width, height;
|
||||||
|
int32_t num_tiles;
|
||||||
|
int32_t num_location_types;
|
||||||
|
RelPtr<LocationTypePOD> locations;
|
||||||
|
RelPtr<int32_t> location_type;
|
||||||
|
RelPtr<RelPtr<char>> tiletype_names;
|
||||||
|
});
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
extern const char *chipdb_blob_25k;
|
||||||
|
extern const char *chipdb_blob_45k;
|
||||||
|
extern const char *chipdb_blob_85k;
|
||||||
|
#else
|
||||||
|
extern const char chipdb_blob_25k[];
|
||||||
|
extern const char chipdb_blob_45k[];
|
||||||
|
extern const char chipdb_blob_85k[];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/************************ End of chipdb section. ************************/
|
||||||
|
|
||||||
|
struct BelIterator
|
||||||
|
{
|
||||||
|
const ChipInfoPOD *chip;
|
||||||
|
int cursor_index;
|
||||||
|
int cursor_tile;
|
||||||
|
|
||||||
|
BelIterator operator++()
|
||||||
|
{
|
||||||
|
cursor_index++;
|
||||||
|
while (cursor_tile < chip->num_tiles &&
|
||||||
|
cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_bels) {
|
||||||
|
cursor_index = 0;
|
||||||
|
cursor_tile++;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
BelIterator operator++(int)
|
||||||
|
{
|
||||||
|
BelIterator prior(*this);
|
||||||
|
++(*this);
|
||||||
|
return prior;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const BelIterator &other) const
|
||||||
|
{
|
||||||
|
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const BelIterator &other) const
|
||||||
|
{
|
||||||
|
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
BelId operator*() const
|
||||||
|
{
|
||||||
|
BelId ret;
|
||||||
|
ret.location.x = cursor_tile % chip->width;
|
||||||
|
ret.location.y = cursor_tile / chip->width;
|
||||||
|
ret.index = cursor_index;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BelRange
|
||||||
|
{
|
||||||
|
BelIterator b, e;
|
||||||
|
BelIterator begin() const { return b; }
|
||||||
|
BelIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct BelPinIterator
|
||||||
|
{
|
||||||
|
const BelPortPOD *ptr = nullptr;
|
||||||
|
Location wire_loc;
|
||||||
|
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.bel.location = wire_loc + ptr->rel_bel_loc;
|
||||||
|
ret.pin = ptr->port;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BelPinRange
|
||||||
|
{
|
||||||
|
BelPinIterator b, e;
|
||||||
|
BelPinIterator begin() const { return b; }
|
||||||
|
BelPinIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct WireIterator
|
||||||
|
{
|
||||||
|
const ChipInfoPOD *chip;
|
||||||
|
int cursor_index;
|
||||||
|
int cursor_tile;
|
||||||
|
|
||||||
|
WireIterator operator++()
|
||||||
|
{
|
||||||
|
cursor_index++;
|
||||||
|
while (cursor_tile < chip->num_tiles &&
|
||||||
|
cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_wires) {
|
||||||
|
cursor_index = 0;
|
||||||
|
cursor_tile++;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WireIterator operator++(int)
|
||||||
|
{
|
||||||
|
WireIterator prior(*this);
|
||||||
|
++(*this);
|
||||||
|
return prior;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const WireIterator &other) const
|
||||||
|
{
|
||||||
|
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const WireIterator &other) const
|
||||||
|
{
|
||||||
|
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId operator*() const
|
||||||
|
{
|
||||||
|
WireId ret;
|
||||||
|
ret.location.x = cursor_tile % chip->width;
|
||||||
|
ret.location.y = cursor_tile / chip->width;
|
||||||
|
ret.index = cursor_index;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WireRange
|
||||||
|
{
|
||||||
|
WireIterator b, e;
|
||||||
|
WireIterator begin() const { return b; }
|
||||||
|
WireIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct AllPipIterator
|
||||||
|
{
|
||||||
|
const ChipInfoPOD *chip;
|
||||||
|
int cursor_index;
|
||||||
|
int cursor_tile;
|
||||||
|
|
||||||
|
AllPipIterator operator++()
|
||||||
|
{
|
||||||
|
cursor_index++;
|
||||||
|
while (cursor_tile < chip->num_tiles &&
|
||||||
|
cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_pips) {
|
||||||
|
cursor_index = 0;
|
||||||
|
cursor_tile++;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
AllPipIterator operator++(int)
|
||||||
|
{
|
||||||
|
AllPipIterator prior(*this);
|
||||||
|
++(*this);
|
||||||
|
return prior;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const AllPipIterator &other) const
|
||||||
|
{
|
||||||
|
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const AllPipIterator &other) const
|
||||||
|
{
|
||||||
|
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipId operator*() const
|
||||||
|
{
|
||||||
|
PipId ret;
|
||||||
|
ret.location.x = cursor_tile % chip->width;
|
||||||
|
ret.location.y = cursor_tile / chip->width;
|
||||||
|
ret.index = cursor_index;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AllPipRange
|
||||||
|
{
|
||||||
|
AllPipIterator b, e;
|
||||||
|
AllPipIterator begin() const { return b; }
|
||||||
|
AllPipIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct PipIterator
|
||||||
|
{
|
||||||
|
|
||||||
|
const PipLocatorPOD *cursor = nullptr;
|
||||||
|
Location wire_loc;
|
||||||
|
|
||||||
|
void operator++() { cursor++; }
|
||||||
|
bool operator!=(const PipIterator &other) const { return cursor != other.cursor; }
|
||||||
|
|
||||||
|
PipId operator*() const
|
||||||
|
{
|
||||||
|
PipId ret;
|
||||||
|
ret.index = cursor->index;
|
||||||
|
ret.location = wire_loc + cursor->rel_loc;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipRange
|
||||||
|
{
|
||||||
|
PipIterator b, e;
|
||||||
|
PipIterator begin() const { return b; }
|
||||||
|
PipIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ArchArgs
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
LFE5U_25F,
|
||||||
|
LFE5U_45F,
|
||||||
|
LFE5U_85F,
|
||||||
|
} type = NONE;
|
||||||
|
std::string package;
|
||||||
|
int speed = 6;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Arch : BaseCtx
|
||||||
|
{
|
||||||
|
const ChipInfoPOD *chip_info;
|
||||||
|
|
||||||
|
mutable std::unordered_map<IdString, BelId> bel_by_name;
|
||||||
|
mutable std::unordered_map<IdString, WireId> wire_by_name;
|
||||||
|
mutable std::unordered_map<IdString, PipId> pip_by_name;
|
||||||
|
|
||||||
|
std::unordered_map<BelId, IdString> bel_to_cell;
|
||||||
|
std::unordered_map<WireId, IdString> wire_to_net;
|
||||||
|
std::unordered_map<PipId, IdString> pip_to_net;
|
||||||
|
std::unordered_map<PipId, IdString> switches_locked;
|
||||||
|
|
||||||
|
ArchArgs args;
|
||||||
|
Arch(ArchArgs args);
|
||||||
|
|
||||||
|
std::string getChipName();
|
||||||
|
|
||||||
|
IdString archId() const { return id("ecp5"); }
|
||||||
|
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;
|
||||||
|
|
||||||
|
template <typename Id> 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());
|
||||||
|
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; }
|
||||||
|
|
||||||
|
void bindBel(BelId bel, IdString cell, PlaceStrength strength)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
NPNR_ASSERT(bel_to_cell[bel] == IdString());
|
||||||
|
bel_to_cell[bel] = cell;
|
||||||
|
cells[cell]->bel = bel;
|
||||||
|
cells[cell]->belStrength = strength;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbindBel(BelId bel)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
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.find(bel) == bel_to_cell.end() || bel_to_cell.at(bel) == IdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString getBoundBelCell(BelId bel) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
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());
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 locInfo(bel)->bel_data[bel.index].type;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId getWireBelPin(BelId bel, PortPin pin) const;
|
||||||
|
|
||||||
|
BelPin getBelPinUphill(WireId wire) const
|
||||||
|
{
|
||||||
|
BelPin ret;
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
BelPinRange getBelPinsDownhill(WireId wire) const
|
||||||
|
{
|
||||||
|
BelPinRange range;
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
WireId getWireByName(IdString name) const;
|
||||||
|
|
||||||
|
IdString getWireName(WireId wire) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
|
||||||
|
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; }
|
||||||
|
|
||||||
|
void bindWire(WireId wire, IdString net, PlaceStrength strength)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
NPNR_ASSERT(wire_to_net[wire] == IdString());
|
||||||
|
wire_to_net[wire] = 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] != IdString());
|
||||||
|
|
||||||
|
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] = IdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
net_wires.erase(it);
|
||||||
|
wire_to_net[wire] = IdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkWireAvail(WireId wire) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
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());
|
||||||
|
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());
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
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] == IdString());
|
||||||
|
|
||||||
|
pip_to_net[pip] = net;
|
||||||
|
|
||||||
|
WireId 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] = 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] != IdString());
|
||||||
|
|
||||||
|
WireId 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] = IdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkPipAvail(PipId pip) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
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());
|
||||||
|
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());
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId getPipSrcWire(PipId pip) const
|
||||||
|
{
|
||||||
|
WireId wire;
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId getPipDstWire(PipId pip) const
|
||||||
|
{
|
||||||
|
WireId wire;
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
DelayInfo getPipDelay(PipId pip) const
|
||||||
|
{
|
||||||
|
DelayInfo delay;
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
delay.delay = locInfo(pip)->pip_data[pip.index].delay;
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipRange getPipsDownhill(WireId wire) const
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipRange getPipsUphill(WireId wire) const
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipRange getWireAliases(WireId wire) const
|
||||||
|
{
|
||||||
|
PipRange range;
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
range.b.cursor = nullptr;
|
||||||
|
range.e.cursor = nullptr;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getPipTiletype(PipId pip) const
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
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<GraphicElement> getFrameGraphics() const;
|
||||||
|
std::vector<GraphicElement> getBelGraphics(BelId bel) const;
|
||||||
|
std::vector<GraphicElement> getWireGraphics(WireId wire) const;
|
||||||
|
std::vector<GraphicElement> getPipGraphics(PipId pip) const;
|
||||||
|
|
||||||
|
bool allGraphicsReload = false;
|
||||||
|
bool frameGraphicsReload = false;
|
||||||
|
std::unordered_set<BelId> belGraphicsReload;
|
||||||
|
std::unordered_set<WireId> wireGraphicsReload;
|
||||||
|
std::unordered_set<PipId> 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;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
// Placement validity checks
|
||||||
|
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
|
||||||
|
bool isBelLocationValid(BelId bel) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
32
ecp5/arch_pybindings.cc
Normal file
32
ecp5/arch_pybindings.cc
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <dave@ds0.me>
|
||||||
|
*
|
||||||
|
* 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
|
75
ecp5/arch_pybindings.h
Normal file
75
ecp5/arch_pybindings.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
namespace PythonConversion {
|
||||||
|
|
||||||
|
template <> struct string_converter<BelId>
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
{
|
||||||
|
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
|
156
ecp5/archdefs.h
Normal file
156
ecp5/archdefs.h
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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 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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.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 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<NEXTPNR_NAMESPACE_PREFIX Location>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Location &loc) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = std::hash<int>()(loc.x);
|
||||||
|
seed ^= std::hash<int>()(loc.y) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelId>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(bel.location);
|
||||||
|
seed ^= std::hash<int>()(bel.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX WireId>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(wire.location);
|
||||||
|
seed ^= std::hash<int>()(wire.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(pip.location);
|
||||||
|
seed ^= std::hash<int>()(pip.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelType> : hash<int>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PortPin> : hash<int>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
} // namespace std
|
255
ecp5/bitstream.cc
Normal file
255
ecp5/bitstream.cc
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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 <fstream>
|
||||||
|
#include <streambuf>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define fmt_str(x) (static_cast<const std::ostringstream&>(std::ostringstream() << x).str())
|
||||||
|
|
||||||
|
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<bool> int_to_bitvector(int val, int size)
|
||||||
|
{
|
||||||
|
std::vector<bool> bv;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
bv.push_back((val & (1 << i)) != 0);
|
||||||
|
}
|
||||||
|
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<std::string> pioabcd_l = {"PICL1", "PICL1_DQS0", "PICL1_DQS3"};
|
||||||
|
static const std::set<std::string> pioabcd_r = {"PICR1", "PICR1_DQS0", "PICR1_DQS3"};
|
||||||
|
static const std::set<std::string> pioa_b = {"PICB0", "EFB0_PICB0", "EFB2_PICB0"};
|
||||||
|
static const std::set<std::string> 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");
|
||||||
|
} else if (pio_name == "PIOB") {
|
||||||
|
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);
|
||||||
|
} else if (pio_name == "PIOB") {
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
} 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<std::string> picab_l = {"PICL0", "PICL0_DQS2"};
|
||||||
|
static const std::set<std::string> piccd_l = {"PICL2", "PICL2_DQS1", "MIB_CIB_LR"};
|
||||||
|
static const std::set<std::string> picab_r = {"PICR0", "PICR0_DQS2"};
|
||||||
|
static const std::set<std::string> piccd_r = {"PICR2", "PICR2_DQS1", "MIB_CIB_LR_A"};
|
||||||
|
|
||||||
|
static const std::set<std::string> pica_b = {"PICB0", "EFB0_PICB0", "EFB2_PICB0"};
|
||||||
|
static const std::set<std::string> 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");
|
||||||
|
} else if (pio_name == "PIOB") {
|
||||||
|
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);
|
||||||
|
} else if (pio_name == "PIOB") {
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
} 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);
|
||||||
|
} 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);
|
||||||
|
} 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<std::string> 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) {
|
||||||
|
log_error("failed to open base config file '%s'\n", base_config_file.c_str());
|
||||||
|
}
|
||||||
|
std::string str((std::istreambuf_iterator<char>(config_file)), std::istreambuf_iterator<char>());
|
||||||
|
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
|
||||||
|
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].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[tile.first].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")) {
|
||||||
|
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"));
|
||||||
|
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")) {
|
||||||
|
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);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
32
ecp5/bitstream.h
Normal file
32
ecp5/bitstream.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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
|
49
ecp5/family.cmake
Normal file
49
ecp5/family.cmake
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
set(devices 45k)
|
||||||
|
|
||||||
|
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 $<TARGET_OBJECTS:ecp5_chipdb> ${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 $<TARGET_OBJECTS:ecp5_chipdb>)
|
||||||
|
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)
|
202
ecp5/main.cc
Normal file
202
ecp5/main.cc
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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 <QApplication>
|
||||||
|
#include "application.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#endif
|
||||||
|
#ifndef NO_PYTHON
|
||||||
|
#include "pybindings.h"
|
||||||
|
#endif
|
||||||
|
#include <boost/filesystem/convenience.hpp>
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#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"
|
||||||
|
#include "place_sa.h"
|
||||||
|
#include "route.h"
|
||||||
|
#include "timing.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
|
||||||
|
options.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
|
||||||
|
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
|
||||||
|
|
||||||
|
options.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format");
|
||||||
|
options.add_options()("bit", po::value<std::string>(), "bitstream file to write");
|
||||||
|
options.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
|
||||||
|
|
||||||
|
po::positional_options_description pos;
|
||||||
|
#ifndef NO_PYTHON
|
||||||
|
options.add_options()("run", po::value<std::vector<std::string>>(), "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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trellis::load_database(TRELLIS_ROOT "/database");
|
||||||
|
|
||||||
|
ArchArgs args;
|
||||||
|
args.type = ArchArgs::LFE5U_45F;
|
||||||
|
args.package = "CABGA381";
|
||||||
|
args.speed = 6;
|
||||||
|
Context ctx(args);
|
||||||
|
|
||||||
|
if (vm.count("verbose")) {
|
||||||
|
ctx.verbose = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("force")) {
|
||||||
|
ctx.force = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("seed")) {
|
||||||
|
ctx.rngseed(vm["seed"].as<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("json")) {
|
||||||
|
std::string filename = vm["json"].as<std::string>();
|
||||||
|
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<double>() * 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");
|
||||||
|
|
||||||
|
std::string basecfg;
|
||||||
|
if (vm.count("basecfg"))
|
||||||
|
basecfg = vm["basecfg"].as<std::string>();
|
||||||
|
|
||||||
|
std::string bitstream;
|
||||||
|
if (vm.count("bit"))
|
||||||
|
bitstream = vm["bit"].as<std::string>();
|
||||||
|
|
||||||
|
std::string textcfg;
|
||||||
|
if (vm.count("textcfg"))
|
||||||
|
textcfg = vm["textcfg"].as<std::string>();
|
||||||
|
write_bitstream(&ctx, basecfg, textcfg, bitstream);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_PYTHON
|
||||||
|
if (vm.count("run")) {
|
||||||
|
init_python(argv[0], true);
|
||||||
|
python_export_global("ctx", ctx);
|
||||||
|
|
||||||
|
std::vector<std::string> files = vm["run"].as<std::vector<std::string>>();
|
||||||
|
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
|
99
ecp5/pack.cc
Normal file
99
ecp5/pack.cc
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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 <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <unordered_set>
|
||||||
|
#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<IdString> packed_cells;
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> 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
|
31
ecp5/pack.h
Normal file
31
ecp5/pack.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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
|
26
ecp5/place_legaliser.cc
Normal file
26
ecp5/place_legaliser.cc
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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
|
31
ecp5/place_legaliser.h
Normal file
31
ecp5/place_legaliser.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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
|
28
ecp5/portpins.inc
Normal file
28
ecp5/portpins.inc
Normal file
@ -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)
|
1
ecp5/synth/.gitignore
vendored
Normal file
1
ecp5/synth/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.bit
|
77
ecp5/synth/blinky.v
Normal file
77
ecp5/synth/blinky.v
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
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" *)
|
||||||
|
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/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));
|
||||||
|
|
||||||
|
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) 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] <= 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] <= pwm_ctr < 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));
|
||||||
|
|
||||||
|
endmodule
|
9
ecp5/synth/blinky.ys
Normal file
9
ecp5/synth/blinky.ys
Normal file
@ -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
|
39
ecp5/synth/cells.v
Normal file
39
ecp5/synth/cells.v
Normal file
@ -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 *) (* keep *)
|
||||||
|
module TRELLIS_IO(
|
||||||
|
inout B,
|
||||||
|
input I,
|
||||||
|
input T,
|
||||||
|
output O,
|
||||||
|
);
|
||||||
|
parameter DIR = "INPUT";
|
||||||
|
|
||||||
|
endmodule
|
68
ecp5/synth/simple_map.v
Normal file
68
ecp5/synth/simple_map.v
Normal file
@ -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({8{LUT[1:0]}})
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.A0(A[0]),
|
||||||
|
.F0(Y)
|
||||||
|
);
|
||||||
|
end
|
||||||
|
if (WIDTH == 2) begin
|
||||||
|
TRELLIS_SLICE #(
|
||||||
|
.MODE("LOGIC"),
|
||||||
|
.LUT0_INITVAL({4{LUT[3:0]}})
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.A0(A[0]),
|
||||||
|
.B0(A[1]),
|
||||||
|
.F0(Y)
|
||||||
|
);
|
||||||
|
end
|
||||||
|
if (WIDTH == 3) begin
|
||||||
|
TRELLIS_SLICE #(
|
||||||
|
.MODE("LOGIC"),
|
||||||
|
.LUT0_INITVAL({2{LUT[7:0]}})
|
||||||
|
) _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
|
18
ecp5/synth/ulx3s.v
Normal file
18
ecp5/synth/ulx3s.v
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module top(input a_pin, output led_pin, output led2_pin, output gpio0_pin);
|
||||||
|
|
||||||
|
wire a;
|
||||||
|
wire led, led2;
|
||||||
|
wire gpio0;
|
||||||
|
(* 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));
|
||||||
|
(* 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 led2 = !a;
|
||||||
|
|
||||||
|
TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
|
||||||
|
endmodule
|
9
ecp5/synth/ulx3s.ys
Normal file
9
ecp5/synth/ulx3s.ys
Normal file
@ -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
|
439
ecp5/synth/ulx3s_empty.config
Normal file
439
ecp5/synth/ulx3s_empty.config
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
.device LFE5U-45F
|
||||||
|
|
||||||
|
.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
|
||||||
|
|
11
ecp5/synth/wire.v
Normal file
11
ecp5/synth/wire.v
Normal file
@ -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
|
9
ecp5/synth/wire.ys
Normal file
9
ecp5/synth/wire.ys
Normal file
@ -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
|
672
ecp5/trellis_import.py
Executable file
672
ecp5/trellis_import.py
Executable file
@ -0,0 +1,672 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
portpins = dict()
|
||||||
|
|
||||||
|
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
|
||||||
|
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[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))
|
||||||
|
|
||||||
|
|
||||||
|
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 ("PICB0" in tt and "SPICB" not 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)])
|
||||||
|
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))
|
||||||
|
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]
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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) ^ 0xffff) + 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)
|
||||||
|
|
||||||
|
|
||||||
|
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 = 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
|
||||||
|
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("tiletype_names", "RelPtr<char>")
|
||||||
|
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")
|
||||||
|
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.r("tiletype_names", "tiletype_names")
|
||||||
|
bba.finalize()
|
||||||
|
return bba
|
||||||
|
|
||||||
|
dev_names = {"25k": "LFE5U-25F", "45k": "LFE5U-45F", "85k": "LFE5U-85F"}
|
||||||
|
|
||||||
|
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(dev_names[args.device])
|
||||||
|
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("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):
|
||||||
|
print(" At R{}C{}".format(y, x))
|
||||||
|
import_location(rg, x, y)
|
||||||
|
print("{} unique location types".format(len(location_types)))
|
||||||
|
bba = write_database(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()
|
0
gui/ecp5/family.cmake
Normal file
0
gui/ecp5/family.cmake
Normal file
50
gui/ecp5/mainwindow.cc
Normal file
50
gui/ecp5/mainwindow.cc
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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
|
46
gui/ecp5/mainwindow.h
Normal file
46
gui/ecp5/mainwindow.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* 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
|
2
gui/ecp5/nextpnr.qrc
Normal file
2
gui/ecp5/nextpnr.qrc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<RCC>
|
||||||
|
</RCC>
|
Loading…
Reference in New Issue
Block a user