Gowin: Remove nextpnr-gowin
Boards with Gowin chips are supported in the Himbaechel architecture with much greater correctness and a wider range of primitives. In fact, at the moment the advice “use himbaechel-gowin” immediately solves a significant part of the issues opened by users. Of course, you need to wait for amendments to oss-cad-suite, at least https://github.com/YosysHQ/oss-cad-suite-build/pull/109 Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
f085950383
commit
b84d07eddd
23
.github/ci/build_gowin.sh
vendored
23
.github/ci/build_gowin.sh
vendored
@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
function get_dependencies {
|
||||
pip3 install apycula==${APYCULA_REVISION}
|
||||
}
|
||||
|
||||
function build_nextpnr {
|
||||
mkdir build
|
||||
pushd build
|
||||
cmake .. -DARCH=gowin -DWERROR=on -DBUILD_GUI=on -DUSE_IPO=off
|
||||
make nextpnr-gowin -j`nproc`
|
||||
popd
|
||||
}
|
||||
|
||||
function run_tests {
|
||||
:
|
||||
}
|
||||
|
||||
function run_archcheck {
|
||||
pushd build
|
||||
./nextpnr-gowin --device GW1N-UV4LQ144C6/I5 --test
|
||||
popd
|
||||
}
|
2
.github/workflows/arch_ci.yml
vendored
2
.github/workflows/arch_ci.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [mistral, ice40, ecp5, generic, nexus, machxo2, gowin, himbaechel]
|
||||
arch: [mistral, ice40, ecp5, generic, nexus, machxo2, himbaechel]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DEPS_PATH: ${{ github.workspace }}/deps
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,7 +5,6 @@
|
||||
/nextpnr-ecp5*
|
||||
/nextpnr-nexus*
|
||||
/nextpnr-fpga_interchange*
|
||||
/nextpnr-gowin*
|
||||
/nextpnr-machxo2*
|
||||
/nextpnr-himbaechel*
|
||||
.cache
|
||||
|
@ -108,9 +108,9 @@ endif()
|
||||
set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables")
|
||||
|
||||
# List of families to build
|
||||
set(FAMILIES generic ice40 ecp5 nexus gowin fpga_interchange machxo2 mistral himbaechel)
|
||||
set(FAMILIES generic ice40 ecp5 nexus fpga_interchange machxo2 mistral himbaechel)
|
||||
set(STABLE_FAMILIES generic ice40 ecp5)
|
||||
set(EXPERIMENTAL_FAMILIES nexus gowin fpga_interchange machxo2 mistral himbaechel)
|
||||
set(EXPERIMENTAL_FAMILIES nexus fpga_interchange machxo2 mistral himbaechel)
|
||||
|
||||
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
|
||||
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
|
||||
|
@ -1,53 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(chipdb-gowin NONE)
|
||||
|
||||
set(ALL_GOWIN_DEVICES GW1N-1 GW1NZ-1 GW1N-4 GW1N-9 GW1N-9C GW1NS-2 GW1NS-4 GW2A-18)
|
||||
set(GOWIN_DEVICES ${ALL_GOWIN_DEVICES} CACHE STRING
|
||||
"Include support for these Gowin devices (available: ${ALL_GOWIN_DEVICES})")
|
||||
message(STATUS "Enabled Gowin devices: ${GOWIN_DEVICES}")
|
||||
|
||||
find_program (GOWIN_BBA_EXECUTABLE gowin_bba)
|
||||
message(STATUS "gowin_bba executable: ${GOWIN_BBA_EXECUTABLE}")
|
||||
|
||||
if(DEFINED GOWIN_CHIPDB)
|
||||
add_custom_target(chipdb-gowin-bbas ALL)
|
||||
else()
|
||||
# shared among all families
|
||||
set(SERIALIZE_CHIPDBS TRUE CACHE BOOL
|
||||
"Serialize device data preprocessing to minimize memory use")
|
||||
|
||||
set(all_device_bbas)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chipdb)
|
||||
foreach(device ${GOWIN_DEVICES})
|
||||
if(NOT device IN_LIST ALL_GOWIN_DEVICES)
|
||||
message(FATAL_ERROR "Device ${device} is not a supported Gowin device")
|
||||
endif()
|
||||
|
||||
set(device_bba chipdb/chipdb-${device}.bba)
|
||||
add_custom_command(
|
||||
OUTPUT ${device_bba}
|
||||
COMMAND ${GOWIN_BBA_EXECUTABLE} -d ${device} -i ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc -o ${device_bba}.new
|
||||
# atomically update
|
||||
COMMAND ${CMAKE_COMMAND} -E rename ${device_bba}.new ${device_bba}
|
||||
DEPENDS
|
||||
${GOWIN_BBA_EXECUTABLE}
|
||||
${PREVIOUS_CHIPDB_TARGET}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/constids.inc
|
||||
VERBATIM)
|
||||
list(APPEND all_device_bbas ${device_bba})
|
||||
if(SERIALIZE_CHIPDBS)
|
||||
set(PREVIOUS_CHIPDB_TARGET ${CMAKE_CURRENT_BINARY_DIR}/${device_bba})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_custom_target(chipdb-gowin-bbas ALL DEPENDS ${all_device_bbas})
|
||||
|
||||
get_directory_property(has_parent PARENT_DIRECTORY)
|
||||
if(has_parent)
|
||||
set(GOWIN_CHIPDB ${CMAKE_CURRENT_BINARY_DIR}/chipdb PARENT_SCOPE)
|
||||
# serialize chipdb build across multiple architectures
|
||||
set(PREVIOUS_CHIPDB_TARGET chipdb-gowin-bbas PARENT_SCOPE)
|
||||
else()
|
||||
message(STATUS "Build nextpnr with -DGOWIN_CHIPDB=${CMAKE_CURRENT_BINARY_DIR}/chipdb")
|
||||
endif()
|
||||
endif()
|
2655
gowin/arch.cc
2655
gowin/arch.cc
File diff suppressed because it is too large
Load Diff
555
gowin/arch.h
555
gowin/arch.h
@ -1,555 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2020 Pepijn de Vos <pepijn@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 GOWIN_ARCH_H
|
||||
#define GOWIN_ARCH_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base_arch.h"
|
||||
#include "idstring.h"
|
||||
#include "nextpnr_namespaces.h"
|
||||
#include "nextpnr_types.h"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
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); }
|
||||
|
||||
T *get_mut() const
|
||||
{
|
||||
return const_cast<T *>(reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset));
|
||||
}
|
||||
|
||||
const T &operator[](std::size_t index) const { return get()[index]; }
|
||||
|
||||
const T &operator*() const { return *(get()); }
|
||||
|
||||
const T *operator->() const { return get(); }
|
||||
|
||||
RelPtr(const RelPtr &) = delete;
|
||||
RelPtr &operator=(const RelPtr &) = delete;
|
||||
};
|
||||
|
||||
NPNR_PACKED_STRUCT(struct PairPOD {
|
||||
uint16_t dest_id;
|
||||
uint16_t src_id;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct BelsPOD {
|
||||
uint16_t type_id;
|
||||
uint16_t num_ports;
|
||||
RelPtr<PairPOD> ports;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct TilePOD /*TidePOD*/ {
|
||||
uint32_t num_bels;
|
||||
RelPtr<BelsPOD> bels;
|
||||
uint32_t num_pips;
|
||||
RelPtr<PairPOD> pips;
|
||||
uint32_t num_clock_pips;
|
||||
RelPtr<PairPOD> clock_pips;
|
||||
uint32_t num_aliases;
|
||||
RelPtr<PairPOD> aliases;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct GlobalAliasPOD {
|
||||
uint16_t dest_row;
|
||||
uint16_t dest_col;
|
||||
uint16_t dest_id;
|
||||
uint16_t src_row;
|
||||
uint16_t src_col;
|
||||
uint16_t src_id;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct TimingPOD {
|
||||
uint32_t name_id;
|
||||
// input, output
|
||||
uint32_t ff;
|
||||
uint32_t fr;
|
||||
uint32_t rf;
|
||||
uint32_t rr;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct TimingGroupPOD {
|
||||
uint32_t name_id;
|
||||
uint32_t num_timings;
|
||||
RelPtr<TimingPOD> timings;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct TimingGroupsPOD {
|
||||
TimingGroupPOD lut;
|
||||
TimingGroupPOD alu;
|
||||
TimingGroupPOD sram;
|
||||
TimingGroupPOD dff;
|
||||
// TimingGroupPOD dl;
|
||||
// TimingGroupPOD iddroddr;
|
||||
// TimingGroupPOD pll;
|
||||
// TimingGroupPOD dll;
|
||||
TimingGroupPOD bram;
|
||||
// TimingGroupPOD dsp;
|
||||
TimingGroupPOD fanout;
|
||||
TimingGroupPOD glbsrc;
|
||||
TimingGroupPOD hclk;
|
||||
TimingGroupPOD iodelay;
|
||||
// TimingGroupPOD io;
|
||||
// TimingGroupPOD iregoreg;
|
||||
TimingGroupPOD wire;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct TimingClassPOD {
|
||||
uint32_t name_id;
|
||||
uint32_t num_groups;
|
||||
RelPtr<TimingGroupsPOD> groups;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct PartnumberPOD {
|
||||
uint32_t name_id;
|
||||
uint32_t package_id;
|
||||
uint32_t device_id;
|
||||
uint32_t speed_id;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct PinPOD {
|
||||
uint16_t index_id;
|
||||
uint16_t loc_id;
|
||||
uint32_t num_cfgs;
|
||||
RelPtr<uint32_t> cfgs;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct PackagePOD {
|
||||
uint32_t name_id;
|
||||
uint32_t num_pins;
|
||||
RelPtr<PinPOD> pins;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct VariantPOD {
|
||||
uint32_t name_id;
|
||||
uint32_t num_packages;
|
||||
RelPtr<PackagePOD> packages;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct DatabasePOD {
|
||||
RelPtr<char> family;
|
||||
uint32_t version;
|
||||
uint16_t rows;
|
||||
uint16_t cols;
|
||||
RelPtr<RelPtr<TilePOD>> grid;
|
||||
uint32_t num_aliases;
|
||||
RelPtr<GlobalAliasPOD> aliases;
|
||||
uint32_t num_speeds;
|
||||
RelPtr<TimingClassPOD> speeds;
|
||||
uint32_t num_partnumbers;
|
||||
RelPtr<PartnumberPOD> partnumber_packages;
|
||||
uint32_t num_variants;
|
||||
RelPtr<VariantPOD> variants;
|
||||
uint16_t num_constids;
|
||||
uint16_t num_ids;
|
||||
RelPtr<RelPtr<char>> id_strs;
|
||||
});
|
||||
|
||||
struct ArchArgs
|
||||
{
|
||||
std::string family;
|
||||
std::string partnumber;
|
||||
// y = mx + c relationship between distance and delay for interconnect
|
||||
// delay estimates
|
||||
double delayScale = 0.4, delayOffset = 0.4;
|
||||
bool gui;
|
||||
};
|
||||
|
||||
struct WireInfo;
|
||||
|
||||
struct PipInfo
|
||||
{
|
||||
IdString name, type;
|
||||
std::map<IdString, std::string> attrs;
|
||||
NetInfo *bound_net;
|
||||
WireId srcWire, dstWire;
|
||||
DelayQuad delay;
|
||||
DecalXY decalxy_active, decalxy_inactive;
|
||||
Loc loc;
|
||||
};
|
||||
|
||||
struct WireInfo
|
||||
{
|
||||
IdString name, type;
|
||||
std::map<IdString, std::string> attrs;
|
||||
NetInfo *bound_net;
|
||||
std::vector<PipId> downhill, uphill;
|
||||
BelPin uphill_bel_pin;
|
||||
std::vector<BelPin> downhill_bel_pins;
|
||||
std::vector<BelPin> bel_pins;
|
||||
DecalXY decalxy_active, decalxy_inactive;
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct PinInfo
|
||||
{
|
||||
IdString name;
|
||||
WireId wire;
|
||||
PortType type;
|
||||
};
|
||||
|
||||
struct BelInfo
|
||||
{
|
||||
IdString name, type;
|
||||
std::map<IdString, std::string> attrs;
|
||||
CellInfo *bound_cell;
|
||||
dict<IdString, PinInfo> pins;
|
||||
std::vector<IdString> pin_cfgs;
|
||||
DecalXY decalxy_active, decalxy_inactive;
|
||||
int x, y, z;
|
||||
bool gb;
|
||||
};
|
||||
|
||||
struct GroupInfo
|
||||
{
|
||||
IdString name;
|
||||
std::vector<BelId> bels;
|
||||
std::vector<WireId> wires;
|
||||
std::vector<PipId> pips;
|
||||
std::vector<GroupId> groups;
|
||||
DecalXY decalxy;
|
||||
};
|
||||
|
||||
struct CellDelayKey
|
||||
{
|
||||
IdString from, to;
|
||||
inline bool operator==(const CellDelayKey &other) const { return from == other.from && to == other.to; }
|
||||
unsigned int hash() const { return mkhash(from.hash(), to.hash()); }
|
||||
};
|
||||
|
||||
struct CellTiming
|
||||
{
|
||||
dict<IdString, TimingPortClass> portClasses;
|
||||
dict<CellDelayKey, DelayQuad> combDelays;
|
||||
dict<IdString, std::vector<TimingClockingInfo>> clockingInfo;
|
||||
};
|
||||
|
||||
struct ArchRanges : BaseArchRanges
|
||||
{
|
||||
using ArchArgsT = ArchArgs;
|
||||
// Bels
|
||||
using AllBelsRangeT = const std::vector<BelId> &;
|
||||
using TileBelsRangeT = const std::vector<BelId> &;
|
||||
using BelAttrsRangeT = const std::map<IdString, std::string> &;
|
||||
using BelPinsRangeT = std::vector<IdString>;
|
||||
using CellBelPinRangeT = std::array<IdString, 1>;
|
||||
// Wires
|
||||
using AllWiresRangeT = const std::vector<WireId> &;
|
||||
using DownhillPipRangeT = const std::vector<PipId> &;
|
||||
using UphillPipRangeT = const std::vector<PipId> &;
|
||||
using WireBelPinRangeT = const std::vector<BelPin> &;
|
||||
using WireAttrsRangeT = const std::map<IdString, std::string> &;
|
||||
// Pips
|
||||
using AllPipsRangeT = const std::vector<PipId> &;
|
||||
using PipAttrsRangeT = const std::map<IdString, std::string> &;
|
||||
// Groups
|
||||
using AllGroupsRangeT = std::vector<GroupId>;
|
||||
using GroupBelsRangeT = const std::vector<BelId> &;
|
||||
using GroupWiresRangeT = const std::vector<WireId> &;
|
||||
using GroupPipsRangeT = const std::vector<PipId> &;
|
||||
using GroupGroupsRangeT = const std::vector<GroupId> &;
|
||||
};
|
||||
|
||||
enum class PLL // fixed PLL locations
|
||||
{
|
||||
left,
|
||||
right
|
||||
};
|
||||
|
||||
struct Arch : BaseArch<ArchRanges>
|
||||
{
|
||||
std::string family;
|
||||
std::string device;
|
||||
const PackagePOD *package;
|
||||
const TimingGroupsPOD *speed;
|
||||
|
||||
dict<IdString, WireInfo> wires;
|
||||
dict<IdString, PipInfo> pips;
|
||||
dict<IdString, BelInfo> bels;
|
||||
dict<GroupId, GroupInfo> groups;
|
||||
|
||||
// These functions include useful errors if not found
|
||||
WireInfo &wire_info(IdString wire);
|
||||
PipInfo &pip_info(IdString pip);
|
||||
BelInfo &bel_info(IdString bel);
|
||||
NetInfo &net_info(IdString net);
|
||||
|
||||
std::vector<IdString> bel_ids, wire_ids, pip_ids;
|
||||
|
||||
dict<Loc, BelId> bel_by_loc;
|
||||
std::vector<std::vector<std::vector<BelId>>> bels_by_tile;
|
||||
|
||||
dict<DecalId, std::vector<GraphicElement>> decal_graphics;
|
||||
|
||||
int gridDimX = 0, gridDimY = 0;
|
||||
std::vector<std::vector<int>> tileBelDimZ;
|
||||
std::vector<std::vector<int>> tilePipDimZ;
|
||||
|
||||
dict<IdString, CellTiming> cellTiming;
|
||||
|
||||
void addWire(IdString name, IdString type, int x, int y);
|
||||
void addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayQuad delay, Loc loc);
|
||||
|
||||
void addBel(IdString name, IdString type, Loc loc, bool gb);
|
||||
void addBelInput(IdString bel, IdString name, IdString wire);
|
||||
void addBelOutput(IdString bel, IdString name, IdString wire);
|
||||
void addBelInout(IdString bel, IdString name, IdString wire);
|
||||
|
||||
void addGroup(IdString name);
|
||||
void addGroupBel(IdString group, IdString bel);
|
||||
void addGroupWire(IdString group, IdString wire);
|
||||
void addGroupPip(IdString group, IdString pip);
|
||||
void addGroupGroup(IdString group, IdString grp);
|
||||
|
||||
void addDecalGraphic(DecalId decal, const GraphicElement &graphic);
|
||||
void setWireDecal(WireId wire, DecalXY active, DecalXY inactive);
|
||||
void setPipDecal(PipId pip, DecalXY active, DecalXY inactive);
|
||||
void setBelDecal(BelId bel, DecalXY active, DecalXY inactive);
|
||||
void setDefaultDecals(void);
|
||||
void setGroupDecal(GroupId group, DecalXY decalxy);
|
||||
std::vector<GraphicElement> getDecalGraphics(DecalId decal) const override;
|
||||
DecalXY getBelDecal(BelId bel) const override;
|
||||
DecalXY getGroupDecal(GroupId grp) const override;
|
||||
DecalXY getPipDecal(PipId pip) const override;
|
||||
DecalXY getWireDecal(WireId pip) const override;
|
||||
|
||||
void setWireAttr(IdString wire, IdString key, const std::string &value);
|
||||
void setPipAttr(IdString pip, IdString key, const std::string &value);
|
||||
void setBelAttr(IdString bel, IdString key, const std::string &value);
|
||||
|
||||
void setDelayScaling(double scale, double offset);
|
||||
|
||||
void addCellTimingClock(IdString cell, IdString port);
|
||||
void addCellTimingClass(IdString cell, IdString port, TimingPortClass cls);
|
||||
void addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay);
|
||||
void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayPair setup, DelayPair hold);
|
||||
void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayQuad clktoq);
|
||||
|
||||
IdString wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString &wire);
|
||||
DelayQuad getWireTypeDelay(IdString wire);
|
||||
void read_cst(std::istream &in);
|
||||
void addMuxBels(const DatabasePOD *db, int row, int col);
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Common Arch API. Every arch must provide the following methods.
|
||||
|
||||
ArchArgs args;
|
||||
Arch(ArchArgs args);
|
||||
|
||||
std::string getChipName() const override { return device; }
|
||||
|
||||
ArchArgs archArgs() const override { return args; }
|
||||
IdString archArgsToId(ArchArgs args) const override { return id_none; }
|
||||
|
||||
int getGridDimX() const override { return gridDimX; }
|
||||
int getGridDimY() const override { return gridDimY; }
|
||||
int getTileBelDimZ(int x, int y) const override { return tileBelDimZ[x][y]; }
|
||||
int getTilePipDimZ(int x, int y) const override { return tilePipDimZ[x][y]; }
|
||||
char getNameDelimiter() const override
|
||||
{
|
||||
return ' '; /* use a non-existent delimiter as we aren't using IdStringLists yet */
|
||||
}
|
||||
|
||||
BelId getBelByName(IdStringList name) const override;
|
||||
IdStringList getBelName(BelId bel) const override;
|
||||
Loc getBelLocation(BelId bel) const override;
|
||||
BelId getBelByLocation(Loc loc) const override;
|
||||
const std::vector<BelId> &getBelsByTile(int x, int y) const override;
|
||||
bool getBelGlobalBuf(BelId bel) const override;
|
||||
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override;
|
||||
void unbindBel(BelId bel) override;
|
||||
bool checkBelAvail(BelId bel) const override;
|
||||
CellInfo *getBoundBelCell(BelId bel) const override;
|
||||
CellInfo *getConflictingBelCell(BelId bel) const override;
|
||||
const std::vector<BelId> &getBels() const override;
|
||||
IdString getBelType(BelId bel) const override;
|
||||
const std::map<IdString, std::string> &getBelAttrs(BelId bel) const override;
|
||||
WireId getBelPinWire(BelId bel, IdString pin) const override;
|
||||
PortType getBelPinType(BelId bel, IdString pin) const override;
|
||||
std::vector<IdString> getBelPins(BelId bel) const override;
|
||||
std::array<IdString, 1> getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override;
|
||||
// Placement validity checks
|
||||
virtual bool isValidBelForCellType(IdString cell_type, BelId bel) const override
|
||||
{
|
||||
return cell_type == id_DUMMY_CELL || cell_type == this->getBelType(bel);
|
||||
}
|
||||
|
||||
WireId getWireByName(IdStringList name) const override;
|
||||
IdStringList getWireName(WireId wire) const override;
|
||||
IdString getWireType(WireId wire) const override;
|
||||
const std::map<IdString, std::string> &getWireAttrs(WireId wire) const override;
|
||||
void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) override;
|
||||
void unbindWire(WireId wire) override;
|
||||
bool checkWireAvail(WireId wire) const override;
|
||||
NetInfo *getBoundWireNet(WireId wire) const override;
|
||||
WireId getConflictingWireWire(WireId wire) const override { return wire; }
|
||||
NetInfo *getConflictingWireNet(WireId wire) const override;
|
||||
DelayQuad getWireDelay(WireId wire) const override { return DelayQuad(0); }
|
||||
const std::vector<WireId> &getWires() const override;
|
||||
const std::vector<BelPin> &getWireBelPins(WireId wire) const override;
|
||||
|
||||
PipId getPipByName(IdStringList name) const override;
|
||||
IdStringList getPipName(PipId pip) const override;
|
||||
IdString getPipType(PipId pip) const override;
|
||||
const std::map<IdString, std::string> &getPipAttrs(PipId pip) const override;
|
||||
void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) override;
|
||||
void unbindPip(PipId pip) override;
|
||||
bool checkPipAvail(PipId pip) const override;
|
||||
NetInfo *getBoundPipNet(PipId pip) const override;
|
||||
WireId getConflictingPipWire(PipId pip) const override;
|
||||
NetInfo *getConflictingPipNet(PipId pip) const override;
|
||||
const std::vector<PipId> &getPips() const override;
|
||||
Loc getPipLocation(PipId pip) const override;
|
||||
WireId getPipSrcWire(PipId pip) const override;
|
||||
WireId getPipDstWire(PipId pip) const override;
|
||||
DelayQuad getPipDelay(PipId pip) const override;
|
||||
const std::vector<PipId> &getPipsDownhill(WireId wire) const override;
|
||||
const std::vector<PipId> &getPipsUphill(WireId wire) const override;
|
||||
|
||||
GroupId getGroupByName(IdStringList name) const override;
|
||||
IdStringList getGroupName(GroupId group) const override;
|
||||
std::vector<GroupId> getGroups() const override;
|
||||
const std::vector<BelId> &getGroupBels(GroupId group) const override;
|
||||
const std::vector<WireId> &getGroupWires(GroupId group) const override;
|
||||
const std::vector<PipId> &getGroupPips(GroupId group) const override;
|
||||
const std::vector<GroupId> &getGroupGroups(GroupId group) const override;
|
||||
|
||||
delay_t estimateDelay(WireId src, WireId dst) const override;
|
||||
delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override;
|
||||
delay_t getDelayEpsilon() const override { return 0.01; }
|
||||
delay_t getRipupDelayPenalty() const override { return 0.4; }
|
||||
float getDelayNS(delay_t v) const override { return v; }
|
||||
|
||||
delay_t getDelayFromNS(float ns) const override { return ns; }
|
||||
|
||||
uint32_t getDelayChecksum(delay_t v) const override { return 0; }
|
||||
|
||||
BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override;
|
||||
|
||||
bool pack() override;
|
||||
bool place() override;
|
||||
bool route() override;
|
||||
|
||||
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const override;
|
||||
// Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port
|
||||
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const override;
|
||||
// Get the TimingClockingInfo of a port
|
||||
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override;
|
||||
|
||||
bool isBelLocationValid(BelId bel, bool explain_invalid = false) const override;
|
||||
|
||||
static const std::string defaultPlacer;
|
||||
static const std::vector<std::string> availablePlacers;
|
||||
static const std::string defaultRouter;
|
||||
static const std::vector<std::string> availableRouters;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Internal usage
|
||||
void assignArchInfo() override;
|
||||
bool cellsCompatible(const CellInfo **cells, int count) const;
|
||||
bool haveBelType(int x, int y, IdString bel_type);
|
||||
bool allocate_longwire(NetInfo *ni, int lw_idx = -1);
|
||||
void fix_longwire_bels();
|
||||
void pre_pack(Context *ctx);
|
||||
void post_pack(Context *ctx);
|
||||
void pre_route(Context *ctx);
|
||||
void post_route(Context *ctx);
|
||||
void auto_longwires();
|
||||
void add_pllvr_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col);
|
||||
void add_rpll_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col);
|
||||
void fix_pll_nets(Context *ctx);
|
||||
bool is_GCLKT_iob(const CellInfo *cell);
|
||||
void bind_pll_to_bel(CellInfo *ci, PLL loc);
|
||||
|
||||
void mark_used_hclk(Context *ctx);
|
||||
IdString apply_local_aliases(int row, int col, const DatabasePOD *db, IdString &wire);
|
||||
|
||||
WireId get_make_port_wire(const DatabasePOD *db, const BelsPOD *bel, int row, int col, IdString port);
|
||||
|
||||
GowinGlobalRouter globals_router;
|
||||
void mark_gowin_globals(Context *ctx);
|
||||
void route_gowin_globals(Context *ctx);
|
||||
|
||||
// chip db version
|
||||
unsigned int const chipdb_version = 2;
|
||||
|
||||
std::vector<IdString> cell_types;
|
||||
|
||||
// clock spines cache
|
||||
// spine_id : [wire_id, wire_id, ...]
|
||||
dict<IdString, std::vector<IdString>> clockSpinesCache;
|
||||
void updateClockSpinesCache(IdString spine_id, IdString wire_id);
|
||||
void fixClockSpineDecals(void);
|
||||
|
||||
// XXX GW1N-9C DDR quirk
|
||||
bool ddr_has_extra_inputs = false;
|
||||
// XXX GW1NR-9 iobuf quirk
|
||||
bool gw1n9_quirk = false;
|
||||
|
||||
// 8 Long wires
|
||||
uint8_t avail_longwires = 0xff;
|
||||
|
||||
// Permissible combinations of modes in a single slice
|
||||
std::map<const IdString, IdString> dff_comp_mode;
|
||||
|
||||
// max global clock wires
|
||||
int max_clock;
|
||||
};
|
||||
|
||||
// Bels Z range
|
||||
namespace BelZ {
|
||||
enum
|
||||
{
|
||||
ioba_z = 0, // IOBA
|
||||
iobb_z = 1, // IOBB
|
||||
mux_0_z = 10, // start Z for the MUX2LUT5 bels
|
||||
lutram_0_z = 30, // start Z for the LUTRAM bels
|
||||
vcc_0_z = 277, // virtual VCC bel Z
|
||||
gnd_0_z = 278, // virtual VSS bel Z
|
||||
osc_z = 280, // Z for the oscillator bels
|
||||
bufs_0_z = 281, // Z for long wire buffer bel
|
||||
pll_z = 289, // PLL
|
||||
pllvr_z = 290, // PLLVR
|
||||
iologic_z = 291, // IOLOGIC
|
||||
oser16_z = 293, // OSER16
|
||||
ides16_z = 294, // IDES16
|
||||
free_z = 295 // Must be the last, one can use z starting from this value, adjust accordingly.
|
||||
};
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif /* GOWIN_ARCH_H */
|
@ -1,160 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2018 gatecat <gatecat@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 "arch_pybindings.h"
|
||||
#include "nextpnr.h"
|
||||
#include "pybindings.h"
|
||||
#include "pywrappers.h"
|
||||
|
||||
using namespace pybind11::literals;
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
namespace PythonConversion {
|
||||
template <> struct string_converter<const IdString &>
|
||||
{
|
||||
const IdString &from_str(Context *ctx, std::string name) { NPNR_ASSERT_FALSE("unsupported"); }
|
||||
|
||||
std::string to_str(Context *ctx, const IdString &id) { return id.str(ctx); }
|
||||
};
|
||||
} // namespace PythonConversion
|
||||
|
||||
void arch_wrap_python(py::module &m)
|
||||
{
|
||||
using namespace PythonConversion;
|
||||
|
||||
auto arch_cls = py::class_<Arch, BaseCtx>(m, "Arch").def(py::init<ArchArgs>());
|
||||
|
||||
auto dxy_cls = py::class_<ContextualWrapper<DecalXY>>(m, "DecalXY_");
|
||||
readwrite_wrapper<DecalXY, decltype(&DecalXY::decal), &DecalXY::decal, conv_to_str<DecalId>,
|
||||
conv_from_str<DecalId>>::def_wrap(dxy_cls, "decal");
|
||||
readwrite_wrapper<DecalXY, decltype(&DecalXY::x), &DecalXY::x, pass_through<float>, pass_through<float>>::def_wrap(
|
||||
dxy_cls, "x");
|
||||
readwrite_wrapper<DecalXY, decltype(&DecalXY::y), &DecalXY::y, pass_through<float>, pass_through<float>>::def_wrap(
|
||||
dxy_cls, "y");
|
||||
|
||||
auto ctx_cls = py::class_<Context, Arch>(m, "Context")
|
||||
.def("checksum", &Context::checksum)
|
||||
.def("pack", &Context::pack)
|
||||
.def("place", &Context::place)
|
||||
.def("route", &Context::route);
|
||||
|
||||
py::class_<BelPin>(m, "BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
|
||||
|
||||
fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<IdString>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
|
||||
fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum");
|
||||
fn_wrapper_3a_v<Context, decltype(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>,
|
||||
addr_and_unwrap<CellInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel");
|
||||
fn_wrapper_1a_v<Context, decltype(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap(
|
||||
ctx_cls, "unbindBel");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getBoundBelCell), &Context::getBoundBelCell, deref_and_wrap<CellInfo>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
|
||||
deref_and_wrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
|
||||
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels,
|
||||
wrap_context<const std::vector<BelId> &>>::def_wrap(ctx_cls, "getBels");
|
||||
|
||||
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
|
||||
conv_from_str<BelId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelPinWire");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getWireBelPins), &Context::getWireBelPins,
|
||||
wrap_context<const std::vector<BelPin> &>, conv_from_str<WireId>>::def_wrap(ctx_cls,
|
||||
"getWireBelPins");
|
||||
|
||||
fn_wrapper_1a<Context, decltype(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum");
|
||||
fn_wrapper_3a_v<Context, decltype(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>,
|
||||
addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire");
|
||||
fn_wrapper_1a_v<Context, decltype(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap(
|
||||
ctx_cls, "unbindWire");
|
||||
fn_wrapper_1a<Context, decltype(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getBoundWireNet), &Context::getBoundWireNet, deref_and_wrap<NetInfo>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getConflictingWireNet), &Context::getConflictingWireNet,
|
||||
deref_and_wrap<NetInfo>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet");
|
||||
|
||||
fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires,
|
||||
wrap_context<const std::vector<WireId> &>>::def_wrap(ctx_cls, "getWires");
|
||||
|
||||
fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips,
|
||||
wrap_context<const std::vector<PipId> &>>::def_wrap(ctx_cls, "getPips");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum");
|
||||
fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>,
|
||||
addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip");
|
||||
fn_wrapper_1a_v<Context, decltype(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap(
|
||||
ctx_cls, "unbindPip");
|
||||
fn_wrapper_1a<Context, decltype(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getBoundPipNet), &Context::getBoundPipNet, deref_and_wrap<NetInfo>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getConflictingPipNet), &Context::getConflictingPipNet,
|
||||
deref_and_wrap<NetInfo>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet");
|
||||
|
||||
fn_wrapper_1a<Context, decltype(&Context::getPipsDownhill), &Context::getPipsDownhill,
|
||||
wrap_context<const std::vector<PipId> &>, conv_from_str<WireId>>::def_wrap(ctx_cls,
|
||||
"getPipsDownhill");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill,
|
||||
wrap_context<const std::vector<PipId> &>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
|
||||
|
||||
fn_wrapper_1a<Context, decltype(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayQuad>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay");
|
||||
|
||||
fn_wrapper_1a<Context, decltype(&Context::getDelayFromNS), &Context::getDelayFromNS, pass_through<delay_t>,
|
||||
pass_through<double>>::def_wrap(ctx_cls, "getDelayFromNS");
|
||||
|
||||
fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
|
||||
ctx_cls, "getChipName");
|
||||
fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
|
||||
"archId");
|
||||
|
||||
fn_wrapper_3a<Context, decltype(&Context::constructDecalXY), &Context::constructDecalXY, wrap_context<DecalXY>,
|
||||
conv_from_str<DecalId>, pass_through<float>, pass_through<float>>::def_wrap(ctx_cls, "DecalXY");
|
||||
|
||||
typedef dict<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||
typedef dict<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||
typedef dict<IdString, HierarchicalCell> HierarchyMap;
|
||||
|
||||
readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
|
||||
"cells");
|
||||
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
|
||||
"nets");
|
||||
|
||||
fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
|
||||
pass_through<float>>::def_wrap(ctx_cls, "addClock");
|
||||
|
||||
WRAP_MAP_UPTR(m, CellMap, "IdCellMap");
|
||||
WRAP_MAP_UPTR(m, NetMap, "IdNetMap");
|
||||
WRAP_MAP(m, HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
|
||||
WRAP_VECTOR(m, const std::vector<IdString>, conv_to_str<IdString>);
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2018 gatecat <gatecat@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 ARCH_PYBINDINGS_H
|
||||
#define ARCH_PYBINDINGS_H
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "nextpnr.h"
|
||||
#include "pybindings.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
||||
#endif
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2020 Pepijn de Vos <pepijn@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 GOWIN_ARCHDEFS_H
|
||||
#define GOWIN_ARCHDEFS_H
|
||||
|
||||
#include "base_clusterinfo.h"
|
||||
#include "idstring.h"
|
||||
#include "nextpnr_namespaces.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
typedef float delay_t;
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
enum ConstIds
|
||||
{
|
||||
ID_NONE
|
||||
#define X(t) , ID_##t
|
||||
#include "constids.inc"
|
||||
#undef X
|
||||
};
|
||||
|
||||
#define X(t) static constexpr auto id_##t = IdString(ID_##t);
|
||||
#include "constids.inc"
|
||||
#undef X
|
||||
#endif
|
||||
|
||||
typedef IdString BelId;
|
||||
typedef IdString WireId;
|
||||
typedef IdString PipId;
|
||||
typedef IdString GroupId;
|
||||
typedef IdString DecalId;
|
||||
typedef IdString BelBucketId;
|
||||
typedef IdString ClusterId;
|
||||
|
||||
struct ArchNetInfo
|
||||
{
|
||||
};
|
||||
|
||||
struct NetInfo;
|
||||
|
||||
struct ArchCellInfo : BaseClusterInfo
|
||||
{
|
||||
// Is the flip-flop of this slice used
|
||||
bool ff_used;
|
||||
// The type of this flip-flop
|
||||
IdString ff_type;
|
||||
// Is a slice type primitive
|
||||
bool is_slice;
|
||||
|
||||
// Only packing rule for slice type primitives is a single clock per tile
|
||||
const NetInfo *slice_clk;
|
||||
const NetInfo *slice_ce;
|
||||
const NetInfo *slice_lsr;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif /* GOWIN_ARCHDEFS_H */
|
290
gowin/cells.cc
290
gowin/cells.cc
@ -1,290 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2019 gatecat <gatecat@ds0.me>
|
||||
* Copyright (C) 2020 Pepijn de Vos <pepijn@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 "cells.h"
|
||||
#include <iostream>
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::string name)
|
||||
{
|
||||
static int auto_idx = 0;
|
||||
IdString name_id =
|
||||
name.empty() ? ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)) : ctx->id(name);
|
||||
auto new_cell = std::make_unique<CellInfo>(ctx, name_id, type);
|
||||
if (type == id_SLICE) {
|
||||
new_cell->params[id_INIT] = 0;
|
||||
new_cell->params[id_FF_USED] = 0;
|
||||
new_cell->params[id_FF_TYPE] = id_DFF.str(ctx);
|
||||
|
||||
IdString names[4] = {id_A, id_B, id_C, id_D};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
new_cell->addInput(names[i]);
|
||||
}
|
||||
|
||||
new_cell->addInput(id_CLK);
|
||||
|
||||
new_cell->addOutput(id_F);
|
||||
new_cell->addOutput(id_Q);
|
||||
new_cell->addInput(id_CE);
|
||||
new_cell->addInput(id_LSR);
|
||||
} else if (type == id_RAMW) {
|
||||
IdString names[8] = {id_A4, id_B4, id_C4, id_D4, id_A5, id_B5, id_C5, id_D5};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
new_cell->addInput(names[i]);
|
||||
}
|
||||
new_cell->addInput(id_CLK);
|
||||
new_cell->addInput(id_CE);
|
||||
new_cell->addInput(id_LSR);
|
||||
} else if (type.in(id_MUX2_LUT5, id_MUX2_LUT6, id_MUX2_LUT7, id_MUX2_LUT7, id_MUX2_LUT8)) {
|
||||
new_cell->addInput(id_I0);
|
||||
new_cell->addInput(id_I1);
|
||||
new_cell->addInput(id_SEL);
|
||||
new_cell->addOutput(id_OF);
|
||||
} else if (type.in(id_IOB, id_IOBS)) {
|
||||
new_cell->params[id_INPUT_USED] = 0;
|
||||
new_cell->params[id_OUTPUT_USED] = 0;
|
||||
new_cell->params[id_ENABLE_USED] = 0;
|
||||
|
||||
new_cell->addInout(id_PAD);
|
||||
new_cell->addInput(id_I);
|
||||
new_cell->addInput(id_OEN);
|
||||
new_cell->addOutput(id_O);
|
||||
} else if (type == id_GSR) {
|
||||
new_cell->addInput(id_GSRI);
|
||||
} else if (type == id_GND) {
|
||||
new_cell->addOutput(id_G);
|
||||
} else if (type == id_VCC) {
|
||||
new_cell->addOutput(id_V);
|
||||
} else if (type == id_BUFS) {
|
||||
new_cell->addInput(id_I);
|
||||
new_cell->addOutput(id_O);
|
||||
} else if (type == id_rPLL) {
|
||||
for (IdString iid :
|
||||
{id_CLKIN, id_CLKFB, id_FBDSEL0, id_FBDSEL1, id_FBDSEL2, id_FBDSEL3, id_FBDSEL4, id_FBDSEL5, id_IDSEL0,
|
||||
id_IDSEL1, id_IDSEL2, id_IDSEL3, id_IDSEL4, id_IDSEL5, id_ODSEL0, id_ODSEL1, id_ODSEL2, id_ODSEL3,
|
||||
id_ODSEL4, id_ODSEL5, id_PSDA0, id_PSDA1, id_PSDA2, id_PSDA3, id_DUTYDA0, id_DUTYDA1, id_DUTYDA2,
|
||||
id_DUTYDA3, id_FDLY0, id_FDLY1, id_FDLY2, id_FDLY3, id_RESET, id_RESET_P}) {
|
||||
new_cell->addInput(iid);
|
||||
}
|
||||
new_cell->addOutput(id_CLKOUT);
|
||||
new_cell->addOutput(id_CLKOUTP);
|
||||
new_cell->addOutput(id_CLKOUTD);
|
||||
new_cell->addOutput(id_CLKOUTD3);
|
||||
new_cell->addOutput(id_LOCK);
|
||||
} else if (type == id_PLLVR) {
|
||||
for (IdString iid :
|
||||
{id_CLKIN, id_CLKFB, id_FBDSEL0, id_FBDSEL1, id_FBDSEL2, id_FBDSEL3, id_FBDSEL4, id_FBDSEL5, id_IDSEL0,
|
||||
id_IDSEL1, id_IDSEL2, id_IDSEL3, id_IDSEL4, id_IDSEL5, id_ODSEL0, id_ODSEL1, id_ODSEL2, id_ODSEL3,
|
||||
id_ODSEL4, id_ODSEL5, id_PSDA0, id_PSDA1, id_PSDA2, id_PSDA3, id_DUTYDA0, id_DUTYDA1, id_DUTYDA2,
|
||||
id_DUTYDA3, id_FDLY0, id_FDLY1, id_FDLY2, id_FDLY3, id_RESET, id_RESET_P, id_VREN}) {
|
||||
new_cell->addInput(iid);
|
||||
}
|
||||
new_cell->addOutput(id_CLKOUT);
|
||||
new_cell->addOutput(id_CLKOUTP);
|
||||
new_cell->addOutput(id_CLKOUTD);
|
||||
new_cell->addOutput(id_CLKOUTD3);
|
||||
new_cell->addOutput(id_LOCK);
|
||||
} else if (type == id_IOLOGIC) {
|
||||
new_cell->addInput(id_FCLK);
|
||||
new_cell->addInput(id_PCLK);
|
||||
new_cell->addInput(id_RESET);
|
||||
} else if (type == id_DUMMY_CELL) {
|
||||
} else {
|
||||
log_error("unable to create generic cell of type %s\n", type.c_str(ctx));
|
||||
}
|
||||
return new_cell;
|
||||
}
|
||||
|
||||
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
||||
{
|
||||
lc->params[id_INIT] = lut->params[id_INIT];
|
||||
lc->cluster = lut->cluster;
|
||||
lc->constr_x = lut->constr_x;
|
||||
lc->constr_y = lut->constr_y;
|
||||
lc->constr_z = lut->constr_z;
|
||||
|
||||
// add itself to the cluster root children list
|
||||
if (lc->cluster != ClusterId()) {
|
||||
CellInfo *cluster_root = ctx->cells.at(lc->cluster).get();
|
||||
lc->constr_x += cluster_root->constr_x;
|
||||
lc->constr_y += cluster_root->constr_y;
|
||||
lc->constr_z += cluster_root->constr_z;
|
||||
if (cluster_root->cluster != cluster_root->name) {
|
||||
lc->cluster = cluster_root->cluster;
|
||||
cluster_root = ctx->cells.at(cluster_root->cluster).get();
|
||||
}
|
||||
cluster_root->constr_children.push_back(lc);
|
||||
}
|
||||
|
||||
IdString sim_names[4] = {id_I0, id_I1, id_I2, id_I3};
|
||||
IdString wire_names[4] = {id_A, id_B, id_C, id_D};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
lut->movePortTo(sim_names[i], lc, wire_names[i]);
|
||||
}
|
||||
|
||||
if (no_dff) {
|
||||
lc->params[id_FF_USED] = 0;
|
||||
lut->movePortTo(id_F, lc, id_F);
|
||||
}
|
||||
}
|
||||
|
||||
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
|
||||
{
|
||||
lc->params[id_FF_USED] = 1;
|
||||
lc->params[id_FF_TYPE] = dff->type.str(ctx);
|
||||
dff->movePortTo(id_CLK, lc, id_CLK);
|
||||
dff->movePortTo(id_CE, lc, id_CE);
|
||||
dff->movePortTo(id_SET, lc, id_LSR);
|
||||
dff->movePortTo(id_RESET, lc, id_LSR);
|
||||
dff->movePortTo(id_CLEAR, lc, id_LSR);
|
||||
dff->movePortTo(id_PRESET, lc, id_LSR);
|
||||
if (pass_thru_lut) {
|
||||
// Fill LUT with alternating 10
|
||||
const int init_size = 1 << 4;
|
||||
std::string init;
|
||||
init.reserve(init_size);
|
||||
for (int i = 0; i < init_size; i += 2)
|
||||
init.append("10");
|
||||
lc->params[id_INIT] = Property::from_string(init);
|
||||
|
||||
dff->movePortTo(id_D, lc, id_A);
|
||||
}
|
||||
|
||||
dff->movePortTo(id_Q, lc, id_Q);
|
||||
}
|
||||
|
||||
void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool<IdString> &todelete_cells)
|
||||
{
|
||||
if (nxio->type == id_IBUF) {
|
||||
if (iob->type == id_IOBS) {
|
||||
// VCC -> OEN
|
||||
iob->connectPort(id_OEN, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
|
||||
}
|
||||
iob->params[id_INPUT_USED] = 1;
|
||||
nxio->movePortTo(id_O, iob, id_O);
|
||||
} else if (nxio->type == id_OBUF) {
|
||||
if (iob->type == id_IOBS) {
|
||||
// VSS -> OEN
|
||||
iob->connectPort(id_OEN, ctx->nets[ctx->id("$PACKER_GND_NET")].get());
|
||||
}
|
||||
iob->params[id_OUTPUT_USED] = 1;
|
||||
nxio->movePortTo(id_I, iob, id_I);
|
||||
} else if (nxio->type == id_TBUF) {
|
||||
iob->params[id_ENABLE_USED] = 1;
|
||||
iob->params[id_OUTPUT_USED] = 1;
|
||||
nxio->movePortTo(id_I, iob, id_I);
|
||||
nxio->movePortTo(id_OEN, iob, id_OEN);
|
||||
} else if (nxio->type == id_IOBUF) {
|
||||
iob->params[id_ENABLE_USED] = 1;
|
||||
iob->params[id_INPUT_USED] = 1;
|
||||
iob->params[id_OUTPUT_USED] = 1;
|
||||
nxio->movePortTo(id_I, iob, id_I);
|
||||
nxio->movePortTo(id_O, iob, id_O);
|
||||
nxio->movePortTo(id_OEN, iob, id_OEN);
|
||||
} else {
|
||||
NPNR_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *new_pll)
|
||||
{
|
||||
pll->movePortTo(id_CLKIN, new_pll, id_CLKIN);
|
||||
pll->movePortTo(id_VREN, new_pll, id_VREN);
|
||||
pll->movePortTo(id_CLKFB, new_pll, id_CLKFB);
|
||||
pll->movePortTo(id_RESET, new_pll, id_RESET);
|
||||
pll->movePortTo(id_RESET_P, new_pll, id_RESET_P);
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
pll->movePortTo(ctx->idf("FBDSEL[%d]", i), new_pll, ctx->idf("FBDSEL%d", i));
|
||||
pll->movePortTo(ctx->idf("IDSEL[%d]", i), new_pll, ctx->idf("IDSEL%d", i));
|
||||
pll->movePortTo(ctx->idf("ODSEL[%d]", i), new_pll, ctx->idf("ODSEL%d", i));
|
||||
if (i < 4) {
|
||||
pll->movePortTo(ctx->idf("PSDA[%d]", i), new_pll, ctx->idf("PSDA%d", i));
|
||||
pll->movePortTo(ctx->idf("DUTYDA[%d]", i), new_pll, ctx->idf("DUTYDA%d", i));
|
||||
pll->movePortTo(ctx->idf("FDLY[%d]", i), new_pll, ctx->idf("FDLY%d", i));
|
||||
}
|
||||
}
|
||||
pll->movePortTo(id_CLKOUT, new_pll, id_CLKOUT);
|
||||
pll->movePortTo(id_CLKOUTP, new_pll, id_CLKOUTP);
|
||||
pll->movePortTo(id_CLKOUTD, new_pll, id_CLKOUTD);
|
||||
pll->movePortTo(id_CLKOUTD3, new_pll, id_CLKOUTD3);
|
||||
pll->movePortTo(id_LOCK, new_pll, id_LOCK);
|
||||
}
|
||||
|
||||
void reconnect_rpll(Context *ctx, CellInfo *pll, CellInfo *new_pll)
|
||||
{
|
||||
pll->movePortTo(id_CLKIN, new_pll, id_CLKIN);
|
||||
pll->movePortTo(id_CLKFB, new_pll, id_CLKFB);
|
||||
pll->movePortTo(id_RESET, new_pll, id_RESET);
|
||||
pll->movePortTo(id_RESET_P, new_pll, id_RESET_P);
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
pll->movePortTo(ctx->idf("FBDSEL[%d]", i), new_pll, ctx->idf("FBDSEL%d", i));
|
||||
pll->movePortTo(ctx->idf("IDSEL[%d]", i), new_pll, ctx->idf("IDSEL%d", i));
|
||||
pll->movePortTo(ctx->idf("ODSEL[%d]", i), new_pll, ctx->idf("ODSEL%d", i));
|
||||
if (i < 4) {
|
||||
pll->movePortTo(ctx->idf("PSDA[%d]", i), new_pll, ctx->idf("PSDA%d", i));
|
||||
pll->movePortTo(ctx->idf("DUTYDA[%d]", i), new_pll, ctx->idf("DUTYDA%d", i));
|
||||
pll->movePortTo(ctx->idf("FDLY[%d]", i), new_pll, ctx->idf("FDLY%d", i));
|
||||
}
|
||||
}
|
||||
pll->movePortTo(id_CLKOUT, new_pll, id_CLKOUT);
|
||||
pll->movePortTo(id_CLKOUTP, new_pll, id_CLKOUTP);
|
||||
pll->movePortTo(id_CLKOUTD, new_pll, id_CLKOUTD);
|
||||
pll->movePortTo(id_CLKOUTD3, new_pll, id_CLKOUTD3);
|
||||
pll->movePortTo(id_LOCK, new_pll, id_LOCK);
|
||||
}
|
||||
|
||||
void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw)
|
||||
{
|
||||
if (ramw->hierpath == IdString())
|
||||
ramw->hierpath = ramw->hierpath;
|
||||
ram->movePortTo(ctx->id("WAD[0]"), ramw, id_A4);
|
||||
ram->movePortTo(ctx->id("WAD[1]"), ramw, id_B4);
|
||||
ram->movePortTo(ctx->id("WAD[2]"), ramw, id_C4);
|
||||
ram->movePortTo(ctx->id("WAD[3]"), ramw, id_D4);
|
||||
|
||||
ram->movePortTo(ctx->id("DI[0]"), ramw, id_A5);
|
||||
ram->movePortTo(ctx->id("DI[1]"), ramw, id_B5);
|
||||
ram->movePortTo(ctx->id("DI[2]"), ramw, id_C5);
|
||||
ram->movePortTo(ctx->id("DI[3]"), ramw, id_D5);
|
||||
|
||||
ram->movePortTo(ctx->id("CLK"), ramw, id_CLK);
|
||||
ram->movePortTo(ctx->id("WRE"), ramw, id_LSR);
|
||||
}
|
||||
|
||||
void sram_to_slice(Context *ctx, CellInfo *ram, CellInfo *slice, int index)
|
||||
{
|
||||
if (slice->hierpath == IdString())
|
||||
slice->hierpath = slice->hierpath;
|
||||
|
||||
slice->params[id_INIT] = ram->params[ctx->idf("INIT_%d", index)];
|
||||
|
||||
ram->movePortTo(ctx->idf("DO[%d]", index), slice, id_F);
|
||||
|
||||
ram->copyPortTo(ctx->id("RAD[0]"), slice, id_A);
|
||||
ram->copyPortTo(ctx->id("RAD[1]"), slice, id_B);
|
||||
ram->copyPortTo(ctx->id("RAD[2]"), slice, id_C);
|
||||
ram->copyPortTo(ctx->id("RAD[3]"), slice, id_D);
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
136
gowin/cells.h
136
gowin/cells.h
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2019 gatecat <gatecat@ds0.me>
|
||||
* Copyright (C) 2020 Pepijn de Vos <pepijn@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 "nextpnr.h"
|
||||
|
||||
#ifndef GENERIC_CELLS_H
|
||||
#define GENERIC_CELLS_H
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// Create a generic arch cell and return it
|
||||
// Name will be automatically assigned if not specified
|
||||
std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::string name = "");
|
||||
|
||||
// Return true if a cell is a LUT
|
||||
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell)
|
||||
{
|
||||
switch (cell->type.index) {
|
||||
case ID_LUT1:
|
||||
case ID_LUT2:
|
||||
case ID_LUT3:
|
||||
case ID_LUT4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if a cell is a wide LUT mux
|
||||
inline bool is_widelut(const BaseCtx *ctx, const CellInfo *cell)
|
||||
{
|
||||
switch (cell->type.index) {
|
||||
case ID_MUX2_LUT5:
|
||||
case ID_MUX2_LUT6:
|
||||
case ID_MUX2_LUT7:
|
||||
case ID_MUX2_LUT8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_alu(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_ALU); }
|
||||
|
||||
// is MUX2_LUT5
|
||||
inline bool is_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT5); }
|
||||
|
||||
// is MUX2_LUT6
|
||||
inline bool is_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT6); }
|
||||
|
||||
// is MUX2_LUT7
|
||||
inline bool is_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT7); }
|
||||
|
||||
// is MUX2_LUT8
|
||||
inline bool is_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT8); }
|
||||
|
||||
// Return true if a cell is a flipflop
|
||||
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
|
||||
{
|
||||
switch (cell->type.index) {
|
||||
case ID_DFF:
|
||||
case ID_DFFE:
|
||||
case ID_DFFS:
|
||||
case ID_DFFSE:
|
||||
case ID_DFFR:
|
||||
case ID_DFFRE:
|
||||
case ID_DFFP:
|
||||
case ID_DFFPE:
|
||||
case ID_DFFC:
|
||||
case ID_DFFCE:
|
||||
case ID_DFFN:
|
||||
case ID_DFFNE:
|
||||
case ID_DFFNS:
|
||||
case ID_DFFNSE:
|
||||
case ID_DFFNR:
|
||||
case ID_DFFNRE:
|
||||
case ID_DFFNP:
|
||||
case ID_DFFNPE:
|
||||
case ID_DFFNC:
|
||||
case ID_DFFNCE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SLICE; }
|
||||
|
||||
inline bool is_sram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_RAM16SDP4; }
|
||||
|
||||
inline bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type == id_IOB || cell->type == id_IOBS); }
|
||||
|
||||
// Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports
|
||||
// as needed. Set no_dff if a DFF is not being used, so that the output
|
||||
// can be reconnected
|
||||
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = true);
|
||||
|
||||
// Convert a DFF primitive to (part of) an GENERIC_SLICE, setting parameters
|
||||
// and reconnecting signals as necessary. If pass_thru_lut is True, the LUT will
|
||||
// be configured as pass through and D connected to I0, otherwise D will be
|
||||
// ignored
|
||||
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
|
||||
|
||||
// Convert a Gowin IO buffer to a IOB bel
|
||||
void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool<IdString> &todelete_cells);
|
||||
|
||||
// Reconnect PLL signals (B)
|
||||
void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *new_pll);
|
||||
void reconnect_rpll(Context *ctx, CellInfo *pll, CellInfo *new_pll);
|
||||
|
||||
// Convert RAM16 to write port
|
||||
void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw);
|
||||
|
||||
// Convert RAM16 to slice
|
||||
void sram_to_slice(Context *ctx, CellInfo *ram, CellInfo *slice, int index);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
1058
gowin/constids.inc
1058
gowin/constids.inc
File diff suppressed because it is too large
Load Diff
40
gowin/cst.cc
40
gowin/cst.cc
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2018 gatecat <gatecat@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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cst.h"
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include "arch.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
bool read_cst(Context *ctx, std::istream &in)
|
||||
{
|
||||
try {
|
||||
ctx->read_cst(in);
|
||||
return true;
|
||||
} catch (log_execution_error_exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
34
gowin/cst.h
34
gowin/cst.h
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2018 gatecat <gatecat@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 CST_H
|
||||
#define CST_H
|
||||
|
||||
#include <iostream>
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// Read the constraints file
|
||||
bool read_cst(Context *ctx, std::istream &in);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
@ -1,53 +0,0 @@
|
||||
add_subdirectory(${family})
|
||||
message(STATUS "Using Gowin chipdb: ${GOWIN_CHIPDB}")
|
||||
|
||||
set(chipdb_sources)
|
||||
set(chipdb_binaries)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${family}/chipdb)
|
||||
foreach(device ${GOWIN_DEVICES})
|
||||
set(chipdb_bba ${GOWIN_CHIPDB}/chipdb-${device}.bba)
|
||||
set(chipdb_bin ${family}/chipdb/chipdb-${device}.bin)
|
||||
set(chipdb_cc ${family}/chipdb/chipdb-${device}.cc)
|
||||
if(BBASM_MODE STREQUAL "binary")
|
||||
add_custom_command(
|
||||
OUTPUT ${chipdb_bin}
|
||||
COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${chipdb_bba} ${chipdb_bin}
|
||||
DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba})
|
||||
list(APPEND chipdb_binaries ${chipdb_bin})
|
||||
elseif(BBASM_MODE STREQUAL "embed")
|
||||
add_custom_command(
|
||||
OUTPUT ${chipdb_cc} ${chipdb_bin}
|
||||
COMMAND bbasm ${BBASM_ENDIAN_FLAG} --e ${chipdb_bba} ${chipdb_cc} ${chipdb_bin}
|
||||
DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba})
|
||||
list(APPEND chipdb_sources ${chipdb_cc})
|
||||
list(APPEND chipdb_binaries ${chipdb_bin})
|
||||
elseif(BBASM_MODE STREQUAL "string")
|
||||
add_custom_command(
|
||||
OUTPUT ${chipdb_cc}
|
||||
COMMAND bbasm ${BBASM_ENDIAN_FLAG} --c ${chipdb_bba} ${chipdb_cc}
|
||||
DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba})
|
||||
list(APPEND chipdb_sources ${chipdb_cc})
|
||||
endif()
|
||||
endforeach()
|
||||
if(WIN32)
|
||||
set(chipdb_rc ${CMAKE_CURRENT_BINARY_DIR}/${family}/resource/chipdb.rc)
|
||||
list(APPEND chipdb_sources ${chipdb_rc})
|
||||
|
||||
file(WRITE ${chipdb_rc})
|
||||
foreach(device ${GOWIN_DEVICES})
|
||||
file(APPEND ${chipdb_rc}
|
||||
"${family}/chipdb-${device}.bin RCDATA \"${CMAKE_CURRENT_BINARY_DIR}/${family}/chipdb/chipdb-${device}.bin\"")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
add_custom_target(chipdb-${family}-bins DEPENDS ${chipdb_sources} ${chipdb_binaries})
|
||||
|
||||
add_library(chipdb-${family} OBJECT ${GOWIN_CHIPDB} ${chipdb_sources})
|
||||
add_dependencies(chipdb-${family} chipdb-${family}-bins)
|
||||
target_compile_options(chipdb-${family} PRIVATE -g0 -O0 -w)
|
||||
target_compile_definitions(chipdb-${family} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family})
|
||||
target_include_directories(chipdb-${family} PRIVATE ${family})
|
||||
|
||||
foreach(family_target ${family_targets})
|
||||
target_sources(${family_target} PRIVATE $<TARGET_OBJECTS:chipdb-${family}>)
|
||||
endforeach()
|
5830
gowin/gfx.cc
5830
gowin/gfx.cc
File diff suppressed because it is too large
Load Diff
40
gowin/gfx.h
40
gowin/gfx.h
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.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 GFX_H
|
||||
#define GFX_H
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
#ifndef NO_GUI
|
||||
void gfxCreateBelDecals(Arch *arch);
|
||||
void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel);
|
||||
void gfxSetIOBWireDecals(Arch *arch, BelInfo &bel);
|
||||
void gfxSetIOBSWireDecals(Arch *arch, BelInfo &bel);
|
||||
void gfxSetPipDefaultDecal(Arch *arch, PipInfo &pip);
|
||||
void gfxSetWireDefaultDecal(Arch *arch, WireInfo &wire);
|
||||
DecalXY gfxGetLutGroupDecalXY(int x, int y, int z);
|
||||
DecalXY gfxGetCruGroupDecalXY(int x, int y);
|
||||
#endif
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // GFX_H
|
332
gowin/globals.cc
332
gowin/globals.cc
@ -1,332 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 gatecat <gatecat@ds0.me>
|
||||
* Copyright (C) 2022 YRabbit <rabbit@yrabbit.cyou>
|
||||
*
|
||||
* 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 <iomanip>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include "cells.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "place_common.h"
|
||||
#include "util.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
bool GowinGlobalRouter::is_clock_port(PortRef const &user)
|
||||
{
|
||||
if ((user.cell->type.in(id_SLICE, id_ODDR, id_ODDRC)) && user.port == id_CLK) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<WireId, BelId> GowinGlobalRouter::clock_src(Context *ctx, PortRef const &driver)
|
||||
{
|
||||
if (driver.cell == nullptr) {
|
||||
return std::make_pair(WireId(), BelId());
|
||||
}
|
||||
|
||||
BelInfo &bel = ctx->bel_info(driver.cell->bel);
|
||||
WireId wire;
|
||||
if (driver.cell->type == id_IOB) {
|
||||
if (ctx->is_GCLKT_iob(driver.cell)) {
|
||||
wire = bel.pins[id_O].wire;
|
||||
return std::make_pair(wire, bel.name);
|
||||
}
|
||||
return std::make_pair(WireId(), BelId());
|
||||
}
|
||||
if (driver.cell->type == id_rPLL || driver.cell->type == id_PLLVR) {
|
||||
if (driver.port == id_CLKOUT || driver.port == id_CLKOUTP || driver.port == id_CLKOUTD ||
|
||||
driver.port == id_CLKOUTD3) {
|
||||
wire = bel.pins[driver.port].wire;
|
||||
return std::make_pair(wire, bel.name);
|
||||
}
|
||||
return std::make_pair(WireId(), BelId());
|
||||
}
|
||||
return std::make_pair(WireId(), BelId());
|
||||
}
|
||||
|
||||
// gather the clock nets
|
||||
void GowinGlobalRouter::gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets)
|
||||
{
|
||||
for (auto const &net : ctx->nets) {
|
||||
NetInfo const *ni = net.second.get();
|
||||
auto new_clock = clock_nets.end();
|
||||
auto clock_wire_bel = clock_src(ctx, ni->driver);
|
||||
if (clock_wire_bel.first != WireId()) {
|
||||
clock_nets.emplace_back(net.first);
|
||||
new_clock = --clock_nets.end();
|
||||
new_clock->clock_wire = clock_wire_bel.first;
|
||||
new_clock->clock_bel = clock_wire_bel.second;
|
||||
}
|
||||
for (auto const &user : ni->users) {
|
||||
if (is_clock_port(user)) {
|
||||
if (new_clock == clock_nets.end()) {
|
||||
clock_nets.emplace_back(net.first);
|
||||
new_clock = --clock_nets.end();
|
||||
}
|
||||
++(new_clock->clock_ports);
|
||||
}
|
||||
}
|
||||
}
|
||||
// need to prioritize the nets
|
||||
std::sort(clock_nets.begin(), clock_nets.end());
|
||||
|
||||
if (ctx->verbose) {
|
||||
for (auto const &net : clock_nets) {
|
||||
log_info(" Net:%s, ports:%d, clock source:%s\n", net.name.c_str(ctx), net.clock_ports,
|
||||
net.clock_wire == WireId() ? "No" : net.clock_wire.c_str(ctx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// non clock port
|
||||
// returns GB pip
|
||||
IdString GowinGlobalRouter::route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock,
|
||||
pool<IdString> &used_pips, pool<IdString> &undo_wires)
|
||||
{
|
||||
static std::vector<IdString> one_hop_0 = {id_W111, id_W121, id_E111, id_E121};
|
||||
static std::vector<IdString> one_hop_4 = {id_S111, id_S121, id_N111, id_N121};
|
||||
// uphill pips
|
||||
for (auto const uphill : ctx->getPipsUphill(dstWire)) {
|
||||
WireId srcWire = ctx->getPipSrcWire(uphill);
|
||||
bool found;
|
||||
if (clock < 4) {
|
||||
found = find(one_hop_0.begin(), one_hop_0.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) !=
|
||||
one_hop_0.end();
|
||||
} else {
|
||||
found = find(one_hop_4.begin(), one_hop_4.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) !=
|
||||
one_hop_4.end();
|
||||
}
|
||||
if (found) {
|
||||
// found one hop pip
|
||||
if (used_wires.count(srcWire)) {
|
||||
if (used_wires[srcWire] != clock) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
WireInfo wi = ctx->wire_info(srcWire);
|
||||
std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1);
|
||||
IdString gb = ctx->idf("R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str());
|
||||
if (ctx->verbose) {
|
||||
log_info(" 1-hop gb:%s\n", gb.c_str(ctx));
|
||||
}
|
||||
// sanity
|
||||
NPNR_ASSERT(find(ctx->getPipsUphill(srcWire).begin(), ctx->getPipsUphill(srcWire).end(), gb) !=
|
||||
ctx->getPipsUphill(srcWire).end());
|
||||
auto up_pips = ctx->getPipsUphill(srcWire);
|
||||
if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) {
|
||||
if (!used_wires.count(srcWire)) {
|
||||
used_wires.insert(std::make_pair(srcWire, clock));
|
||||
undo_wires.insert(srcWire);
|
||||
}
|
||||
used_pips.insert(uphill);
|
||||
if (ctx->verbose) {
|
||||
log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx));
|
||||
}
|
||||
return gb;
|
||||
}
|
||||
}
|
||||
}
|
||||
return IdString();
|
||||
}
|
||||
|
||||
// route one net
|
||||
void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net)
|
||||
{
|
||||
// For failed routing undo
|
||||
pool<IdString> used_pips;
|
||||
pool<IdString> undo_wires;
|
||||
|
||||
log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), net.clock);
|
||||
for (auto const &user : ctx->net_info(net.name).users) {
|
||||
// >>> port <- GB<clock>0
|
||||
WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0);
|
||||
if (ctx->verbose) {
|
||||
log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx),
|
||||
dstWire.c_str(ctx));
|
||||
}
|
||||
|
||||
char buf[30];
|
||||
PipId gb_pip_id;
|
||||
if (user.port == id_CLK || user.port == id_CLKIN) {
|
||||
WireInfo const wi = ctx->wire_info(dstWire);
|
||||
gb_pip_id =
|
||||
ctx->idf("R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, net.clock, ctx->wire_info(dstWire).type.c_str(ctx));
|
||||
// sanity
|
||||
NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) !=
|
||||
ctx->getPipsUphill(dstWire).end());
|
||||
} else {
|
||||
// Non clock port
|
||||
gb_pip_id = route_to_non_clock_port(ctx, dstWire, net.clock, used_pips, undo_wires);
|
||||
if (gb_pip_id == IdString()) {
|
||||
if (ctx->verbose) {
|
||||
log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", dstWire.c_str(ctx),
|
||||
net.name.c_str(ctx));
|
||||
}
|
||||
for (IdString const undo : undo_wires) {
|
||||
used_wires.erase(undo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ctx->verbose) {
|
||||
log_info(" GB Pip:%s\n", gb_pip_id.c_str(ctx));
|
||||
}
|
||||
|
||||
if (used_pips.count(gb_pip_id)) {
|
||||
if (ctx->verbose) {
|
||||
log_info(" ^routed already^\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
used_pips.insert(gb_pip_id);
|
||||
|
||||
// >>> GBOx <- GTx0
|
||||
dstWire = ctx->getPipSrcWire(gb_pip_id);
|
||||
WireInfo dstWireInfo = ctx->wire_info(dstWire);
|
||||
int branch_tap_idx = net.clock > 3 ? 1 : 0;
|
||||
PipId gt_pip_id =
|
||||
ctx->idf("R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx, branch_tap_idx);
|
||||
if (ctx->verbose) {
|
||||
log_info(" GT Pip:%s\n", gt_pip_id.c_str(ctx));
|
||||
}
|
||||
// sanity
|
||||
NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) !=
|
||||
ctx->getPipsUphill(dstWire).end());
|
||||
// if already routed
|
||||
if (used_pips.count(gt_pip_id)) {
|
||||
if (ctx->verbose) {
|
||||
log_info(" ^routed already^\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
used_pips.insert(gt_pip_id);
|
||||
|
||||
// >>> GTx0 <- SPINExx
|
||||
// XXX no optimization here, we need to store
|
||||
// the SPINE <-> clock# correspondence in the database. In the
|
||||
// meantime, we define in run-time in a completely suboptimal way.
|
||||
std::vector<std::string> clock_spine;
|
||||
dstWire = ctx->getPipSrcWire(gt_pip_id);
|
||||
for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
|
||||
std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx);
|
||||
if (name.rfind("SPINE", 0) == 0) {
|
||||
clock_spine.push_back(name);
|
||||
}
|
||||
}
|
||||
sort(clock_spine.begin(), clock_spine.end(), [](const std::string &a, const std::string &b) -> bool {
|
||||
return (a.size() < b.size()) || (a.size() == b.size() && a < b);
|
||||
});
|
||||
dstWireInfo = ctx->wire_info(dstWire);
|
||||
snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1,
|
||||
clock_spine[net.clock - branch_tap_idx * 4].c_str(), branch_tap_idx);
|
||||
PipId spine_pip_id = ctx->id(buf);
|
||||
if (ctx->verbose) {
|
||||
log_info(" Spine Pip:%s\n", buf);
|
||||
}
|
||||
// sanity
|
||||
NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), spine_pip_id) !=
|
||||
ctx->getPipsUphill(dstWire).end());
|
||||
// if already routed
|
||||
if (used_pips.count(spine_pip_id)) {
|
||||
if (ctx->verbose) {
|
||||
log_info(" ^routed already^\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
used_pips.insert(spine_pip_id);
|
||||
|
||||
// >>> SPINExx <- Src
|
||||
dstWire = ctx->getPipSrcWire(spine_pip_id);
|
||||
dstWireInfo = ctx->wire_info(dstWire);
|
||||
PipId src_pip_id = PipId();
|
||||
for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
|
||||
if (ctx->getPipSrcWire(uphill_pip) == net.clock_wire) {
|
||||
src_pip_id = uphill_pip;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ctx->verbose) {
|
||||
log_info(" Src Pip:%s\n", src_pip_id.c_str(ctx));
|
||||
}
|
||||
NPNR_ASSERT(src_pip_id != PipId());
|
||||
// if already routed
|
||||
if (used_pips.count(src_pip_id)) {
|
||||
if (ctx->verbose) {
|
||||
log_info(" ^routed already^\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
used_pips.insert(src_pip_id);
|
||||
}
|
||||
log_info(" Net %s is routed.\n", net.name.c_str(ctx));
|
||||
if (!ctx->net_info(net.name).users.empty()) {
|
||||
for (auto const pip : used_pips) {
|
||||
ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
||||
}
|
||||
ctx->bindWire(net.clock_wire, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
void GowinGlobalRouter::route_globals(Context *ctx)
|
||||
{
|
||||
log_info("Routing globals...\n");
|
||||
|
||||
for (auto const &net : nets) {
|
||||
route_net(ctx, net);
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate networks that will be routed through the global system.
|
||||
// Mark their driver cells as global buffers to exclude them from the analysis.
|
||||
void GowinGlobalRouter::mark_globals(Context *ctx)
|
||||
{
|
||||
log_info("Find global nets...\n");
|
||||
|
||||
std::vector<globalnet_t> clock_nets;
|
||||
gather_clock_nets(ctx, clock_nets);
|
||||
// XXX we need to use the list of indexes of clocks from the database
|
||||
// use 6 clocks (XXX 3 for GW1NZ-1)
|
||||
int max_clock = ctx->max_clock, cur_clock = -1;
|
||||
for (auto &net : clock_nets) {
|
||||
// XXX only IO clock for now
|
||||
if (net.clock_wire == WireId()) {
|
||||
log_info(" Non clock source, skip %s.\n", net.name.c_str(ctx));
|
||||
continue;
|
||||
}
|
||||
if (++cur_clock >= max_clock) {
|
||||
log_info(" No more clock wires left, skip the remaining nets.\n");
|
||||
break;
|
||||
}
|
||||
if (ctx->net_info(net.name).users.empty()) {
|
||||
--cur_clock;
|
||||
net.clock = -1;
|
||||
} else {
|
||||
net.clock = cur_clock;
|
||||
}
|
||||
BelInfo &bi = ctx->bel_info(net.clock_bel);
|
||||
bi.gb = true;
|
||||
nets.emplace_back(net);
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 gatecat <gatecat@ds0.me>
|
||||
* Copyright (C) 2022 YRabbit <rabbit@yrabbit.cyou>
|
||||
*
|
||||
* 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 GOWIN_GLOBALS_H
|
||||
#define GOWIN_GLOBALS_H
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
class GowinGlobalRouter
|
||||
{
|
||||
public:
|
||||
GowinGlobalRouter() {}
|
||||
|
||||
private:
|
||||
// wire -> clock#
|
||||
dict<WireId, int> used_wires;
|
||||
|
||||
// ordered nets
|
||||
struct globalnet_t
|
||||
{
|
||||
IdString name;
|
||||
int clock_ports;
|
||||
BelId clock_bel;
|
||||
WireId clock_wire; // clock wire if there is one
|
||||
int clock; // clock #
|
||||
|
||||
globalnet_t()
|
||||
{
|
||||
name = IdString();
|
||||
clock_ports = 0;
|
||||
clock_bel = BelId();
|
||||
clock_wire = WireId();
|
||||
clock = -1;
|
||||
}
|
||||
globalnet_t(IdString _name)
|
||||
{
|
||||
name = _name;
|
||||
clock_ports = 0;
|
||||
clock_bel = BelId();
|
||||
clock_wire = WireId();
|
||||
clock = -1;
|
||||
}
|
||||
|
||||
// sort
|
||||
bool operator<(const globalnet_t &other) const
|
||||
{
|
||||
if ((clock_wire != WireId()) ^ (other.clock_wire != WireId())) {
|
||||
return !(clock_wire != WireId());
|
||||
}
|
||||
return clock_ports < other.clock_ports;
|
||||
}
|
||||
// search
|
||||
bool operator==(const globalnet_t &other) const { return name == other.name; }
|
||||
};
|
||||
|
||||
// discovered nets
|
||||
std::vector<globalnet_t> nets;
|
||||
|
||||
bool is_clock_port(PortRef const &user);
|
||||
std::pair<WireId, BelId> clock_src(Context *ctx, PortRef const &driver);
|
||||
void gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets);
|
||||
IdString route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, pool<IdString> &used_pips,
|
||||
pool<IdString> &undo_wires);
|
||||
void route_net(Context *ctx, globalnet_t const &net);
|
||||
|
||||
public:
|
||||
void mark_globals(Context *ctx);
|
||||
void route_globals(Context *ctx);
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif // GOWIN_GLOBALS_H
|
129
gowin/main.cc
129
gowin/main.cc
@ -1,129 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2020 Pepijn de Vos <pepijn@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
|
||||
|
||||
#include <fstream>
|
||||
#include <locale>
|
||||
#include <regex>
|
||||
#include "command.h"
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "timing.h"
|
||||
|
||||
USING_NEXTPNR_NAMESPACE
|
||||
|
||||
class GowinCommandHandler : public CommandHandler
|
||||
{
|
||||
public:
|
||||
GowinCommandHandler(int argc, char **argv);
|
||||
virtual ~GowinCommandHandler(){};
|
||||
std::unique_ptr<Context> createContext(dict<std::string, Property> &values) override;
|
||||
void setupArchContext(Context *ctx) override{};
|
||||
void customAfterLoad(Context *ctx) override;
|
||||
|
||||
protected:
|
||||
po::options_description getArchOptions() override;
|
||||
};
|
||||
|
||||
GowinCommandHandler::GowinCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
|
||||
|
||||
po::options_description GowinCommandHandler::getArchOptions()
|
||||
{
|
||||
po::options_description specific("Architecture specific options");
|
||||
specific.add_options()("device", po::value<std::string>(), "device name");
|
||||
specific.add_options()("family", po::value<std::string>(), "family name");
|
||||
specific.add_options()("cst", po::value<std::string>(), "physical constraints file");
|
||||
specific.add_options()("enable-globals", "enable separate routing of the clocks");
|
||||
specific.add_options()("disable-globals", "disable separate routing of the clocks");
|
||||
specific.add_options()("enable-auto-longwires", "automatic detection and routing of long wires");
|
||||
return specific;
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> GowinCommandHandler::createContext(dict<std::string, Property> &values)
|
||||
{
|
||||
if (!vm.count("device")) {
|
||||
log_error("The device must be specified\n");
|
||||
}
|
||||
|
||||
std::regex devicere = std::regex("GW1N([SZ]?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*");
|
||||
std::smatch match;
|
||||
std::string device = vm["device"].as<std::string>();
|
||||
bool GW2 = device == "GW2A-LV18PG256C8/I7";
|
||||
|
||||
if (!GW2 && !std::regex_match(device, match, devicere)) {
|
||||
log_error("Invalid device %s\n", device.c_str());
|
||||
}
|
||||
ArchArgs chipArgs;
|
||||
chipArgs.gui = vm.count("gui") != 0;
|
||||
if (vm.count("family")) {
|
||||
chipArgs.family = vm["family"].as<std::string>();
|
||||
} else {
|
||||
if (!GW2) {
|
||||
char buf[36];
|
||||
// GW1N and GW1NR variants share the same database.
|
||||
// Most Gowin devices are a System in Package with some SDRAM wirebonded to a GPIO bank.
|
||||
// However, it appears that the S series with embedded ARM core are unique silicon.
|
||||
snprintf(buf, 36, "GW1N%s-%s", match[1].str().c_str(), match[3].str().c_str());
|
||||
chipArgs.family = buf;
|
||||
} else {
|
||||
chipArgs.family = "GW2A-18";
|
||||
}
|
||||
}
|
||||
if (!GW2) {
|
||||
chipArgs.partnumber = match[0];
|
||||
} else {
|
||||
chipArgs.partnumber = device;
|
||||
}
|
||||
|
||||
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
||||
// routing options
|
||||
ctx->settings[ctx->id("arch.enable-globals")] = 1;
|
||||
ctx->settings[ctx->id("arch.enable-auto-longwires")] = 0;
|
||||
if (vm.count("disable-globals")) {
|
||||
ctx->settings[ctx->id("arch.enable-globals")] = 0;
|
||||
}
|
||||
if (vm.count("enable-auto-longwires")) {
|
||||
ctx->settings[ctx->id("arch.enable-auto-longwires")] = 1;
|
||||
}
|
||||
// XXX disable clock lines for now
|
||||
if (GW2) {
|
||||
ctx->settings[ctx->id("arch.enable-globals")] = 0;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void GowinCommandHandler::customAfterLoad(Context *ctx)
|
||||
{
|
||||
if (vm.count("cst")) {
|
||||
std::string filename = vm["cst"].as<std::string>();
|
||||
std::ifstream in(filename);
|
||||
if (!in)
|
||||
log_error("Failed to open input CST file %s.\n", filename.c_str());
|
||||
ctx->read_cst(in);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
GowinCommandHandler handler(argc, argv);
|
||||
return handler.exec();
|
||||
}
|
||||
#endif
|
1919
gowin/pack.cc
1919
gowin/pack.cc
File diff suppressed because it is too large
Load Diff
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Miodrag Milanovic <micko@yosyshq.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"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cst.h"
|
||||
|
||||
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
|
||||
: BaseMainWindow(std::move(context), handler, parent)
|
||||
{
|
||||
initMainResource();
|
||||
std::string title = "nextpnr-gowin - [EMPTY]";
|
||||
setWindowTitle(title.c_str());
|
||||
connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext);
|
||||
createMenu();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {}
|
||||
|
||||
void MainWindow::newContext(Context *ctx)
|
||||
{
|
||||
std::string title = "nextpnr-gowin - " + ctx->getChipName();
|
||||
setWindowTitle(title.c_str());
|
||||
}
|
||||
|
||||
void MainWindow::load_cst(std::string filename)
|
||||
{
|
||||
disableActions();
|
||||
std::ifstream f(filename);
|
||||
if (read_cst(ctx.get(), f)) {
|
||||
log("Loading CST successful.\n");
|
||||
actionPack->setEnabled(true);
|
||||
} else {
|
||||
actionLoadCST->setEnabled(true);
|
||||
log("Loading CST failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::createMenu()
|
||||
{
|
||||
actionLoadCST = new QAction("Open CST", this);
|
||||
actionLoadCST->setIcon(QIcon(":/icons/resources/open_cst.png"));
|
||||
actionLoadCST->setStatusTip("Open CST file");
|
||||
actionLoadCST->setEnabled(false);
|
||||
connect(actionLoadCST, &QAction::triggered, this, &MainWindow::open_cst);
|
||||
|
||||
// Add actions in menus
|
||||
mainActionBar->addSeparator();
|
||||
mainActionBar->addAction(actionLoadCST);
|
||||
|
||||
menuDesign->addSeparator();
|
||||
menuDesign->addAction(actionLoadCST);
|
||||
}
|
||||
|
||||
void MainWindow::new_proj() {}
|
||||
|
||||
void MainWindow::open_cst()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this, QString("Open CST"), QString(), QString("*.cst"));
|
||||
if (!fileName.isEmpty()) {
|
||||
load_cst(fileName.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onDisableActions() { actionLoadCST->setEnabled(false); }
|
||||
|
||||
void MainWindow::onUpdateActions()
|
||||
{
|
||||
if (ctx->settings.find(ctx->id("synth")) != ctx->settings.end()) {
|
||||
actionLoadCST->setEnabled(true);
|
||||
}
|
||||
if (ctx->settings.find(ctx->id("cst")) != ctx->settings.end()) {
|
||||
actionLoadCST->setEnabled(false);
|
||||
}
|
||||
if (ctx->settings.find(ctx->id("pack")) != ctx->settings.end()) {
|
||||
actionLoadCST->setEnabled(false);
|
||||
}
|
||||
}
|
||||
NEXTPNR_NAMESPACE_END
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Miodrag Milanovic <micko@yosyshq.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(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
|
||||
virtual ~MainWindow();
|
||||
|
||||
public:
|
||||
void createMenu();
|
||||
|
||||
protected:
|
||||
void onDisableActions() override;
|
||||
void onUpdateActions() override;
|
||||
|
||||
void load_cst(std::string filename);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void new_proj() override;
|
||||
|
||||
void open_cst();
|
||||
|
||||
void newContext(Context *ctx);
|
||||
|
||||
private:
|
||||
QAction *actionLoadCST;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // MAINWINDOW_H
|
@ -1,5 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/icons">
|
||||
<file>resources/open_cst.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
Binary file not shown.
Before Width: | Height: | Size: 9.2 KiB |
Loading…
Reference in New Issue
Block a user