Merge branch 'xc7' into xc7_gui
This commit is contained in:
commit
ede0e93206
12
.cirrus.yml
Normal file
12
.cirrus.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
task:
|
||||||
|
name: build-test-ubuntu1604
|
||||||
|
container:
|
||||||
|
cpu: 4
|
||||||
|
memory: 16
|
||||||
|
dockerfile: .cirrus/Dockerfile.ubuntu16.04
|
||||||
|
|
||||||
|
build_script: mkdir build && cd build && cmake .. -DARCH=all -DTRELLIS_ROOT=/usr/local/src/prjtrellis -DBUILD_TESTS=on && make -j $(nproc)
|
||||||
|
test_generic_script: cd build && ./nextpnr-generic-test
|
||||||
|
test_ice40_script: cd build && ./nextpnr-ice40-test
|
||||||
|
smoketest_ice40_script: export NEXTPNR=$(pwd)/build/nextpnr-ice40 && cd ice40/smoketest/attosoc && ./smoketest.sh
|
||||||
|
test_ecp5_script: cd build && ./nextpnr-ecp5-test
|
55
.cirrus/Dockerfile.ubuntu16.04
Normal file
55
.cirrus/Dockerfile.ubuntu16.04
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
FROM ubuntu:xenial-20181113
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN set -e -x ;\
|
||||||
|
apt-get -y update ;\
|
||||||
|
apt-get -y upgrade ;\
|
||||||
|
apt-get -y install \
|
||||||
|
build-essential autoconf cmake clang bison wget flex gperf \
|
||||||
|
libreadline-dev gawk tcl-dev libffi-dev graphviz xdot python3-dev \
|
||||||
|
libboost-all-dev qt5-default git libftdi-dev pkg-config
|
||||||
|
|
||||||
|
RUN set -e -x ;\
|
||||||
|
mkdir -p /usr/local/src ;\
|
||||||
|
cd /usr/local/src ;\
|
||||||
|
git clone --recursive https://github.com/steveicarus/iverilog.git ;\
|
||||||
|
cd iverilog ;\
|
||||||
|
git reset --hard 172d7eb0a3665f89b91d601b5912c33acedc81e5 ;\
|
||||||
|
sh autoconf.sh ;\
|
||||||
|
./configure ;\
|
||||||
|
make -j $(nproc) ;\
|
||||||
|
make install ;\
|
||||||
|
rm -rf /usr/local/src/iverilog
|
||||||
|
|
||||||
|
RUN set -e -x ;\
|
||||||
|
mkdir -p /usr/local/src ;\
|
||||||
|
cd /usr/local/src ;\
|
||||||
|
git clone --recursive https://github.com/cliffordwolf/icestorm.git ;\
|
||||||
|
cd icestorm ;\
|
||||||
|
git reset --hard 9671b760f84ca4006f0ef101a3e3b201df4eabb5 ;\
|
||||||
|
make -j $(nproc) ;\
|
||||||
|
make install
|
||||||
|
|
||||||
|
RUN set -e -x ;\
|
||||||
|
mkdir -p /usr/local/src ;\
|
||||||
|
cd /usr/local/src ;\
|
||||||
|
git clone --recursive https://github.com/YosysHQ/yosys.git ;\
|
||||||
|
cd yosys ;\
|
||||||
|
git reset --hard 47a5dfdaa4bd7d400c6e3d58476de80904df460d ;\
|
||||||
|
make -j $(nproc) ;\
|
||||||
|
make install ;\
|
||||||
|
rm -rf /usr/local/src/yosys
|
||||||
|
|
||||||
|
RUN set -e -x ;\
|
||||||
|
mkdir -p /usr/local/src ;\
|
||||||
|
cd /usr/local/src ;\
|
||||||
|
git clone --recursive https://github.com/SymbiFlow/prjtrellis.git ;\
|
||||||
|
cd prjtrellis ;\
|
||||||
|
git reset --hard de035a6e5e5818804a66b9408f0ad381b10957db ;\
|
||||||
|
cd libtrellis ;\
|
||||||
|
cmake -DCMAKE_INSTALL_PREFIX=/usr . ;\
|
||||||
|
make -j $(nproc) ;\
|
||||||
|
make install
|
||||||
|
|
||||||
|
|
@ -153,3 +153,25 @@ void pyinterpreter_release()
|
|||||||
{
|
{
|
||||||
PyEval_ReleaseThread(m_threadState);
|
PyEval_ReleaseThread(m_threadState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string pyinterpreter_execute_file(const char *python_file, int *errorCode)
|
||||||
|
{
|
||||||
|
PyEval_AcquireThread(m_threadState);
|
||||||
|
*errorCode = 0;
|
||||||
|
std::string res;
|
||||||
|
FILE *fp = fopen(python_file, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
*errorCode = 1;
|
||||||
|
res = "Fatal error: file not found " + std::string(python_file) + "\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyRun_SimpleFile(fp, python_file)==-1) {
|
||||||
|
*errorCode = 1;
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
res = redirector_take_output(m_threadState);
|
||||||
|
|
||||||
|
PyEval_ReleaseThread(m_threadState);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@ -33,4 +33,5 @@ void pyinterpreter_initialize();
|
|||||||
void pyinterpreter_finalize();
|
void pyinterpreter_finalize();
|
||||||
void pyinterpreter_aquire();
|
void pyinterpreter_aquire();
|
||||||
void pyinterpreter_release();
|
void pyinterpreter_release();
|
||||||
|
std::string pyinterpreter_execute_file(const char *python_file, int *errorCode);
|
||||||
#endif // PYINTERPRETER_H
|
#endif // PYINTERPRETER_H
|
||||||
|
@ -48,8 +48,8 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
|||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996 /wd4127")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996 /wd4127")
|
||||||
else()
|
else()
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -fPIC -ggdb")
|
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -fPIC -ggdb -pipe")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O3 -g")
|
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O3 -g -pipe")
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_DEFIN)
|
set(CMAKE_DEFIN)
|
||||||
|
|
||||||
|
@ -49,8 +49,9 @@ Getting started
|
|||||||
|
|
||||||
### nextpnr-ice40
|
### nextpnr-ice40
|
||||||
|
|
||||||
To build the iCE40 version of nextpnr, install [icestorm](http://www.clifford.at/icestorm/) with chipdbs installed in `/usr/local/share/icebox`
|
To build the iCE40 version of nextpnr, install [icestorm](http://www.clifford.at/icestorm/) with chipdbs installed in `/usr/local/share/icebox`,
|
||||||
(or another location, which should be passed as -DICEBOX_ROOT=/path/to/icebox to CMake).
|
or another location, which should be passed as `-DICEBOX_ROOT=/path/to/share/icebox` (ensure to point it to `share/icebox` and not where the
|
||||||
|
icebox binaries are installed) to CMake.
|
||||||
Then build and install `nextpnr-ice40` using the following commands:
|
Then build and install `nextpnr-ice40` using the following commands:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -51,6 +51,7 @@ std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F
|
|||||||
CellChain chain;
|
CellChain chain;
|
||||||
CellInfo *end = start;
|
CellInfo *end = start;
|
||||||
while (end != nullptr) {
|
while (end != nullptr) {
|
||||||
|
if (chained.insert(end->name).second)
|
||||||
chain.cells.push_back(end);
|
chain.cells.push_back(end);
|
||||||
end = get_next(ctx, end);
|
end = get_next(ctx, end);
|
||||||
}
|
}
|
||||||
|
@ -131,9 +131,9 @@ void CommandHandler::setupContext(Context *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("quiet")) {
|
if (vm.count("quiet")) {
|
||||||
log_streams.push_back(std::make_pair(&std::cerr, LogLevel::WARNING));
|
log_streams.push_back(std::make_pair(&std::cerr, LogLevel::WARNING_MSG));
|
||||||
} else {
|
} else {
|
||||||
log_streams.push_back(std::make_pair(&std::cerr, LogLevel::LOG));
|
log_streams.push_back(std::make_pair(&std::cerr, LogLevel::LOG_MSG));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("log")) {
|
if (vm.count("log")) {
|
||||||
@ -141,7 +141,7 @@ void CommandHandler::setupContext(Context *ctx)
|
|||||||
logfile = std::ofstream(logfilename);
|
logfile = std::ofstream(logfilename);
|
||||||
if (!logfile)
|
if (!logfile)
|
||||||
log_error("Failed to open log file '%s' for writing.\n", logfilename.c_str());
|
log_error("Failed to open log file '%s' for writing.\n", logfilename.c_str());
|
||||||
log_streams.push_back(std::make_pair(&logfile, LogLevel::LOG));
|
log_streams.push_back(std::make_pair(&logfile, LogLevel::LOG_MSG));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("force")) {
|
if (vm.count("force")) {
|
||||||
@ -285,8 +285,8 @@ void CommandHandler::conflicting_options(const boost::program_options::variables
|
|||||||
|
|
||||||
void CommandHandler::printFooter()
|
void CommandHandler::printFooter()
|
||||||
{
|
{
|
||||||
int warning_count = get_or_default(message_count_by_level, LogLevel::WARNING, 0),
|
int warning_count = get_or_default(message_count_by_level, LogLevel::WARNING_MSG, 0),
|
||||||
error_count = get_or_default(message_count_by_level, LogLevel::ERROR, 0);
|
error_count = get_or_default(message_count_by_level, LogLevel::ERROR_MSG, 0);
|
||||||
if (warning_count > 0 || error_count > 0)
|
if (warning_count > 0 || error_count > 0)
|
||||||
log_always("%d warning%s, %d error%s\n", warning_count, warning_count == 1 ? "" : "s", error_count,
|
log_always("%d warning%s, %d error%s\n", warning_count, warning_count == 1 ? "" : "s", error_count,
|
||||||
error_count == 1 ? "" : "s");
|
error_count == 1 ? "" : "s");
|
||||||
|
@ -84,7 +84,7 @@ std::string vstringf(const char *fmt, va_list ap)
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
void logv(const char *format, va_list ap, LogLevel level = LogLevel::LOG)
|
void logv(const char *format, va_list ap, LogLevel level = LogLevel::LOG_MSG)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Trim newlines from the beginning
|
// Trim newlines from the beginning
|
||||||
@ -132,7 +132,7 @@ void log_always(const char *format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
logv(format, ap, LogLevel::ALWAYS);
|
logv(format, ap, LogLevel::ALWAYS_MSG);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ void log(const char *format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
logv(format, ap, LogLevel::LOG);
|
logv(format, ap, LogLevel::LOG_MSG);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ void log_info(const char *format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
logv_prefixed("Info: ", format, ap, LogLevel::INFO);
|
logv_prefixed("Info: ", format, ap, LogLevel::INFO_MSG);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ void log_warning(const char *format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
logv_prefixed("Warning: ", format, ap, LogLevel::WARNING);
|
logv_prefixed("Warning: ", format, ap, LogLevel::WARNING_MSG);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ void log_error(const char *format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR);
|
logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR_MSG);
|
||||||
|
|
||||||
if (log_error_atexit)
|
if (log_error_atexit)
|
||||||
log_error_atexit();
|
log_error_atexit();
|
||||||
@ -184,7 +184,7 @@ void log_nonfatal_error(const char *format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR);
|
logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR_MSG);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
had_nonfatal_error = true;
|
had_nonfatal_error = true;
|
||||||
}
|
}
|
||||||
|
10
common/log.h
10
common/log.h
@ -44,11 +44,11 @@ struct log_execution_error_exception
|
|||||||
|
|
||||||
enum class LogLevel
|
enum class LogLevel
|
||||||
{
|
{
|
||||||
LOG,
|
LOG_MSG,
|
||||||
INFO,
|
INFO_MSG,
|
||||||
WARNING,
|
WARNING_MSG,
|
||||||
ERROR,
|
ERROR_MSG,
|
||||||
ALWAYS
|
ALWAYS_MSG
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
|
extern std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
|
||||||
|
@ -37,6 +37,8 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
|
|||||||
if (driver_gb)
|
if (driver_gb)
|
||||||
return 0;
|
return 0;
|
||||||
int clock_count;
|
int clock_count;
|
||||||
|
if (ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) == TMG_IGNORE)
|
||||||
|
return 0;
|
||||||
bool timing_driven = ctx->timing_driven && type == MetricType::COST &&
|
bool timing_driven = ctx->timing_driven && type == MetricType::COST &&
|
||||||
ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE;
|
ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE;
|
||||||
delay_t negative_slack = 0;
|
delay_t negative_slack = 0;
|
||||||
|
@ -512,6 +512,21 @@ struct Router1
|
|||||||
WireId next_wire = ctx->getPipDstWire(pip);
|
WireId next_wire = ctx->getPipDstWire(pip);
|
||||||
next_delay += ctx->getWireDelay(next_wire).maxDelay();
|
next_delay += ctx->getWireDelay(next_wire).maxDelay();
|
||||||
|
|
||||||
|
#ifdef ARCH_XC7
|
||||||
|
// For BUFG routing, do not exit the global network until the destination tile is reached
|
||||||
|
if (ctx->isGlobalNet(net_info)) {
|
||||||
|
if (torc_info->wire_is_global[src_wire.index] && !torc_info->wire_is_global[next_wire.index]) {
|
||||||
|
const auto &arc = torc_info->pip_to_arc[pip.index];
|
||||||
|
const auto &next_tw = arc.getSinkTilewire();
|
||||||
|
const auto &next_loc = torc_info->tile_to_xy[next_tw.getTileIndex()];
|
||||||
|
const auto &dst_tw = torc_info->wire_to_tilewire[dst_wire.index];
|
||||||
|
const auto &dst_loc = torc_info->tile_to_xy[dst_tw.getTileIndex()];
|
||||||
|
if (next_loc.second != dst_loc.second || next_loc.first != dst_loc.first)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
WireId conflictWireWire = WireId(), conflictPipWire = WireId();
|
WireId conflictWireWire = WireId(), conflictPipWire = WireId();
|
||||||
NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr;
|
NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr;
|
||||||
|
|
||||||
@ -618,7 +633,11 @@ struct Router1
|
|||||||
next_qw.penalty = next_penalty;
|
next_qw.penalty = next_penalty;
|
||||||
next_qw.bonus = next_bonus;
|
next_qw.bonus = next_bonus;
|
||||||
if (cfg.useEstimate) {
|
if (cfg.useEstimate) {
|
||||||
|
#ifdef ARCH_XC7
|
||||||
|
next_qw.togo = ctx->estimateDelay(next_wire, imux_wire);
|
||||||
|
#else
|
||||||
next_qw.togo = ctx->estimateDelay(next_wire, dst_wire);
|
next_qw.togo = ctx->estimateDelay(next_wire, dst_wire);
|
||||||
|
#endif
|
||||||
delay_t this_est = next_qw.delay + next_qw.togo;
|
delay_t this_est = next_qw.delay + next_qw.togo;
|
||||||
if (this_est / 2 - cfg.estimatePrecision > best_est)
|
if (this_est / 2 - cfg.estimatePrecision > best_est)
|
||||||
continue;
|
continue;
|
||||||
|
@ -9,7 +9,7 @@ bool check_all_nets_driven(Context *ctx)
|
|||||||
{
|
{
|
||||||
const bool debug = false;
|
const bool debug = false;
|
||||||
|
|
||||||
log_info("Rule checker, Verifying pre-placed design\n");
|
log_info("Rule checker, verifying imported design\n");
|
||||||
|
|
||||||
for (auto &cell_entry : ctx->cells) {
|
for (auto &cell_entry : ctx->cells) {
|
||||||
CellInfo *cell = cell_entry.second.get();
|
CellInfo *cell = cell_entry.second.get();
|
||||||
|
219
common/timing.cc
219
common/timing.cc
@ -86,6 +86,7 @@ struct CriticalPath
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unordered_map<ClockPair, CriticalPath> CriticalPathMap;
|
typedef std::unordered_map<ClockPair, CriticalPath> CriticalPathMap;
|
||||||
|
typedef std::unordered_map<IdString, NetCriticalityInfo> NetCriticalityMap;
|
||||||
|
|
||||||
struct Timing
|
struct Timing
|
||||||
{
|
{
|
||||||
@ -95,6 +96,7 @@ struct Timing
|
|||||||
delay_t min_slack;
|
delay_t min_slack;
|
||||||
CriticalPathMap *crit_path;
|
CriticalPathMap *crit_path;
|
||||||
DelayFrequency *slack_histogram;
|
DelayFrequency *slack_histogram;
|
||||||
|
NetCriticalityMap *net_crit;
|
||||||
IdString async_clock;
|
IdString async_clock;
|
||||||
|
|
||||||
struct TimingData
|
struct TimingData
|
||||||
@ -105,13 +107,15 @@ struct Timing
|
|||||||
unsigned max_path_length = 0;
|
unsigned max_path_length = 0;
|
||||||
delay_t min_remaining_budget;
|
delay_t min_remaining_budget;
|
||||||
bool false_startpoint = false;
|
bool false_startpoint = false;
|
||||||
|
std::vector<delay_t> min_required;
|
||||||
std::unordered_map<ClockEvent, delay_t> arrival_time;
|
std::unordered_map<ClockEvent, delay_t> arrival_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
Timing(Context *ctx, bool net_delays, bool update, CriticalPathMap *crit_path = nullptr,
|
Timing(Context *ctx, bool net_delays, bool update, CriticalPathMap *crit_path = nullptr,
|
||||||
DelayFrequency *slack_histogram = nullptr)
|
DelayFrequency *slack_histogram = nullptr, NetCriticalityMap *net_crit = nullptr)
|
||||||
: ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->target_freq),
|
: ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->target_freq),
|
||||||
crit_path(crit_path), slack_histogram(slack_histogram), async_clock(ctx->id("$async$"))
|
crit_path(crit_path), slack_histogram(slack_histogram), net_crit(net_crit),
|
||||||
|
async_clock(ctx->id("$async$"))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,7 +414,6 @@ struct Timing
|
|||||||
while (crit_net) {
|
while (crit_net) {
|
||||||
const PortInfo *crit_ipin = nullptr;
|
const PortInfo *crit_ipin = nullptr;
|
||||||
delay_t max_arrival = std::numeric_limits<delay_t>::min();
|
delay_t max_arrival = std::numeric_limits<delay_t>::min();
|
||||||
|
|
||||||
// Look at all input ports on its driving cell
|
// Look at all input ports on its driving cell
|
||||||
for (const auto &port : crit_net->driver.cell->ports) {
|
for (const auto &port : crit_net->driver.cell->ports) {
|
||||||
if (port.second.type != PORT_IN || !port.second.net)
|
if (port.second.type != PORT_IN || !port.second.net)
|
||||||
@ -424,14 +427,21 @@ struct Timing
|
|||||||
int port_clocks;
|
int port_clocks;
|
||||||
TimingPortClass portClass =
|
TimingPortClass portClass =
|
||||||
ctx->getPortTimingClass(crit_net->driver.cell, port.first, port_clocks);
|
ctx->getPortTimingClass(crit_net->driver.cell, port.first, port_clocks);
|
||||||
if (portClass == TMG_REGISTER_INPUT || portClass == TMG_CLOCK_INPUT ||
|
if (portClass == TMG_CLOCK_INPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE ||
|
||||||
portClass == TMG_ENDPOINT || portClass == TMG_IGNORE)
|
portClass == TMG_REGISTER_INPUT)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// And find the fanin net with the latest arrival time
|
// And find the fanin net with the latest arrival time
|
||||||
if (net_data.count(port.second.net) &&
|
if (net_data.count(port.second.net) &&
|
||||||
net_data.at(port.second.net).count(crit_pair.first.start)) {
|
net_data.at(port.second.net).count(crit_pair.first.start)) {
|
||||||
const auto net_arrival = net_data.at(port.second.net).at(crit_pair.first.start).max_arrival;
|
auto net_arrival = net_data.at(port.second.net).at(crit_pair.first.start).max_arrival;
|
||||||
|
if (net_delays) {
|
||||||
|
for (auto &user : port.second.net->users)
|
||||||
|
if (user.port == port.first && user.cell == crit_net->driver.cell) {
|
||||||
|
net_arrival += ctx->getNetinfoRouteDelay(port.second.net, user);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
net_arrival += comb_delay.maxDelay();
|
||||||
if (net_arrival > max_arrival) {
|
if (net_arrival > max_arrival) {
|
||||||
max_arrival = net_arrival;
|
max_arrival = net_arrival;
|
||||||
crit_ipin = &port.second;
|
crit_ipin = &port.second;
|
||||||
@ -441,7 +451,6 @@ struct Timing
|
|||||||
|
|
||||||
if (!crit_ipin)
|
if (!crit_ipin)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Now convert PortInfo* into a PortRef*
|
// Now convert PortInfo* into a PortRef*
|
||||||
for (auto &usr : crit_ipin->net->users) {
|
for (auto &usr : crit_ipin->net->users) {
|
||||||
if (usr.cell->name == crit_net->driver.cell->name && usr.port == crit_ipin->name) {
|
if (usr.cell->name == crit_net->driver.cell->name && usr.port == crit_ipin->name) {
|
||||||
@ -454,6 +463,180 @@ struct Timing
|
|||||||
std::reverse(cp_ports.begin(), cp_ports.end());
|
std::reverse(cp_ports.begin(), cp_ports.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (net_crit) {
|
||||||
|
NPNR_ASSERT(crit_path);
|
||||||
|
// Go through in reverse topographical order to set required times
|
||||||
|
for (auto net : boost::adaptors::reverse(topographical_order)) {
|
||||||
|
if (!net_data.count(net))
|
||||||
|
continue;
|
||||||
|
auto &nd_map = net_data.at(net);
|
||||||
|
for (auto &startdomain : nd_map) {
|
||||||
|
auto &nd = startdomain.second;
|
||||||
|
if (nd.false_startpoint)
|
||||||
|
continue;
|
||||||
|
if (startdomain.first.clock == async_clock)
|
||||||
|
continue;
|
||||||
|
if (nd.min_required.empty())
|
||||||
|
nd.min_required.resize(net->users.size(), std::numeric_limits<delay_t>::max());
|
||||||
|
delay_t net_min_required = std::numeric_limits<delay_t>::max();
|
||||||
|
for (size_t i = 0; i < net->users.size(); i++) {
|
||||||
|
auto &usr = net->users.at(i);
|
||||||
|
auto net_delay = ctx->getNetinfoRouteDelay(net, usr);
|
||||||
|
int port_clocks;
|
||||||
|
TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, port_clocks);
|
||||||
|
if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT) {
|
||||||
|
auto process_endpoint = [&](IdString clksig, ClockEdge edge, delay_t setup) {
|
||||||
|
delay_t period;
|
||||||
|
// Set default period
|
||||||
|
if (edge == startdomain.first.edge) {
|
||||||
|
period = clk_period;
|
||||||
|
} else {
|
||||||
|
period = clk_period / 2;
|
||||||
|
}
|
||||||
|
if (clksig != async_clock) {
|
||||||
|
if (ctx->nets.at(clksig)->clkconstr) {
|
||||||
|
if (edge == startdomain.first.edge) {
|
||||||
|
// same edge
|
||||||
|
period = ctx->nets.at(clksig)->clkconstr->period.minDelay();
|
||||||
|
} else if (edge == RISING_EDGE) {
|
||||||
|
// falling -> rising
|
||||||
|
period = ctx->nets.at(clksig)->clkconstr->low.minDelay();
|
||||||
|
} else if (edge == FALLING_EDGE) {
|
||||||
|
// rising -> falling
|
||||||
|
period = ctx->nets.at(clksig)->clkconstr->high.minDelay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nd.min_required.at(i) = std::min(period - setup, nd.min_required.at(i));
|
||||||
|
};
|
||||||
|
if (portClass == TMG_REGISTER_INPUT) {
|
||||||
|
for (int j = 0; j < port_clocks; j++) {
|
||||||
|
TimingClockingInfo clkInfo = ctx->getPortClockingInfo(usr.cell, usr.port, j);
|
||||||
|
const NetInfo *clknet = get_net_or_empty(usr.cell, clkInfo.clock_port);
|
||||||
|
IdString clksig = clknet ? clknet->name : async_clock;
|
||||||
|
process_endpoint(clksig, clknet ? clkInfo.edge : RISING_EDGE,
|
||||||
|
clkInfo.setup.maxDelay());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
process_endpoint(async_clock, RISING_EDGE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
net_min_required = std::min(net_min_required, nd.min_required.at(i) - net_delay);
|
||||||
|
}
|
||||||
|
PortRef &drv = net->driver;
|
||||||
|
if (drv.cell == nullptr)
|
||||||
|
continue;
|
||||||
|
for (const auto &port : drv.cell->ports) {
|
||||||
|
if (port.second.type != PORT_IN || !port.second.net)
|
||||||
|
continue;
|
||||||
|
DelayInfo comb_delay;
|
||||||
|
bool is_path = ctx->getCellDelay(drv.cell, port.first, drv.port, comb_delay);
|
||||||
|
if (!is_path)
|
||||||
|
continue;
|
||||||
|
int cc;
|
||||||
|
auto pclass = ctx->getPortTimingClass(drv.cell, port.first, cc);
|
||||||
|
if (pclass != TMG_COMB_INPUT)
|
||||||
|
continue;
|
||||||
|
NetInfo *sink_net = port.second.net;
|
||||||
|
if (net_data.count(sink_net) && net_data.at(sink_net).count(startdomain.first)) {
|
||||||
|
auto &sink_nd = net_data.at(sink_net).at(startdomain.first);
|
||||||
|
if (sink_nd.min_required.empty())
|
||||||
|
sink_nd.min_required.resize(sink_net->users.size(),
|
||||||
|
std::numeric_limits<delay_t>::max());
|
||||||
|
for (size_t i = 0; i < sink_net->users.size(); i++) {
|
||||||
|
auto &user = sink_net->users.at(i);
|
||||||
|
if (user.cell == drv.cell && user.port == port.first) {
|
||||||
|
sink_nd.min_required.at(i) = net_min_required - comb_delay.maxDelay();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::unordered_map<ClockEvent, delay_t> worst_slack;
|
||||||
|
|
||||||
|
// Assign slack values
|
||||||
|
for (auto &net_entry : net_data) {
|
||||||
|
const NetInfo *net = net_entry.first;
|
||||||
|
for (auto &startdomain : net_entry.second) {
|
||||||
|
auto &nd = startdomain.second;
|
||||||
|
if (startdomain.first.clock == async_clock)
|
||||||
|
continue;
|
||||||
|
if (nd.min_required.empty())
|
||||||
|
continue;
|
||||||
|
auto &nc = (*net_crit)[net->name];
|
||||||
|
if (nc.slack.empty())
|
||||||
|
nc.slack.resize(net->users.size(), std::numeric_limits<delay_t>::max());
|
||||||
|
#if 0
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info("Net %s cd %s\n", net->name.c_str(ctx), startdomain.first.clock.c_str(ctx));
|
||||||
|
#endif
|
||||||
|
for (size_t i = 0; i < net->users.size(); i++) {
|
||||||
|
delay_t slack = nd.min_required.at(i) -
|
||||||
|
(nd.max_arrival + ctx->getNetinfoRouteDelay(net, net->users.at(i)));
|
||||||
|
#if 0
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" user %s.%s required %.02fns arrival %.02f route %.02f slack %.02f\n",
|
||||||
|
net->users.at(i).cell->name.c_str(ctx), net->users.at(i).port.c_str(ctx),
|
||||||
|
ctx->getDelayNS(nd.min_required.at(i)), ctx->getDelayNS(nd.max_arrival),
|
||||||
|
ctx->getDelayNS(ctx->getNetinfoRouteDelay(net, net->users.at(i))), ctx->getDelayNS(slack));
|
||||||
|
#endif
|
||||||
|
if (worst_slack.count(startdomain.first))
|
||||||
|
worst_slack.at(startdomain.first) = std::min(worst_slack.at(startdomain.first), slack);
|
||||||
|
else
|
||||||
|
worst_slack[startdomain.first] = slack;
|
||||||
|
nc.slack.at(i) = slack;
|
||||||
|
}
|
||||||
|
if (ctx->debug)
|
||||||
|
log_break();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Assign criticality values
|
||||||
|
for (auto &net_entry : net_data) {
|
||||||
|
const NetInfo *net = net_entry.first;
|
||||||
|
for (auto &startdomain : net_entry.second) {
|
||||||
|
if (startdomain.first.clock == async_clock)
|
||||||
|
continue;
|
||||||
|
auto &nd = startdomain.second;
|
||||||
|
if (nd.min_required.empty())
|
||||||
|
continue;
|
||||||
|
auto &nc = (*net_crit)[net->name];
|
||||||
|
if (nc.slack.empty())
|
||||||
|
continue;
|
||||||
|
if (nc.criticality.empty())
|
||||||
|
nc.criticality.resize(net->users.size(), 0);
|
||||||
|
// Only consider intra-clock paths for criticality
|
||||||
|
if (!crit_path->count(ClockPair{startdomain.first, startdomain.first}))
|
||||||
|
continue;
|
||||||
|
delay_t dmax = crit_path->at(ClockPair{startdomain.first, startdomain.first}).path_delay;
|
||||||
|
for (size_t i = 0; i < net->users.size(); i++) {
|
||||||
|
float criticality = 1.0f - (float(nc.slack.at(i) - worst_slack.at(startdomain.first)) / dmax);
|
||||||
|
nc.criticality.at(i) = criticality;
|
||||||
|
}
|
||||||
|
nc.max_path_length = nd.max_path_length;
|
||||||
|
nc.cd_worst_slack = worst_slack.at(startdomain.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
if (ctx->debug) {
|
||||||
|
for (auto &nc : *net_crit) {
|
||||||
|
NetInfo *net = ctx->nets.at(nc.first).get();
|
||||||
|
log_info("Net %s maxlen %d worst_slack %.02fns: \n", nc.first.c_str(ctx), nc.second.max_path_length,
|
||||||
|
ctx->getDelayNS(nc.second.cd_worst_slack));
|
||||||
|
if (!nc.second.criticality.empty() && !nc.second.slack.empty()) {
|
||||||
|
for (size_t i = 0; i < net->users.size(); i++) {
|
||||||
|
log_info(" user %s.%s slack %.02fns crit %.03f\n", net->users.at(i).cell->name.c_str(ctx),
|
||||||
|
net->users.at(i).port.c_str(ctx), ctx->getDelayNS(nc.second.slack.at(i)),
|
||||||
|
nc.second.criticality.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_break();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
return min_slack;
|
return min_slack;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,6 +784,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
int port_clocks;
|
int port_clocks;
|
||||||
auto portClass = ctx->getPortTimingClass(front_driver.cell, front_driver.port, port_clocks);
|
auto portClass = ctx->getPortTimingClass(front_driver.cell, front_driver.port, port_clocks);
|
||||||
IdString last_port = front_driver.port;
|
IdString last_port = front_driver.port;
|
||||||
|
int clock_start = -1;
|
||||||
if (portClass == TMG_REGISTER_OUTPUT) {
|
if (portClass == TMG_REGISTER_OUTPUT) {
|
||||||
for (int i = 0; i < port_clocks; i++) {
|
for (int i = 0; i < port_clocks; i++) {
|
||||||
TimingClockingInfo clockInfo = ctx->getPortClockingInfo(front_driver.cell, front_driver.port, i);
|
TimingClockingInfo clockInfo = ctx->getPortClockingInfo(front_driver.cell, front_driver.port, i);
|
||||||
@ -608,8 +792,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
if (clknet != nullptr && clknet->name == clocks.start.clock &&
|
if (clknet != nullptr && clknet->name == clocks.start.clock &&
|
||||||
clockInfo.edge == clocks.start.edge) {
|
clockInfo.edge == clocks.start.edge) {
|
||||||
last_port = clockInfo.clock_port;
|
last_port = clockInfo.clock_port;
|
||||||
total += clockInfo.clockToQ.maxDelay();
|
clock_start = i;
|
||||||
logic_total += clockInfo.clockToQ.maxDelay();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,11 +806,15 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
auto &driver = net->driver;
|
auto &driver = net->driver;
|
||||||
auto driver_cell = driver.cell;
|
auto driver_cell = driver.cell;
|
||||||
DelayInfo comb_delay;
|
DelayInfo comb_delay;
|
||||||
if (last_port == driver.port) {
|
if (clock_start != -1) {
|
||||||
|
auto clockInfo = ctx->getPortClockingInfo(driver_cell, driver.port, clock_start);
|
||||||
|
comb_delay = clockInfo.clockToQ;
|
||||||
|
clock_start = -1;
|
||||||
|
} else if (last_port == driver.port) {
|
||||||
// Case where we start with a STARTPOINT etc
|
// Case where we start with a STARTPOINT etc
|
||||||
comb_delay = ctx->getDelayFromNS(0);
|
comb_delay = ctx->getDelayFromNS(0);
|
||||||
} else {
|
} else {
|
||||||
ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
|
ctx->getCellDelay(driver_cell, last_port, driver.port, comb_delay);
|
||||||
}
|
}
|
||||||
total += comb_delay.maxDelay();
|
total += comb_delay.maxDelay();
|
||||||
logic_total += comb_delay.maxDelay();
|
logic_total += comb_delay.maxDelay();
|
||||||
@ -766,4 +953,12 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void get_criticalities(Context *ctx, NetCriticalityMap *net_crit)
|
||||||
|
{
|
||||||
|
CriticalPathMap crit_paths;
|
||||||
|
net_crit->clear();
|
||||||
|
Timing timing(ctx, true, true, &crit_paths, nullptr, net_crit);
|
||||||
|
timing.walk_paths();
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -32,6 +32,19 @@ void assign_budget(Context *ctx, bool quiet = false);
|
|||||||
void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_fmax = true, bool print_path = false,
|
void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_fmax = true, bool print_path = false,
|
||||||
bool warn_on_failure = false);
|
bool warn_on_failure = false);
|
||||||
|
|
||||||
|
// Data for the timing optimisation algorithm
|
||||||
|
struct NetCriticalityInfo
|
||||||
|
{
|
||||||
|
// One each per user
|
||||||
|
std::vector<delay_t> slack;
|
||||||
|
std::vector<float> criticality;
|
||||||
|
unsigned max_path_length = 0;
|
||||||
|
delay_t cd_worst_slack = std::numeric_limits<delay_t>::max();
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unordered_map<IdString, NetCriticalityInfo> NetCriticalityMap;
|
||||||
|
void get_criticalities(Context *ctx, NetCriticalityMap *net_crit);
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
624
common/timing_opt.cc
Normal file
624
common/timing_opt.cc
Normal file
@ -0,0 +1,624 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timing-optimised detailed placement algorithm using BFS of the neighbour graph created from cells
|
||||||
|
* on a critical path
|
||||||
|
*
|
||||||
|
* Based on "An Effective Timing-Driven Detailed Placement Algorithm for FPGAs"
|
||||||
|
* https://www.cerc.utexas.edu/utda/publications/C205.pdf
|
||||||
|
*
|
||||||
|
* Modifications made to deal with the smaller Bels that nextpnr uses instead of swapping whole tiles,
|
||||||
|
* and deal with the fact that not every cell on the crit path may be swappable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "timing_opt.h"
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
#include <queue>
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "timing.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <> struct hash<std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX IdString>>
|
||||||
|
{
|
||||||
|
std::size_t
|
||||||
|
operator()(const std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX IdString> &idp) const
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.first));
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.second));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<std::pair<int, NEXTPNR_NAMESPACE_PREFIX BelId>>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const std::pair<int, NEXTPNR_NAMESPACE_PREFIX BelId> &idp) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<int>()(idp.first));
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX BelId>()(idp.second));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#ifndef ARCH_GENERIC
|
||||||
|
template <> struct hash<std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX BelId>>
|
||||||
|
{
|
||||||
|
std::size_t
|
||||||
|
operator()(const std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX BelId> &idp) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.first));
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX BelId>()(idp.second));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class TimingOptimiser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TimingOptimiser(Context *ctx, TimingOptCfg cfg) : ctx(ctx), cfg(cfg){};
|
||||||
|
bool optimise()
|
||||||
|
{
|
||||||
|
log_info("Running timing-driven placement optimisation...\n");
|
||||||
|
if (ctx->verbose)
|
||||||
|
timing_analysis(ctx, false, true, false, false);
|
||||||
|
for (int i = 0; i < 30; i++) {
|
||||||
|
log_info(" Iteration %d...\n", i);
|
||||||
|
get_criticalities(ctx, &net_crit);
|
||||||
|
setup_delay_limits();
|
||||||
|
auto crit_paths = find_crit_paths(0.98, 50000);
|
||||||
|
for (auto &path : crit_paths)
|
||||||
|
optimise_path(path);
|
||||||
|
if (ctx->verbose)
|
||||||
|
timing_analysis(ctx, false, true, false, false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setup_delay_limits()
|
||||||
|
{
|
||||||
|
max_net_delay.clear();
|
||||||
|
for (auto net : sorted(ctx->nets)) {
|
||||||
|
NetInfo *ni = net.second;
|
||||||
|
for (auto usr : ni->users) {
|
||||||
|
max_net_delay[std::make_pair(usr.cell->name, usr.port)] = std::numeric_limits<delay_t>::max();
|
||||||
|
}
|
||||||
|
if (!net_crit.count(net.first))
|
||||||
|
continue;
|
||||||
|
auto &nc = net_crit.at(net.first);
|
||||||
|
if (nc.slack.empty())
|
||||||
|
continue;
|
||||||
|
for (size_t i = 0; i < ni->users.size(); i++) {
|
||||||
|
auto &usr = ni->users.at(i);
|
||||||
|
delay_t net_delay = ctx->getNetinfoRouteDelay(ni, usr);
|
||||||
|
if (nc.max_path_length != 0) {
|
||||||
|
max_net_delay[std::make_pair(usr.cell->name, usr.port)] =
|
||||||
|
net_delay + ((nc.slack.at(i) - nc.cd_worst_slack) / 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_cell_delay_limits(CellInfo *cell)
|
||||||
|
{
|
||||||
|
for (const auto &port : cell->ports) {
|
||||||
|
int nc;
|
||||||
|
if (ctx->getPortTimingClass(cell, port.first, nc) == TMG_IGNORE)
|
||||||
|
continue;
|
||||||
|
NetInfo *net = port.second.net;
|
||||||
|
if (net == nullptr)
|
||||||
|
continue;
|
||||||
|
if (port.second.type == PORT_IN) {
|
||||||
|
if (net->driver.cell == nullptr || net->driver.cell->bel == BelId())
|
||||||
|
continue;
|
||||||
|
for (auto user : net->users) {
|
||||||
|
if (user.cell == cell && user.port == port.first) {
|
||||||
|
if (ctx->predictDelay(net, user) >
|
||||||
|
1.1 * max_net_delay.at(std::make_pair(cell->name, port.first)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (port.second.type == PORT_OUT) {
|
||||||
|
for (auto user : net->users) {
|
||||||
|
// This could get expensive for high-fanout nets??
|
||||||
|
BelId dstBel = user.cell->bel;
|
||||||
|
if (dstBel == BelId())
|
||||||
|
continue;
|
||||||
|
if (ctx->predictDelay(net, user) >
|
||||||
|
1.1 * max_net_delay.at(std::make_pair(user.cell->name, user.port))) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BelId cell_swap_bel(CellInfo *cell, BelId newBel)
|
||||||
|
{
|
||||||
|
BelId oldBel = cell->bel;
|
||||||
|
if (oldBel == newBel)
|
||||||
|
return oldBel;
|
||||||
|
CellInfo *other_cell = ctx->getBoundBelCell(newBel);
|
||||||
|
NPNR_ASSERT(other_cell == nullptr || other_cell->belStrength <= STRENGTH_WEAK);
|
||||||
|
ctx->unbindBel(oldBel);
|
||||||
|
if (other_cell != nullptr) {
|
||||||
|
ctx->unbindBel(newBel);
|
||||||
|
ctx->bindBel(oldBel, other_cell, STRENGTH_WEAK);
|
||||||
|
}
|
||||||
|
ctx->bindBel(newBel, cell, STRENGTH_WEAK);
|
||||||
|
return oldBel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that a series of moves are both legal and remain within maximum delay bounds
|
||||||
|
// Moves are specified as a vector of pairs <cell, oldBel>
|
||||||
|
bool acceptable_move(std::vector<std::pair<CellInfo *, BelId>> &move, bool check_delays = true)
|
||||||
|
{
|
||||||
|
for (auto &entry : move) {
|
||||||
|
if (!ctx->isBelLocationValid(entry.first->bel))
|
||||||
|
return false;
|
||||||
|
if (!ctx->isBelLocationValid(entry.second))
|
||||||
|
return false;
|
||||||
|
if (!check_delays)
|
||||||
|
continue;
|
||||||
|
if (!check_cell_delay_limits(entry.first))
|
||||||
|
return false;
|
||||||
|
// We might have swapped another cell onto the original bel. Check this for max delay violations
|
||||||
|
// too
|
||||||
|
CellInfo *swapped = ctx->getBoundBelCell(entry.second);
|
||||||
|
if (swapped != nullptr && !check_cell_delay_limits(swapped))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_neighbours(CellInfo *cell, IdString prev_cell, int d, bool allow_swap)
|
||||||
|
{
|
||||||
|
BelId curr = cell->bel;
|
||||||
|
Loc curr_loc = ctx->getBelLocation(curr);
|
||||||
|
int found_count = 0;
|
||||||
|
cell_neighbour_bels[cell->name] = std::unordered_set<BelId>{};
|
||||||
|
for (int dy = -d; dy <= d; dy++) {
|
||||||
|
for (int dx = -d; dx <= d; dx++) {
|
||||||
|
// Go through all the Bels at this location
|
||||||
|
// First, find all bels of the correct type that are either unbound or bound normally
|
||||||
|
// Strongly bound bels are ignored
|
||||||
|
// FIXME: This means that we cannot touch carry chains or similar relatively constrained macros
|
||||||
|
std::vector<BelId> free_bels_at_loc;
|
||||||
|
std::vector<BelId> bound_bels_at_loc;
|
||||||
|
for (auto bel : ctx->getBelsByTile(curr_loc.x + dx, curr_loc.y + dy)) {
|
||||||
|
if (ctx->getBelType(bel) != cell->type)
|
||||||
|
continue;
|
||||||
|
CellInfo *bound = ctx->getBoundBelCell(bel);
|
||||||
|
if (bound == nullptr) {
|
||||||
|
free_bels_at_loc.push_back(bel);
|
||||||
|
} else if (bound->belStrength <= STRENGTH_WEAK && bound->constr_parent == nullptr &&
|
||||||
|
bound->constr_children.empty()) {
|
||||||
|
bound_bels_at_loc.push_back(bel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BelId candidate;
|
||||||
|
|
||||||
|
while (!free_bels_at_loc.empty() || !bound_bels_at_loc.empty()) {
|
||||||
|
BelId try_bel;
|
||||||
|
if (!free_bels_at_loc.empty()) {
|
||||||
|
int try_idx = ctx->rng(int(free_bels_at_loc.size()));
|
||||||
|
try_bel = free_bels_at_loc.at(try_idx);
|
||||||
|
free_bels_at_loc.erase(free_bels_at_loc.begin() + try_idx);
|
||||||
|
} else {
|
||||||
|
int try_idx = ctx->rng(int(bound_bels_at_loc.size()));
|
||||||
|
try_bel = bound_bels_at_loc.at(try_idx);
|
||||||
|
bound_bels_at_loc.erase(bound_bels_at_loc.begin() + try_idx);
|
||||||
|
}
|
||||||
|
if (bel_candidate_cells.count(try_bel) && !allow_swap) {
|
||||||
|
// Overlap is only allowed if it is with the previous cell (this is handled by removing those
|
||||||
|
// edges in the graph), or if allow_swap is true to deal with cases where overlap means few
|
||||||
|
// neighbours are identified
|
||||||
|
if (bel_candidate_cells.at(try_bel).size() > 1 ||
|
||||||
|
(bel_candidate_cells.at(try_bel).size() == 1 &&
|
||||||
|
*(bel_candidate_cells.at(try_bel).begin()) != prev_cell))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// TODO: what else to check here?
|
||||||
|
candidate = try_bel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate != BelId()) {
|
||||||
|
cell_neighbour_bels[cell->name].insert(candidate);
|
||||||
|
bel_candidate_cells[candidate].insert(cell->name);
|
||||||
|
// Work out if we need to delete any overlap
|
||||||
|
std::vector<IdString> overlap;
|
||||||
|
for (auto other : bel_candidate_cells[candidate])
|
||||||
|
if (other != cell->name && other != prev_cell)
|
||||||
|
overlap.push_back(other);
|
||||||
|
if (overlap.size() > 0)
|
||||||
|
NPNR_ASSERT(allow_swap);
|
||||||
|
for (auto ov : overlap) {
|
||||||
|
bel_candidate_cells[candidate].erase(ov);
|
||||||
|
cell_neighbour_bels[ov].erase(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<PortRef *>> find_crit_paths(float crit_thresh, size_t max_count)
|
||||||
|
{
|
||||||
|
std::vector<std::vector<PortRef *>> crit_paths;
|
||||||
|
std::vector<std::pair<NetInfo *, int>> crit_nets;
|
||||||
|
std::vector<IdString> netnames;
|
||||||
|
std::transform(ctx->nets.begin(), ctx->nets.end(), std::back_inserter(netnames),
|
||||||
|
[](const std::pair<const IdString, std::unique_ptr<NetInfo>> &kv) { return kv.first; });
|
||||||
|
ctx->sorted_shuffle(netnames);
|
||||||
|
for (auto net : netnames) {
|
||||||
|
if (crit_nets.size() >= max_count)
|
||||||
|
break;
|
||||||
|
if (!net_crit.count(net))
|
||||||
|
continue;
|
||||||
|
auto crit_user = std::max_element(net_crit[net].criticality.begin(), net_crit[net].criticality.end());
|
||||||
|
if (*crit_user > crit_thresh)
|
||||||
|
crit_nets.push_back(
|
||||||
|
std::make_pair(ctx->nets[net].get(), crit_user - net_crit[net].criticality.begin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto port_user_index = [](CellInfo *cell, PortInfo &port) -> size_t {
|
||||||
|
NPNR_ASSERT(port.net != nullptr);
|
||||||
|
for (size_t i = 0; i < port.net->users.size(); i++) {
|
||||||
|
auto &usr = port.net->users.at(i);
|
||||||
|
if (usr.cell == cell && usr.port == port.name)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
NPNR_ASSERT_FALSE("port user not found on net");
|
||||||
|
};
|
||||||
|
std::unordered_set<PortRef *> used_ports;
|
||||||
|
|
||||||
|
for (auto crit_net : crit_nets) {
|
||||||
|
|
||||||
|
if (used_ports.count(&(crit_net.first->users.at(crit_net.second))))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::deque<PortRef *> crit_path;
|
||||||
|
|
||||||
|
// FIXME: This will fail badly on combinational loops
|
||||||
|
|
||||||
|
// Iterate backwards following greatest criticality
|
||||||
|
NetInfo *back_cursor = crit_net.first;
|
||||||
|
while (back_cursor != nullptr) {
|
||||||
|
float max_crit = 0;
|
||||||
|
std::pair<NetInfo *, size_t> crit_sink{nullptr, 0};
|
||||||
|
CellInfo *cell = back_cursor->driver.cell;
|
||||||
|
if (cell == nullptr)
|
||||||
|
break;
|
||||||
|
for (auto port : cell->ports) {
|
||||||
|
if (port.second.type != PORT_IN)
|
||||||
|
continue;
|
||||||
|
NetInfo *pn = port.second.net;
|
||||||
|
if (pn == nullptr)
|
||||||
|
continue;
|
||||||
|
if (!net_crit.count(pn->name) || net_crit.at(pn->name).criticality.empty())
|
||||||
|
continue;
|
||||||
|
int ccount;
|
||||||
|
DelayInfo combDelay;
|
||||||
|
TimingPortClass tpclass = ctx->getPortTimingClass(cell, port.first, ccount);
|
||||||
|
if (tpclass != TMG_COMB_INPUT)
|
||||||
|
continue;
|
||||||
|
bool is_path = ctx->getCellDelay(cell, port.first, back_cursor->driver.port, combDelay);
|
||||||
|
if (!is_path)
|
||||||
|
continue;
|
||||||
|
size_t user_idx = port_user_index(cell, port.second);
|
||||||
|
float usr_crit = net_crit.at(pn->name).criticality.at(user_idx);
|
||||||
|
if (used_ports.count(&(pn->users.at(user_idx))))
|
||||||
|
continue;
|
||||||
|
if (usr_crit >= max_crit) {
|
||||||
|
max_crit = usr_crit;
|
||||||
|
crit_sink = std::make_pair(pn, user_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crit_sink.first != nullptr) {
|
||||||
|
crit_path.push_front(&(crit_sink.first->users.at(crit_sink.second)));
|
||||||
|
used_ports.insert(&(crit_sink.first->users.at(crit_sink.second)));
|
||||||
|
}
|
||||||
|
back_cursor = crit_sink.first;
|
||||||
|
}
|
||||||
|
// Iterate forwards following greatest criticiality
|
||||||
|
PortRef *fwd_cursor = &(crit_net.first->users.at(crit_net.second));
|
||||||
|
while (fwd_cursor != nullptr) {
|
||||||
|
crit_path.push_back(fwd_cursor);
|
||||||
|
float max_crit = 0;
|
||||||
|
std::pair<NetInfo *, size_t> crit_sink{nullptr, 0};
|
||||||
|
CellInfo *cell = fwd_cursor->cell;
|
||||||
|
for (auto port : cell->ports) {
|
||||||
|
if (port.second.type != PORT_OUT)
|
||||||
|
continue;
|
||||||
|
NetInfo *pn = port.second.net;
|
||||||
|
if (pn == nullptr)
|
||||||
|
continue;
|
||||||
|
if (!net_crit.count(pn->name) || net_crit.at(pn->name).criticality.empty())
|
||||||
|
continue;
|
||||||
|
int ccount;
|
||||||
|
DelayInfo combDelay;
|
||||||
|
TimingPortClass tpclass = ctx->getPortTimingClass(cell, port.first, ccount);
|
||||||
|
if (tpclass != TMG_COMB_OUTPUT && tpclass != TMG_REGISTER_OUTPUT)
|
||||||
|
continue;
|
||||||
|
bool is_path = ctx->getCellDelay(cell, fwd_cursor->port, port.first, combDelay);
|
||||||
|
if (!is_path)
|
||||||
|
continue;
|
||||||
|
auto &crits = net_crit.at(pn->name).criticality;
|
||||||
|
for (size_t i = 0; i < crits.size(); i++) {
|
||||||
|
if (used_ports.count(&(pn->users.at(i))))
|
||||||
|
continue;
|
||||||
|
if (crits.at(i) >= max_crit) {
|
||||||
|
max_crit = crits.at(i);
|
||||||
|
crit_sink = std::make_pair(pn, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (crit_sink.first != nullptr) {
|
||||||
|
fwd_cursor = &(crit_sink.first->users.at(crit_sink.second));
|
||||||
|
used_ports.insert(&(crit_sink.first->users.at(crit_sink.second)));
|
||||||
|
} else {
|
||||||
|
fwd_cursor = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PortRef *> crit_path_vec;
|
||||||
|
std::copy(crit_path.begin(), crit_path.end(), std::back_inserter(crit_path_vec));
|
||||||
|
crit_paths.push_back(crit_path_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return crit_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void optimise_path(std::vector<PortRef *> &path)
|
||||||
|
{
|
||||||
|
path_cells.clear();
|
||||||
|
cell_neighbour_bels.clear();
|
||||||
|
bel_candidate_cells.clear();
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info("Optimising the following path: \n");
|
||||||
|
|
||||||
|
auto front_port = path.front();
|
||||||
|
NetInfo *front_net = front_port->cell->ports.at(front_port->port).net;
|
||||||
|
if (front_net != nullptr && front_net->driver.cell != nullptr) {
|
||||||
|
auto front_cell = front_net->driver.cell;
|
||||||
|
if (front_cell->belStrength <= STRENGTH_WEAK && cfg.cellTypes.count(front_cell->type) &&
|
||||||
|
front_cell->constr_parent == nullptr && front_cell->constr_children.empty()) {
|
||||||
|
path_cells.push_back(front_cell->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto port : path) {
|
||||||
|
if (ctx->debug) {
|
||||||
|
float crit = 0;
|
||||||
|
NetInfo *pn = port->cell->ports.at(port->port).net;
|
||||||
|
if (net_crit.count(pn->name) && !net_crit.at(pn->name).criticality.empty())
|
||||||
|
for (size_t i = 0; i < pn->users.size(); i++)
|
||||||
|
if (pn->users.at(i).cell == port->cell && pn->users.at(i).port == port->port)
|
||||||
|
crit = net_crit.at(pn->name).criticality.at(i);
|
||||||
|
log_info(" %s.%s at %s crit %0.02f\n", port->cell->name.c_str(ctx), port->port.c_str(ctx),
|
||||||
|
ctx->getBelName(port->cell->bel).c_str(ctx), crit);
|
||||||
|
}
|
||||||
|
if (std::find(path_cells.begin(), path_cells.end(), port->cell->name) != path_cells.end())
|
||||||
|
continue;
|
||||||
|
if (port->cell->belStrength > STRENGTH_WEAK || !cfg.cellTypes.count(port->cell->type) ||
|
||||||
|
port->cell->constr_parent != nullptr || !port->cell->constr_children.empty())
|
||||||
|
continue;
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" can move\n");
|
||||||
|
path_cells.push_back(port->cell->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path_cells.size() < 2) {
|
||||||
|
if (ctx->debug) {
|
||||||
|
log_info("Too few moveable cells; skipping path\n");
|
||||||
|
log_break();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate original delay before touching anything
|
||||||
|
delay_t original_delay = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < path.size(); i++) {
|
||||||
|
NetInfo *pn = path.at(i)->cell->ports.at(path.at(i)->port).net;
|
||||||
|
for (size_t j = 0; j < pn->users.size(); j++) {
|
||||||
|
auto &usr = pn->users.at(j);
|
||||||
|
if (usr.cell == path.at(i)->cell && usr.port == path.at(i)->port) {
|
||||||
|
original_delay += ctx->predictDelay(pn, usr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString last_cell;
|
||||||
|
const int d = 2; // FIXME: how to best determine d
|
||||||
|
for (auto cell : path_cells) {
|
||||||
|
// FIXME: when should we allow swapping due to a lack of candidates
|
||||||
|
find_neighbours(ctx->cells[cell].get(), last_cell, d, false);
|
||||||
|
last_cell = cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->debug) {
|
||||||
|
for (auto cell : path_cells) {
|
||||||
|
log_info("Candidate neighbours for %s (%s):\n", cell.c_str(ctx),
|
||||||
|
ctx->getBelName(ctx->cells[cell]->bel).c_str(ctx));
|
||||||
|
for (auto neigh : cell_neighbour_bels.at(cell)) {
|
||||||
|
log_info(" %s\n", ctx->getBelName(neigh).c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual BFS path optimisation algorithm
|
||||||
|
std::unordered_map<IdString, std::unordered_map<BelId, delay_t>> cumul_costs;
|
||||||
|
std::unordered_map<std::pair<IdString, BelId>, std::pair<IdString, BelId>> backtrace;
|
||||||
|
std::queue<std::pair<int, BelId>> visit;
|
||||||
|
std::unordered_set<std::pair<int, BelId>> to_visit;
|
||||||
|
|
||||||
|
for (auto startbel : cell_neighbour_bels[path_cells.front()]) {
|
||||||
|
// Swap for legality check
|
||||||
|
CellInfo *cell = ctx->cells.at(path_cells.front()).get();
|
||||||
|
BelId origBel = cell_swap_bel(cell, startbel);
|
||||||
|
std::vector<std::pair<CellInfo *, BelId>> move{std::make_pair(cell, origBel)};
|
||||||
|
if (acceptable_move(move)) {
|
||||||
|
auto entry = std::make_pair(0, startbel);
|
||||||
|
visit.push(entry);
|
||||||
|
cumul_costs[path_cells.front()][startbel] = 0;
|
||||||
|
}
|
||||||
|
// Swap back
|
||||||
|
cell_swap_bel(cell, origBel);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!visit.empty()) {
|
||||||
|
auto entry = visit.front();
|
||||||
|
visit.pop();
|
||||||
|
auto cellname = path_cells.at(entry.first);
|
||||||
|
if (entry.first == int(path_cells.size()) - 1)
|
||||||
|
continue;
|
||||||
|
std::vector<std::pair<CellInfo *, BelId>> move;
|
||||||
|
// Apply the entire backtrace for accurate legality and delay checks
|
||||||
|
// This is probably pretty expensive (but also probably pales in comparison to the number of swaps
|
||||||
|
// SA will make...)
|
||||||
|
std::vector<std::pair<IdString, BelId>> route_to_entry;
|
||||||
|
auto cursor = std::make_pair(cellname, entry.second);
|
||||||
|
route_to_entry.push_back(cursor);
|
||||||
|
while (backtrace.count(cursor)) {
|
||||||
|
cursor = backtrace.at(cursor);
|
||||||
|
route_to_entry.push_back(cursor);
|
||||||
|
}
|
||||||
|
for (auto rt_entry : boost::adaptors::reverse(route_to_entry)) {
|
||||||
|
CellInfo *cell = ctx->cells.at(rt_entry.first).get();
|
||||||
|
BelId origBel = cell_swap_bel(cell, rt_entry.second);
|
||||||
|
move.push_back(std::make_pair(cell, origBel));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have a look at where we can travel from here
|
||||||
|
for (auto neighbour : cell_neighbour_bels.at(path_cells.at(entry.first + 1))) {
|
||||||
|
// Edges between overlapping bels are deleted
|
||||||
|
if (neighbour == entry.second)
|
||||||
|
continue;
|
||||||
|
// Experimentally swap the next path cell onto the neighbour bel we are trying
|
||||||
|
IdString ncname = path_cells.at(entry.first + 1);
|
||||||
|
CellInfo *next_cell = ctx->cells.at(ncname).get();
|
||||||
|
BelId origBel = cell_swap_bel(next_cell, neighbour);
|
||||||
|
move.push_back(std::make_pair(next_cell, origBel));
|
||||||
|
|
||||||
|
delay_t total_delay = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < path.size(); i++) {
|
||||||
|
NetInfo *pn = path.at(i)->cell->ports.at(path.at(i)->port).net;
|
||||||
|
for (size_t j = 0; j < pn->users.size(); j++) {
|
||||||
|
auto &usr = pn->users.at(j);
|
||||||
|
if (usr.cell == path.at(i)->cell && usr.port == path.at(i)->port) {
|
||||||
|
total_delay += ctx->predictDelay(pn, usr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (path.at(i)->cell == next_cell)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, check if the move is actually worthwhile from a delay point of view before the expensive
|
||||||
|
// legality check
|
||||||
|
if (!cumul_costs.count(ncname) || !cumul_costs.at(ncname).count(neighbour) ||
|
||||||
|
cumul_costs.at(ncname).at(neighbour) > total_delay) {
|
||||||
|
// Now check that the swaps we have made to get here are legal and meet max delay requirements
|
||||||
|
if (acceptable_move(move)) {
|
||||||
|
cumul_costs[ncname][neighbour] = total_delay;
|
||||||
|
backtrace[std::make_pair(ncname, neighbour)] = std::make_pair(cellname, entry.second);
|
||||||
|
if (!to_visit.count(std::make_pair(entry.first + 1, neighbour)))
|
||||||
|
visit.push(std::make_pair(entry.first + 1, neighbour));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Revert the experimental swap
|
||||||
|
cell_swap_bel(move.back().first, move.back().second);
|
||||||
|
move.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert move by swapping cells back to their original order
|
||||||
|
// Execute swaps in reverse order to how we made them originally
|
||||||
|
for (auto move_entry : boost::adaptors::reverse(move)) {
|
||||||
|
cell_swap_bel(move_entry.first, move_entry.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did we find a solution??
|
||||||
|
if (cumul_costs.count(path_cells.back())) {
|
||||||
|
// Find the end position with the lowest total delay
|
||||||
|
auto &end_options = cumul_costs.at(path_cells.back());
|
||||||
|
auto lowest = std::min_element(end_options.begin(), end_options.end(),
|
||||||
|
[](const std::pair<BelId, delay_t> &a, const std::pair<BelId, delay_t> &b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
NPNR_ASSERT(lowest != end_options.end());
|
||||||
|
|
||||||
|
std::vector<std::pair<IdString, BelId>> route_to_solution;
|
||||||
|
auto cursor = std::make_pair(path_cells.back(), lowest->first);
|
||||||
|
route_to_solution.push_back(cursor);
|
||||||
|
while (backtrace.count(cursor)) {
|
||||||
|
cursor = backtrace.at(cursor);
|
||||||
|
route_to_solution.push_back(cursor);
|
||||||
|
}
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info("Found a solution with cost %.02f ns (existing path %.02f ns)\n",
|
||||||
|
ctx->getDelayNS(lowest->second), ctx->getDelayNS(original_delay));
|
||||||
|
for (auto rt_entry : boost::adaptors::reverse(route_to_solution)) {
|
||||||
|
CellInfo *cell = ctx->cells.at(rt_entry.first).get();
|
||||||
|
cell_swap_bel(cell, rt_entry.second);
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" %s at %s\n", rt_entry.first.c_str(ctx), ctx->getBelName(rt_entry.second).c_str(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info("Solution was not found\n");
|
||||||
|
}
|
||||||
|
if (ctx->debug)
|
||||||
|
log_break();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current candidate Bels for cells (linked in both direction>
|
||||||
|
std::vector<IdString> path_cells;
|
||||||
|
std::unordered_map<IdString, std::unordered_set<BelId>> cell_neighbour_bels;
|
||||||
|
std::unordered_map<BelId, std::unordered_set<IdString>> bel_candidate_cells;
|
||||||
|
// Map cell ports to net delay limit
|
||||||
|
std::unordered_map<std::pair<IdString, IdString>, delay_t> max_net_delay;
|
||||||
|
// Criticality data from timing analysis
|
||||||
|
NetCriticalityMap net_crit;
|
||||||
|
Context *ctx;
|
||||||
|
TimingOptCfg cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool timing_opt(Context *ctx, TimingOptCfg cfg) { return TimingOptimiser(ctx, cfg).optimise(); }
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
37
common/timing_opt.h
Normal file
37
common/timing_opt.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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 "nextpnr.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct TimingOptCfg : public Settings
|
||||||
|
{
|
||||||
|
TimingOptCfg(Context *ctx) : Settings(ctx) {}
|
||||||
|
|
||||||
|
// The timing optimiser will *only* optimise cells of these types
|
||||||
|
// Normally these would only be logic cells (or tiles if applicable), the algorithm makes little sense
|
||||||
|
// for other cell types
|
||||||
|
std::unordered_set<IdString> cellTypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern bool timing_opt(Context *ctx, TimingOptCfg cfg);
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
23
ecp5/arch.cc
23
ecp5/arch.cc
@ -579,6 +579,8 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
|
|||||||
return false;
|
return false;
|
||||||
} else if (cell->type == id_DP16KD) {
|
} else if (cell->type == id_DP16KD) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) {
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -669,8 +671,19 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
|
|||||||
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
|
||||||
}
|
}
|
||||||
return TMG_IGNORE;
|
return TMG_IGNORE;
|
||||||
|
} else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) {
|
||||||
|
if (port == id_CLK || port == id_ECLK) {
|
||||||
|
return TMG_CLOCK_INPUT;
|
||||||
|
} else if (port == id_IOLDO || port == id_IOLDOI || port == id_IOLDOD || port == id_IOLTO || port == id_PADDI ||
|
||||||
|
port == id_DQSR90 || port == id_DQSW || port == id_DQSW270) {
|
||||||
|
return TMG_IGNORE;
|
||||||
} else {
|
} else {
|
||||||
NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'");
|
clockInfoCount = 1;
|
||||||
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("cell type '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this),
|
||||||
|
cell->name.c_str(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,6 +756,14 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
|||||||
info.setup = getDelayFromNS(1);
|
info.setup = getDelayFromNS(1);
|
||||||
info.hold = getDelayFromNS(0);
|
info.hold = getDelayFromNS(0);
|
||||||
}
|
}
|
||||||
|
} else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) {
|
||||||
|
info.clock_port = id_CLK;
|
||||||
|
if (cell->ports.at(port).type == PORT_OUT) {
|
||||||
|
info.clockToQ = getDelayFromNS(0.5);
|
||||||
|
} else {
|
||||||
|
info.setup = getDelayFromNS(0.1);
|
||||||
|
info.hold = getDelayFromNS(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -745,6 +745,12 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
}
|
}
|
||||||
if (ci->attrs.count(ctx->id("SLEWRATE")))
|
if (ci->attrs.count(ctx->id("SLEWRATE")))
|
||||||
cc.tiles[pio_tile].add_enum(pio + ".SLEWRATE", str_or_default(ci->attrs, ctx->id("SLEWRATE"), "SLOW"));
|
cc.tiles[pio_tile].add_enum(pio + ".SLEWRATE", str_or_default(ci->attrs, ctx->id("SLEWRATE"), "SLOW"));
|
||||||
|
std::string datamux_oddr = str_or_default(ci->params, ctx->id("DATAMUX_ODDR"), "PADDO");
|
||||||
|
if (datamux_oddr != "PADDO")
|
||||||
|
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_ODDR", datamux_oddr);
|
||||||
|
std::string datamux_mddr = str_or_default(ci->params, ctx->id("DATAMUX_MDDR"), "PADDO");
|
||||||
|
if (datamux_mddr != "PADDO")
|
||||||
|
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_MDDR", datamux_mddr);
|
||||||
} else if (ci->type == ctx->id("DCCA")) {
|
} else if (ci->type == ctx->id("DCCA")) {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
} else if (ci->type == ctx->id("DP16KD")) {
|
} else if (ci->type == ctx->id("DP16KD")) {
|
||||||
@ -1078,6 +1084,18 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_ENABLE_FILTEROPAMP"), 0), 1));
|
int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_ENABLE_FILTEROPAMP"), 0), 1));
|
||||||
|
|
||||||
cc.tilegroups.push_back(tg);
|
cc.tilegroups.push_back(tg);
|
||||||
|
} else if (ci->type == id_IOLOGIC || ci->type == id_SIOLOGIC) {
|
||||||
|
Loc pio_loc = ctx->getBelLocation(ci->bel);
|
||||||
|
pio_loc.z -= ci->type == id_SIOLOGIC ? 2 : 4;
|
||||||
|
std::string pic_tile = get_pic_tile(ctx, ctx->getBelByLocation(pio_loc));
|
||||||
|
std::string prim = std::string("IOLOGIC") + "ABCD"[pio_loc.z];
|
||||||
|
for (auto ¶m : ci->params) {
|
||||||
|
if (param.first == ctx->id("DELAY.DEL_VALUE"))
|
||||||
|
cc.tiles[pic_tile].add_word(prim + "." + param.first.str(ctx),
|
||||||
|
int_to_bitvector(std::stoi(param.second), 7));
|
||||||
|
else
|
||||||
|
cc.tiles[pic_tile].add_enum(prim + "." + param.first.str(ctx), param.second);
|
||||||
|
}
|
||||||
} else if (ci->type == id_DCUA) {
|
} else if (ci->type == id_DCUA) {
|
||||||
TileGroup tg;
|
TileGroup tg;
|
||||||
tg.tiles = get_dcu_tiles(ctx, ci->bel);
|
tg.tiles = get_dcu_tiles(ctx, ci->bel);
|
||||||
|
@ -41,6 +41,22 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
|
|||||||
new_cell->name = ctx->id(name);
|
new_cell->name = ctx->id(name);
|
||||||
}
|
}
|
||||||
new_cell->type = type;
|
new_cell->type = type;
|
||||||
|
|
||||||
|
auto copy_bel_ports = [&]() {
|
||||||
|
// First find a Bel of the target type
|
||||||
|
BelId tgt;
|
||||||
|
for (auto bel : ctx->getBels()) {
|
||||||
|
if (ctx->getBelType(bel) == type) {
|
||||||
|
tgt = bel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NPNR_ASSERT(tgt != BelId());
|
||||||
|
for (auto port : ctx->getBelPins(tgt)) {
|
||||||
|
add_port(ctx, new_cell.get(), port.str(ctx), ctx->getBelPinType(tgt, port));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (type == ctx->id("TRELLIS_SLICE")) {
|
if (type == ctx->id("TRELLIS_SLICE")) {
|
||||||
new_cell->params[ctx->id("MODE")] = "LOGIC";
|
new_cell->params[ctx->id("MODE")] = "LOGIC";
|
||||||
new_cell->params[ctx->id("GSR")] = "DISABLED";
|
new_cell->params[ctx->id("GSR")] = "DISABLED";
|
||||||
@ -111,11 +127,17 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
|
|||||||
} else if (type == ctx->id("TRELLIS_IO")) {
|
} else if (type == ctx->id("TRELLIS_IO")) {
|
||||||
new_cell->params[ctx->id("DIR")] = "INPUT";
|
new_cell->params[ctx->id("DIR")] = "INPUT";
|
||||||
new_cell->attrs[ctx->id("IO_TYPE")] = "LVCMOS33";
|
new_cell->attrs[ctx->id("IO_TYPE")] = "LVCMOS33";
|
||||||
|
new_cell->params[ctx->id("DATAMUX_ODDR")] = "PADDO";
|
||||||
|
new_cell->params[ctx->id("DATAMUX_MDDR")] = "PADDO";
|
||||||
|
|
||||||
add_port(ctx, new_cell.get(), "B", PORT_INOUT);
|
add_port(ctx, new_cell.get(), "B", PORT_INOUT);
|
||||||
add_port(ctx, new_cell.get(), "I", PORT_IN);
|
add_port(ctx, new_cell.get(), "I", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "T", PORT_IN);
|
add_port(ctx, new_cell.get(), "T", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "O", PORT_OUT);
|
add_port(ctx, new_cell.get(), "O", PORT_OUT);
|
||||||
|
|
||||||
|
add_port(ctx, new_cell.get(), "IOLDO", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "IOLTO", PORT_IN);
|
||||||
|
|
||||||
} else if (type == ctx->id("LUT4")) {
|
} else if (type == ctx->id("LUT4")) {
|
||||||
new_cell->params[ctx->id("INIT")] = "0";
|
new_cell->params[ctx->id("INIT")] = "0";
|
||||||
|
|
||||||
@ -150,6 +172,35 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
|
|||||||
add_port(ctx, new_cell.get(), "CLKI", PORT_IN);
|
add_port(ctx, new_cell.get(), "CLKI", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "CLKO", PORT_OUT);
|
add_port(ctx, new_cell.get(), "CLKO", PORT_OUT);
|
||||||
add_port(ctx, new_cell.get(), "CE", PORT_IN);
|
add_port(ctx, new_cell.get(), "CE", PORT_IN);
|
||||||
|
} else if (type == id_IOLOGIC || type == id_SIOLOGIC) {
|
||||||
|
new_cell->params[ctx->id("MODE")] = "NONE";
|
||||||
|
new_cell->params[ctx->id("GSR")] = "DISABLED";
|
||||||
|
new_cell->params[ctx->id("CLKIMUX")] = "CLK";
|
||||||
|
new_cell->params[ctx->id("CLKOMUX")] = "CLK";
|
||||||
|
new_cell->params[ctx->id("LSRIMUX")] = "0";
|
||||||
|
new_cell->params[ctx->id("LSROMUX")] = "0";
|
||||||
|
new_cell->params[ctx->id("LSRMUX")] = "LSR";
|
||||||
|
|
||||||
|
new_cell->params[ctx->id("DELAY.OUTDEL")] = "DISABLED";
|
||||||
|
new_cell->params[ctx->id("DELAY.DEL_VALUE")] = "0";
|
||||||
|
new_cell->params[ctx->id("DELAY.WAIT_FOR_EDGE")] = "DISABLED";
|
||||||
|
|
||||||
|
if (type == id_IOLOGIC) {
|
||||||
|
new_cell->params[ctx->id("IDDRXN.MODE")] = "NONE";
|
||||||
|
new_cell->params[ctx->id("ODDRXN.MODE")] = "NONE";
|
||||||
|
|
||||||
|
new_cell->params[ctx->id("MIDDRX.MODE")] = "NONE";
|
||||||
|
new_cell->params[ctx->id("MODDRX.MODE")] = "NONE";
|
||||||
|
new_cell->params[ctx->id("MTDDRX.MODE")] = "NONE";
|
||||||
|
|
||||||
|
new_cell->params[ctx->id("IOLTOMUX")] = "NONE";
|
||||||
|
new_cell->params[ctx->id("MTDDRX.DQSW_INVERT")] = "DISABLED";
|
||||||
|
new_cell->params[ctx->id("MTDDRX.REGSET")] = "RESET";
|
||||||
|
|
||||||
|
new_cell->params[ctx->id("MIDDRX_MODDRX.WRCLKMUX")] = "NONE";
|
||||||
|
}
|
||||||
|
// Just copy ports from the Bel
|
||||||
|
copy_bel_ports();
|
||||||
} else {
|
} else {
|
||||||
log_error("unable to create ECP5 cell of type %s", type.c_str(ctx));
|
log_error("unable to create ECP5 cell of type %s", type.c_str(ctx));
|
||||||
}
|
}
|
||||||
@ -365,7 +416,7 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::u
|
|||||||
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
|
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
|
||||||
ctx->id("Y"));
|
ctx->id("Y"));
|
||||||
if (tbuf) {
|
if (tbuf) {
|
||||||
replace_port(tbuf, ctx->id("I"), trio, ctx->id("I"));
|
replace_port(tbuf, ctx->id("A"), trio, ctx->id("I"));
|
||||||
// Need to invert E to form T
|
// Need to invert E to form T
|
||||||
std::unique_ptr<CellInfo> inv_lut = create_ecp5_cell(ctx, ctx->id("LUT4"), trio->name.str(ctx) + "$invert_T");
|
std::unique_ptr<CellInfo> inv_lut = create_ecp5_cell(ctx, ctx->id("LUT4"), trio->name.str(ctx) + "$invert_T");
|
||||||
replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("A"));
|
replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("A"));
|
||||||
|
@ -1142,3 +1142,43 @@ X(PAD)
|
|||||||
X(PADDI)
|
X(PADDI)
|
||||||
X(PADDO)
|
X(PADDO)
|
||||||
X(PADDT)
|
X(PADDT)
|
||||||
|
|
||||||
|
X(IOLOGIC)
|
||||||
|
X(SIOLOGIC)
|
||||||
|
X(DI)
|
||||||
|
X(IOLDO)
|
||||||
|
X(IOLDOD)
|
||||||
|
X(IOLDOI)
|
||||||
|
X(IOLTO)
|
||||||
|
X(INDD)
|
||||||
|
X(LOADN)
|
||||||
|
X(MOVE)
|
||||||
|
X(DIRECTION)
|
||||||
|
X(TSDATA0)
|
||||||
|
X(TXDATA0)
|
||||||
|
X(TXDATA1)
|
||||||
|
X(RXDATA0)
|
||||||
|
X(RXDATA1)
|
||||||
|
X(INFF)
|
||||||
|
X(CFLAG)
|
||||||
|
X(ECLK)
|
||||||
|
X(TSDATA1)
|
||||||
|
X(TXDATA2)
|
||||||
|
X(TXDATA3)
|
||||||
|
X(RXDATA2)
|
||||||
|
X(RXDATA3)
|
||||||
|
X(TXDATA4)
|
||||||
|
X(TXDATA5)
|
||||||
|
X(TXDATA6)
|
||||||
|
X(RXDATA4)
|
||||||
|
X(RXDATA5)
|
||||||
|
X(RXDATA6)
|
||||||
|
X(DQSR90)
|
||||||
|
X(DQSW270)
|
||||||
|
X(DQSW)
|
||||||
|
X(RDPNTR0)
|
||||||
|
X(RDPNTR1)
|
||||||
|
X(RDPNTR2)
|
||||||
|
X(WRPNTR0)
|
||||||
|
X(WRPNTR1)
|
||||||
|
X(WRPNTR2)
|
||||||
|
@ -58,6 +58,8 @@ class Ecp5GlobalRouter
|
|||||||
if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK ||
|
if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK ||
|
||||||
user.port == id_CH0_FF_TXI_CLK || user.port == id_CH1_FF_TXI_CLK))
|
user.port == id_CH0_FF_TXI_CLK || user.port == id_CH1_FF_TXI_CLK))
|
||||||
return true;
|
return true;
|
||||||
|
if ((user.cell->type == id_IOLOGIC || user.cell->type == id_SIOLOGIC) && user.port == id_CLK)
|
||||||
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,6 +300,8 @@ class Ecp5GlobalRouter
|
|||||||
} else {
|
} else {
|
||||||
// Check for dedicated routing
|
// Check for dedicated routing
|
||||||
if (has_short_route(ctx->getBelPinWire(drv_bel, drv.port), ctx->getBelPinWire(dcc->bel, id_CLKI))) {
|
if (has_short_route(ctx->getBelPinWire(drv_bel, drv.port), ctx->getBelPinWire(dcc->bel, id_CLKI))) {
|
||||||
|
// log_info("dedicated route %s -> %s\n", ctx->getWireName(ctx->getBelPinWire(drv_bel,
|
||||||
|
// drv.port)).c_str(ctx), ctx->getBelName(dcc->bel).c_str(ctx));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Driver is locked
|
// Driver is locked
|
||||||
@ -308,7 +312,7 @@ class Ecp5GlobalRouter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return true if a short (<5) route exists between two wires
|
// Return true if a short (<5) route exists between two wires
|
||||||
bool has_short_route(WireId src, WireId dst, int thresh = 5)
|
bool has_short_route(WireId src, WireId dst, int thresh = 7)
|
||||||
{
|
{
|
||||||
std::queue<WireId> visit;
|
std::queue<WireId> visit;
|
||||||
std::unordered_map<WireId, PipId> backtrace;
|
std::unordered_map<WireId, PipId> backtrace;
|
||||||
@ -316,7 +320,7 @@ class Ecp5GlobalRouter
|
|||||||
WireId cursor;
|
WireId cursor;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
if (visit.empty() || visit.size() > 1000) {
|
if (visit.empty() || visit.size() > 10000) {
|
||||||
// log_info ("dist %s -> %s = inf\n", ctx->getWireName(src).c_str(ctx),
|
// log_info ("dist %s -> %s = inf\n", ctx->getWireName(src).c_str(ctx),
|
||||||
// ctx->getWireName(dst).c_str(ctx));
|
// ctx->getWireName(dst).c_str(ctx));
|
||||||
return false;
|
return false;
|
||||||
|
202
ecp5/pack.cc
202
ecp5/pack.cc
@ -299,7 +299,16 @@ class Ecp5Packer
|
|||||||
// iobuf
|
// iobuf
|
||||||
log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx),
|
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));
|
ci->type.c_str(ctx), ci->name.c_str(ctx));
|
||||||
|
|
||||||
NetInfo *net = trio->ports.at(ctx->id("B")).net;
|
NetInfo *net = trio->ports.at(ctx->id("B")).net;
|
||||||
|
if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) &&
|
||||||
|
net->users.size() > 1) ||
|
||||||
|
(ci->type == ctx->id("$nextpnr_obuf") &&
|
||||||
|
(net->users.size() > 2 || net->driver.cell != nullptr)) ||
|
||||||
|
(ci->type == ctx->id("$nextpnr_iobuf") && ci->ports.at(ctx->id("I")).net != nullptr &&
|
||||||
|
ci->ports.at(ctx->id("I")).net->driver.cell != nullptr))
|
||||||
|
log_error("Pin B of %s '%s' connected to more than a single top level IO.\n",
|
||||||
|
trio->type.c_str(ctx), trio->name.c_str(ctx));
|
||||||
if (net != nullptr) {
|
if (net != nullptr) {
|
||||||
ctx->nets.erase(net->name);
|
ctx->nets.erase(net->name);
|
||||||
trio->ports.at(ctx->id("B")).net = nullptr;
|
trio->ports.at(ctx->id("B")).net = nullptr;
|
||||||
@ -1323,13 +1332,206 @@ class Ecp5Packer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preplace PLL
|
||||||
|
void preplace_plls()
|
||||||
|
{
|
||||||
|
std::set<BelId> available_plls;
|
||||||
|
for (auto bel : ctx->getBels()) {
|
||||||
|
if (ctx->getBelType(bel) == id_EHXPLLL && ctx->checkBelAvail(bel))
|
||||||
|
available_plls.insert(bel);
|
||||||
|
}
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == id_EHXPLLL && ci->attrs.count(ctx->id("BEL")))
|
||||||
|
available_plls.erase(ctx->getBelByName(ctx->id(ci->attrs.at(ctx->id("BEL")))));
|
||||||
|
}
|
||||||
|
// Place PLL connected to fixed drivers such as IO close to their source
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == id_EHXPLLL && !ci->attrs.count(ctx->id("BEL"))) {
|
||||||
|
const NetInfo *drivernet = net_or_nullptr(ci, id_CLKI);
|
||||||
|
if (drivernet == nullptr || drivernet->driver.cell == nullptr)
|
||||||
|
continue;
|
||||||
|
const CellInfo *drivercell = drivernet->driver.cell;
|
||||||
|
if (!drivercell->attrs.count(ctx->id("BEL")))
|
||||||
|
continue;
|
||||||
|
BelId drvbel = ctx->getBelByName(ctx->id(drivercell->attrs.at(ctx->id("BEL"))));
|
||||||
|
Loc drvloc = ctx->getBelLocation(drvbel);
|
||||||
|
BelId closest_pll;
|
||||||
|
int closest_distance = std::numeric_limits<int>::max();
|
||||||
|
for (auto bel : available_plls) {
|
||||||
|
Loc pllloc = ctx->getBelLocation(bel);
|
||||||
|
int distance = std::abs(drvloc.x - pllloc.x) + std::abs(drvloc.y - pllloc.y);
|
||||||
|
if (distance < closest_distance) {
|
||||||
|
closest_pll = bel;
|
||||||
|
closest_distance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (closest_pll == BelId())
|
||||||
|
log_error("failed to place PLL '%s'\n", ci->name.c_str(ctx));
|
||||||
|
available_plls.erase(closest_pll);
|
||||||
|
ci->attrs[ctx->id("BEL")] = ctx->getBelName(closest_pll).str(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Place PLLs driven by logic, etc, randomly
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == id_EHXPLLL && !ci->attrs.count(ctx->id("BEL"))) {
|
||||||
|
if (available_plls.empty())
|
||||||
|
log_error("failed to place PLL '%s'\n", ci->name.c_str(ctx));
|
||||||
|
BelId next_pll = *(available_plls.begin());
|
||||||
|
available_plls.erase(next_pll);
|
||||||
|
ci->attrs[ctx->id("BEL")] = ctx->getBelName(next_pll).str(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if two nets have identical constant drivers
|
||||||
|
bool equal_constant(NetInfo *a, NetInfo *b)
|
||||||
|
{
|
||||||
|
if (a->driver.cell == nullptr || b->driver.cell == nullptr)
|
||||||
|
return (a->driver.cell == nullptr && b->driver.cell == nullptr);
|
||||||
|
if (a->driver.cell->type != ctx->id("GND") && a->driver.cell->type != ctx->id("VCC"))
|
||||||
|
return false;
|
||||||
|
return a->driver.cell->type == b->driver.cell->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack IOLOGIC
|
||||||
|
void pack_iologic()
|
||||||
|
{
|
||||||
|
std::unordered_map<IdString, CellInfo *> pio_iologic;
|
||||||
|
|
||||||
|
auto set_iologic_sclk = [&](CellInfo *iol, CellInfo *prim, IdString port, bool input) {
|
||||||
|
NetInfo *sclk = nullptr;
|
||||||
|
if (prim->ports.count(port))
|
||||||
|
sclk = prim->ports[port].net;
|
||||||
|
if (sclk == nullptr) {
|
||||||
|
iol->params[input ? ctx->id("CLKIMUX") : ctx->id("CLKOMUX")] = "0";
|
||||||
|
} else {
|
||||||
|
iol->params[input ? ctx->id("CLKIMUX") : ctx->id("CLKOMUX")] = "CLK";
|
||||||
|
if (iol->ports[id_CLK].net != nullptr) {
|
||||||
|
if (iol->ports[id_CLK].net != sclk && !equal_constant(iol->ports[id_CLK].net, sclk))
|
||||||
|
log_error("IOLOGIC '%s' has conflicting clocks '%s' and '%s'\n", iol->name.c_str(ctx),
|
||||||
|
iol->ports[id_CLK].net->name.c_str(ctx), sclk->name.c_str(ctx));
|
||||||
|
} else {
|
||||||
|
connect_port(ctx, sclk, iol, id_CLK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prim->ports.count(port))
|
||||||
|
disconnect_port(ctx, prim, port);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto set_iologic_lsr = [&](CellInfo *iol, CellInfo *prim, IdString port, bool input) {
|
||||||
|
NetInfo *lsr = nullptr;
|
||||||
|
if (prim->ports.count(port))
|
||||||
|
lsr = prim->ports[port].net;
|
||||||
|
if (lsr == nullptr) {
|
||||||
|
iol->params[input ? ctx->id("LSRIMUX") : ctx->id("LSROMUX")] = "0";
|
||||||
|
} else {
|
||||||
|
iol->params[input ? ctx->id("LSRIMUX") : ctx->id("LSROMUX")] = "LSRMUX";
|
||||||
|
if (iol->ports[id_LSR].net != nullptr && !equal_constant(iol->ports[id_LSR].net, lsr)) {
|
||||||
|
if (iol->ports[id_LSR].net != lsr)
|
||||||
|
log_error("IOLOGIC '%s' has conflicting LSR signals '%s' and '%s'\n", iol->name.c_str(ctx),
|
||||||
|
iol->ports[id_LSR].net->name.c_str(ctx), lsr->name.c_str(ctx));
|
||||||
|
} else {
|
||||||
|
connect_port(ctx, lsr, iol, id_LSR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prim->ports.count(port))
|
||||||
|
disconnect_port(ctx, prim, port);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto set_iologic_mode = [&](CellInfo *iol, std::string mode) {
|
||||||
|
auto &curr_mode = iol->params[ctx->id("MODE")];
|
||||||
|
if (curr_mode != "NONE" && curr_mode != mode)
|
||||||
|
log_error("IOLOGIC '%s' has conflicting modes '%s' and '%s'\n", iol->name.c_str(ctx), curr_mode.c_str(),
|
||||||
|
mode.c_str());
|
||||||
|
curr_mode = mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto create_pio_iologic = [&](CellInfo *pio, CellInfo *curr) {
|
||||||
|
if (!pio->attrs.count(ctx->id("BEL")))
|
||||||
|
log_error("IOLOGIC functionality (DDR, DELAY, DQS, etc) can only be used with pin-constrained PIO "
|
||||||
|
"(while processing '%s').\n",
|
||||||
|
curr->name.c_str(ctx));
|
||||||
|
BelId bel = ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL"))));
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
log_info("IOLOGIC component %s connected to PIO Bel %s\n", curr->name.c_str(ctx),
|
||||||
|
ctx->getBelName(bel).c_str(ctx));
|
||||||
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
|
bool s = false;
|
||||||
|
if (loc.y == 0 || loc.y == (ctx->chip_info->height - 1))
|
||||||
|
s = true;
|
||||||
|
std::unique_ptr<CellInfo> iol =
|
||||||
|
create_ecp5_cell(ctx, s ? id_SIOLOGIC : id_IOLOGIC, pio->name.str(ctx) + "$IOL");
|
||||||
|
|
||||||
|
loc.z += s ? 2 : 4;
|
||||||
|
iol->attrs[ctx->id("BEL")] = ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx);
|
||||||
|
|
||||||
|
CellInfo *iol_ptr = iol.get();
|
||||||
|
pio_iologic[pio->name] = iol_ptr;
|
||||||
|
new_cells.push_back(std::move(iol));
|
||||||
|
return iol_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == ctx->id("IDDRX1F")) {
|
||||||
|
CellInfo *pio = net_driven_by(ctx, ci->ports.at(ctx->id("D")).net, is_trellis_io, id_O);
|
||||||
|
if (pio == nullptr || ci->ports.at(ctx->id("D")).net->users.size() > 1)
|
||||||
|
log_error("IDDRX1F '%s' D input must be connected only to a top level input\n",
|
||||||
|
ci->name.c_str(ctx));
|
||||||
|
CellInfo *iol;
|
||||||
|
if (pio_iologic.count(pio->name))
|
||||||
|
iol = pio_iologic.at(pio->name);
|
||||||
|
else
|
||||||
|
iol = create_pio_iologic(pio, ci);
|
||||||
|
set_iologic_mode(iol, "IDDRX1_ODDRX1");
|
||||||
|
replace_port(ci, ctx->id("D"), iol, id_PADDI);
|
||||||
|
set_iologic_sclk(iol, ci, ctx->id("SCLK"), true);
|
||||||
|
set_iologic_lsr(iol, ci, ctx->id("RST"), true);
|
||||||
|
replace_port(ci, ctx->id("Q0"), iol, id_RXDATA0);
|
||||||
|
replace_port(ci, ctx->id("Q1"), iol, id_RXDATA1);
|
||||||
|
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
||||||
|
packed_cells.insert(cell.first);
|
||||||
|
} else if (ci->type == ctx->id("ODDRX1F")) {
|
||||||
|
CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true);
|
||||||
|
if (pio == nullptr)
|
||||||
|
log_error("ODDRX1F '%s' Q output must be connected only to a top level output\n",
|
||||||
|
ci->name.c_str(ctx));
|
||||||
|
CellInfo *iol;
|
||||||
|
if (pio_iologic.count(pio->name))
|
||||||
|
iol = pio_iologic.at(pio->name);
|
||||||
|
else
|
||||||
|
iol = create_pio_iologic(pio, ci);
|
||||||
|
set_iologic_mode(iol, "IDDRX1_ODDRX1");
|
||||||
|
replace_port(ci, ctx->id("Q"), iol, id_IOLDO);
|
||||||
|
if (!pio->ports.count(id_IOLDO)) {
|
||||||
|
pio->ports[id_IOLDO].name = id_IOLDO;
|
||||||
|
pio->ports[id_IOLDO].type = PORT_IN;
|
||||||
|
}
|
||||||
|
replace_port(pio, id_I, pio, id_IOLDO);
|
||||||
|
pio->params[ctx->id("DATAMUX_ODDR")] = "IOLDO";
|
||||||
|
set_iologic_sclk(iol, ci, ctx->id("SCLK"), false);
|
||||||
|
set_iologic_lsr(iol, ci, ctx->id("RST"), false);
|
||||||
|
replace_port(ci, ctx->id("D0"), iol, id_TXDATA0);
|
||||||
|
replace_port(ci, ctx->id("D1"), iol, id_TXDATA1);
|
||||||
|
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
||||||
|
packed_cells.insert(cell.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush_cells();
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void pack()
|
void pack()
|
||||||
{
|
{
|
||||||
pack_io();
|
pack_io();
|
||||||
|
pack_iologic();
|
||||||
pack_ebr();
|
pack_ebr();
|
||||||
pack_dsps();
|
pack_dsps();
|
||||||
pack_dcus();
|
pack_dcus();
|
||||||
|
preplace_plls();
|
||||||
pack_constants();
|
pack_constants();
|
||||||
pack_dram();
|
pack_dram();
|
||||||
pack_carries();
|
pack_carries();
|
||||||
|
@ -41,6 +41,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
{
|
{
|
||||||
QSurfaceFormat fmt;
|
QSurfaceFormat fmt;
|
||||||
fmt.setSamples(10);
|
fmt.setSamples(10);
|
||||||
|
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
QSurfaceFormat::setDefaultFormat(fmt);
|
QSurfaceFormat::setDefaultFormat(fmt);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetConsoleCtrlHandler((PHANDLER_ROUTINE)WinHandler, TRUE);
|
SetConsoleCtrlHandler((PHANDLER_ROUTINE)WinHandler, TRUE);
|
||||||
|
@ -22,5 +22,6 @@
|
|||||||
<file>resources/route.png</file>
|
<file>resources/route.png</file>
|
||||||
<file>resources/time_add.png</file>
|
<file>resources/time_add.png</file>
|
||||||
<file>resources/open_json.png</file>
|
<file>resources/open_json.png</file>
|
||||||
|
<file>resources/py.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -189,6 +189,12 @@ void BaseMainWindow::createMenusAndBars()
|
|||||||
actionRoute->setEnabled(false);
|
actionRoute->setEnabled(false);
|
||||||
connect(actionRoute, &QAction::triggered, task, &TaskManager::route);
|
connect(actionRoute, &QAction::triggered, task, &TaskManager::route);
|
||||||
|
|
||||||
|
actionExecutePy = new QAction("Execute Python", this);
|
||||||
|
actionExecutePy->setIcon(QIcon(":/icons/resources/py.png"));
|
||||||
|
actionExecutePy->setStatusTip("Execute Python script");
|
||||||
|
actionExecutePy->setEnabled(true);
|
||||||
|
connect(actionExecutePy, &QAction::triggered, this, &BaseMainWindow::execute_python);
|
||||||
|
|
||||||
// Worker control toolbar actions
|
// Worker control toolbar actions
|
||||||
actionPlay = new QAction("Play", this);
|
actionPlay = new QAction("Play", this);
|
||||||
actionPlay->setIcon(QIcon(":/icons/resources/control_play.png"));
|
actionPlay->setIcon(QIcon(":/icons/resources/control_play.png"));
|
||||||
@ -249,6 +255,8 @@ void BaseMainWindow::createMenusAndBars()
|
|||||||
menuDesign->addAction(actionAssignBudget);
|
menuDesign->addAction(actionAssignBudget);
|
||||||
menuDesign->addAction(actionPlace);
|
menuDesign->addAction(actionPlace);
|
||||||
menuDesign->addAction(actionRoute);
|
menuDesign->addAction(actionRoute);
|
||||||
|
menuDesign->addSeparator();
|
||||||
|
menuDesign->addAction(actionExecutePy);
|
||||||
|
|
||||||
// Add Help menu actions
|
// Add Help menu actions
|
||||||
menuHelp->addAction(actionAbout);
|
menuHelp->addAction(actionAbout);
|
||||||
@ -268,6 +276,7 @@ void BaseMainWindow::createMenusAndBars()
|
|||||||
mainActionBar->addAction(actionAssignBudget);
|
mainActionBar->addAction(actionAssignBudget);
|
||||||
mainActionBar->addAction(actionPlace);
|
mainActionBar->addAction(actionPlace);
|
||||||
mainActionBar->addAction(actionRoute);
|
mainActionBar->addAction(actionRoute);
|
||||||
|
mainActionBar->addAction(actionExecutePy);
|
||||||
|
|
||||||
// Add worker control toolbar
|
// Add worker control toolbar
|
||||||
QToolBar *workerControlToolBar = new QToolBar("Worker");
|
QToolBar *workerControlToolBar = new QToolBar("Worker");
|
||||||
@ -412,6 +421,7 @@ void BaseMainWindow::disableActions()
|
|||||||
actionAssignBudget->setEnabled(false);
|
actionAssignBudget->setEnabled(false);
|
||||||
actionPlace->setEnabled(false);
|
actionPlace->setEnabled(false);
|
||||||
actionRoute->setEnabled(false);
|
actionRoute->setEnabled(false);
|
||||||
|
actionExecutePy->setEnabled(true);
|
||||||
|
|
||||||
actionPlay->setEnabled(false);
|
actionPlay->setEnabled(false);
|
||||||
actionPause->setEnabled(false);
|
actionPause->setEnabled(false);
|
||||||
@ -454,6 +464,14 @@ void BaseMainWindow::open_proj()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseMainWindow::execute_python()
|
||||||
|
{
|
||||||
|
QString fileName = QFileDialog::getOpenFileName(this, QString("Execute Python"), QString(), QString("*.py"));
|
||||||
|
if (!fileName.isEmpty()) {
|
||||||
|
console->execute_python(fileName.toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BaseMainWindow::notifyChangeContext() { Q_EMIT contextChanged(ctx.get()); }
|
void BaseMainWindow::notifyChangeContext() { Q_EMIT contextChanged(ctx.get()); }
|
||||||
void BaseMainWindow::save_proj()
|
void BaseMainWindow::save_proj()
|
||||||
{
|
{
|
||||||
|
@ -78,6 +78,8 @@ class BaseMainWindow : public QMainWindow
|
|||||||
void budget();
|
void budget();
|
||||||
void place();
|
void place();
|
||||||
|
|
||||||
|
void execute_python();
|
||||||
|
|
||||||
void pack_finished(bool status);
|
void pack_finished(bool status);
|
||||||
void budget_finish(bool status);
|
void budget_finish(bool status);
|
||||||
void place_finished(bool status);
|
void place_finished(bool status);
|
||||||
@ -122,6 +124,9 @@ class BaseMainWindow : public QMainWindow
|
|||||||
QAction *actionAssignBudget;
|
QAction *actionAssignBudget;
|
||||||
QAction *actionPlace;
|
QAction *actionPlace;
|
||||||
QAction *actionRoute;
|
QAction *actionRoute;
|
||||||
|
|
||||||
|
QAction *actionExecutePy;
|
||||||
|
|
||||||
QAction *actionPlay;
|
QAction *actionPlay;
|
||||||
QAction *actionPause;
|
QAction *actionPause;
|
||||||
QAction *actionStop;
|
QAction *actionStop;
|
||||||
|
@ -76,4 +76,21 @@ void PythonConsole::moveCursorToEnd()
|
|||||||
setTextCursor(cursor);
|
setTextCursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PythonConsole::execute_python(std::string filename)
|
||||||
|
{
|
||||||
|
int errorCode = 0;
|
||||||
|
std::string res;
|
||||||
|
res = pyinterpreter_execute_file(filename.c_str(), &errorCode);
|
||||||
|
if (res.size()) {
|
||||||
|
if (errorCode) {
|
||||||
|
setTextColor(ERROR_COLOR);
|
||||||
|
} else {
|
||||||
|
setTextColor(OUTPUT_COLOR);
|
||||||
|
}
|
||||||
|
append(res.c_str());
|
||||||
|
setTextColor(NORMAL_COLOR);
|
||||||
|
moveCursorToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -43,6 +43,7 @@ class PythonConsole : public QTextEdit, public ParseListener
|
|||||||
void displayString(QString text);
|
void displayString(QString text);
|
||||||
void moveCursorToEnd();
|
void moveCursorToEnd();
|
||||||
virtual void parseEvent(const ParseMessage &message);
|
virtual void parseEvent(const ParseMessage &message);
|
||||||
|
void execute_python(std::string filename);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const QColor NORMAL_COLOR;
|
static const QColor NORMAL_COLOR;
|
||||||
|
@ -114,4 +114,6 @@ void PythonTab::clearBuffer() { console->clear(); }
|
|||||||
|
|
||||||
void PythonTab::info(std::string str) { console->displayString(str.c_str()); }
|
void PythonTab::info(std::string str) { console->displayString(str.c_str()); }
|
||||||
|
|
||||||
|
void PythonTab::execute_python(std::string filename) { console->execute_python(filename); }
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -45,6 +45,7 @@ class PythonTab : public QWidget
|
|||||||
void newContext(Context *ctx);
|
void newContext(Context *ctx);
|
||||||
void info(std::string str);
|
void info(std::string str);
|
||||||
void clearBuffer();
|
void clearBuffer();
|
||||||
|
void execute_python(std::string filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PythonConsole *console;
|
PythonConsole *console;
|
||||||
|
BIN
gui/resources/py.png
Normal file
BIN
gui/resources/py.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
@ -126,6 +126,7 @@ TaskManager::TaskManager() : toTerminate(false), toPause(false)
|
|||||||
|
|
||||||
TaskManager::~TaskManager()
|
TaskManager::~TaskManager()
|
||||||
{
|
{
|
||||||
|
log_write_function = nullptr;
|
||||||
if (workerThread.isRunning())
|
if (workerThread.isRunning())
|
||||||
terminate_thread();
|
terminate_thread();
|
||||||
workerThread.quit();
|
workerThread.quit();
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "placer1.h"
|
#include "placer1.h"
|
||||||
#include "router1.h"
|
#include "router1.h"
|
||||||
|
#include "timing_opt.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
@ -626,7 +627,18 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); }
|
bool Arch::place()
|
||||||
|
{
|
||||||
|
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
|
||||||
|
return false;
|
||||||
|
if (bool_or_default(settings, id("opt_timing"), false)) {
|
||||||
|
TimingOptCfg tocfg(getCtx());
|
||||||
|
tocfg.cellTypes.insert(id_ICESTORM_LC);
|
||||||
|
return timing_opt(getCtx(), tocfg);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
|
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
|
||||||
|
|
||||||
@ -950,8 +962,12 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
|
|||||||
if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2)
|
if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2)
|
||||||
return TMG_IGNORE;
|
return TMG_IGNORE;
|
||||||
return TMG_ENDPOINT;
|
return TMG_ENDPOINT;
|
||||||
|
} else if (cell->type == id_SB_LEDDA_IP) {
|
||||||
|
if (port == id_CLK || port == id_CLOCK)
|
||||||
|
return TMG_CLOCK_INPUT;
|
||||||
|
return TMG_IGNORE;
|
||||||
}
|
}
|
||||||
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
|
log_error("cell type '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this), cell->name.c_str(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
|
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
|
||||||
|
@ -156,7 +156,9 @@ void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *ce
|
|||||||
// Lattice's weird string style params, not sure if
|
// Lattice's weird string style params, not sure if
|
||||||
// prefixes other than 0b should be supported, only 0b features in docs
|
// prefixes other than 0b should be supported, only 0b features in docs
|
||||||
std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
|
std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
|
||||||
assert(raw.substr(0, 2) == "0b");
|
if (raw.substr(0, 2) != "0b")
|
||||||
|
log_error("expected configuration string starting with '0b' for parameter '%s' on cell '%s'\n",
|
||||||
|
p.first.c_str(), cell->name.c_str(ctx));
|
||||||
raw = raw.substr(2);
|
raw = raw.substr(2);
|
||||||
value.resize(raw.length());
|
value.resize(raw.length());
|
||||||
for (int i = 0; i < (int)raw.length(); i++) {
|
for (int i = 0; i < (int)raw.length(); i++) {
|
||||||
@ -168,7 +170,13 @@ void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *ce
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int ival = get_param_or_def(cell, ctx->id(p.first), 0);
|
int ival;
|
||||||
|
try {
|
||||||
|
ival = get_param_or_def(cell, ctx->id(p.first), 0);
|
||||||
|
} catch (std::invalid_argument &e) {
|
||||||
|
log_error("expected numeric value for parameter '%s' on cell '%s'\n", p.first.c_str(),
|
||||||
|
cell->name.c_str(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < p.second; i++)
|
for (int i = 0; i < p.second; i++)
|
||||||
value.push_back((ival >> i) & 0x1);
|
value.push_back((ival >> i) & 0x1);
|
||||||
@ -486,7 +494,7 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
|
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
|
||||||
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
|
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
|
||||||
bool pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
|
bool pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
|
||||||
bool lvds = get_param_str_or_def(cell.second.get(), ctx->id("IO_STANDARD")) == "SB_LVDS_INPUT";
|
bool lvds = cell.second->ioInfo.lvds;
|
||||||
bool used_by_pll_out = sb_io_used_by_pll_out.count(Loc(x, y, z)) > 0;
|
bool used_by_pll_out = sb_io_used_by_pll_out.count(Loc(x, y, z)) > 0;
|
||||||
bool used_by_pll_pad = sb_io_used_by_pll_pad.count(Loc(x, y, z)) > 0;
|
bool used_by_pll_pad = sb_io_used_by_pll_pad.count(Loc(x, y, z)) > 0;
|
||||||
|
|
||||||
@ -495,27 +503,21 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
|
set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
|
||||||
}
|
}
|
||||||
set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
|
set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
|
||||||
|
|
||||||
|
bool input_en = false;
|
||||||
|
if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
|
||||||
|
(ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
|
||||||
|
input_en = true;
|
||||||
|
}
|
||||||
|
input_en = (input_en & !used_by_pll_out) | used_by_pll_pad;
|
||||||
|
input_en |= cell.second->ioInfo.global;
|
||||||
|
|
||||||
|
if (!lvds) {
|
||||||
auto ieren = get_ieren(bi, x, y, z);
|
auto ieren = get_ieren(bi, x, y, z);
|
||||||
int iex, iey, iez;
|
int iex, iey, iez;
|
||||||
std::tie(iex, iey, iez) = ieren;
|
std::tie(iex, iey, iez) = ieren;
|
||||||
NPNR_ASSERT(iez != -1);
|
NPNR_ASSERT(iez != -1);
|
||||||
|
|
||||||
bool input_en;
|
|
||||||
if (lvds) {
|
|
||||||
input_en = false;
|
|
||||||
pullup = false;
|
|
||||||
} else {
|
|
||||||
if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
|
|
||||||
(ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
|
|
||||||
input_en = true;
|
|
||||||
} else {
|
|
||||||
input_en = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input_en = (input_en & !used_by_pll_out) | used_by_pll_pad;
|
|
||||||
input_en |= cell.second->ioInfo.global;
|
|
||||||
|
|
||||||
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
||||||
@ -524,6 +526,48 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->args.type == ArchArgs::UP5K) {
|
||||||
|
std::string pullup_resistor = "100K";
|
||||||
|
if (cell.second->attrs.count(ctx->id("PULLUP_RESISTOR")))
|
||||||
|
pullup_resistor = cell.second->attrs.at(ctx->id("PULLUP_RESISTOR"));
|
||||||
|
NPNR_ASSERT(pullup_resistor == "100K" || pullup_resistor == "10K" || pullup_resistor == "6P8K" ||
|
||||||
|
pullup_resistor == "3P3K");
|
||||||
|
if (iez == 0) {
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39",
|
||||||
|
(!pullup) || (pullup_resistor != "100K"));
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_36", pullup && pullup_resistor == "3P3K");
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_37", pullup && pullup_resistor == "6P8K");
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_38", pullup && pullup_resistor == "10K");
|
||||||
|
} else if (iez == 1) {
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35",
|
||||||
|
(!pullup) || (pullup_resistor != "100K"));
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_32", pullup && pullup_resistor == "3P3K");
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_33", pullup && pullup_resistor == "6P8K");
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_34", pullup && pullup_resistor == "10K");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NPNR_ASSERT(z == 0);
|
||||||
|
// Only enable the actual LVDS buffer if input is used for something
|
||||||
|
set_config(ti, config.at(y).at(x), "IoCtrl.LVDS", input_en);
|
||||||
|
|
||||||
|
// Set both IO config
|
||||||
|
for (int cz = 0; cz < 2; cz++) {
|
||||||
|
auto ieren = get_ieren(bi, x, y, cz);
|
||||||
|
int iex, iey, iez;
|
||||||
|
std::tie(iex, iey, iez) = ieren;
|
||||||
|
NPNR_ASSERT(iez != -1);
|
||||||
|
|
||||||
|
pullup &= !input_en; /* If input is used, force disable pullups */
|
||||||
|
|
||||||
|
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
||||||
|
} else {
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), false);
|
||||||
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->args.type == ArchArgs::UP5K) {
|
if (ctx->args.type == ArchArgs::UP5K) {
|
||||||
if (iez == 0) {
|
if (iez == 0) {
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
|
||||||
@ -531,29 +575,6 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lvds) {
|
|
||||||
NPNR_ASSERT(z == 0);
|
|
||||||
set_config(ti, config.at(y).at(x), "IoCtrl.LVDS", true);
|
|
||||||
// Set comp IO config
|
|
||||||
auto comp_ieren = get_ieren(bi, x, y, 1);
|
|
||||||
int ciex, ciey, ciez;
|
|
||||||
std::tie(ciex, ciey, ciez) = comp_ieren;
|
|
||||||
|
|
||||||
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
|
||||||
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), !input_en);
|
|
||||||
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
|
|
||||||
} else {
|
|
||||||
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), input_en);
|
|
||||||
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->args.type == ArchArgs::UP5K) {
|
|
||||||
if (ciez == 0) {
|
|
||||||
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_39", !pullup);
|
|
||||||
} else if (iez == 1) {
|
|
||||||
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_35", !pullup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (cell.second->type == ctx->id("SB_GB")) {
|
} else if (cell.second->type == ctx->id("SB_GB")) {
|
||||||
@ -591,7 +612,8 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
{"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}};
|
{"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}};
|
||||||
configure_extra_cell(config, ctx, cell.second.get(), rgba_params, true, std::string("IpConfig."));
|
configure_extra_cell(config, ctx, cell.second.get(), rgba_params, true, std::string("IpConfig."));
|
||||||
set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGBA_DRV_EN", true, "IpConfig.");
|
set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGBA_DRV_EN", true, "IpConfig.");
|
||||||
} else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
|
} else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC") ||
|
||||||
|
cell.second->type == ctx->id("SB_LEDDA_IP")) {
|
||||||
// No config needed
|
// No config needed
|
||||||
} else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
|
} else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
|
||||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||||
@ -688,7 +710,7 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
int iex, iey, iez;
|
int iex, iey, iez;
|
||||||
std::tie(iex, iey, iez) = ieren;
|
std::tie(iex, iey, iez) = ieren;
|
||||||
if (iez != -1) {
|
if (iez != -1) {
|
||||||
// IO is not actually unused if part of an LVDS pair
|
// If IO is in LVDS pair, it will be configured by the other pair
|
||||||
if (z == 1) {
|
if (z == 1) {
|
||||||
BelId lvds0 = ctx->getBelByLocation(Loc{x, y, 0});
|
BelId lvds0 = ctx->getBelByLocation(Loc{x, y, 0});
|
||||||
const CellInfo *lvds0cell = ctx->getBoundBelCell(lvds0);
|
const CellInfo *lvds0cell = ctx->getBoundBelCell(lvds0);
|
||||||
|
@ -260,6 +260,21 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
|
|||||||
add_port(ctx, new_cell.get(), "RGB0", PORT_OUT);
|
add_port(ctx, new_cell.get(), "RGB0", PORT_OUT);
|
||||||
add_port(ctx, new_cell.get(), "RGB1", PORT_OUT);
|
add_port(ctx, new_cell.get(), "RGB1", PORT_OUT);
|
||||||
add_port(ctx, new_cell.get(), "RGB2", PORT_OUT);
|
add_port(ctx, new_cell.get(), "RGB2", PORT_OUT);
|
||||||
|
} else if (type == ctx->id("SB_LEDDA_IP")) {
|
||||||
|
add_port(ctx, new_cell.get(), "LEDDCS", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "LEDDCLK", PORT_IN);
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
add_port(ctx, new_cell.get(), "LEDDDAT" + std::to_string(i), PORT_IN);
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
add_port(ctx, new_cell.get(), "LEDDADDR" + std::to_string(i), PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "LEDDDEN", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "LEDDEXE", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "LEDDRST", PORT_IN); // doesn't actually exist, for icecube code compatibility
|
||||||
|
// only
|
||||||
|
add_port(ctx, new_cell.get(), "PWMOUT0", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "PWMOUT1", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "PWMOUT2", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "LEDDON", PORT_OUT);
|
||||||
} else {
|
} else {
|
||||||
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
|
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,8 @@ inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell-
|
|||||||
|
|
||||||
inline bool is_sb_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGBA_DRV"); }
|
inline bool is_sb_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGBA_DRV"); }
|
||||||
|
|
||||||
|
inline bool is_sb_ledda_ip(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LEDDA_IP"); }
|
||||||
|
|
||||||
inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
|
inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
{
|
{
|
||||||
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
|
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
|
||||||
|
@ -62,7 +62,7 @@ class ChainConstrainer
|
|||||||
bool split_chain = (!ctx->logicCellsCompatible(tile.data(), tile.size())) ||
|
bool split_chain = (!ctx->logicCellsCompatible(tile.data(), tile.size())) ||
|
||||||
(int(chains.back().cells.size()) > max_length);
|
(int(chains.back().cells.size()) > max_length);
|
||||||
if (split_chain) {
|
if (split_chain) {
|
||||||
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
|
CellInfo *passout = make_carry_pass_out((*(curr_cell - 1))->ports.at(ctx->id("COUT")));
|
||||||
tile.pop_back();
|
tile.pop_back();
|
||||||
chains.back().cells.back() = passout;
|
chains.back().cells.back() = passout;
|
||||||
start_of_chain = true;
|
start_of_chain = true;
|
||||||
@ -74,10 +74,10 @@ class ChainConstrainer
|
|||||||
(net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), false) !=
|
(net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), false) !=
|
||||||
net_only_drives(ctx, carry_net, is_lc, ctx->id("CIN"), false)) ||
|
net_only_drives(ctx, carry_net, is_lc, ctx->id("CIN"), false)) ||
|
||||||
(at_end && !net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), true))) {
|
(at_end && !net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), true))) {
|
||||||
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
|
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")),
|
||||||
|
at_end ? nullptr : *(curr_cell + 1));
|
||||||
chains.back().cells.push_back(passout);
|
chains.back().cells.push_back(passout);
|
||||||
tile.push_back(passout);
|
tile.push_back(passout);
|
||||||
start_of_chain = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++curr_cell;
|
++curr_cell;
|
||||||
@ -87,30 +87,75 @@ class ChainConstrainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert a logic cell to legalise a COUT->fabric connection
|
// Insert a logic cell to legalise a COUT->fabric connection
|
||||||
CellInfo *make_carry_pass_out(PortInfo &cout_port)
|
CellInfo *make_carry_pass_out(PortInfo &cout_port, CellInfo *cin_cell = nullptr)
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(cout_port.net != nullptr);
|
NPNR_ASSERT(cout_port.net != nullptr);
|
||||||
std::unique_ptr<CellInfo> lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
|
std::unique_ptr<CellInfo> lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
|
||||||
lc->params[ctx->id("LUT_INIT")] = "65280"; // 0xff00: O = I3
|
lc->params[ctx->id("LUT_INIT")] = "65280"; // 0xff00: O = I3
|
||||||
lc->params[ctx->id("CARRY_ENABLE")] = "1";
|
lc->params[ctx->id("CARRY_ENABLE")] = "1";
|
||||||
lc->ports.at(ctx->id("O")).net = cout_port.net;
|
lc->ports.at(id_O).net = cout_port.net;
|
||||||
std::unique_ptr<NetInfo> co_i3_net(new NetInfo());
|
std::unique_ptr<NetInfo> co_i3_net(new NetInfo());
|
||||||
co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3");
|
co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3");
|
||||||
co_i3_net->driver = cout_port.net->driver;
|
co_i3_net->driver = cout_port.net->driver;
|
||||||
PortRef i3_r;
|
PortRef i3_r;
|
||||||
i3_r.port = ctx->id("I3");
|
i3_r.port = id_I3;
|
||||||
i3_r.cell = lc.get();
|
i3_r.cell = lc.get();
|
||||||
co_i3_net->users.push_back(i3_r);
|
co_i3_net->users.push_back(i3_r);
|
||||||
PortRef o_r;
|
PortRef o_r;
|
||||||
o_r.port = ctx->id("O");
|
o_r.port = id_O;
|
||||||
o_r.cell = lc.get();
|
o_r.cell = lc.get();
|
||||||
cout_port.net->driver = o_r;
|
cout_port.net->driver = o_r;
|
||||||
lc->ports.at(ctx->id("I3")).net = co_i3_net.get();
|
lc->ports.at(id_I3).net = co_i3_net.get();
|
||||||
cout_port.net = co_i3_net.get();
|
cout_port.net = co_i3_net.get();
|
||||||
|
|
||||||
IdString co_i3_name = co_i3_net->name;
|
IdString co_i3_name = co_i3_net->name;
|
||||||
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
|
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
|
||||||
ctx->nets[co_i3_name] = std::move(co_i3_net);
|
ctx->nets[co_i3_name] = std::move(co_i3_net);
|
||||||
|
|
||||||
|
// If COUT also connects to a CIN; preserve the carry chain
|
||||||
|
if (cin_cell) {
|
||||||
|
std::unique_ptr<NetInfo> co_cin_net(new NetInfo());
|
||||||
|
co_cin_net->name = ctx->id(lc->name.str(ctx) + "$COUT");
|
||||||
|
|
||||||
|
// Connect I1 to 1 to preserve carry chain
|
||||||
|
NetInfo *vcc = ctx->nets.at(ctx->id("$PACKER_VCC_NET")).get();
|
||||||
|
lc->ports.at(id_I1).net = vcc;
|
||||||
|
PortRef i1_r;
|
||||||
|
i1_r.port = id_I1;
|
||||||
|
i1_r.cell = lc.get();
|
||||||
|
vcc->users.push_back(i1_r);
|
||||||
|
|
||||||
|
// Connect co_cin_net to the COUT of the LC
|
||||||
|
PortRef co_r;
|
||||||
|
co_r.port = id_COUT;
|
||||||
|
co_r.cell = lc.get();
|
||||||
|
co_cin_net->driver = co_r;
|
||||||
|
lc->ports.at(id_COUT).net = co_cin_net.get();
|
||||||
|
|
||||||
|
// Find the user corresponding to the next CIN
|
||||||
|
int replaced_ports = 0;
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info("cell: %s\n", cin_cell->name.c_str(ctx));
|
||||||
|
for (auto port : {id_CIN, id_I3}) {
|
||||||
|
auto &usr = lc->ports.at(id_O).net->users;
|
||||||
|
if (ctx->debug)
|
||||||
|
for (auto user : usr)
|
||||||
|
log_info("%s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx));
|
||||||
|
auto fnd_user = std::find_if(usr.begin(), usr.end(),
|
||||||
|
[&](const PortRef &pr) { return pr.cell == cin_cell && pr.port == port; });
|
||||||
|
if (fnd_user != usr.end()) {
|
||||||
|
co_cin_net->users.push_back(*fnd_user);
|
||||||
|
usr.erase(fnd_user);
|
||||||
|
cin_cell->ports.at(port).net = co_cin_net.get();
|
||||||
|
++replaced_ports;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NPNR_ASSERT(replaced_ports > 0);
|
||||||
|
IdString co_cin_name = co_cin_net->name;
|
||||||
|
NPNR_ASSERT(ctx->nets.find(co_cin_name) == ctx->nets.end());
|
||||||
|
ctx->nets[co_cin_name] = std::move(co_cin_net);
|
||||||
|
}
|
||||||
|
|
||||||
IdString name = lc->name;
|
IdString name = lc->name;
|
||||||
ctx->assignCellInfo(lc.get());
|
ctx->assignCellInfo(lc.get());
|
||||||
ctx->cells[lc->name] = std::move(lc);
|
ctx->cells[lc->name] = std::move(lc);
|
||||||
@ -163,8 +208,8 @@ class ChainConstrainer
|
|||||||
|
|
||||||
void process_carries()
|
void process_carries()
|
||||||
{
|
{
|
||||||
std::vector<CellChain> carry_chains =
|
std::vector<CellChain> carry_chains = find_chains(
|
||||||
find_chains(ctx, [](const Context *ctx, const CellInfo *cell) { return is_lc(ctx, cell); },
|
ctx, [](const Context *ctx, const CellInfo *cell) { return is_lc(ctx, cell); },
|
||||||
[](const Context *ctx, const
|
[](const Context *ctx, const
|
||||||
|
|
||||||
CellInfo *cell) {
|
CellInfo *cell) {
|
||||||
@ -172,18 +217,20 @@ class ChainConstrainer
|
|||||||
net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT"));
|
net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT"));
|
||||||
if (carry_prev != nullptr)
|
if (carry_prev != nullptr)
|
||||||
return carry_prev;
|
return carry_prev;
|
||||||
/*CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(ctx->id("I3")).net, is_lc,
|
CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(ctx->id("I3")).net, is_lc, ctx->id("COUT"));
|
||||||
ctx->id("COUT")); if (i3_prev != nullptr) return i3_prev;*/
|
if (i3_prev != nullptr)
|
||||||
|
return i3_prev;
|
||||||
return (CellInfo *)nullptr;
|
return (CellInfo *)nullptr;
|
||||||
},
|
},
|
||||||
[](const Context *ctx, const CellInfo *cell) {
|
[](const Context *ctx, const CellInfo *cell) {
|
||||||
CellInfo *carry_next = net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc,
|
CellInfo *carry_next =
|
||||||
ctx->id("CIN"), false);
|
net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("CIN"), false);
|
||||||
if (carry_next != nullptr)
|
if (carry_next != nullptr)
|
||||||
return carry_next;
|
return carry_next;
|
||||||
/*CellInfo *i3_next =
|
CellInfo *i3_next =
|
||||||
net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("I3"),
|
net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("I3"), false);
|
||||||
false); if (i3_next != nullptr) return i3_next;*/
|
if (i3_next != nullptr)
|
||||||
|
return i3_next;
|
||||||
return (CellInfo *)nullptr;
|
return (CellInfo *)nullptr;
|
||||||
});
|
});
|
||||||
std::unordered_set<IdString> chained;
|
std::unordered_set<IdString> chained;
|
||||||
|
@ -65,7 +65,13 @@ po::options_description Ice40CommandHandler::getArchOptions()
|
|||||||
specific.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
|
specific.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
|
||||||
specific.add_options()("asc", po::value<std::string>(), "asc bitstream file to write");
|
specific.add_options()("asc", po::value<std::string>(), "asc bitstream file to write");
|
||||||
specific.add_options()("read", po::value<std::string>(), "asc bitstream file to read");
|
specific.add_options()("read", po::value<std::string>(), "asc bitstream file to read");
|
||||||
|
specific.add_options()("promote-logic",
|
||||||
|
"enable promotion of 'logic' globals (in addition to clk/ce/sr by default)");
|
||||||
|
specific.add_options()("no-promote-globals", "disable all global promotion");
|
||||||
|
specific.add_options()("opt-timing", "run post-placement timing optimisation pass (experimental)");
|
||||||
specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
|
specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
|
||||||
|
specific.add_options()("pcf-allow-unconstrained", "don't require PCF to constrain all IO");
|
||||||
|
|
||||||
return specific;
|
return specific;
|
||||||
}
|
}
|
||||||
void Ice40CommandHandler::validate()
|
void Ice40CommandHandler::validate()
|
||||||
@ -83,6 +89,8 @@ void Ice40CommandHandler::customAfterLoad(Context *ctx)
|
|||||||
std::ifstream pcf(filename);
|
std::ifstream pcf(filename);
|
||||||
if (!apply_pcf(ctx, filename, pcf))
|
if (!apply_pcf(ctx, filename, pcf))
|
||||||
log_error("Loading PCF failed.\n");
|
log_error("Loading PCF failed.\n");
|
||||||
|
} else {
|
||||||
|
log_warning("No PCF file specified; IO pins will be placed automatically\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Ice40CommandHandler::customBitstream(Context *ctx)
|
void Ice40CommandHandler::customBitstream(Context *ctx)
|
||||||
@ -152,7 +160,18 @@ std::unique_ptr<Context> Ice40CommandHandler::createContext()
|
|||||||
if (vm.count("package"))
|
if (vm.count("package"))
|
||||||
chipArgs.package = vm["package"].as<std::string>();
|
chipArgs.package = vm["package"].as<std::string>();
|
||||||
|
|
||||||
return std::unique_ptr<Context>(new Context(chipArgs));
|
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
||||||
|
|
||||||
|
if (vm.count("promote-logic"))
|
||||||
|
ctx->settings[ctx->id("promote_logic")] = "1";
|
||||||
|
if (vm.count("no-promote-globals"))
|
||||||
|
ctx->settings[ctx->id("no_promote_globals")] = "1";
|
||||||
|
if (vm.count("opt-timing"))
|
||||||
|
ctx->settings[ctx->id("opt_timing")] = "1";
|
||||||
|
if (vm.count("pcf-allow-unconstrained"))
|
||||||
|
ctx->settings[ctx->id("pcf_allow_unconstrained")] = "1";
|
||||||
|
|
||||||
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
318
ice40/pack.cc
318
ice40/pack.cc
@ -518,10 +518,10 @@ static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
|
|||||||
!is_sb_pll40(ctx, port.cell);
|
!is_sb_pll40(ctx, port.cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
|
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic, int fanout)
|
||||||
{
|
{
|
||||||
log_info("promoting %s%s%s%s\n", net->name.c_str(ctx), is_reset ? " [reset]" : "", is_cen ? " [cen]" : "",
|
log_info("promoting %s%s%s%s (fanout %d)\n", net->name.c_str(ctx), is_reset ? " [reset]" : "",
|
||||||
is_logic ? " [logic]" : "");
|
is_cen ? " [cen]" : "", is_logic ? " [logic]" : "", fanout);
|
||||||
|
|
||||||
std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk"));
|
std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk"));
|
||||||
std::unique_ptr<CellInfo> gb = create_ice_cell(ctx, ctx->id("SB_GB"), "$gbuf_" + glb_name);
|
std::unique_ptr<CellInfo> gb = create_ice_cell(ctx, ctx->id("SB_GB"), "$gbuf_" + glb_name);
|
||||||
@ -565,7 +565,8 @@ static void promote_globals(Context *ctx)
|
|||||||
{
|
{
|
||||||
log_info("Promoting globals..\n");
|
log_info("Promoting globals..\n");
|
||||||
const int logic_fanout_thresh = 15;
|
const int logic_fanout_thresh = 15;
|
||||||
const int enable_fanout_thresh = 5;
|
const int enable_fanout_thresh = 15;
|
||||||
|
const int reset_fanout_thresh = 15;
|
||||||
std::map<IdString, int> clock_count, reset_count, cen_count, logic_count;
|
std::map<IdString, int> clock_count, reset_count, cen_count, logic_count;
|
||||||
for (auto net : sorted(ctx->nets)) {
|
for (auto net : sorted(ctx->nets)) {
|
||||||
NetInfo *ni = net.second;
|
NetInfo *ni = net.second;
|
||||||
@ -637,18 +638,20 @@ static void promote_globals(Context *ctx)
|
|||||||
});
|
});
|
||||||
if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh &&
|
if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh &&
|
||||||
(global_logic->second > global_cen->second || prom_cens >= cens_available) &&
|
(global_logic->second > global_cen->second || prom_cens >= cens_available) &&
|
||||||
(global_logic->second > global_reset->second || prom_resets >= resets_available)) {
|
(global_logic->second > global_reset->second || prom_resets >= resets_available) &&
|
||||||
|
bool_or_default(ctx->settings, ctx->id("promote_logic"), false)) {
|
||||||
NetInfo *logicnet = ctx->nets[global_logic->first].get();
|
NetInfo *logicnet = ctx->nets[global_logic->first].get();
|
||||||
insert_global(ctx, logicnet, false, false, true);
|
insert_global(ctx, logicnet, false, false, true, global_logic->second);
|
||||||
++prom_globals;
|
++prom_globals;
|
||||||
++prom_logics;
|
++prom_logics;
|
||||||
clock_count.erase(logicnet->name);
|
clock_count.erase(logicnet->name);
|
||||||
reset_count.erase(logicnet->name);
|
reset_count.erase(logicnet->name);
|
||||||
cen_count.erase(logicnet->name);
|
cen_count.erase(logicnet->name);
|
||||||
logic_count.erase(logicnet->name);
|
logic_count.erase(logicnet->name);
|
||||||
} else if (global_reset->second > global_clock->second && prom_resets < resets_available) {
|
} else if (global_reset->second > global_clock->second && prom_resets < resets_available &&
|
||||||
|
global_reset->second > reset_fanout_thresh) {
|
||||||
NetInfo *rstnet = ctx->nets[global_reset->first].get();
|
NetInfo *rstnet = ctx->nets[global_reset->first].get();
|
||||||
insert_global(ctx, rstnet, true, false, false);
|
insert_global(ctx, rstnet, true, false, false, global_reset->second);
|
||||||
++prom_globals;
|
++prom_globals;
|
||||||
++prom_resets;
|
++prom_resets;
|
||||||
clock_count.erase(rstnet->name);
|
clock_count.erase(rstnet->name);
|
||||||
@ -658,7 +661,7 @@ static void promote_globals(Context *ctx)
|
|||||||
} else if (global_cen->second > global_clock->second && prom_cens < cens_available &&
|
} else if (global_cen->second > global_clock->second && prom_cens < cens_available &&
|
||||||
global_cen->second > enable_fanout_thresh) {
|
global_cen->second > enable_fanout_thresh) {
|
||||||
NetInfo *cennet = ctx->nets[global_cen->first].get();
|
NetInfo *cennet = ctx->nets[global_cen->first].get();
|
||||||
insert_global(ctx, cennet, false, true, false);
|
insert_global(ctx, cennet, false, true, false, global_cen->second);
|
||||||
++prom_globals;
|
++prom_globals;
|
||||||
++prom_cens;
|
++prom_cens;
|
||||||
clock_count.erase(cennet->name);
|
clock_count.erase(cennet->name);
|
||||||
@ -667,7 +670,7 @@ static void promote_globals(Context *ctx)
|
|||||||
logic_count.erase(cennet->name);
|
logic_count.erase(cennet->name);
|
||||||
} else if (global_clock->second != 0) {
|
} else if (global_clock->second != 0) {
|
||||||
NetInfo *clknet = ctx->nets[global_clock->first].get();
|
NetInfo *clknet = ctx->nets[global_clock->first].get();
|
||||||
insert_global(ctx, clknet, false, false, false);
|
insert_global(ctx, clknet, false, false, false, global_clock->second);
|
||||||
++prom_globals;
|
++prom_globals;
|
||||||
clock_count.erase(clknet->name);
|
clock_count.erase(clknet->name);
|
||||||
reset_count.erase(clknet->name);
|
reset_count.erase(clknet->name);
|
||||||
@ -679,6 +682,202 @@ static void promote_globals(Context *ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Figure out where to place PLLs
|
||||||
|
static void place_plls(Context *ctx)
|
||||||
|
{
|
||||||
|
std::map<BelId, std::pair<BelPin, BelPin>> pll_all_bels;
|
||||||
|
std::map<BelId, CellInfo *> pll_used_bels;
|
||||||
|
std::vector<CellInfo *> pll_cells;
|
||||||
|
std::map<BelId, CellInfo *> bel2io;
|
||||||
|
|
||||||
|
log_info("Placing PLLs..\n");
|
||||||
|
|
||||||
|
// Find all the PLLs BELs and matching IO sites
|
||||||
|
for (auto bel : ctx->getBels()) {
|
||||||
|
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
|
||||||
|
continue;
|
||||||
|
if (ctx->isBelLocked(bel))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto io_a_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
|
||||||
|
auto io_b_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_B);
|
||||||
|
|
||||||
|
pll_all_bels[bel] = std::make_pair(io_a_pin, io_b_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all the PLLs cells we need to place and do pre-checks
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (!is_sb_pll40(ctx, ci))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If it's constrained already, add to already used list
|
||||||
|
if (ci->attrs.count(ctx->id("BEL"))) {
|
||||||
|
BelId bel_constrain = ctx->getBelByName(ctx->id(ci->attrs[ctx->id("BEL")]));
|
||||||
|
if (pll_all_bels.count(bel_constrain) == 0)
|
||||||
|
log_error("PLL '%s' is constrained to invalid BEL '%s'\n", ci->name.c_str(ctx),
|
||||||
|
ci->attrs[ctx->id("BEL")].c_str());
|
||||||
|
pll_used_bels[bel_constrain] = ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it to our list of PLLs to process
|
||||||
|
pll_cells.push_back(ci);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan all the PAD PLLs
|
||||||
|
for (auto ci : pll_cells) {
|
||||||
|
if (!is_sb_pll40_pad(ctx, ci))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check PACKAGEPIN connection
|
||||||
|
if (!ci->ports.count(ctx->id("PACKAGEPIN")))
|
||||||
|
log_error("PLL '%s' is of PAD type but doesn't have a PACKAGEPIN port\n", ci->name.c_str(ctx));
|
||||||
|
|
||||||
|
NetInfo *ni = ci->ports.at(ctx->id("PACKAGEPIN")).net;
|
||||||
|
if (ni == nullptr || ni->driver.cell == nullptr)
|
||||||
|
log_error("PLL '%s' is of PAD type but doesn't have a valid PACKAGEPIN connection\n", ci->name.c_str(ctx));
|
||||||
|
|
||||||
|
CellInfo *io_cell = ni->driver.cell;
|
||||||
|
if (io_cell->type != id_SB_IO || ni->driver.port != id_D_IN_0)
|
||||||
|
log_error("PLL '%s' has a PACKAGEPIN driven by an %s, should be directly connected to an input "
|
||||||
|
"SB_IO.D_IN_0 port\n",
|
||||||
|
ci->name.c_str(ctx), io_cell->type.c_str(ctx));
|
||||||
|
if (ni->users.size() != 1)
|
||||||
|
log_error("PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx), ni->name.c_str(ctx));
|
||||||
|
if (!io_cell->attrs.count(ctx->id("BEL")))
|
||||||
|
log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
|
||||||
|
io_cell->name.c_str(ctx));
|
||||||
|
|
||||||
|
BelId io_bel = ctx->getBelByName(ctx->id(io_cell->attrs.at(ctx->id("BEL"))));
|
||||||
|
BelId found_bel;
|
||||||
|
|
||||||
|
// Find the PLL BEL that would suit that connection
|
||||||
|
for (auto pll_bel : pll_all_bels) {
|
||||||
|
if (std::get<0>(pll_bel.second).bel == io_bel) {
|
||||||
|
found_bel = pll_bel.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_bel == BelId())
|
||||||
|
log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is not connected to any PLL BEL\n", ci->name.c_str(ctx),
|
||||||
|
io_cell->name.c_str(ctx));
|
||||||
|
if (pll_used_bels.count(found_bel)) {
|
||||||
|
CellInfo *conflict_cell = pll_used_bels.at(found_bel);
|
||||||
|
log_error("PLL '%s' PACKAGEPIN forces it to BEL %s but BEL is already assigned to PLL '%s'\n",
|
||||||
|
ci->name.c_str(ctx), ctx->getBelName(found_bel).c_str(ctx), conflict_cell->name.c_str(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is it user constrained ?
|
||||||
|
if (ci->attrs.count(ctx->id("BEL"))) {
|
||||||
|
// Yes. Check it actually matches !
|
||||||
|
BelId bel_constrain = ctx->getBelByName(ctx->id(ci->attrs[ctx->id("BEL")]));
|
||||||
|
if (bel_constrain != found_bel)
|
||||||
|
log_error("PLL '%s' is user constrained to %s but can only be placed in %s based on its PACKAGEPIN "
|
||||||
|
"connection\n",
|
||||||
|
ci->name.c_str(ctx), ctx->getBelName(bel_constrain).c_str(ctx),
|
||||||
|
ctx->getBelName(found_bel).c_str(ctx));
|
||||||
|
} else {
|
||||||
|
// No, we can constrain it ourselves
|
||||||
|
ci->attrs[ctx->id("BEL")] = ctx->getBelName(found_bel).str(ctx);
|
||||||
|
pll_used_bels[found_bel] = ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inform user
|
||||||
|
log_info(" constrained PLL '%s' to %s\n", ci->name.c_str(ctx), ctx->getBelName(found_bel).c_str(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan all SB_IOs to check for conflict with PLL BELs
|
||||||
|
for (auto io_cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *io_ci = io_cell.second;
|
||||||
|
if (!is_sb_io(ctx, io_ci))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Only consider bound IO that are used as inputs
|
||||||
|
if (!io_ci->attrs.count(ctx->id("BEL")))
|
||||||
|
continue;
|
||||||
|
if ((!io_ci->ports.count(id_D_IN_0) || (io_ci->ports[id_D_IN_0].net == nullptr)) &&
|
||||||
|
(!io_ci->ports.count(id_D_IN_1) || (io_ci->ports[id_D_IN_1].net == nullptr)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check all placed PLL (either forced by user, or forced by PACKAGEPIN)
|
||||||
|
BelId io_bel = ctx->getBelByName(ctx->id(io_ci->attrs[ctx->id("BEL")]));
|
||||||
|
|
||||||
|
for (auto placed_pll : pll_used_bels) {
|
||||||
|
BelPin pll_io_a, pll_io_b;
|
||||||
|
std::tie(pll_io_a, pll_io_b) = pll_all_bels[placed_pll.first];
|
||||||
|
if (io_bel == pll_io_a.bel) {
|
||||||
|
// All the PAD type PLL stuff already checked above,so only
|
||||||
|
// check for conflict with a user placed CORE PLL
|
||||||
|
if (!is_sb_pll40_pad(ctx, placed_pll.second))
|
||||||
|
log_error("PLL '%s' A output conflict with SB_IO '%s' that's used as input\n",
|
||||||
|
placed_pll.second->name.c_str(ctx), io_cell.second->name.c_str(ctx));
|
||||||
|
} else if (io_bel == pll_io_b.bel) {
|
||||||
|
if (is_sb_pll40_dual(ctx, placed_pll.second))
|
||||||
|
log_error("PLL '%s' B output conflicts with SB_IO '%s' that's used as input\n",
|
||||||
|
placed_pll.second->name.c_str(ctx), io_cell.second->name.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save for later checks
|
||||||
|
bel2io[io_bel] = io_ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan all the CORE PLLs and place them in remaining available PLL BELs
|
||||||
|
// (in two pass ... first do the dual ones, harder to place, then single port)
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
for (auto ci : pll_cells) {
|
||||||
|
if (is_sb_pll40_pad(ctx, ci))
|
||||||
|
continue;
|
||||||
|
if (is_sb_pll40_dual(ctx, ci) ^ i)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check REFERENCECLK connection
|
||||||
|
if (!ci->ports.count(id_REFERENCECLK))
|
||||||
|
log_error("PLL '%s' is of CORE type but doesn't have a REFERENCECLK port\n", ci->name.c_str(ctx));
|
||||||
|
|
||||||
|
NetInfo *ni = ci->ports.at(id_REFERENCECLK).net;
|
||||||
|
if (ni == nullptr || ni->driver.cell == nullptr)
|
||||||
|
log_error("PLL '%s' is of CORE type but doesn't have a valid REFERENCECLK connection\n",
|
||||||
|
ci->name.c_str(ctx));
|
||||||
|
|
||||||
|
// Could this be a PAD PLL ?
|
||||||
|
bool could_be_pad = false;
|
||||||
|
BelId pad_bel;
|
||||||
|
if (ni->users.size() == 1 && is_sb_io(ctx, ni->driver.cell) && ni->driver.cell->attrs.count(ctx->id("BEL")))
|
||||||
|
pad_bel = ctx->getBelByName(ctx->id(ni->driver.cell->attrs[ctx->id("BEL")]));
|
||||||
|
|
||||||
|
// Find a BEL for it
|
||||||
|
BelId found_bel;
|
||||||
|
for (auto bel_pll : pll_all_bels) {
|
||||||
|
BelPin pll_io_a, pll_io_b;
|
||||||
|
std::tie(pll_io_a, pll_io_b) = bel_pll.second;
|
||||||
|
if (bel2io.count(pll_io_a.bel)) {
|
||||||
|
if (pll_io_a.bel == pad_bel)
|
||||||
|
could_be_pad = !bel2io.count(pll_io_b.bel) || !is_sb_pll40_dual(ctx, ci);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci))
|
||||||
|
continue;
|
||||||
|
found_bel = bel_pll.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply constrain & Inform user of result
|
||||||
|
if (found_bel == BelId())
|
||||||
|
log_error("PLL '%s' couldn't be placed anywhere, no suitable BEL found.%s\n", ci->name.c_str(ctx),
|
||||||
|
could_be_pad ? " Did you mean to use a PAD PLL ?" : "");
|
||||||
|
|
||||||
|
log_info(" constrained PLL '%s' to %s\n", ci->name.c_str(ctx), ctx->getBelName(found_bel).c_str(ctx));
|
||||||
|
if (could_be_pad)
|
||||||
|
log_info(" (given its connections, this PLL could have been a PAD PLL)\n");
|
||||||
|
|
||||||
|
ci->attrs[ctx->id("BEL")] = ctx->getBelName(found_bel).str(ctx);
|
||||||
|
pll_used_bels[found_bel] = ci;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// spliceLUT adds a pass-through LUT LC between the given cell's output port
|
// spliceLUT adds a pass-through LUT LC between the given cell's output port
|
||||||
// and either all users or only non_LUT users.
|
// and either all users or only non_LUT users.
|
||||||
static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
|
static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
|
||||||
@ -844,6 +1043,10 @@ static void pack_special(Context *ctx)
|
|||||||
ci->ports.erase(ctx->id("RGB0"));
|
ci->ports.erase(ctx->id("RGB0"));
|
||||||
ci->ports.erase(ctx->id("RGB1"));
|
ci->ports.erase(ctx->id("RGB1"));
|
||||||
ci->ports.erase(ctx->id("RGB2"));
|
ci->ports.erase(ctx->id("RGB2"));
|
||||||
|
} else if (is_sb_ledda_ip(ctx, ci)) {
|
||||||
|
/* Force placement (no choices anyway) */
|
||||||
|
cell_place_unique(ctx, ci);
|
||||||
|
|
||||||
} else if (is_sb_pll40(ctx, ci)) {
|
} else if (is_sb_pll40(ctx, ci)) {
|
||||||
bool is_pad = is_sb_pll40_pad(ctx, ci);
|
bool is_pad = is_sb_pll40_pad(ctx, ci);
|
||||||
bool is_core = !is_pad;
|
bool is_core = !is_pad;
|
||||||
@ -877,13 +1080,17 @@ static void pack_special(Context *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
|
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
|
||||||
packed->params[ctx->id("FEEDBACK_PATH")] =
|
std::string fbp_value = feedback_path == "DELAY"
|
||||||
feedback_path == "DELAY"
|
|
||||||
? "0"
|
? "0"
|
||||||
: feedback_path == "SIMPLE" ? "1"
|
: feedback_path == "SIMPLE"
|
||||||
|
? "1"
|
||||||
: feedback_path == "PHASE_AND_DELAY"
|
: feedback_path == "PHASE_AND_DELAY"
|
||||||
? "2"
|
? "2"
|
||||||
: feedback_path == "EXTERNAL" ? "6" : feedback_path;
|
: feedback_path == "EXTERNAL" ? "6" : feedback_path;
|
||||||
|
if (!std::all_of(fbp_value.begin(), fbp_value.end(), isdigit))
|
||||||
|
log_error("PLL '%s' has unsupported FEEDBACK_PATH value '%s'\n", ci->name.c_str(ctx),
|
||||||
|
feedback_path.c_str());
|
||||||
|
packed->params[ctx->id("FEEDBACK_PATH")] = fbp_value;
|
||||||
packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
|
packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
|
||||||
|
|
||||||
NetInfo *pad_packagepin_net = nullptr;
|
NetInfo *pad_packagepin_net = nullptr;
|
||||||
@ -939,82 +1146,25 @@ static void pack_special(Context *ctx)
|
|||||||
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If PLL is not constrained already, do that - we need this
|
// PLL must have been placed already in place_plls()
|
||||||
// information to then constrain the LOCK LUT.
|
BelId pll_bel = ctx->getBelByName(ctx->id(packed->attrs[ctx->id("BEL")]));
|
||||||
BelId pll_bel;
|
NPNR_ASSERT(pll_bel != BelId());
|
||||||
bool constrained = false;
|
|
||||||
if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) {
|
|
||||||
for (auto bel : ctx->getBels()) {
|
|
||||||
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
|
|
||||||
continue;
|
|
||||||
if (ctx->isBelLocked(bel))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared
|
// Deal with PAD PLL peculiarities
|
||||||
// with PLLOUT_A.
|
|
||||||
if (is_pad) {
|
if (is_pad) {
|
||||||
auto pll_sb_io_belpin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
|
|
||||||
NPNR_ASSERT(pad_packagepin_net != nullptr);
|
NPNR_ASSERT(pad_packagepin_net != nullptr);
|
||||||
auto pll_packagepin_driver = pad_packagepin_net->driver;
|
auto pll_packagepin_driver = pad_packagepin_net->driver;
|
||||||
NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
|
NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
|
||||||
if (pll_packagepin_driver.cell->type != ctx->id("SB_IO")) {
|
|
||||||
log_error("PLL '%s' has a PACKAGEPIN driven by "
|
|
||||||
"an %s, should be directly connected to an input SB_IO\n",
|
|
||||||
ci->name.c_str(ctx), pll_packagepin_driver.cell->type.c_str(ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto packagepin_cell = pll_packagepin_driver.cell;
|
auto packagepin_cell = pll_packagepin_driver.cell;
|
||||||
auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
|
auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
|
||||||
if (packagepin_bel_name == packagepin_cell->attrs.end()) {
|
|
||||||
log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
|
|
||||||
packagepin_cell->name.c_str(ctx));
|
|
||||||
}
|
|
||||||
auto packagepin_bel = ctx->getBelByName(ctx->id(packagepin_bel_name->second));
|
|
||||||
if (pll_sb_io_belpin.bel != packagepin_bel) {
|
|
||||||
log_error("PLL '%s' PACKAGEPIN is connected to pin %s, can only be pin %s\n",
|
|
||||||
ci->name.c_str(ctx), ctx->getBelPackagePin(packagepin_bel).c_str(),
|
|
||||||
ctx->getBelPackagePin(pll_sb_io_belpin.bel).c_str());
|
|
||||||
}
|
|
||||||
if (pad_packagepin_net->users.size() != 1) {
|
|
||||||
log_error("PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx),
|
|
||||||
pad_packagepin_net->name.c_str(ctx));
|
|
||||||
}
|
|
||||||
// Set an attribute about this PLL's PAD SB_IO.
|
// Set an attribute about this PLL's PAD SB_IO.
|
||||||
packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second;
|
packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second;
|
||||||
// Remove the connection from the SB_IO to the PLL.
|
|
||||||
packagepin_cell->ports.erase(pll_packagepin_driver.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx),
|
// Disconnect PACKAGEPIN (it's a physical HW link)
|
||||||
ctx->getBelName(bel).c_str(ctx));
|
for (auto user : pad_packagepin_net->users)
|
||||||
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
|
|
||||||
pll_bel = bel;
|
|
||||||
constrained = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!constrained) {
|
|
||||||
log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n",
|
|
||||||
packed->name.c_str(ctx));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pll_bel = ctx->getBelByName(ctx->id(packed->attrs[ctx->id("BEL")]));
|
|
||||||
if (ctx->getBelType(pll_bel) != id_ICESTORM_PLL)
|
|
||||||
log_error("PLL '%s' is constrained to BEL %s which isn't a ICESTORM_PLL BEL\n",
|
|
||||||
packed->name.c_str(ctx), ctx->getBelName(pll_bel).c_str(ctx));
|
|
||||||
if (ctx->isBelLocked(pll_bel))
|
|
||||||
log_error("PLL '%s' is constrained to locked BEL %s\n", packed->name.c_str(ctx),
|
|
||||||
ctx->getBelName(pll_bel).c_str(ctx));
|
|
||||||
log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx),
|
|
||||||
ctx->getBelName(pll_bel).c_str(ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the original PACKAGEPIN net if needed.
|
|
||||||
if (pad_packagepin_net != nullptr) {
|
|
||||||
for (auto user : pad_packagepin_net->users) {
|
|
||||||
user.cell->ports.erase(user.port);
|
user.cell->ports.erase(user.port);
|
||||||
}
|
packagepin_cell->ports.erase(pll_packagepin_driver.port);
|
||||||
if (pad_packagepin_net->driver.cell != nullptr)
|
|
||||||
pad_packagepin_net->driver.cell->ports.erase(pad_packagepin_net->driver.port);
|
|
||||||
ctx->nets.erase(pad_packagepin_net->name);
|
ctx->nets.erase(pad_packagepin_net->name);
|
||||||
pad_packagepin_net = nullptr;
|
pad_packagepin_net = nullptr;
|
||||||
}
|
}
|
||||||
@ -1028,20 +1178,26 @@ static void pack_special(Context *ctx)
|
|||||||
log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n", ci->name.c_str(ctx));
|
log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n", ci->name.c_str(ctx));
|
||||||
bool found_lut = false;
|
bool found_lut = false;
|
||||||
bool all_luts = true;
|
bool all_luts = true;
|
||||||
|
bool found_carry = false;
|
||||||
unsigned int lut_count = 0;
|
unsigned int lut_count = 0;
|
||||||
for (const auto &user : port.net->users) {
|
for (const auto &user : port.net->users) {
|
||||||
NPNR_ASSERT(user.cell != nullptr);
|
NPNR_ASSERT(user.cell != nullptr);
|
||||||
if (user.cell->type == ctx->id("ICESTORM_LC")) {
|
if (user.cell->type == ctx->id("ICESTORM_LC")) {
|
||||||
|
if (bool_or_default(user.cell->params, ctx->id("CARRY_ENABLE"), false)) {
|
||||||
|
found_carry = true;
|
||||||
|
all_luts = false;
|
||||||
|
} else {
|
||||||
found_lut = true;
|
found_lut = true;
|
||||||
lut_count++;
|
lut_count++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
all_luts = false;
|
all_luts = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found_lut && all_luts) {
|
if (found_lut && all_luts && lut_count < 8) {
|
||||||
// Every user is a LUT, carry on now.
|
// Every user is a LUT, carry on now.
|
||||||
} else if (found_lut && !all_luts && lut_count < 8) {
|
} else if (found_lut && !all_luts && !found_carry && lut_count < 8) {
|
||||||
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
|
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
|
||||||
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
|
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
|
||||||
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
|
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
|
||||||
@ -1118,7 +1274,9 @@ bool Arch::pack()
|
|||||||
pack_nonlut_ffs(ctx);
|
pack_nonlut_ffs(ctx);
|
||||||
pack_carries(ctx);
|
pack_carries(ctx);
|
||||||
pack_ram(ctx);
|
pack_ram(ctx);
|
||||||
|
place_plls(ctx);
|
||||||
pack_special(ctx);
|
pack_special(ctx);
|
||||||
|
if (!bool_or_default(ctx->settings, ctx->id("no_promote_globals"), false))
|
||||||
promote_globals(ctx);
|
promote_globals(ctx);
|
||||||
ctx->assignArchInfo();
|
ctx->assignArchInfo();
|
||||||
constrain_chains(ctx);
|
constrain_chains(ctx);
|
||||||
|
46
ice40/pcf.cc
46
ice40/pcf.cc
@ -21,6 +21,7 @@
|
|||||||
#include "pcf.h"
|
#include "pcf.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -47,16 +48,42 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
|
|||||||
if (words.size() == 0)
|
if (words.size() == 0)
|
||||||
continue;
|
continue;
|
||||||
std::string cmd = words.at(0);
|
std::string cmd = words.at(0);
|
||||||
|
bool nowarn = false;
|
||||||
if (cmd == "set_io") {
|
if (cmd == "set_io") {
|
||||||
size_t args_end = 1;
|
size_t args_end = 1;
|
||||||
while (args_end < words.size() && words.at(args_end).at(0) == '-')
|
std::vector<std::pair<IdString, std::string>> extra_attrs;
|
||||||
|
while (args_end < words.size() && words.at(args_end).at(0) == '-') {
|
||||||
|
const auto &setting = words.at(args_end);
|
||||||
|
if (setting == "-pullup") {
|
||||||
|
const auto &value = words.at(++args_end);
|
||||||
|
if (value == "yes" || value == "1")
|
||||||
|
extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP"), "1"));
|
||||||
|
else if (value == "no" || value == "0")
|
||||||
|
extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP"), "0"));
|
||||||
|
else
|
||||||
|
log_error("Invalid value '%s' for -pullup (on line %d)\n", value.c_str(), lineno);
|
||||||
|
} else if (setting == "-pullup_resistor") {
|
||||||
|
const auto &value = words.at(++args_end);
|
||||||
|
if (ctx->args.type != ArchArgs::UP5K)
|
||||||
|
log_error("Pullup resistance can only be set on UP5K (on line %d)\n", lineno);
|
||||||
|
if (value != "3P3K" && value != "6P8K" && value != "10K" && value != "100K")
|
||||||
|
log_error("Invalid value '%s' for -pullup_resistor (on line %d)\n", value.c_str(), lineno);
|
||||||
|
extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP_RESISTOR"), value));
|
||||||
|
} else if (setting == "-nowarn") {
|
||||||
|
nowarn = true;
|
||||||
|
} else if (setting == "--warn-no-port") {
|
||||||
|
} else {
|
||||||
|
log_warning("Ignoring PCF setting '%s' (on line %d)\n", setting.c_str(), lineno);
|
||||||
|
}
|
||||||
args_end++;
|
args_end++;
|
||||||
|
}
|
||||||
if (args_end >= words.size() - 1)
|
if (args_end >= words.size() - 1)
|
||||||
log_error("expected PCF syntax 'set_io cell pin' (on line %d)\n", lineno);
|
log_error("expected PCF syntax 'set_io cell pin' (on line %d)\n", lineno);
|
||||||
std::string cell = words.at(args_end);
|
std::string cell = words.at(args_end);
|
||||||
std::string pin = words.at(args_end + 1);
|
std::string pin = words.at(args_end + 1);
|
||||||
auto fnd_cell = ctx->cells.find(ctx->id(cell));
|
auto fnd_cell = ctx->cells.find(ctx->id(cell));
|
||||||
if (fnd_cell == ctx->cells.end()) {
|
if (fnd_cell == ctx->cells.end()) {
|
||||||
|
if (!nowarn)
|
||||||
log_warning("unmatched constraint '%s' (on line %d)\n", cell.c_str(), lineno);
|
log_warning("unmatched constraint '%s' (on line %d)\n", cell.c_str(), lineno);
|
||||||
} else {
|
} else {
|
||||||
BelId pin_bel = ctx->getPackagePinBel(pin);
|
BelId pin_bel = ctx->getPackagePinBel(pin);
|
||||||
@ -67,11 +94,28 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
|
|||||||
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
|
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
|
||||||
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
|
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
|
||||||
fnd_cell->second->attrs[ctx->id("BEL")].c_str());
|
fnd_cell->second->attrs[ctx->id("BEL")].c_str());
|
||||||
|
for (const auto &attr : extra_attrs)
|
||||||
|
fnd_cell->second->attrs[attr.first] = attr.second;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log_error("unsupported PCF command '%s' (on line %d)\n", cmd.c_str(), lineno);
|
log_error("unsupported PCF command '%s' (on line %d)\n", cmd.c_str(), lineno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_obuf") ||
|
||||||
|
ci->type == ctx->id("$nextpnr_iobuf")) {
|
||||||
|
if (!ci->attrs.count(ctx->id("BEL"))) {
|
||||||
|
if (bool_or_default(ctx->settings, ctx->id("pcf_allow_unconstrained")))
|
||||||
|
log_warning("IO '%s' is unconstrained in PCF and will be automatically placed\n",
|
||||||
|
cell.first.c_str(ctx));
|
||||||
|
else
|
||||||
|
log_error("IO '%s' is unconstrained in PCF (override this error with "
|
||||||
|
"--pcf-allow-unconstrained)\n",
|
||||||
|
cell.first.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ctx->settings.emplace(ctx->id("input/pcf"), filename);
|
ctx->settings.emplace(ctx->id("input/pcf"), filename);
|
||||||
return true;
|
return true;
|
||||||
} catch (log_execution_error_exception) {
|
} catch (log_execution_error_exception) {
|
||||||
|
@ -22,7 +22,7 @@ for i in range(num_runs):
|
|||||||
ascfile = "picorv32_work/picorv32_s{}.asc".format(run)
|
ascfile = "picorv32_work/picorv32_s{}.asc".format(run)
|
||||||
if path.exists(ascfile):
|
if path.exists(ascfile):
|
||||||
os.remove(ascfile)
|
os.remove(ascfile)
|
||||||
result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "70"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "40", "--opt-timing"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
print("Run {} failed!".format(run))
|
print("Run {} failed!".format(run))
|
||||||
else:
|
else:
|
||||||
|
6
ice40/smoketest/attosoc/.gitignore
vendored
Normal file
6
ice40/smoketest/attosoc/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
attosoc_pnr.v
|
||||||
|
attosoc.asc
|
||||||
|
attosoc.json
|
||||||
|
attosoc_pnr_tb
|
||||||
|
testbench.vcd
|
||||||
|
output.txt
|
10
ice40/smoketest/attosoc/attosoc.pcf
Normal file
10
ice40/smoketest/attosoc/attosoc.pcf
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
set_io clk E4
|
||||||
|
set_io led[0] B2
|
||||||
|
set_io led[1] F5
|
||||||
|
set_io led[2] B1
|
||||||
|
set_io led[3] C1
|
||||||
|
set_io led[4] C2
|
||||||
|
set_io led[5] F4
|
||||||
|
set_io led[6] D2
|
||||||
|
set_io led[7] G5
|
||||||
|
|
127
ice40/smoketest/attosoc/attosoc.v
Normal file
127
ice40/smoketest/attosoc/attosoc.v
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* ECP5 PicoRV32 demo
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
`ifdef PICORV32_V
|
||||||
|
`error "attosoc.v must be read before picorv32.v!"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`define PICORV32_REGS picosoc_regs
|
||||||
|
|
||||||
|
module attosoc (
|
||||||
|
input clk,
|
||||||
|
output reg [7:0] led
|
||||||
|
);
|
||||||
|
|
||||||
|
reg [5:0] reset_cnt = 0;
|
||||||
|
wire resetn = &reset_cnt;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
reset_cnt <= reset_cnt + !resetn;
|
||||||
|
end
|
||||||
|
|
||||||
|
parameter integer MEM_WORDS = 256;
|
||||||
|
parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory
|
||||||
|
parameter [31:0] PROGADDR_RESET = 32'h 0000_0000; // ROM at 0x0
|
||||||
|
parameter integer ROM_BYTES = 256;
|
||||||
|
|
||||||
|
reg [7:0] rom [0:ROM_BYTES-1];
|
||||||
|
wire [31:0] rom_rdata = {rom[mem_addr+3], rom[mem_addr+2], rom[mem_addr+1], rom[mem_addr+0]};
|
||||||
|
initial $readmemh("firmware.hex", rom);
|
||||||
|
|
||||||
|
wire mem_valid;
|
||||||
|
wire mem_instr;
|
||||||
|
wire mem_ready;
|
||||||
|
wire [31:0] mem_addr;
|
||||||
|
wire [31:0] mem_wdata;
|
||||||
|
wire [3:0] mem_wstrb;
|
||||||
|
wire [31:0] mem_rdata;
|
||||||
|
|
||||||
|
wire rom_ready = mem_valid && mem_addr[31:24] == 8'h00;
|
||||||
|
|
||||||
|
wire iomem_valid;
|
||||||
|
wire iomem_ready;
|
||||||
|
wire [31:0] iomem_addr;
|
||||||
|
wire [31:0] iomem_wdata;
|
||||||
|
wire [3:0] iomem_wstrb;
|
||||||
|
wire [31:0] iomem_rdata;
|
||||||
|
|
||||||
|
assign iomem_valid = mem_valid && (mem_addr[31:24] > 8'h 01);
|
||||||
|
assign iomem_ready = 1'b1;
|
||||||
|
assign iomem_wstrb = mem_wstrb;
|
||||||
|
assign iomem_addr = mem_addr;
|
||||||
|
assign iomem_wdata = mem_wdata;
|
||||||
|
|
||||||
|
wire [31:0] spimemio_cfgreg_do;
|
||||||
|
|
||||||
|
|
||||||
|
always @(posedge clk)
|
||||||
|
if (iomem_valid && iomem_wstrb[0])
|
||||||
|
led <= iomem_wdata[7:0];
|
||||||
|
|
||||||
|
assign mem_ready = (iomem_valid && iomem_ready) || rom_ready;
|
||||||
|
|
||||||
|
assign mem_rdata = rom_rdata;
|
||||||
|
|
||||||
|
picorv32 #(
|
||||||
|
.STACKADDR(STACKADDR),
|
||||||
|
.PROGADDR_RESET(PROGADDR_RESET),
|
||||||
|
.PROGADDR_IRQ(32'h 0000_0000),
|
||||||
|
.BARREL_SHIFTER(0),
|
||||||
|
.COMPRESSED_ISA(0),
|
||||||
|
.ENABLE_MUL(0),
|
||||||
|
.ENABLE_DIV(0),
|
||||||
|
.ENABLE_IRQ(0),
|
||||||
|
.ENABLE_IRQ_QREGS(0)
|
||||||
|
) cpu (
|
||||||
|
.clk (clk ),
|
||||||
|
.resetn (resetn ),
|
||||||
|
.mem_valid (mem_valid ),
|
||||||
|
.mem_instr (mem_instr ),
|
||||||
|
.mem_ready (mem_ready ),
|
||||||
|
.mem_addr (mem_addr ),
|
||||||
|
.mem_wdata (mem_wdata ),
|
||||||
|
.mem_wstrb (mem_wstrb ),
|
||||||
|
.mem_rdata (mem_rdata )
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// Implementation note:
|
||||||
|
// Replace the following two modules with wrappers for your SRAM cells.
|
||||||
|
|
||||||
|
module picosoc_regs (
|
||||||
|
input clk, wen,
|
||||||
|
input [5:0] waddr,
|
||||||
|
input [5:0] raddr1,
|
||||||
|
input [5:0] raddr2,
|
||||||
|
input [31:0] wdata,
|
||||||
|
output [31:0] rdata1,
|
||||||
|
output [31:0] rdata2
|
||||||
|
);
|
||||||
|
reg [31:0] regs [0:31];
|
||||||
|
|
||||||
|
always @(posedge clk)
|
||||||
|
if (wen) regs[waddr[4:0]] <= wdata;
|
||||||
|
|
||||||
|
assign rdata1 = regs[raddr1[4:0]];
|
||||||
|
assign rdata2 = regs[raddr2[4:0]];
|
||||||
|
endmodule
|
32
ice40/smoketest/attosoc/attosoc_tb.v
Normal file
32
ice40/smoketest/attosoc/attosoc_tb.v
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
module testbench();
|
||||||
|
integer out;
|
||||||
|
reg clk;
|
||||||
|
|
||||||
|
always #5 clk = (clk === 1'b0);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
out = $fopen("output.txt","w");
|
||||||
|
$dumpfile("testbench.vcd");
|
||||||
|
$dumpvars(0, testbench);
|
||||||
|
|
||||||
|
repeat (100) begin
|
||||||
|
repeat (256) @(posedge clk);
|
||||||
|
$display("+256 cycles");
|
||||||
|
end
|
||||||
|
$fclose(out);
|
||||||
|
#100;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
wire [7:0] led;
|
||||||
|
|
||||||
|
always @(led) begin
|
||||||
|
#1 $display("%b", led);
|
||||||
|
$fwrite(out, "%b\n", led);
|
||||||
|
end
|
||||||
|
|
||||||
|
attosoc uut (
|
||||||
|
.clk (clk ),
|
||||||
|
.led (led )
|
||||||
|
);
|
||||||
|
endmodule
|
6
ice40/smoketest/attosoc/firmware.hex
Normal file
6
ice40/smoketest/attosoc/firmware.hex
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@00000000
|
||||||
|
13 04 10 00 B7 04 00 02 93 09 00 10 13 04 14 00
|
||||||
|
63 44 34 01 13 04 20 00 13 09 20 00 63 5E 89 00
|
||||||
|
13 05 04 00 93 05 09 00 EF 00 80 01 63 08 05 00
|
||||||
|
13 09 19 00 6F F0 9F FE 23 A0 84 00 6F F0 1F FD
|
||||||
|
93 02 10 00 33 05 B5 40 E3 5E 55 FE 67 80 00 00
|
16
ice40/smoketest/attosoc/golden.txt
Normal file
16
ice40/smoketest/attosoc/golden.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
00000000
|
||||||
|
00000010
|
||||||
|
00000011
|
||||||
|
00000101
|
||||||
|
00000111
|
||||||
|
00001011
|
||||||
|
00001101
|
||||||
|
00010001
|
||||||
|
00010011
|
||||||
|
00010111
|
||||||
|
00011101
|
||||||
|
00011111
|
||||||
|
00100101
|
||||||
|
00101001
|
||||||
|
00101011
|
||||||
|
00101111
|
2979
ice40/smoketest/attosoc/picorv32.v
Normal file
2979
ice40/smoketest/attosoc/picorv32.v
Normal file
File diff suppressed because it is too large
Load Diff
9
ice40/smoketest/attosoc/smoketest.sh
Executable file
9
ice40/smoketest/attosoc/smoketest.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -ex
|
||||||
|
yosys -q -p 'synth_ice40 -json attosoc.json -top attosoc' attosoc.v picorv32.v
|
||||||
|
$NEXTPNR --hx8k --json attosoc.json --pcf attosoc.pcf --asc attosoc.asc --freq 50
|
||||||
|
icetime -tmd hx8k -c 50 attosoc.asc
|
||||||
|
icebox_vlog -L -l -p attosoc.pcf -c -n attosoc attosoc.asc > attosoc_pnr.v
|
||||||
|
iverilog -o attosoc_pnr_tb attosoc_pnr.v attosoc_tb.v `yosys-config --datdir/ice40/cells_sim.v`
|
||||||
|
vvp attosoc_pnr_tb
|
||||||
|
diff output.txt golden.txt
|
@ -594,7 +594,11 @@ void json_import_cell(Context *ctx, string modname, const std::vector<IdString>
|
|||||||
if (type == PORT_IN || type == PORT_INOUT) {
|
if (type == PORT_IN || type == PORT_INOUT) {
|
||||||
net->users.push_back(pr);
|
net->users.push_back(pr);
|
||||||
} else if (type == PORT_OUT) {
|
} else if (type == PORT_OUT) {
|
||||||
assert(net->driver.cell == nullptr);
|
if (net->driver.cell != nullptr)
|
||||||
|
log_error("multiple drivers on net '%s' (%s.%s and %s.%s)\n",
|
||||||
|
net->name.c_str(ctx), net->driver.cell->name.c_str(ctx),
|
||||||
|
net->driver.port.c_str(ctx), pr.cell->name.c_str(ctx),
|
||||||
|
pr.port.c_str(ctx));
|
||||||
net->driver = pr;
|
net->driver = pr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,7 +627,9 @@ static void insert_iobuf(Context *ctx, NetInfo *net, PortType type, const string
|
|||||||
iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT};
|
iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT};
|
||||||
// Special case: input, etc, directly drives inout
|
// Special case: input, etc, directly drives inout
|
||||||
if (net->driver.cell != nullptr) {
|
if (net->driver.cell != nullptr) {
|
||||||
assert(net->driver.cell->type == ctx->id("$nextpnr_iobuf"));
|
if (net->driver.cell->type != ctx->id("$nextpnr_iobuf"))
|
||||||
|
log_error("Top-level input '%s' also driven by %s.%s.\n", name.c_str(),
|
||||||
|
net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx));
|
||||||
net = net->driver.cell->ports.at(ctx->id("I")).net;
|
net = net->driver.cell->ports.at(ctx->id("I")).net;
|
||||||
}
|
}
|
||||||
assert(net->driver.cell == nullptr);
|
assert(net->driver.cell == nullptr);
|
||||||
@ -686,8 +692,28 @@ void json_import(Context *ctx, string modname, JsonNode *node)
|
|||||||
|
|
||||||
log_info("Importing module %s\n", modname.c_str());
|
log_info("Importing module %s\n", modname.c_str());
|
||||||
|
|
||||||
|
// Multiple labels might refer to the same net. For now we resolve conflicts thus:
|
||||||
|
// - names with fewer $ are always prefered
|
||||||
|
// - between equal $ counts, fewer .s are prefered
|
||||||
|
// - ties are resolved alphabetically
|
||||||
|
auto prefer_netlabel = [](const std::string &a, const std::string &b) {
|
||||||
|
if (b.empty())
|
||||||
|
return true;
|
||||||
|
long a_dollars = std::count(a.begin(), a.end(), '$'), b_dollars = std::count(b.begin(), b.end(), '$');
|
||||||
|
if (a_dollars < b_dollars)
|
||||||
|
return true;
|
||||||
|
else if (a_dollars > b_dollars)
|
||||||
|
return false;
|
||||||
|
long a_dots = std::count(a.begin(), a.end(), '.'), b_dots = std::count(b.begin(), b.end(), '.');
|
||||||
|
if (a_dots < b_dots)
|
||||||
|
return true;
|
||||||
|
else if (a_dots > b_dots)
|
||||||
|
return false;
|
||||||
|
return a < b;
|
||||||
|
};
|
||||||
|
|
||||||
// Import netnames
|
// Import netnames
|
||||||
std::vector<IdString> netnames;
|
std::vector<std::string> netlabels;
|
||||||
if (node->data_dict.count("netnames")) {
|
if (node->data_dict.count("netnames")) {
|
||||||
JsonNode *cell_parent = node->data_dict.at("netnames");
|
JsonNode *cell_parent = node->data_dict.at("netnames");
|
||||||
for (int nnid = 0; nnid < GetSize(cell_parent->data_dict_keys); nnid++) {
|
for (int nnid = 0; nnid < GetSize(cell_parent->data_dict_keys); nnid++) {
|
||||||
@ -701,15 +727,19 @@ void json_import(Context *ctx, string modname, JsonNode *node)
|
|||||||
size_t num_bits = bits->data_array.size();
|
size_t num_bits = bits->data_array.size();
|
||||||
for (size_t i = 0; i < num_bits; i++) {
|
for (size_t i = 0; i < num_bits; i++) {
|
||||||
int netid = bits->data_array.at(i)->data_number;
|
int netid = bits->data_array.at(i)->data_number;
|
||||||
if (netid >= int(netnames.size()))
|
if (netid >= int(netlabels.size()))
|
||||||
netnames.resize(netid + 1);
|
netlabels.resize(netid + 1);
|
||||||
netnames.at(netid) = ctx->id(
|
std::string name =
|
||||||
basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(i) + std::string("]")));
|
basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(i) + std::string("]"));
|
||||||
|
if (prefer_netlabel(name, netlabels.at(netid)))
|
||||||
|
netlabels.at(netid) = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::vector<IdString> netids;
|
||||||
|
std::transform(netlabels.begin(), netlabels.end(), std::back_inserter(netids),
|
||||||
|
[ctx](const std::string &s) { return ctx->id(s); });
|
||||||
if (node->data_dict.count("cells")) {
|
if (node->data_dict.count("cells")) {
|
||||||
JsonNode *cell_parent = node->data_dict.at("cells");
|
JsonNode *cell_parent = node->data_dict.at("cells");
|
||||||
//
|
//
|
||||||
@ -719,7 +749,7 @@ void json_import(Context *ctx, string modname, JsonNode *node)
|
|||||||
//
|
//
|
||||||
for (int cellid = 0; cellid < GetSize(cell_parent->data_dict_keys); cellid++) {
|
for (int cellid = 0; cellid < GetSize(cell_parent->data_dict_keys); cellid++) {
|
||||||
JsonNode *here = cell_parent->data_dict.at(cell_parent->data_dict_keys[cellid]);
|
JsonNode *here = cell_parent->data_dict.at(cell_parent->data_dict_keys[cellid]);
|
||||||
json_import_cell(ctx, modname, netnames, here, cell_parent->data_dict_keys[cellid]);
|
json_import_cell(ctx, modname, netids, here, cell_parent->data_dict_keys[cellid]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,7 +763,7 @@ void json_import(Context *ctx, string modname, JsonNode *node)
|
|||||||
JsonNode *here;
|
JsonNode *here;
|
||||||
|
|
||||||
here = ports_parent->data_dict.at(ports_parent->data_dict_keys[portid]);
|
here = ports_parent->data_dict.at(ports_parent->data_dict_keys[portid]);
|
||||||
json_import_toplevel_port(ctx, modname, netnames, ports_parent->data_dict_keys[portid], here);
|
json_import_toplevel_port(ctx, modname, netids, ports_parent->data_dict_keys[portid], here);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check_all_nets_driven(ctx);
|
check_all_nets_driven(ctx);
|
||||||
|
177
xc7/125MHz_to_60MHz.v
Executable file
177
xc7/125MHz_to_60MHz.v
Executable file
@ -0,0 +1,177 @@
|
|||||||
|
// file: clk_wiz_v3_6.v
|
||||||
|
//
|
||||||
|
// (c) Copyright 2008 - 2011 Xilinx, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// This file contains confidential and proprietary information
|
||||||
|
// of Xilinx, Inc. and is protected under U.S. and
|
||||||
|
// international copyright and other intellectual property
|
||||||
|
// laws.
|
||||||
|
//
|
||||||
|
// DISCLAIMER
|
||||||
|
// This disclaimer is not a license and does not grant any
|
||||||
|
// rights to the materials distributed herewith. Except as
|
||||||
|
// otherwise provided in a valid license issued to you by
|
||||||
|
// Xilinx, and to the maximum extent permitted by applicable
|
||||||
|
// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
|
||||||
|
// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
|
||||||
|
// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
|
||||||
|
// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
|
||||||
|
// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
|
||||||
|
// (2) Xilinx shall not be liable (whether in contract or tort,
|
||||||
|
// including negligence, or under any other theory of
|
||||||
|
// liability) for any loss or damage of any kind or nature
|
||||||
|
// related to, arising under or in connection with these
|
||||||
|
// materials, including for any direct, or any indirect,
|
||||||
|
// special, incidental, or consequential loss or damage
|
||||||
|
// (including loss of data, profits, goodwill, or any type of
|
||||||
|
// loss or damage suffered as a result of any action brought
|
||||||
|
// by a third party) even if such damage or loss was
|
||||||
|
// reasonably foreseeable or Xilinx had been advised of the
|
||||||
|
// possibility of the same.
|
||||||
|
//
|
||||||
|
// CRITICAL APPLICATIONS
|
||||||
|
// Xilinx products are not designed or intended to be fail-
|
||||||
|
// safe, or for use in any application requiring fail-safe
|
||||||
|
// performance, such as life-support or safety devices or
|
||||||
|
// systems, Class III medical devices, nuclear facilities,
|
||||||
|
// applications related to the deployment of airbags, or any
|
||||||
|
// other applications that could lead to death, personal
|
||||||
|
// injury, or severe property or environmental damage
|
||||||
|
// (individually and collectively, "Critical
|
||||||
|
// Applications"). Customer assumes the sole risk and
|
||||||
|
// liability of any use of Xilinx products in Critical
|
||||||
|
// Applications, subject only to applicable laws and
|
||||||
|
// regulations governing limitations on product liability.
|
||||||
|
//
|
||||||
|
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
|
||||||
|
// PART OF THIS FILE AT ALL TIMES.
|
||||||
|
//
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// User entered comments
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// None
|
||||||
|
//
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// "Output Output Phase Duty Pk-to-Pk Phase"
|
||||||
|
// "Clock Freq (MHz) (degrees) Cycle (%) Jitter (ps) Error (ps)"
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// CLK_OUT1____60.000______0.000______50.0______231.736____234.038
|
||||||
|
//
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// "Input Clock Freq (MHz) Input Jitter (UI)"
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// __primary_________125.000____________0.010
|
||||||
|
|
||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
(* CORE_GENERATION_INFO = "clk_wiz_v3_6,clk_wiz_v3_6,{component_name=clk_wiz_v3_6,use_phase_alignment=false,use_min_o_jitter=false,use_max_i_jitter=false,use_dyn_phase_shift=false,use_inclk_switchover=false,use_dyn_reconfig=false,feedback_source=FDBK_AUTO,primtype_sel=MMCM_ADV,num_out_clk=1,clkin1_period=8.000,clkin2_period=10.000,use_power_down=false,use_reset=false,use_locked=false,use_inclk_stopped=false,use_status=false,use_freeze=false,use_clk_valid=false,feedback_type=SINGLE,clock_mgr_type=MANUAL,manual_override=false}" *)
|
||||||
|
module clk_wiz_v3_6
|
||||||
|
(// Clock in ports
|
||||||
|
input CLK_IN1,
|
||||||
|
// Clock out ports
|
||||||
|
output CLK_OUT1
|
||||||
|
);
|
||||||
|
|
||||||
|
// Input buffering
|
||||||
|
//------------------------------------
|
||||||
|
assign clkin1 = CLK_IN1;
|
||||||
|
|
||||||
|
|
||||||
|
// Clocking primitive
|
||||||
|
//------------------------------------
|
||||||
|
// Instantiation of the MMCM primitive
|
||||||
|
// * Unused inputs are tied off
|
||||||
|
// * Unused outputs are labeled unused
|
||||||
|
wire [15:0] do_unused;
|
||||||
|
wire drdy_unused;
|
||||||
|
wire psdone_unused;
|
||||||
|
wire locked_unused;
|
||||||
|
wire clkfbout;
|
||||||
|
wire clkfboutb_unused;
|
||||||
|
wire clkout0b_unused;
|
||||||
|
wire clkout1_unused;
|
||||||
|
wire clkout1b_unused;
|
||||||
|
wire clkout2_unused;
|
||||||
|
wire clkout2b_unused;
|
||||||
|
wire clkout3_unused;
|
||||||
|
wire clkout3b_unused;
|
||||||
|
wire clkout4_unused;
|
||||||
|
wire clkout5_unused;
|
||||||
|
wire clkout6_unused;
|
||||||
|
wire clkfbstopped_unused;
|
||||||
|
wire clkinstopped_unused;
|
||||||
|
|
||||||
|
MMCME2_ADV
|
||||||
|
#(.BANDWIDTH ("OPTIMIZED"),
|
||||||
|
.CLKOUT4_CASCADE ("FALSE"),
|
||||||
|
.COMPENSATION ("ZHOLD"),
|
||||||
|
.STARTUP_WAIT ("FALSE"),
|
||||||
|
.DIVCLK_DIVIDE (5),
|
||||||
|
.CLKFBOUT_MULT_F (40.500),
|
||||||
|
.CLKFBOUT_PHASE (0.000),
|
||||||
|
.CLKFBOUT_USE_FINE_PS ("FALSE"),
|
||||||
|
.CLKOUT0_DIVIDE_F (16.875),
|
||||||
|
.CLKOUT0_PHASE (0.000),
|
||||||
|
.CLKOUT0_DUTY_CYCLE (0.500),
|
||||||
|
.CLKOUT0_USE_FINE_PS ("FALSE"),
|
||||||
|
.CLKIN1_PERIOD (8.000),
|
||||||
|
.REF_JITTER1 (0.010))
|
||||||
|
mmcm_adv_inst
|
||||||
|
// Output clocks
|
||||||
|
(.CLKFBOUT (clkfbout),
|
||||||
|
.CLKFBOUTB (clkfboutb_unused),
|
||||||
|
.CLKOUT0 (clkout0),
|
||||||
|
.CLKOUT0B (clkout0b_unused),
|
||||||
|
.CLKOUT1 (clkout1_unused),
|
||||||
|
.CLKOUT1B (clkout1b_unused),
|
||||||
|
.CLKOUT2 (clkout2_unused),
|
||||||
|
.CLKOUT2B (clkout2b_unused),
|
||||||
|
.CLKOUT3 (clkout3_unused),
|
||||||
|
.CLKOUT3B (clkout3b_unused),
|
||||||
|
.CLKOUT4 (clkout4_unused),
|
||||||
|
.CLKOUT5 (clkout5_unused),
|
||||||
|
.CLKOUT6 (clkout6_unused),
|
||||||
|
// Input clock control
|
||||||
|
.CLKFBIN (clkfbout),
|
||||||
|
.CLKIN1 (clkin1),
|
||||||
|
.CLKIN2 (1'b0),
|
||||||
|
// Tied to always select the primary input clock
|
||||||
|
.CLKINSEL (1'b1),
|
||||||
|
// Ports for dynamic reconfiguration
|
||||||
|
.DADDR (7'h0),
|
||||||
|
.DCLK (1'b0),
|
||||||
|
.DEN (1'b0),
|
||||||
|
.DI (16'h0),
|
||||||
|
.DO (do_unused),
|
||||||
|
.DRDY (drdy_unused),
|
||||||
|
.DWE (1'b0),
|
||||||
|
// Ports for dynamic phase shift
|
||||||
|
.PSCLK (1'b0),
|
||||||
|
.PSEN (1'b0),
|
||||||
|
.PSINCDEC (1'b0),
|
||||||
|
.PSDONE (psdone_unused),
|
||||||
|
// Other control and status signals
|
||||||
|
.LOCKED (locked_unused),
|
||||||
|
.CLKINSTOPPED (clkinstopped_unused),
|
||||||
|
.CLKFBSTOPPED (clkfbstopped_unused),
|
||||||
|
.PWRDWN (1'b0),
|
||||||
|
.RST (1'b0));
|
||||||
|
|
||||||
|
// Output buffering
|
||||||
|
//-----------------------------------
|
||||||
|
|
||||||
|
//BUFG clkout1_buf
|
||||||
|
// (.O (CLK_OUT1),
|
||||||
|
// .I (clkout0));
|
||||||
|
|
||||||
|
// BUFG not currently supported
|
||||||
|
BUFGCTRL clkout1_buf (
|
||||||
|
.I0(clkout0),
|
||||||
|
.CE0(1'b1),
|
||||||
|
.S0(1'b1),
|
||||||
|
.O(CLK_OUT1)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
endmodule
|
477
xc7/arch.cc
477
xc7/arch.cc
@ -28,27 +28,8 @@
|
|||||||
#include "router1.h"
|
#include "router1.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <boost/serialization/unique_ptr.hpp>
|
|
||||||
#include <boost/serialization/vector.hpp>
|
|
||||||
#include <boost/serialization/unordered_map.hpp>
|
|
||||||
#include <boost/archive/binary_iarchive.hpp>
|
|
||||||
#include <boost/archive/binary_oarchive.hpp>
|
|
||||||
#include <boost/iostreams/filter/zlib.hpp>
|
|
||||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
|
||||||
struct nextpnr_binary_iarchive : public boost::archive::binary_iarchive {
|
|
||||||
nextpnr_binary_iarchive(boost::iostreams::filtering_istreambuf &ifs, NEXTPNR_NAMESPACE::BaseCtx* ctx, const std::string& inDeviceName, const std::string& inPackageName) : boost::archive::binary_iarchive(ifs), ctx(ctx), inDeviceName(inDeviceName), inPackageName(inPackageName) {}
|
|
||||||
NEXTPNR_NAMESPACE::BaseCtx *ctx;
|
|
||||||
std::string inDeviceName, inPackageName;
|
|
||||||
};
|
|
||||||
struct nextpnr_binary_oarchive : public boost::archive::binary_oarchive {
|
|
||||||
nextpnr_binary_oarchive(boost::iostreams::filtering_ostreambuf &ofs, NEXTPNR_NAMESPACE::BaseCtx* ctx) : boost::archive::binary_oarchive(ofs), ctx(ctx) {}
|
|
||||||
NEXTPNR_NAMESPACE::BaseCtx *ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "torc/common/DirectoryTree.hpp"
|
#include "torc/common/DirectoryTree.hpp"
|
||||||
|
|
||||||
//#define TORC_INFO_DB "torc_info.ar"
|
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
std::unique_ptr<const TorcInfo> torc_info;
|
std::unique_ptr<const TorcInfo> torc_info;
|
||||||
@ -57,6 +38,16 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
{
|
{
|
||||||
static const boost::regex re_loc(".+_X(\\d+)Y(\\d+)");
|
static const boost::regex re_loc(".+_X(\\d+)Y(\\d+)");
|
||||||
boost::cmatch what;
|
boost::cmatch what;
|
||||||
|
tile_to_xy.resize(tiles.getTileCount());
|
||||||
|
for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) {
|
||||||
|
const auto &tileInfo = tiles.getTileInfo(tileIndex);
|
||||||
|
if (!boost::regex_match(tileInfo.getName(), what, re_loc))
|
||||||
|
throw;
|
||||||
|
const auto x = boost::lexical_cast<int>(what.str(1));
|
||||||
|
const auto y = boost::lexical_cast<int>(what.str(2));
|
||||||
|
tile_to_xy[tileIndex] = std::make_pair(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
bel_to_site_index.reserve(sites.getSiteCount() * 4);
|
bel_to_site_index.reserve(sites.getSiteCount() * 4);
|
||||||
bel_to_loc.reserve(sites.getSiteCount() * 4);
|
bel_to_loc.reserve(sites.getSiteCount() * 4);
|
||||||
site_index_to_bel.resize(sites.getSiteCount());
|
site_index_to_bel.resize(sites.getSiteCount());
|
||||||
@ -67,11 +58,8 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
const auto &site = sites.getSite(i);
|
const auto &site = sites.getSite(i);
|
||||||
const auto &pd = site.getPrimitiveDefPtr();
|
const auto &pd = site.getPrimitiveDefPtr();
|
||||||
const auto &type = pd->getName();
|
const auto &type = pd->getName();
|
||||||
const auto &tileInfo = tiles.getTileInfo(site.getTileIndex());
|
int x, y;
|
||||||
if (!boost::regex_match(tileInfo.getName(), what, re_loc))
|
std::tie(x,y) = tile_to_xy[site.getTileIndex()];
|
||||||
throw;
|
|
||||||
const auto x = boost::lexical_cast<int>(what.str(1));
|
|
||||||
const auto y = boost::lexical_cast<int>(what.str(2));
|
|
||||||
|
|
||||||
if (type == "SLICEL" || type == "SLICEM") {
|
if (type == "SLICEL" || type == "SLICEM") {
|
||||||
bel_to_site_index.push_back(i);
|
bel_to_site_index.push_back(i);
|
||||||
@ -83,7 +71,6 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
if (!boost::regex_match(site_name.c_str(), what, re_loc))
|
if (!boost::regex_match(site_name.c_str(), what, re_loc))
|
||||||
throw;
|
throw;
|
||||||
const auto sx = boost::lexical_cast<int>(what.str(1));
|
const auto sx = boost::lexical_cast<int>(what.str(1));
|
||||||
const auto site_name_back = site_name.back();
|
|
||||||
if ((sx & 1) == 0) {
|
if ((sx & 1) == 0) {
|
||||||
bel_to_loc.emplace_back(x, y, 0);
|
bel_to_loc.emplace_back(x, y, 0);
|
||||||
bel_to_loc.emplace_back(x, y, 1);
|
bel_to_loc.emplace_back(x, y, 1);
|
||||||
@ -100,6 +87,14 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
} else if (type == "IOB33S" || type == "IOB33M") {
|
} else if (type == "IOB33S" || type == "IOB33M") {
|
||||||
bel_to_site_index.push_back(i);
|
bel_to_site_index.push_back(i);
|
||||||
site_index_to_type[i] = id_IOB33;
|
site_index_to_type[i] = id_IOB33;
|
||||||
|
// TODO: Fix z when two IOBs on same tile
|
||||||
|
bel_to_loc.emplace_back(x, y, 0);
|
||||||
|
site_index_to_bel[i] = b;
|
||||||
|
++b.index;
|
||||||
|
} else if (type == "IOB18S" || type == "IOB18M") {
|
||||||
|
bel_to_site_index.push_back(i);
|
||||||
|
site_index_to_type[i] = id_IOB18;
|
||||||
|
// TODO: Fix z when two IOBs on same tile
|
||||||
bel_to_loc.emplace_back(x, y, 0);
|
bel_to_loc.emplace_back(x, y, 0);
|
||||||
site_index_to_bel[i] = b;
|
site_index_to_bel[i] = b;
|
||||||
++b.index;
|
++b.index;
|
||||||
@ -122,11 +117,11 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
const boost::regex re_BOUNCE_NS("(BYP|FAN)_BOUNCE_[NS]3_\\d");
|
const boost::regex re_BOUNCE_NS("(BYP|FAN)_BOUNCE_[NS]3_\\d");
|
||||||
const boost::regex re_FAN("FAN(_ALT)?\\d");
|
const boost::regex re_FAN("FAN(_ALT)?\\d");
|
||||||
const boost::regex re_CLB_I1_6("CLBL[LM]_(L|LL|M)_[A-D]([1-6])");
|
const boost::regex re_CLB_I1_6("CLBL[LM]_(L|LL|M)_[A-D]([1-6])");
|
||||||
const boost::regex bufg_i("(CMT|CLK)_BUFG_BUFGCTRL\\d+_I0");
|
const boost::regex bufg_i("CLK_BUFG_BUFGCTRL\\d+_I0");
|
||||||
const boost::regex bufg_o("(CMT|CLK)_BUFG_BUFGCTRL\\d+_O");
|
const boost::regex bufg_o("CLK_BUFG_BUFGCTRL\\d+_O");
|
||||||
const boost::regex int_clk("CLK(_L)?[01]");
|
const boost::regex hrow("CLK_HROW_CLK[01]_[34]");
|
||||||
const boost::regex gclk("GCLK_(L_)?B\\d+(_EAST|_WEST)?");
|
|
||||||
std::unordered_map</*TileTypeIndex*/ unsigned, std::vector<delay_t>> delay_lookup;
|
std::unordered_map</*TileTypeIndex*/ unsigned, std::vector<delay_t>> delay_lookup;
|
||||||
|
std::unordered_map<Segments::SegmentReference, TileIndex> segment_to_anchor;
|
||||||
Tilewire currentTilewire;
|
Tilewire currentTilewire;
|
||||||
WireId w;
|
WireId w;
|
||||||
w.index = 0;
|
w.index = 0;
|
||||||
@ -141,8 +136,28 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
const auto ¤tSegment = segments.getTilewireSegment(currentTilewire);
|
const auto ¤tSegment = segments.getTilewireSegment(currentTilewire);
|
||||||
|
|
||||||
if (!currentSegment.isTrivial()) {
|
if (!currentSegment.isTrivial()) {
|
||||||
if (currentSegment.getAnchorTileIndex() != tileIndex)
|
auto r = segment_to_anchor.emplace(currentSegment, currentSegment.getAnchorTileIndex());
|
||||||
|
if (r.second) {
|
||||||
|
TilewireVector segment;
|
||||||
|
const_cast<DDB &>(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone);
|
||||||
|
// expand all of the arcs
|
||||||
|
TilewireVector::const_iterator sep = segment.begin();
|
||||||
|
TilewireVector::const_iterator see = segment.end();
|
||||||
|
while(sep < see) {
|
||||||
|
// expand the tilewire sinks
|
||||||
|
const Tilewire& tilewire = *sep++;
|
||||||
|
|
||||||
|
const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex());
|
||||||
|
const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
|
||||||
|
if (boost::starts_with(tileTypeName, "INT") || boost::starts_with(tileTypeName, "CLB")) {
|
||||||
|
r.first->second = tilewire.getTileIndex();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r.first->second != tileIndex)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
segment_to_wire.emplace(currentSegment, w);
|
segment_to_wire.emplace(currentSegment, w);
|
||||||
} else
|
} else
|
||||||
trivial_to_wire.emplace(currentTilewire, w);
|
trivial_to_wire.emplace(currentTilewire, w);
|
||||||
@ -224,9 +239,11 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
++w.index;
|
++w.index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
segment_to_anchor.clear();
|
||||||
wire_to_tilewire.shrink_to_fit();
|
wire_to_tilewire.shrink_to_fit();
|
||||||
wire_to_delay.shrink_to_fit();
|
wire_to_delay.shrink_to_fit();
|
||||||
num_wires = wire_to_tilewire.size();
|
num_wires = wire_to_tilewire.size();
|
||||||
|
wire_is_global.resize(num_wires);
|
||||||
|
|
||||||
wire_to_pips_downhill.resize(num_wires);
|
wire_to_pips_downhill.resize(num_wires);
|
||||||
// std::unordered_map<Arc, int> arc_to_pip;
|
// std::unordered_map<Arc, int> arc_to_pip;
|
||||||
@ -238,24 +255,45 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
const auto ¤tTilewire = wire_to_tilewire[w.index];
|
const auto ¤tTilewire = wire_to_tilewire[w.index];
|
||||||
if (currentTilewire.isUndefined())
|
if (currentTilewire.isUndefined())
|
||||||
continue;
|
continue;
|
||||||
arcs.clear();
|
|
||||||
|
|
||||||
const auto &tileInfo = tiles.getTileInfo(currentTilewire.getTileIndex());
|
const auto &tileInfo = tiles.getTileInfo(currentTilewire.getTileIndex());
|
||||||
const auto tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
|
const auto tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
|
||||||
const bool clb = boost::starts_with(
|
const bool clb = boost::starts_with(
|
||||||
tileTypeName, "CLB"); // Disable all CLB route-throughs (i.e. LUT in->out, LUT A->AMUX, for now)
|
tileTypeName, "CLB"); // Disable all CLB route-throughs (i.e. LUT in->out, LUT A->AMUX, for now)
|
||||||
|
|
||||||
arcs.clear();
|
|
||||||
const_cast<DDB &>(*ddb).expandSegmentSinks(currentTilewire, arcs, DDB::eExpandDirectionNone,
|
|
||||||
false /* inUseTied */, true /*inUseRegular */,
|
|
||||||
true /* inUseIrregular */, !clb /* inUseRoutethrough */);
|
|
||||||
|
|
||||||
auto &pips = wire_to_pips_downhill[w.index];
|
auto &pips = wire_to_pips_downhill[w.index];
|
||||||
pips.reserve(arcs.size());
|
const bool clk_tile = boost::starts_with(tileTypeName, "CLK");
|
||||||
const bool clk_tile = boost::starts_with(tileTypeName, "CMT") || boost::starts_with(tileTypeName, "CLK");
|
|
||||||
const bool int_tile = boost::starts_with(tileTypeName, "INT");
|
bool global_tile = false;
|
||||||
|
|
||||||
|
arcs.clear();
|
||||||
|
//const_cast<DDB &>(*ddb).expandSegmentSinks(currentTilewire, arcs, DDB::eExpandDirectionNone,
|
||||||
|
// false /* inUseTied */, true /*inUseRegular */,
|
||||||
|
// true /* inUseIrregular */, !clb /* inUseRoutethrough */);
|
||||||
|
{
|
||||||
|
// expand the segment
|
||||||
|
TilewireVector segment;
|
||||||
|
const_cast<DDB &>(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone);
|
||||||
|
// expand all of the arcs
|
||||||
|
TilewireVector::const_iterator sep = segment.begin();
|
||||||
|
TilewireVector::const_iterator see = segment.end();
|
||||||
|
while(sep < see) {
|
||||||
|
// expand the tilewire sinks
|
||||||
|
const Tilewire& tilewire = *sep++;
|
||||||
|
|
||||||
|
const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex());
|
||||||
|
const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
|
||||||
|
global_tile = global_tile || boost::starts_with(tileTypeName, "CLK") || boost::starts_with(tileTypeName, "HCLK") || boost::starts_with(tileTypeName, "CFG");
|
||||||
|
|
||||||
|
TilewireVector sinks;
|
||||||
|
const_cast<DDB &>(*ddb).expandTilewireSinks(tilewire, sinks, false /*inUseTied*/, true /*inUseRegular*/, true /*inUseIrregular*/,
|
||||||
|
!clb /* inUseRoutethrough */);
|
||||||
|
// rewrite the sinks as arcs
|
||||||
|
TilewireVector::const_iterator sip = sinks.begin();
|
||||||
|
TilewireVector::const_iterator sie = sinks.end();
|
||||||
|
while(sip < sie) {
|
||||||
|
Arc a(tilewire, *sip++);
|
||||||
|
|
||||||
for (const auto &a : arcs) {
|
|
||||||
// Disable BUFG I0 -> O routethrough
|
// Disable BUFG I0 -> O routethrough
|
||||||
if (clk_tile) {
|
if (clk_tile) {
|
||||||
ewi.set(a.getSourceTilewire());
|
ewi.set(a.getSourceTilewire());
|
||||||
@ -265,33 +303,29 @@ TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::str
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Disable CLK inputs from being driven from the fabric (must be from global clock network)
|
|
||||||
else if (int_tile) {
|
// Disable entering HROW from INT_[LR].CLK[01]
|
||||||
ewi.set(a.getSinkTilewire());
|
if (boost::starts_with(tileTypeName, "CLK_HROW")) {
|
||||||
if (boost::regex_match(ewi.mWireName, int_clk)) {
|
|
||||||
ewi.set(a.getSourceTilewire());
|
ewi.set(a.getSourceTilewire());
|
||||||
if (!boost::regex_match(ewi.mWireName, gclk))
|
if (boost::regex_match(ewi.mWireName, hrow))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
pips.emplace_back(p);
|
pips.emplace_back(p);
|
||||||
pip_to_arc.emplace_back(a);
|
pip_to_arc.emplace_back(a);
|
||||||
// arc_to_pip.emplace(a, p.index);
|
// arc_to_pip.emplace(a, p.index);
|
||||||
const auto &tw = a.getSinkTilewire();
|
|
||||||
pip_to_dst_wire.emplace_back(tilewire_to_wire(tw));
|
|
||||||
++p.index;
|
++p.index;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pips.shrink_to_fit();
|
pips.shrink_to_fit();
|
||||||
|
|
||||||
|
if (global_tile)
|
||||||
|
wire_is_global[w.index] = true;
|
||||||
}
|
}
|
||||||
pip_to_arc.shrink_to_fit();
|
pip_to_arc.shrink_to_fit();
|
||||||
num_pips = pip_to_arc.size();
|
num_pips = pip_to_arc.size();
|
||||||
|
|
||||||
pip_to_dst_wire.reserve(num_pips);
|
|
||||||
for (const auto &arc : pip_to_arc) {
|
|
||||||
const auto &tw = arc.getSinkTilewire();
|
|
||||||
pip_to_dst_wire.emplace_back(tilewire_to_wire(tw));
|
|
||||||
}
|
|
||||||
|
|
||||||
height = (int)tiles.getRowCount();
|
height = (int)tiles.getRowCount();
|
||||||
width = (int)tiles.getColCount();
|
width = (int)tiles.getColCount();
|
||||||
}
|
}
|
||||||
@ -316,29 +350,9 @@ Arch::Arch(ArchArgs args) : args(args)
|
|||||||
{
|
{
|
||||||
torc::common::DirectoryTree directoryTree("/opt/torc/src/torc");
|
torc::common::DirectoryTree directoryTree("/opt/torc/src/torc");
|
||||||
if (args.type == ArchArgs::Z020) {
|
if (args.type == ArchArgs::Z020) {
|
||||||
#ifdef TORC_INFO_DB
|
|
||||||
std::ifstream ifs(TORC_INFO_DB, std::ios::binary);
|
|
||||||
if (ifs) {
|
|
||||||
boost::iostreams::filtering_istreambuf fifs;
|
|
||||||
fifs.push(boost::iostreams::zlib_decompressor());
|
|
||||||
fifs.push(ifs);
|
|
||||||
nextpnr_binary_iarchive ia(fifs, this, "xc7z020", args.package);
|
|
||||||
ia >> torc_info;
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7z020", args.package));
|
torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7z020", args.package));
|
||||||
#ifdef TORC_INFO_DB
|
} else if (args.type == ArchArgs::VX980) {
|
||||||
std::ofstream ofs(TORC_INFO_DB, std::ios::binary);
|
torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7vx980t", args.package));
|
||||||
if (ofs) {
|
|
||||||
boost::iostreams::filtering_ostreambuf fofs;
|
|
||||||
fofs.push(boost::iostreams::zlib_compressor());
|
|
||||||
fofs.push(ofs);
|
|
||||||
nextpnr_binary_oarchive oa(fofs, this);
|
|
||||||
oa << torc_info;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log_error("Unsupported XC7 chip type.\n");
|
log_error("Unsupported XC7 chip type.\n");
|
||||||
}
|
}
|
||||||
@ -351,21 +365,9 @@ Arch::Arch(ArchArgs args) : args(args)
|
|||||||
log_info("Number of pips: %d\n", torc_info->num_pips);
|
log_info("Number of pips: %d\n", torc_info->num_pips);
|
||||||
}
|
}
|
||||||
|
|
||||||
// package_info = nullptr;
|
|
||||||
// for (int i = 0; i < chip_info->num_packages; i++) {
|
|
||||||
// if (chip_info->packages_data[i].name.get() == args.package) {
|
|
||||||
// package_info = &(chip_info->packages_data[i]);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (package_info == nullptr)
|
|
||||||
// log_error("Unsupported package '%s'.\n", args.package.c_str());
|
|
||||||
|
|
||||||
// bel_carry.resize(chip_info->num_bels);
|
|
||||||
bel_to_cell.resize(torc_info->num_bels);
|
bel_to_cell.resize(torc_info->num_bels);
|
||||||
wire_to_net.resize(torc_info->num_wires);
|
wire_to_net.resize(torc_info->num_wires);
|
||||||
pip_to_net.resize(torc_info->num_pips);
|
pip_to_net.resize(torc_info->num_pips);
|
||||||
// switches_locked.resize(chip_info->num_switches);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
@ -374,6 +376,8 @@ std::string Arch::getChipName() const
|
|||||||
{
|
{
|
||||||
if (args.type == ArchArgs::Z020) {
|
if (args.type == ArchArgs::Z020) {
|
||||||
return "z020";
|
return "z020";
|
||||||
|
} else if (args.type == ArchArgs::VX980) {
|
||||||
|
return "vx980";
|
||||||
} else {
|
} else {
|
||||||
log_error("Unsupported XC7 chip type.\n");
|
log_error("Unsupported XC7 chip type.\n");
|
||||||
}
|
}
|
||||||
@ -385,6 +389,8 @@ IdString Arch::archArgsToId(ArchArgs args) const
|
|||||||
{
|
{
|
||||||
if (args.type == ArchArgs::Z020)
|
if (args.type == ArchArgs::Z020)
|
||||||
return id("z020");
|
return id("z020");
|
||||||
|
if (args.type == ArchArgs::VX980)
|
||||||
|
return id("vx980");
|
||||||
return IdString();
|
return IdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,10 +516,9 @@ WireId Arch::getBelPinWire(BelId bel, IdString pin) const
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (bel_type == id_PS7) {
|
} else if (bel_type == id_PS7 || bel_type == id_MMCME2_ADV) {
|
||||||
// e.g. Convert DDRARB[0] -> DDRARB0
|
// e.g. Convert DDRARB[0] -> DDRARB0
|
||||||
boost::erase_all(pin_name, "[");
|
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
|
||||||
boost::erase_all(pin_name, "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto site_index = torc_info->bel_to_site_index[bel.index];
|
auto site_index = torc_info->bel_to_site_index[bel.index];
|
||||||
@ -525,49 +530,12 @@ WireId Arch::getBelPinWire(BelId bel, IdString pin) const
|
|||||||
pin_name.c_str());
|
pin_name.c_str());
|
||||||
|
|
||||||
return torc_info->tilewire_to_wire(tw);
|
return torc_info->tilewire_to_wire(tw);
|
||||||
|
|
||||||
// NPNR_ASSERT(bel != BelId());
|
|
||||||
//
|
|
||||||
// int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
|
|
||||||
// const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
|
|
||||||
//
|
|
||||||
// if (num_bel_wires < 7) {
|
|
||||||
// for (int i = 0; i < num_bel_wires; i++) {
|
|
||||||
// if (bel_wires[i].port == pin.index) {
|
|
||||||
// ret.index = bel_wires[i].wire_index;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// int b = 0, e = num_bel_wires - 1;
|
|
||||||
// while (b <= e) {
|
|
||||||
// int i = (b + e) / 2;
|
|
||||||
// if (bel_wires[i].port == pin.index) {
|
|
||||||
// ret.index = bel_wires[i].wire_index;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// if (bel_wires[i].port > pin.index)
|
|
||||||
// e = i - 1;
|
|
||||||
// else
|
|
||||||
// b = i + 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IdString> Arch::getBelPins(BelId bel) const
|
std::vector<IdString> Arch::getBelPins(BelId bel) const
|
||||||
{
|
{
|
||||||
std::vector<IdString> ret;
|
std::vector<IdString> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
/* NPNR_ASSERT(bel != BelId());
|
|
||||||
|
|
||||||
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
|
|
||||||
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
|
|
||||||
|
|
||||||
for (int i = 0; i < num_bel_wires; i++)
|
|
||||||
ret.push_back(IdString(bel_wires[i].port));
|
|
||||||
*/
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,7 +544,6 @@ std::vector<IdString> Arch::getBelPins(BelId bel) const
|
|||||||
WireId Arch::getWireByName(IdString name) const
|
WireId Arch::getWireByName(IdString name) const
|
||||||
{
|
{
|
||||||
WireId ret;
|
WireId ret;
|
||||||
|
|
||||||
if (wire_by_name.empty()) {
|
if (wire_by_name.empty()) {
|
||||||
for (int i = 0; i < torc_info->num_wires; i++)
|
for (int i = 0; i < torc_info->num_wires; i++)
|
||||||
wire_by_name[id(torc_info->wire_to_name(i))] = i;
|
wire_by_name[id(torc_info->wire_to_name(i))] = i;
|
||||||
@ -592,38 +559,7 @@ WireId Arch::getWireByName(IdString name) const
|
|||||||
IdString Arch::getWireType(WireId wire) const
|
IdString Arch::getWireType(WireId wire) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(wire != WireId());
|
NPNR_ASSERT(wire != WireId());
|
||||||
// switch (chip_info->wire_data[wire.index].type) {
|
NPNR_ASSERT("TODO");
|
||||||
// case WireInfoPOD::WIRE_TYPE_NONE:
|
|
||||||
// return IdString();
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_GLB2LOCAL:
|
|
||||||
// return id("GLB2LOCAL");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_GLB_NETWK:
|
|
||||||
// return id("GLB_NETWK");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LOCAL:
|
|
||||||
// return id("LOCAL");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LUTFF_IN:
|
|
||||||
// return id("LUTFF_IN");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT:
|
|
||||||
// return id("LUTFF_IN_LUT");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LUTFF_LOUT:
|
|
||||||
// return id("LUTFF_LOUT");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LUTFF_OUT:
|
|
||||||
// return id("LUTFF_OUT");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LUTFF_COUT:
|
|
||||||
// return id("LUTFF_COUT");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LUTFF_GLOBAL:
|
|
||||||
// return id("LUTFF_GLOBAL");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_CARRY_IN_MUX:
|
|
||||||
// return id("CARRY_IN_MUX");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_SP4_V:
|
|
||||||
// return id("SP4_V");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_SP4_H:
|
|
||||||
// return id("SP4_H");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_SP12_V:
|
|
||||||
// return id("SP12_V");
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_SP12_H:
|
|
||||||
// return id("SP12_H");
|
|
||||||
// }
|
|
||||||
return IdString();
|
return IdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,6 +567,7 @@ IdString Arch::getWireType(WireId wire) const
|
|||||||
std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
|
std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
|
||||||
{
|
{
|
||||||
std::vector<std::pair<IdString, std::string>> ret;
|
std::vector<std::pair<IdString, std::string>> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,27 +601,12 @@ IdString Arch::getPipName(PipId pip) const
|
|||||||
std::stringstream pip_name;
|
std::stringstream pip_name;
|
||||||
pip_name << ewi_src.mTileName << "." << ewi_src.mWireName << ".->." << ewi_dst.mWireName;
|
pip_name << ewi_src.mTileName << "." << ewi_src.mWireName << ".->." << ewi_dst.mWireName;
|
||||||
return id(pip_name.str());
|
return id(pip_name.str());
|
||||||
|
|
||||||
//#if 1
|
|
||||||
// int x = chip_info->pip_data[pip.index].x;
|
|
||||||
// int y = chip_info->pip_data[pip.index].y;
|
|
||||||
//
|
|
||||||
// std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get();
|
|
||||||
// std::replace(src_name.begin(), src_name.end(), '/', '.');
|
|
||||||
//
|
|
||||||
// std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get();
|
|
||||||
// std::replace(dst_name.begin(), dst_name.end(), '/', '.');
|
|
||||||
//
|
|
||||||
// return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name);
|
|
||||||
//#else
|
|
||||||
// return id(chip_info->pip_data[pip.index].name.get());
|
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<IdString, std::string>> Arch::getPipAttrs(PipId pip) const
|
std::vector<std::pair<IdString, std::string>> Arch::getPipAttrs(PipId pip) const
|
||||||
{
|
{
|
||||||
std::vector<std::pair<IdString, std::string>> ret;
|
std::vector<std::pair<IdString, std::string>> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -694,11 +616,7 @@ BelId Arch::getPackagePinBel(const std::string &pin) const { return getBelByName
|
|||||||
|
|
||||||
std::string Arch::getBelPackagePin(BelId bel) const
|
std::string Arch::getBelPackagePin(BelId bel) const
|
||||||
{
|
{
|
||||||
// for (int i = 0; i < package_info->num_pins; i++) {
|
NPNR_ASSERT("TODO");
|
||||||
// if (package_info->pins[i].bel_index == bel.index) {
|
|
||||||
// return std::string(package_info->pins[i].name.get());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -717,39 +635,7 @@ IdString Arch::getGroupName(GroupId group) const
|
|||||||
std::string suffix;
|
std::string suffix;
|
||||||
|
|
||||||
switch (group.type) {
|
switch (group.type) {
|
||||||
case GroupId::TYPE_FRAME:
|
NPNR_ASSERT("TODO");
|
||||||
suffix = "tile";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_MAIN_SW:
|
|
||||||
suffix = "main_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LOCAL_SW:
|
|
||||||
suffix = "local_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LC0_SW:
|
|
||||||
suffix = "lc0_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LC1_SW:
|
|
||||||
suffix = "lc1_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LC2_SW:
|
|
||||||
suffix = "lc2_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LC3_SW:
|
|
||||||
suffix = "lc3_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LC4_SW:
|
|
||||||
suffix = "lc4_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LC5_SW:
|
|
||||||
suffix = "lc5_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LC6_SW:
|
|
||||||
suffix = "lc6_sw";
|
|
||||||
break;
|
|
||||||
case GroupId::TYPE_LC7_SW:
|
|
||||||
suffix = "lc7_sw";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return IdString();
|
return IdString();
|
||||||
}
|
}
|
||||||
@ -760,52 +646,7 @@ IdString Arch::getGroupName(GroupId group) const
|
|||||||
std::vector<GroupId> Arch::getGroups() const
|
std::vector<GroupId> Arch::getGroups() const
|
||||||
{
|
{
|
||||||
std::vector<GroupId> ret;
|
std::vector<GroupId> ret;
|
||||||
/*
|
NPNR_ASSERT("TODO");
|
||||||
for (int y = 0; y < chip_info->height; y++) {
|
|
||||||
for (int x = 0; x < chip_info->width; x++) {
|
|
||||||
TileType type = chip_info->tile_grid[y * chip_info->width + x];
|
|
||||||
if (type == TILE_NONE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
GroupId group;
|
|
||||||
group.type = GroupId::TYPE_FRAME;
|
|
||||||
group.x = x;
|
|
||||||
group.y = y;
|
|
||||||
// ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_MAIN_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_LOCAL_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
if (type == TILE_LOGIC) {
|
|
||||||
group.type = GroupId::TYPE_LC0_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_LC1_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_LC2_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_LC3_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_LC4_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_LC5_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_LC6_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
|
|
||||||
group.type = GroupId::TYPE_LC7_SW;
|
|
||||||
ret.push_back(group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -824,12 +665,14 @@ std::vector<WireId> Arch::getGroupWires(GroupId group) const
|
|||||||
std::vector<PipId> Arch::getGroupPips(GroupId group) const
|
std::vector<PipId> Arch::getGroupPips(GroupId group) const
|
||||||
{
|
{
|
||||||
std::vector<PipId> ret;
|
std::vector<PipId> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
|
std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
|
||||||
{
|
{
|
||||||
std::vector<GroupId> ret;
|
std::vector<GroupId> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -965,7 +808,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
|
|||||||
}
|
}
|
||||||
// TODO
|
// TODO
|
||||||
// if (port == id_OMUX)
|
// if (port == id_OMUX)
|
||||||
} else if (cell->type == id_IOB33) {
|
} else if (cell->type == id_IOB33 || cell->type == id_IOB18) {
|
||||||
if (port == id_I)
|
if (port == id_I)
|
||||||
return TMG_STARTPOINT;
|
return TMG_STARTPOINT;
|
||||||
else if (port == id_O)
|
else if (port == id_O)
|
||||||
@ -977,6 +820,8 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
|
|||||||
} else if (cell->type == id_PS7) {
|
} else if (cell->type == id_PS7) {
|
||||||
// TODO
|
// TODO
|
||||||
return TMG_IGNORE;
|
return TMG_IGNORE;
|
||||||
|
} else if (cell->type == id_MMCME2_ADV) {
|
||||||
|
return TMG_IGNORE;
|
||||||
}
|
}
|
||||||
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
|
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
|
||||||
}
|
}
|
||||||
@ -1056,109 +901,3 @@ void Arch::assignCellInfo(CellInfo *cell)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
// outside of any namespace
|
|
||||||
BOOST_SERIALIZATION_SPLIT_FREE(Segments::SegmentReference)
|
|
||||||
BOOST_SERIALIZATION_SPLIT_FREE(CompactSegmentIndex)
|
|
||||||
BOOST_SERIALIZATION_SPLIT_FREE(TileIndex)
|
|
||||||
BOOST_SERIALIZATION_SPLIT_FREE(Arc)
|
|
||||||
BOOST_SERIALIZATION_SPLIT_FREE(Tilewire)
|
|
||||||
BOOST_SERIALIZATION_SPLIT_FREE(WireIndex)
|
|
||||||
BOOST_SERIALIZATION_SPLIT_FREE(SiteIndex)
|
|
||||||
BOOST_SERIALIZATION_SPLIT_FREE(NEXTPNR_NAMESPACE::IdString)
|
|
||||||
|
|
||||||
namespace boost { namespace serialization {
|
|
||||||
|
|
||||||
template<class Archive>
|
|
||||||
inline void load_construct_data(
|
|
||||||
Archive & ar, NEXTPNR_NAMESPACE::TorcInfo * t, const unsigned int file_version
|
|
||||||
){
|
|
||||||
const auto& inDeviceName = static_cast<const nextpnr_binary_iarchive&>(ar).inDeviceName;
|
|
||||||
const auto& inPackageName = static_cast<const nextpnr_binary_iarchive&>(ar).inPackageName;
|
|
||||||
::new(t)NEXTPNR_NAMESPACE::TorcInfo(inDeviceName, inPackageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Archive>
|
|
||||||
void save(Archive& ar, const Segments::SegmentReference& o, unsigned int) {
|
|
||||||
ar & o.getCompactSegmentIndex();
|
|
||||||
ar & o.getAnchorTileIndex();
|
|
||||||
}
|
|
||||||
template<class Archive>
|
|
||||||
void load(Archive& ar, Segments::SegmentReference& o, unsigned int) {
|
|
||||||
CompactSegmentIndex i;
|
|
||||||
TileIndex j;
|
|
||||||
ar & i;
|
|
||||||
ar & j;
|
|
||||||
o = Segments::SegmentReference(i, j);
|
|
||||||
}
|
|
||||||
#define SERIALIZE_POD(__T__) \
|
|
||||||
template<class Archive> \
|
|
||||||
void save(Archive& ar, const __T__& o, unsigned int) { \
|
|
||||||
ar & static_cast<__T__::pod>(o); \
|
|
||||||
} \
|
|
||||||
template<class Archive> \
|
|
||||||
void load(Archive& ar, __T__& o, unsigned int) { \
|
|
||||||
__T__::pod i; \
|
|
||||||
ar & i; \
|
|
||||||
o = __T__(i); \
|
|
||||||
}
|
|
||||||
SERIALIZE_POD(CompactSegmentIndex)
|
|
||||||
SERIALIZE_POD(TileIndex)
|
|
||||||
SERIALIZE_POD(WireIndex)
|
|
||||||
SERIALIZE_POD(SiteIndex)
|
|
||||||
template<class Archive>
|
|
||||||
void save(Archive& ar, const Arc& o, unsigned int) {
|
|
||||||
ar & o.getSourceTilewire();
|
|
||||||
ar & o.getSinkTilewire();
|
|
||||||
}
|
|
||||||
template<class Archive>
|
|
||||||
void load(Archive& ar, Arc& o, unsigned int) {
|
|
||||||
Tilewire s, t;
|
|
||||||
ar & s;
|
|
||||||
ar & t;
|
|
||||||
o = Arc(s, t);
|
|
||||||
}
|
|
||||||
template<class Archive>
|
|
||||||
void save(Archive& ar, const Tilewire& o, unsigned int) {
|
|
||||||
ar & o.getTileIndex();
|
|
||||||
ar & o.getWireIndex();
|
|
||||||
}
|
|
||||||
template<class Archive>
|
|
||||||
void load(Archive& ar, Tilewire& o, unsigned int) {
|
|
||||||
TileIndex i;
|
|
||||||
WireIndex j;
|
|
||||||
ar & i;
|
|
||||||
ar & j;
|
|
||||||
o.setTileIndex(TileIndex(i));
|
|
||||||
o.setWireIndex(WireIndex(j));
|
|
||||||
}
|
|
||||||
template<class Archive>
|
|
||||||
void serialize(Archive& ar, NEXTPNR_NAMESPACE::Loc& o, unsigned int) {
|
|
||||||
ar & o.x;
|
|
||||||
ar & o.y;
|
|
||||||
ar & o.z;
|
|
||||||
}
|
|
||||||
template<class Archive>
|
|
||||||
void serialize(Archive& ar, NEXTPNR_NAMESPACE::DelayInfo& o, unsigned int) {
|
|
||||||
ar & o.delay;
|
|
||||||
}
|
|
||||||
template<class Archive>
|
|
||||||
void save(Archive& ar, const NEXTPNR_NAMESPACE::IdString& o, unsigned int) {
|
|
||||||
const std::string i = o.str(static_cast<const nextpnr_binary_oarchive&>(ar).ctx);
|
|
||||||
ar & i;
|
|
||||||
}
|
|
||||||
template<class Archive>
|
|
||||||
void load(Archive& ar, NEXTPNR_NAMESPACE::IdString& o, unsigned int) {
|
|
||||||
std::string i;
|
|
||||||
ar & i;
|
|
||||||
o = static_cast<nextpnr_binary_iarchive&>(ar).ctx->id(i);
|
|
||||||
}
|
|
||||||
#define SERIALIZE_INDEX(__T__) \
|
|
||||||
template<class Archive> \
|
|
||||||
void serialize(Archive& ar, __T__& o, unsigned int) { \
|
|
||||||
ar & o.index; \
|
|
||||||
}
|
|
||||||
SERIALIZE_INDEX(NEXTPNR_NAMESPACE::BelId)
|
|
||||||
SERIALIZE_INDEX(NEXTPNR_NAMESPACE::WireId)
|
|
||||||
SERIALIZE_INDEX(NEXTPNR_NAMESPACE::PipId)
|
|
||||||
}} // namespace boost::serialization
|
|
||||||
|
74
xc7/arch.h
74
xc7/arch.h
@ -21,8 +21,6 @@
|
|||||||
#error Include "arch.h" via "nextpnr.h" only.
|
#error Include "arch.h" via "nextpnr.h" only.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <boost/serialization/access.hpp>
|
|
||||||
|
|
||||||
#include "torc/Architecture.hpp"
|
#include "torc/Architecture.hpp"
|
||||||
#include "torc/Common.hpp"
|
#include "torc/Common.hpp"
|
||||||
using namespace torc::architecture;
|
using namespace torc::architecture;
|
||||||
@ -332,33 +330,12 @@ struct TorcInfo
|
|||||||
std::vector<std::vector<PipId>> wire_to_pips_downhill;
|
std::vector<std::vector<PipId>> wire_to_pips_downhill;
|
||||||
std::vector<Arc> pip_to_arc;
|
std::vector<Arc> pip_to_arc;
|
||||||
int num_pips;
|
int num_pips;
|
||||||
std::vector<WireId> pip_to_dst_wire;
|
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
std::vector<bool> wire_is_global;
|
||||||
|
std::vector<std::pair<int,int>> tile_to_xy;
|
||||||
|
|
||||||
TorcInfo(const std::string &inDeviceName, const std::string &inPackageName);
|
TorcInfo(const std::string &inDeviceName, const std::string &inPackageName);
|
||||||
private:
|
|
||||||
friend class boost::serialization::access;
|
|
||||||
//TorcInfo(const std::string &inDeviceName, const std::string &inPackageName);
|
|
||||||
//template<class Archive, class T> friend inline void load_construct_data(Archive &ar, T *t, const unsigned int file_version);
|
|
||||||
template<class Archive>
|
|
||||||
void serialize(Archive & ar, const unsigned int /*version*/)
|
|
||||||
{
|
|
||||||
ar & bel_to_site_index;
|
|
||||||
ar & num_bels;
|
|
||||||
ar & site_index_to_bel;
|
|
||||||
ar & site_index_to_type;
|
|
||||||
ar & bel_to_loc;
|
|
||||||
ar & segment_to_wire;
|
|
||||||
ar & trivial_to_wire;
|
|
||||||
ar & wire_to_tilewire;
|
|
||||||
ar & num_wires;
|
|
||||||
ar & wire_to_delay;
|
|
||||||
ar & wire_to_pips_downhill;
|
|
||||||
ar & pip_to_arc;
|
|
||||||
ar & num_pips;
|
|
||||||
ar & pip_to_dst_wire;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
extern std::unique_ptr<const TorcInfo> torc_info;
|
extern std::unique_ptr<const TorcInfo> torc_info;
|
||||||
|
|
||||||
@ -500,7 +477,8 @@ struct ArchArgs
|
|||||||
enum ArchArgsTypes
|
enum ArchArgsTypes
|
||||||
{
|
{
|
||||||
NONE,
|
NONE,
|
||||||
Z020
|
Z020,
|
||||||
|
VX980
|
||||||
} type = NONE;
|
} type = NONE;
|
||||||
std::string package;
|
std::string package;
|
||||||
};
|
};
|
||||||
@ -683,7 +661,6 @@ struct Arch : BaseCtx
|
|||||||
auto pip = it->second.pip;
|
auto pip = it->second.pip;
|
||||||
if (pip != PipId()) {
|
if (pip != PipId()) {
|
||||||
pip_to_net[pip.index] = nullptr;
|
pip_to_net[pip.index] = nullptr;
|
||||||
// switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
net_wires.erase(it);
|
net_wires.erase(it);
|
||||||
@ -716,10 +693,7 @@ struct Arch : BaseCtx
|
|||||||
BelPinRange getWireBelPins(WireId wire) const
|
BelPinRange getWireBelPins(WireId wire) const
|
||||||
{
|
{
|
||||||
BelPinRange range;
|
BelPinRange range;
|
||||||
// NPNR_ASSERT(wire != WireId());
|
// TODO
|
||||||
// range.b.ptr = chip_info->wire_data[wire.index].bel_pins.get();
|
|
||||||
// range.e.ptr = range.b.ptr + chip_info->wire_data[wire.index].num_bel_pins;
|
|
||||||
//throw;
|
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,10 +713,8 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
|
NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
|
||||||
// NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == nullptr);
|
|
||||||
|
|
||||||
pip_to_net[pip.index] = net;
|
pip_to_net[pip.index] = net;
|
||||||
// switches_locked[chip_info->pip_data[pip.index].switch_index] = net;
|
|
||||||
|
|
||||||
WireId dst = getPipDstWire(pip);
|
WireId dst = getPipDstWire(pip);
|
||||||
NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
|
NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
|
||||||
@ -757,7 +729,6 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
|
NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
|
||||||
// NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != nullptr);
|
|
||||||
|
|
||||||
WireId dst = getPipDstWire(pip);
|
WireId dst = getPipDstWire(pip);
|
||||||
NPNR_ASSERT(wire_to_net[dst.index] != nullptr);
|
NPNR_ASSERT(wire_to_net[dst.index] != nullptr);
|
||||||
@ -765,7 +736,6 @@ struct Arch : BaseCtx
|
|||||||
pip_to_net[pip.index]->wires.erase(dst);
|
pip_to_net[pip.index]->wires.erase(dst);
|
||||||
|
|
||||||
pip_to_net[pip.index] = nullptr;
|
pip_to_net[pip.index] = nullptr;
|
||||||
// switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
|
|
||||||
refreshUiPip(pip);
|
refreshUiPip(pip);
|
||||||
refreshUiWire(dst);
|
refreshUiWire(dst);
|
||||||
}
|
}
|
||||||
@ -773,25 +743,6 @@ struct Arch : BaseCtx
|
|||||||
bool checkPipAvail(PipId pip) const
|
bool checkPipAvail(PipId pip) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
// auto &pi = chip_info->pip_data[pip.index];
|
|
||||||
// auto &si = chip_info->bits_info->switches[pi.switch_index];
|
|
||||||
|
|
||||||
// if (switches_locked[pi.switch_index] != nullptr)
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
// if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) {
|
|
||||||
// NPNR_ASSERT(si.bel >= 0);
|
|
||||||
// if (bel_to_cell[si.bel] != nullptr)
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// if (pi.flags & PipInfoPOD::FLAG_NOCARRY) {
|
|
||||||
// NPNR_ASSERT(si.bel >= 0);
|
|
||||||
// if (bel_carry[si.bel])
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// return true;
|
|
||||||
return pip_to_net[pip.index] == nullptr;
|
return pip_to_net[pip.index] == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,7 +757,6 @@ struct Arch : BaseCtx
|
|||||||
NetInfo *getConflictingPipNet(PipId pip) const
|
NetInfo *getConflictingPipNet(PipId pip) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
// return switches_locked[chip_info->pip_data[pip.index].switch_index];
|
|
||||||
return pip_to_net[pip.index];
|
return pip_to_net[pip.index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -820,14 +770,8 @@ struct Arch : BaseCtx
|
|||||||
|
|
||||||
Loc getPipLocation(PipId pip) const
|
Loc getPipLocation(PipId pip) const
|
||||||
{
|
{
|
||||||
const auto &arc = torc_info->pip_to_arc[pip.index];
|
|
||||||
const auto &tw = arc.getSourceTilewire();
|
|
||||||
const auto &tile_info = torc_info->tiles.getTileInfo(tw.getTileIndex());
|
|
||||||
|
|
||||||
Loc loc;
|
Loc loc;
|
||||||
loc.x = tile_info.getCol();
|
NPNR_ASSERT("TODO");
|
||||||
loc.y = tile_info.getRow();
|
|
||||||
loc.z = 0;
|
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,7 +794,9 @@ struct Arch : BaseCtx
|
|||||||
WireId getPipDstWire(PipId pip) const
|
WireId getPipDstWire(PipId pip) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
return torc_info->pip_to_dst_wire[pip.index];
|
const auto &arc = torc_info->pip_to_arc[pip.index];
|
||||||
|
const auto &tw = arc.getSinkTilewire();
|
||||||
|
return torc_info->tilewire_to_wire(tw);
|
||||||
}
|
}
|
||||||
|
|
||||||
DelayInfo getPipDelay(PipId pip) const
|
DelayInfo getPipDelay(PipId pip) const
|
||||||
@ -985,6 +931,4 @@ struct Arch : BaseCtx
|
|||||||
float placer_constraintWeight = 10;
|
float placer_constraintWeight = 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
// void ice40DelayFuzzerMain(Context *ctx);
|
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -29,43 +29,14 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
bool Arch::logicCellsCompatible(const CellInfo **it, const size_t size) const
|
bool Arch::logicCellsCompatible(const CellInfo **it, const size_t size) const
|
||||||
{
|
{
|
||||||
// bool dffs_exist = false, dffs_neg = false;
|
// TODO: Check clock, clock-enable, and set-reset compatiility
|
||||||
// const NetInfo *cen = nullptr, *clk = nullptr, *sr = nullptr;
|
|
||||||
//
|
|
||||||
// for (auto cell : boost::make_iterator_range(it, it+size)) {
|
|
||||||
// NPNR_ASSERT(cell->belType == id_ICESTORM_LC);
|
|
||||||
// if (cell->lcInfo.dffEnable) {
|
|
||||||
// if (!dffs_exist) {
|
|
||||||
// dffs_exist = true;
|
|
||||||
// cen = cell->lcInfo.cen;
|
|
||||||
// clk = cell->lcInfo.clk;
|
|
||||||
// sr = cell->lcInfo.sr;
|
|
||||||
//
|
|
||||||
// if (cell->lcInfo.negClk) {
|
|
||||||
// dffs_neg = true;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// if (cen != cell->lcInfo.cen)
|
|
||||||
// return false;
|
|
||||||
// if (clk != cell->lcInfo.clk)
|
|
||||||
// return false;
|
|
||||||
// if (sr != cell->lcInfo.sr)
|
|
||||||
// return false;
|
|
||||||
// if (dffs_neg != cell->lcInfo.negClk)
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// locals_count += cell->lcInfo.inputCount;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return locals_count <= 32;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Arch::isBelLocationValid(BelId bel) const
|
bool Arch::isBelLocationValid(BelId bel) const
|
||||||
{
|
{
|
||||||
if (getBelType(bel) == id_ICESTORM_LC) {
|
if (getBelType(bel) == id("XC7_LC")) {
|
||||||
std::array<const CellInfo *, 8> bel_cells;
|
std::array<const CellInfo *, 4> bel_cells;
|
||||||
size_t num_cells = 0;
|
size_t num_cells = 0;
|
||||||
Loc bel_loc = getBelLocation(bel);
|
Loc bel_loc = getBelLocation(bel);
|
||||||
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
@ -85,70 +56,23 @@ bool Arch::isBelLocationValid(BelId bel) const
|
|||||||
|
|
||||||
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
||||||
{
|
{
|
||||||
// if (cell->type == id_ICESTORM_LC) {
|
if (cell->type == id("XC7_LC")) {
|
||||||
// NPNR_ASSERT(getBelType(bel) == id_ICESTORM_LC);
|
std::array<const CellInfo *, 4> bel_cells;
|
||||||
//
|
size_t num_cells = 0;
|
||||||
// std::array<const CellInfo *, 8> bel_cells;
|
|
||||||
// size_t num_cells = 0;
|
Loc bel_loc = getBelLocation(bel);
|
||||||
//
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
// Loc bel_loc = getBelLocation(bel);
|
CellInfo *ci_other = getBoundBelCell(bel_other);
|
||||||
// for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
if (ci_other != nullptr && bel_other != bel)
|
||||||
// CellInfo *ci_other = getBoundBelCell(bel_other);
|
bel_cells[num_cells++] = ci_other;
|
||||||
// if (ci_other != nullptr && bel_other != bel)
|
}
|
||||||
// bel_cells[num_cells++] = ci_other;
|
|
||||||
// }
|
bel_cells[num_cells++] = cell;
|
||||||
//
|
return logicCellsCompatible(bel_cells.data(), num_cells);
|
||||||
// bel_cells[num_cells++] = cell;
|
}
|
||||||
// return logicCellsCompatible(bel_cells.data(), num_cells);
|
else {
|
||||||
// } else if (cell->type == id_SB_IO) {
|
|
||||||
// // Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to.
|
|
||||||
//
|
|
||||||
// // Find shared PLL by looking for driving bel siblings from D_IN_0
|
|
||||||
// // that are a PLL clock output.
|
|
||||||
// auto wire = getBelPinWire(bel, id_D_IN_0);
|
|
||||||
// IdString pll_bel_pin;
|
|
||||||
// BelId pll_bel;
|
|
||||||
// for (auto pin : getWireBelPins(wire)) {
|
|
||||||
// if (pin.pin == id_PLLOUT_A || pin.pin == id_PLLOUT_B) {
|
|
||||||
// pll_bel = pin.bel;
|
|
||||||
// pll_bel_pin = pin.pin;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // Is there a PLL that shares this IO buffer?
|
|
||||||
// if (pll_bel.index != -1) {
|
|
||||||
// auto pll_cell = getBoundBelCell(pll_bel);
|
|
||||||
// // Is a PLL placed in this PLL bel?
|
|
||||||
// if (pll_cell != nullptr) {
|
|
||||||
// // Is the shared port driving a net?
|
|
||||||
// auto pi = pll_cell->ports[pll_bel_pin];
|
|
||||||
// if (pi.net != nullptr) {
|
|
||||||
// // Are we perhaps a PAD INPUT Bel that can be placed here?
|
|
||||||
// if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this)) {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return getBelPackagePin(bel) != "";
|
|
||||||
// } else if (cell->type == id_SB_GB) {
|
|
||||||
// NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr);
|
|
||||||
// const NetInfo *net = cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net;
|
|
||||||
// IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT));
|
|
||||||
// int glb_id = std::stoi(std::string("") + glb_net.str(this).back());
|
|
||||||
// if (net->is_reset && net->is_enable)
|
|
||||||
// return false;
|
|
||||||
// else if (net->is_reset)
|
|
||||||
// return (glb_id % 2) == 0;
|
|
||||||
// else if (net->is_enable)
|
|
||||||
// return (glb_id % 2) == 1;
|
|
||||||
// else
|
|
||||||
// return true;
|
|
||||||
// } else {
|
|
||||||
// // TODO: IO cell clock checks
|
|
||||||
return true;
|
return true;
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
9
xc7/attosoc.pcf
Normal file
9
xc7/attosoc.pcf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
COMP "led[0]" LOCATE = SITE "M14" LEVEL 1;
|
||||||
|
COMP "led[1]" LOCATE = SITE "M15" LEVEL 1;
|
||||||
|
COMP "led[2]" LOCATE = SITE "G14" LEVEL 1;
|
||||||
|
COMP "led[3]" LOCATE = SITE "D18" LEVEL 1;
|
||||||
|
COMP "clk" LOCATE = SITE "K17" LEVEL 1;
|
||||||
|
COMP "pll.mmcm_adv_inst" LOCATE = SITE "MMCME2_ADV_X1Y2" LEVEL 1;
|
||||||
|
NET "pll.clkin1" PERIOD = 8 nS ;
|
||||||
|
#PIN "clk_pin" = BEL "clk.PAD" PINNAME PAD;
|
||||||
|
#PIN "clk_pin" CLOCK_DEDICATED_ROUTE = FALSE;
|
12
xc7/attosoc.sh
Executable file
12
xc7/attosoc.sh
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#set -ex
|
||||||
|
#rm -f picorv32.v
|
||||||
|
#wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
|
||||||
|
yosys attosoc.ys
|
||||||
|
../nextpnr-xc7 --json attosoc.json --xdl attosoc.xdl --pcf attosoc.pcf --freq 150 |& tee attosoc.log
|
||||||
|
xdl -xdl2ncd attosoc.xdl
|
||||||
|
bitgen -w attosoc.ncd -g UnconstrainedPins:Allow
|
||||||
|
trce attosoc.ncd -v 10
|
||||||
|
|
||||||
|
netgen -sim -ofmt vhdl attosoc.ncd -w attosoc_pnr.vhd
|
||||||
|
ghdl -c -fexplicit --no-vital-checks --ieee=synopsys -Pxilinx-ise attosoc_tb.vhd attosoc_pnr.vhd -r testbench
|
127
xc7/attosoc.v
Normal file
127
xc7/attosoc.v
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* ECP5 PicoRV32 demo
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
`ifdef PICORV32_V
|
||||||
|
`error "attosoc.v must be read before picorv32.v!"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`define PICORV32_REGS picosoc_regs
|
||||||
|
|
||||||
|
module attosoc (
|
||||||
|
input clk,
|
||||||
|
output reg [7:0] led
|
||||||
|
);
|
||||||
|
|
||||||
|
reg [5:0] reset_cnt = 0;
|
||||||
|
wire resetn = &reset_cnt;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
reset_cnt <= reset_cnt + !resetn;
|
||||||
|
end
|
||||||
|
|
||||||
|
parameter integer MEM_WORDS = 256;
|
||||||
|
parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory
|
||||||
|
parameter [31:0] PROGADDR_RESET = 32'h 0000_0000; // ROM at 0x0
|
||||||
|
parameter integer ROM_BYTES = 256;
|
||||||
|
|
||||||
|
reg [7:0] rom [0:ROM_BYTES-1];
|
||||||
|
wire [31:0] rom_rdata = {rom[mem_addr+3], rom[mem_addr+2], rom[mem_addr+1], rom[mem_addr+0]};
|
||||||
|
initial $readmemh("firmware.hex", rom);
|
||||||
|
|
||||||
|
wire mem_valid;
|
||||||
|
wire mem_instr;
|
||||||
|
wire mem_ready;
|
||||||
|
wire [31:0] mem_addr;
|
||||||
|
wire [31:0] mem_wdata;
|
||||||
|
wire [3:0] mem_wstrb;
|
||||||
|
wire [31:0] mem_rdata;
|
||||||
|
|
||||||
|
wire rom_ready = mem_valid && mem_addr[31:24] == 8'h00;
|
||||||
|
|
||||||
|
wire iomem_valid;
|
||||||
|
wire iomem_ready;
|
||||||
|
wire [31:0] iomem_addr;
|
||||||
|
wire [31:0] iomem_wdata;
|
||||||
|
wire [3:0] iomem_wstrb;
|
||||||
|
wire [31:0] iomem_rdata;
|
||||||
|
|
||||||
|
assign iomem_valid = mem_valid && (mem_addr[31:24] > 8'h 01);
|
||||||
|
assign iomem_ready = 1'b1;
|
||||||
|
assign iomem_wstrb = mem_wstrb;
|
||||||
|
assign iomem_addr = mem_addr;
|
||||||
|
assign iomem_wdata = mem_wdata;
|
||||||
|
|
||||||
|
wire [31:0] spimemio_cfgreg_do;
|
||||||
|
|
||||||
|
|
||||||
|
always @(posedge clk)
|
||||||
|
if (iomem_valid && iomem_wstrb[0])
|
||||||
|
led <= iomem_wdata[7:0];
|
||||||
|
|
||||||
|
assign mem_ready = (iomem_valid && iomem_ready) || rom_ready;
|
||||||
|
|
||||||
|
assign mem_rdata = rom_rdata;
|
||||||
|
|
||||||
|
picorv32 #(
|
||||||
|
.STACKADDR(STACKADDR),
|
||||||
|
.PROGADDR_RESET(PROGADDR_RESET),
|
||||||
|
.PROGADDR_IRQ(32'h 0000_0000),
|
||||||
|
.BARREL_SHIFTER(0),
|
||||||
|
.COMPRESSED_ISA(0),
|
||||||
|
.ENABLE_MUL(0),
|
||||||
|
.ENABLE_DIV(0),
|
||||||
|
.ENABLE_IRQ(0),
|
||||||
|
.ENABLE_IRQ_QREGS(0)
|
||||||
|
) cpu (
|
||||||
|
.clk (clk ),
|
||||||
|
.resetn (resetn ),
|
||||||
|
.mem_valid (mem_valid ),
|
||||||
|
.mem_instr (mem_instr ),
|
||||||
|
.mem_ready (mem_ready ),
|
||||||
|
.mem_addr (mem_addr ),
|
||||||
|
.mem_wdata (mem_wdata ),
|
||||||
|
.mem_wstrb (mem_wstrb ),
|
||||||
|
.mem_rdata (mem_rdata )
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// Implementation note:
|
||||||
|
// Replace the following two modules with wrappers for your SRAM cells.
|
||||||
|
|
||||||
|
module picosoc_regs (
|
||||||
|
input clk, wen,
|
||||||
|
input [5:0] waddr,
|
||||||
|
input [5:0] raddr1,
|
||||||
|
input [5:0] raddr2,
|
||||||
|
input [31:0] wdata,
|
||||||
|
output [31:0] rdata1,
|
||||||
|
output [31:0] rdata2
|
||||||
|
);
|
||||||
|
reg [31:0] regs [0:31];
|
||||||
|
|
||||||
|
always @(posedge clk)
|
||||||
|
if (wen) regs[waddr[4:0]] <= wdata;
|
||||||
|
|
||||||
|
assign rdata1 = regs[raddr1[4:0]];
|
||||||
|
assign rdata2 = regs[raddr2[4:0]];
|
||||||
|
endmodule
|
56
xc7/attosoc.ys
Normal file
56
xc7/attosoc.ys
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
read_verilog attosoc_top.v
|
||||||
|
read_verilog attosoc.v
|
||||||
|
read_verilog picorv32.v
|
||||||
|
read_verilog 125MHz_to_60MHz.v
|
||||||
|
|
||||||
|
#synth_xilinx -top picorv32
|
||||||
|
|
||||||
|
#begin:
|
||||||
|
read_verilog -lib +/xilinx/cells_sim.v
|
||||||
|
read_verilog -lib +/xilinx/cells_xtra.v
|
||||||
|
# read_verilog -lib +/xilinx/brams_bb.v
|
||||||
|
# read_verilog -lib +/xilinx/drams_bb.v
|
||||||
|
hierarchy -check -top top
|
||||||
|
|
||||||
|
#flatten: (only if -flatten)
|
||||||
|
proc
|
||||||
|
flatten
|
||||||
|
|
||||||
|
#coarse:
|
||||||
|
synth -run coarse
|
||||||
|
|
||||||
|
#bram:
|
||||||
|
# memory_bram -rules +/xilinx/brams.txt
|
||||||
|
# techmap -map +/xilinx/brams_map.v
|
||||||
|
#
|
||||||
|
#dram:
|
||||||
|
# memory_bram -rules +/xilinx/drams.txt
|
||||||
|
# techmap -map +/xilinx/drams_map.v
|
||||||
|
|
||||||
|
fine:
|
||||||
|
opt -fast -full
|
||||||
|
memory_map
|
||||||
|
dffsr2dff
|
||||||
|
# dff2dffe
|
||||||
|
opt -full
|
||||||
|
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
|
||||||
|
opt -fast
|
||||||
|
|
||||||
|
map_luts:
|
||||||
|
abc -luts 2:2,3,6:5 #,10,20 [-dff]
|
||||||
|
clean
|
||||||
|
|
||||||
|
map_cells:
|
||||||
|
techmap -map +/xilinx/cells_map.v
|
||||||
|
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
|
||||||
|
clean
|
||||||
|
|
||||||
|
check:
|
||||||
|
hierarchy -check
|
||||||
|
stat
|
||||||
|
check -noinit
|
||||||
|
|
||||||
|
#edif: (only if -edif)
|
||||||
|
# write_edif <file-name>
|
||||||
|
|
||||||
|
write_json attosoc.json
|
25
xc7/attosoc_tb.vhd
Normal file
25
xc7/attosoc_tb.vhd
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
library IEEE;
|
||||||
|
use IEEE.STD_LOGIC_1164.ALL;
|
||||||
|
|
||||||
|
entity testbench is
|
||||||
|
end entity;
|
||||||
|
architecture rtl of testbench is
|
||||||
|
signal clk : STD_LOGIC;
|
||||||
|
signal led : STD_LOGIC_VECTOR(3 downto 0);
|
||||||
|
begin
|
||||||
|
process begin
|
||||||
|
clk <= '0';
|
||||||
|
wait for 4 ns;
|
||||||
|
clk <= '1';
|
||||||
|
wait for 4 ns;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
uut: entity work.name port map(clk_PAD_PAD => clk, led_0_OUTBUF_OUT => led(0), led_1_OUTBUF_OUT => led(1), led_2_OUTBUF_OUT => led(2), led_3_OUTBUF_OUT => led(3));
|
||||||
|
|
||||||
|
process
|
||||||
|
begin
|
||||||
|
report "led = " & std_logic'image(led(3)) & std_logic'image(led(2)) & std_logic'image(led(1)) & std_logic'image(led(0));
|
||||||
|
wait on led;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
end rtl;
|
15
xc7/attosoc_top.v
Normal file
15
xc7/attosoc_top.v
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module top (
|
||||||
|
input clk,
|
||||||
|
output [3:0] led
|
||||||
|
);
|
||||||
|
|
||||||
|
(* keep *)
|
||||||
|
wire led_unused;
|
||||||
|
|
||||||
|
wire gclk;
|
||||||
|
clk_wiz_v3_6 pll(.CLK_IN1(clk), .CLK_OUT1(gclk));
|
||||||
|
//assign gclk = clk;
|
||||||
|
attosoc soc(.clk(gclk), .led({led_unused, led}));
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -3,6 +3,7 @@ COMP "led1" LOCATE = SITE "M15" LEVEL 1;
|
|||||||
COMP "led2" LOCATE = SITE "G14" LEVEL 1;
|
COMP "led2" LOCATE = SITE "G14" LEVEL 1;
|
||||||
COMP "led3" LOCATE = SITE "D18" LEVEL 1;
|
COMP "led3" LOCATE = SITE "D18" LEVEL 1;
|
||||||
COMP "clki" LOCATE = SITE "K17" LEVEL 1;
|
COMP "clki" LOCATE = SITE "K17" LEVEL 1;
|
||||||
|
COMP "clk_gb" LOCATE = SITE "BUFGCTRL_X0Y31" LEVEL 1;
|
||||||
NET "clki" PERIOD = 8 nS ;
|
NET "clki" PERIOD = 8 nS ;
|
||||||
PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
|
PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
|
||||||
PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;
|
PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;
|
||||||
|
295
xc7/cells.cc
295
xc7/cells.cc
@ -48,8 +48,6 @@ std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::stri
|
|||||||
new_cell->params[ctx->id("NEG_CLK")] = "0";
|
new_cell->params[ctx->id("NEG_CLK")] = "0";
|
||||||
new_cell->params[ctx->id("CARRY_ENABLE")] = "0";
|
new_cell->params[ctx->id("CARRY_ENABLE")] = "0";
|
||||||
new_cell->params[ctx->id("DFF_ENABLE")] = "0";
|
new_cell->params[ctx->id("DFF_ENABLE")] = "0";
|
||||||
new_cell->params[ctx->id("SET_NORESET")] = "0";
|
|
||||||
new_cell->params[ctx->id("ASYNC_SR")] = "0";
|
|
||||||
new_cell->params[ctx->id("CIN_CONST")] = "0";
|
new_cell->params[ctx->id("CIN_CONST")] = "0";
|
||||||
new_cell->params[ctx->id("CIN_SET")] = "0";
|
new_cell->params[ctx->id("CIN_SET")] = "0";
|
||||||
|
|
||||||
@ -70,186 +68,15 @@ std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::stri
|
|||||||
add_port(ctx, new_cell.get(), "OMUX", PORT_OUT);
|
add_port(ctx, new_cell.get(), "OMUX", PORT_OUT);
|
||||||
add_port(ctx, new_cell.get(), "COUT", PORT_OUT);
|
add_port(ctx, new_cell.get(), "COUT", PORT_OUT);
|
||||||
} else if (type == ctx->id("IOBUF")) {
|
} else if (type == ctx->id("IOBUF")) {
|
||||||
|
if (ctx->args.type == ArchArgs::Z020)
|
||||||
new_cell->type = id_IOB33;
|
new_cell->type = id_IOB33;
|
||||||
new_cell->params[ctx->id("PIN_TYPE")] = "0";
|
else
|
||||||
new_cell->params[ctx->id("PULLUP")] = "0";
|
new_cell->type = id_IOB18;
|
||||||
new_cell->params[ctx->id("NEG_TRIGGER")] = "0";
|
|
||||||
new_cell->params[ctx->id("IOSTANDARD")] = "SB_LVCMOS";
|
|
||||||
|
|
||||||
// add_port(ctx, new_cell.get(), "PACKAGE_PIN", PORT_INOUT);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "LATCH_INPUT_VALUE", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CLOCK_ENABLE", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "INPUT_CLK", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "OUTPUT_CLK", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "OUTPUT_ENABLE", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "D_OUT_0", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "D_OUT_1", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "D_IN_0", PORT_OUT);
|
|
||||||
// add_port(ctx, new_cell.get(), "D_IN_1", PORT_OUT);
|
|
||||||
add_port(ctx, new_cell.get(), "I", PORT_OUT);
|
add_port(ctx, new_cell.get(), "I", PORT_OUT);
|
||||||
add_port(ctx, new_cell.get(), "O", PORT_IN);
|
add_port(ctx, new_cell.get(), "O", PORT_IN);
|
||||||
// } else if (type == ctx->id("ICESTORM_RAM")) {
|
|
||||||
// new_cell->params[ctx->id("NEG_CLK_W")] = "0";
|
|
||||||
// new_cell->params[ctx->id("NEG_CLK_R")] = "0";
|
|
||||||
// new_cell->params[ctx->id("WRITE_MODE")] = "0";
|
|
||||||
// new_cell->params[ctx->id("READ_MODE")] = "0";
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "RCLK", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "RCLKE", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "RE", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "WCLK", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "WCLKE", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "WE", PORT_IN);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < 16; i++) {
|
|
||||||
// add_port(ctx, new_cell.get(), "WDATA_" + std::to_string(i), PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "MASK_" + std::to_string(i), PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "RDATA_" + std::to_string(i), PORT_OUT);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < 11; i++) {
|
|
||||||
// add_port(ctx, new_cell.get(), "RADDR_" + std::to_string(i), PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "WADDR_" + std::to_string(i), PORT_IN);
|
|
||||||
// }
|
|
||||||
// } else if (type == ctx->id("ICESTORM_LFOSC")) {
|
|
||||||
// add_port(ctx, new_cell.get(), "CLKLFEN", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CLKLFPU", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CLKLF", PORT_OUT);
|
|
||||||
// add_port(ctx, new_cell.get(), "CLKLF_FABRIC", PORT_OUT);
|
|
||||||
// } else if (type == ctx->id("ICESTORM_HFOSC")) {
|
|
||||||
// new_cell->params[ctx->id("CLKHF_DIV")] = "0b00";
|
|
||||||
// new_cell->params[ctx->id("TRIM_EN")] = "0b0";
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "CLKHFEN", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CLKHFPU", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CLKHF", PORT_OUT);
|
|
||||||
// add_port(ctx, new_cell.get(), "CLKHF_FABRIC", PORT_OUT);
|
|
||||||
// for (int i = 0; i < 10; i++)
|
|
||||||
// add_port(ctx, new_cell.get(), "TRIM" + std::to_string(i), PORT_IN);
|
|
||||||
} else if (type == id_BUFGCTRL) {
|
} else if (type == id_BUFGCTRL) {
|
||||||
add_port(ctx, new_cell.get(), "I0", PORT_IN);
|
add_port(ctx, new_cell.get(), "I0", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "O", PORT_OUT);
|
add_port(ctx, new_cell.get(), "O", PORT_OUT);
|
||||||
// } else if (type == ctx->id("ICESTORM_SPRAM")) {
|
|
||||||
// add_port(ctx, new_cell.get(), "WREN", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CHIPSELECT", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CLOCK", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "STANDBY", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "SLEEP", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "POWEROFF", PORT_IN);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < 16; i++) {
|
|
||||||
// add_port(ctx, new_cell.get(), "DATAIN_" + std::to_string(i), PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "DATAOUT_" + std::to_string(i), PORT_OUT);
|
|
||||||
// }
|
|
||||||
// for (int i = 0; i < 14; i++) {
|
|
||||||
// add_port(ctx, new_cell.get(), "ADDRESS_" + std::to_string(i), PORT_IN);
|
|
||||||
// }
|
|
||||||
// for (int i = 0; i < 4; i++) {
|
|
||||||
// add_port(ctx, new_cell.get(), "MASKWREN_" + std::to_string(i), PORT_IN);
|
|
||||||
// }
|
|
||||||
// } else if (type == ctx->id("ICESTORM_DSP")) {
|
|
||||||
// new_cell->params[ctx->id("NEG_TRIGGER")] = "0";
|
|
||||||
//
|
|
||||||
// new_cell->params[ctx->id("C_REG")] = "0";
|
|
||||||
// new_cell->params[ctx->id("A_REG")] = "0";
|
|
||||||
// new_cell->params[ctx->id("B_REG")] = "0";
|
|
||||||
// new_cell->params[ctx->id("D_REG")] = "0";
|
|
||||||
// new_cell->params[ctx->id("TOP_8x8_MULT_REG")] = "0";
|
|
||||||
// new_cell->params[ctx->id("BOT_8x8_MULT_REG")] = "0";
|
|
||||||
// new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG1")] = "0";
|
|
||||||
// new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG2")] = "0";
|
|
||||||
//
|
|
||||||
// new_cell->params[ctx->id("TOPOUTPUT_SELECT")] = "0";
|
|
||||||
// new_cell->params[ctx->id("TOPADDSUB_LOWERINPUT")] = "0";
|
|
||||||
// new_cell->params[ctx->id("TOPADDSUB_UPPERINPUT")] = "0";
|
|
||||||
// new_cell->params[ctx->id("TOPADDSUB_CARRYSELECT")] = "0";
|
|
||||||
//
|
|
||||||
// new_cell->params[ctx->id("BOTOUTPUT_SELECT")] = "0";
|
|
||||||
// new_cell->params[ctx->id("BOTADDSUB_LOWERINPUT")] = "0";
|
|
||||||
// new_cell->params[ctx->id("BOTADDSUB_UPPERINPUT")] = "0";
|
|
||||||
// new_cell->params[ctx->id("BOTADDSUB_CARRYSELECT")] = "0";
|
|
||||||
//
|
|
||||||
// new_cell->params[ctx->id("MODE_8x8")] = "0";
|
|
||||||
// new_cell->params[ctx->id("A_SIGNED")] = "0";
|
|
||||||
// new_cell->params[ctx->id("B_SIGNED")] = "0";
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "CLK", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CE", PORT_IN);
|
|
||||||
// for (int i = 0; i < 16; i++) {
|
|
||||||
// add_port(ctx, new_cell.get(), "C_" + std::to_string(i), PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "A_" + std::to_string(i), PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "B_" + std::to_string(i), PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "D_" + std::to_string(i), PORT_IN);
|
|
||||||
// }
|
|
||||||
// add_port(ctx, new_cell.get(), "AHOLD", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "BHOLD", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "CHOLD", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "DHOLD", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "IRSTTOP", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "IRSTBOT", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "ORSTTOP", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "ORSTBOT", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "OLOADTOP", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "OLOADBOT", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "ADDSUBTOP", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "ADDSUBBOT", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "OHOLDTOP", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "OHOLDBOT", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "CI", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "ACCUMCI", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "SIGNEXTIN", PORT_IN);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < 32; i++) {
|
|
||||||
// add_port(ctx, new_cell.get(), "O_" + std::to_string(i), PORT_OUT);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "CO", PORT_OUT);
|
|
||||||
// add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT);
|
|
||||||
// add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT);
|
|
||||||
//
|
|
||||||
// } else if (type == ctx->id("ICESTORM_PLL")) {
|
|
||||||
// new_cell->params[ctx->id("DELAY_ADJMODE_FB")] = "0";
|
|
||||||
// new_cell->params[ctx->id("DELAY_ADJMODE_REL")] = "0";
|
|
||||||
//
|
|
||||||
// new_cell->params[ctx->id("DIVF")] = "0";
|
|
||||||
// new_cell->params[ctx->id("DIVQ")] = "0";
|
|
||||||
// new_cell->params[ctx->id("DIVR")] = "0";
|
|
||||||
//
|
|
||||||
// new_cell->params[ctx->id("FDA_FEEDBACK")] = "0";
|
|
||||||
// new_cell->params[ctx->id("FDA_RELATIVE")] = "0";
|
|
||||||
// new_cell->params[ctx->id("FEEDBACK_PATH")] = "0";
|
|
||||||
// new_cell->params[ctx->id("FILTER_RANGE")] = "0";
|
|
||||||
//
|
|
||||||
// new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0";
|
|
||||||
// new_cell->params[ctx->id("PLLOUT_SELECT_B")] = "0";
|
|
||||||
//
|
|
||||||
// new_cell->params[ctx->id("PLLTYPE")] = "0";
|
|
||||||
// new_cell->params[ctx->id("SHIFTREG_DIVMODE")] = "0";
|
|
||||||
// new_cell->params[ctx->id("TEST_MODE")] = "0";
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "BYPASS", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "DYNAMICDELAY", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "EXTFEEDBACK", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "LATCHINPUTVALUE", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "REFERENCECLK", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "RESETB", PORT_IN);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "SCLK", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "SDI", PORT_IN);
|
|
||||||
// add_port(ctx, new_cell.get(), "SDI", PORT_OUT);
|
|
||||||
//
|
|
||||||
// add_port(ctx, new_cell.get(), "LOCK", PORT_OUT);
|
|
||||||
// add_port(ctx, new_cell.get(), "PLLOUT_A", PORT_OUT);
|
|
||||||
// add_port(ctx, new_cell.get(), "PLLOUT_B", PORT_OUT);
|
|
||||||
} else {
|
} else {
|
||||||
log_error("unable to create XC7 cell of type %s\n", type.c_str(ctx));
|
log_error("unable to create XC7 cell of type %s\n", type.c_str(ctx));
|
||||||
}
|
}
|
||||||
@ -259,17 +86,18 @@ std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::stri
|
|||||||
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
||||||
{
|
{
|
||||||
lc->params[ctx->id("INIT")] = lut->params[ctx->id("INIT")];
|
lc->params[ctx->id("INIT")] = lut->params[ctx->id("INIT")];
|
||||||
replace_port(lut, ctx->id("I0"), lc, id_I6);
|
int i = 6;
|
||||||
if (get_net_or_empty(lut, id_I1))
|
|
||||||
replace_port(lut, id_I1, lc, id_I5);
|
|
||||||
if (get_net_or_empty(lut, id_I2))
|
|
||||||
replace_port(lut, id_I2, lc, id_I4);
|
|
||||||
if (get_net_or_empty(lut, id_I3))
|
|
||||||
replace_port(lut, id_I3, lc, id_I3);
|
|
||||||
if (get_net_or_empty(lut, id_I4))
|
|
||||||
replace_port(lut, id_I4, lc, id_I2);
|
|
||||||
if (get_net_or_empty(lut, id_I5))
|
if (get_net_or_empty(lut, id_I5))
|
||||||
replace_port(lut, id_I5, lc, id_I1);
|
replace_port(lut, id_I5, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (get_net_or_empty(lut, id_I4))
|
||||||
|
replace_port(lut, id_I4, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (get_net_or_empty(lut, id_I3))
|
||||||
|
replace_port(lut, id_I3, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (get_net_or_empty(lut, id_I2))
|
||||||
|
replace_port(lut, id_I2, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (get_net_or_empty(lut, id_I1))
|
||||||
|
replace_port(lut, id_I1, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
replace_port(lut, ctx->id("I0"), lc, ctx->id("I" + std::to_string(i--)));
|
||||||
if (no_dff) {
|
if (no_dff) {
|
||||||
replace_port(lut, id_O, lc, id_O);
|
replace_port(lut, id_O, lc, id_O);
|
||||||
lc->params[ctx->id("DFF_ENABLE")] = "0";
|
lc->params[ctx->id("DFF_ENABLE")] = "0";
|
||||||
@ -282,43 +110,65 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l
|
|||||||
lc->params[ctx->id("DFF_ENABLE")] = "1";
|
lc->params[ctx->id("DFF_ENABLE")] = "1";
|
||||||
std::string config = dff->type.str(ctx).substr(2);
|
std::string config = dff->type.str(ctx).substr(2);
|
||||||
auto citer = config.begin();
|
auto citer = config.begin();
|
||||||
replace_port(dff, ctx->id("C"), lc, ctx->id("CLK"));
|
replace_port(dff, ctx->id("C"), lc, id_CLK);
|
||||||
|
|
||||||
if (citer != config.end()) {
|
if (citer != config.end()) {
|
||||||
if (*citer == 'C' || *citer == 'P')
|
auto gnd_net = ctx->nets.at(ctx->id("$PACKER_GND_NET")).get();
|
||||||
lc->params[ctx->id("ASYNC_SR")] = "1";
|
|
||||||
else
|
|
||||||
lc->params[ctx->id("ASYNC_SR")] = "0";
|
|
||||||
|
|
||||||
if (*citer == 'S') {
|
if (*citer == 'S') {
|
||||||
citer++;
|
citer++;
|
||||||
replace_port(dff, ctx->id("S"), lc, ctx->id("SR"));
|
if (get_net_or_empty(dff, id_S) != gnd_net) {
|
||||||
lc->params[ctx->id("SET_NORESET")] = "1";
|
lc->params[id_SR] = "SRHIGH";
|
||||||
|
lc->params[ctx->id("SYNC_ATTR")] = "SYNC";
|
||||||
|
replace_port(dff, id_S, lc, id_SR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, id_S);
|
||||||
} else if (*citer == 'R') {
|
} else if (*citer == 'R') {
|
||||||
citer++;
|
citer++;
|
||||||
replace_port(dff, ctx->id("R"), lc, ctx->id("SR"));
|
if (get_net_or_empty(dff, id_R) != gnd_net) {
|
||||||
lc->params[ctx->id("SET_NORESET")] = "0";
|
lc->params[id_SR] = "SRLOW";
|
||||||
|
lc->params[ctx->id("SYNC_ATTR")] = "SYNC";
|
||||||
|
replace_port(dff, id_R, lc, id_SR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, id_R);
|
||||||
} else if (*citer == 'C') {
|
} else if (*citer == 'C') {
|
||||||
citer++;
|
citer++;
|
||||||
replace_port(dff, ctx->id("CLR"), lc, ctx->id("SR"));
|
if (get_net_or_empty(dff, id_CLR) != gnd_net) {
|
||||||
lc->params[ctx->id("SET_NORESET")] = "0";
|
lc->params[id_SR] = "SRLOW";
|
||||||
|
lc->params[ctx->id("SYNC_ATTR")] = "ASYNC";
|
||||||
|
replace_port(dff, id_CLR, lc, id_SR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, id_CLR);
|
||||||
} else {
|
} else {
|
||||||
NPNR_ASSERT(*citer == 'P');
|
NPNR_ASSERT(*citer == 'P');
|
||||||
citer++;
|
citer++;
|
||||||
replace_port(dff, ctx->id("PRE"), lc, ctx->id("SR"));
|
if (get_net_or_empty(dff, id_PRE) != gnd_net) {
|
||||||
lc->params[ctx->id("SET_NORESET")] = "1";
|
lc->params[id_SR] = "SRHIGH";
|
||||||
|
lc->params[ctx->id("SYNC_ATTR")] = "ASYNC";
|
||||||
|
replace_port(dff, id_PRE, lc, id_SR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, id_PRE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (citer != config.end() && *citer == 'E') {
|
if (citer != config.end() && *citer == 'E') {
|
||||||
replace_port(dff, ctx->id("CE"), lc, ctx->id("CE"));
|
auto vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC_NET")).get();
|
||||||
|
|
||||||
++citer;
|
++citer;
|
||||||
|
if (get_net_or_empty(dff, ctx->id("CE")) != vcc_net)
|
||||||
|
replace_port(dff, ctx->id("CE"), lc, ctx->id("CE"));
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, ctx->id("CE"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NPNR_ASSERT(citer == config.end());
|
NPNR_ASSERT(citer == config.end());
|
||||||
|
|
||||||
if (pass_thru_lut) {
|
if (pass_thru_lut) {
|
||||||
lc->params[ctx->id("INIT")] = "1";
|
lc->params[ctx->id("INIT")] = "2";
|
||||||
replace_port(dff, ctx->id("D"), lc, id_I1);
|
replace_port(dff, ctx->id("D"), lc, id_I1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +176,7 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l
|
|||||||
|
|
||||||
auto it = dff->params.find(ctx->id("INIT"));
|
auto it = dff->params.find(ctx->id("INIT"));
|
||||||
if (it != dff->params.end())
|
if (it != dff->params.end())
|
||||||
lc->params[ctx->id("DFF_INIT")] = it->second;
|
lc->params[ctx->id("FFINIT")] = it->second == "1" ? "INIT1" : "INIT0";
|
||||||
}
|
}
|
||||||
|
|
||||||
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
|
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
|
||||||
@ -365,34 +215,11 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell)
|
|
||||||
{
|
|
||||||
if (cell->type == ctx->id("SB_PLL40_PAD"))
|
|
||||||
return 2;
|
|
||||||
if (cell->type == ctx->id("SB_PLL40_2_PAD"))
|
|
||||||
return 4;
|
|
||||||
if (cell->type == ctx->id("SB_PLL40_2F_PAD"))
|
|
||||||
return 5;
|
|
||||||
if (cell->type == ctx->id("SB_PLL40_CORE"))
|
|
||||||
return 3;
|
|
||||||
if (cell->type == ctx->id("SB_PLL40_2F_CORE"))
|
|
||||||
return 7;
|
|
||||||
NPNR_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
|
bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
|
||||||
{
|
{
|
||||||
if (port.cell == nullptr)
|
if (port.cell == nullptr)
|
||||||
return false;
|
return false;
|
||||||
if (is_ff(ctx, port.cell))
|
NPNR_ASSERT("TODO");
|
||||||
return port.port == ctx->id("C");
|
|
||||||
if (port.cell->type == ctx->id("ICESTORM_LC"))
|
|
||||||
return port.port == ctx->id("CLK");
|
|
||||||
if (is_ram(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_RAM"))
|
|
||||||
return port.port == ctx->id("RCLK") || port.port == ctx->id("WCLK") || port.port == ctx->id("RCLKN") ||
|
|
||||||
port.port == ctx->id("WCLKN");
|
|
||||||
if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
|
|
||||||
return port.port == ctx->id("CLK");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,13 +227,7 @@ bool is_reset_port(const BaseCtx *ctx, const PortRef &port)
|
|||||||
{
|
{
|
||||||
if (port.cell == nullptr)
|
if (port.cell == nullptr)
|
||||||
return false;
|
return false;
|
||||||
if (is_ff(ctx, port.cell))
|
NPNR_ASSERT("TODO");
|
||||||
return port.port == ctx->id("R") || port.port == ctx->id("S");
|
|
||||||
if (port.cell->type == ctx->id("ICESTORM_LC"))
|
|
||||||
return port.port == ctx->id("SR");
|
|
||||||
if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
|
|
||||||
return port.port == ctx->id("IRSTTOP") || port.port == ctx->id("IRSTBOT") || port.port == ctx->id("ORSTTOP") ||
|
|
||||||
port.port == ctx->id("ORSTBOT");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,13 +235,7 @@ bool is_enable_port(const BaseCtx *ctx, const PortRef &port)
|
|||||||
{
|
{
|
||||||
if (port.cell == nullptr)
|
if (port.cell == nullptr)
|
||||||
return false;
|
return false;
|
||||||
if (is_ff(ctx, port.cell))
|
NPNR_ASSERT("TODO");
|
||||||
return port.port == ctx->id("E");
|
|
||||||
if (port.cell->type == ctx->id("ICESTORM_LC"))
|
|
||||||
return port.port == ctx->id("CEN");
|
|
||||||
// FIXME
|
|
||||||
// if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
|
|
||||||
// return port.port == ctx->id("CE");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,10 @@ X(COUT)
|
|||||||
X(CEN)
|
X(CEN)
|
||||||
X(CLK)
|
X(CLK)
|
||||||
X(SR)
|
X(SR)
|
||||||
|
X(S)
|
||||||
|
X(R)
|
||||||
|
X(PRE)
|
||||||
|
X(CLR)
|
||||||
|
|
||||||
X(MASK_0)
|
X(MASK_0)
|
||||||
X(MASK_1)
|
X(MASK_1)
|
||||||
@ -458,4 +462,6 @@ X(FDPE)
|
|||||||
X(BUFGCTRL)
|
X(BUFGCTRL)
|
||||||
X(SLICE_LUT6)
|
X(SLICE_LUT6)
|
||||||
X(IOB33)
|
X(IOB33)
|
||||||
|
X(IOB18)
|
||||||
X(PS7)
|
X(PS7)
|
||||||
|
X(MMCME2_ADV)
|
||||||
|
100
xc7/delay.cc
100
xc7/delay.cc
@ -25,89 +25,16 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
#define NUM_FUZZ_ROUTES 100000
|
#define NUM_FUZZ_ROUTES 100000
|
||||||
|
|
||||||
void ice40DelayFuzzerMain(Context *ctx)
|
|
||||||
{
|
|
||||||
// std::vector<WireId> srcWires, dstWires;
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < ctx->chip_info->num_wires; i++) {
|
|
||||||
// WireId wire;
|
|
||||||
// wire.index = i;
|
|
||||||
//
|
|
||||||
// switch (ctx->chip_info->wire_data[i].type) {
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LUTFF_OUT:
|
|
||||||
// srcWires.push_back(wire);
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// case WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT:
|
|
||||||
// dstWires.push_back(wire);
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// default:
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ctx->shuffle(srcWires);
|
|
||||||
// ctx->shuffle(dstWires);
|
|
||||||
//
|
|
||||||
// int index = 0;
|
|
||||||
// int cnt = 0;
|
|
||||||
//
|
|
||||||
// while (cnt < NUM_FUZZ_ROUTES) {
|
|
||||||
// if (index >= int(srcWires.size()) || index >= int(dstWires.size())) {
|
|
||||||
// index = 0;
|
|
||||||
// ctx->shuffle(srcWires);
|
|
||||||
// ctx->shuffle(dstWires);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// WireId src = srcWires[index];
|
|
||||||
// WireId dst = dstWires[index++];
|
|
||||||
// std::unordered_map<WireId, PipId> route;
|
|
||||||
//
|
|
||||||
//#if NUM_FUZZ_ROUTES <= 1000
|
|
||||||
// if (!ctx->getActualRouteDelay(src, dst, nullptr, &route, false))
|
|
||||||
// continue;
|
|
||||||
//#else
|
|
||||||
// if (!ctx->getActualRouteDelay(src, dst, nullptr, &route, true))
|
|
||||||
// continue;
|
|
||||||
//#endif
|
|
||||||
//
|
|
||||||
// WireId cursor = dst;
|
|
||||||
// delay_t delay = 0;
|
|
||||||
//
|
|
||||||
// while (1) {
|
|
||||||
// delay += ctx->getWireDelay(cursor).maxDelay();
|
|
||||||
//
|
|
||||||
// printf("%s %d %d %s %s %d %d\n", cursor == dst ? "dst" : "src",
|
|
||||||
// int(ctx->chip_info->wire_data[cursor.index].x), int(ctx->chip_info->wire_data[cursor.index].y),
|
|
||||||
// ctx->getWireType(cursor).c_str(ctx), ctx->getWireName(cursor).c_str(ctx), int(delay),
|
|
||||||
// int(ctx->estimateDelay(cursor, dst)));
|
|
||||||
//
|
|
||||||
// if (cursor == src)
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// PipId pip = route.at(cursor);
|
|
||||||
// delay += ctx->getPipDelay(pip).maxDelay();
|
|
||||||
// cursor = ctx->getPipSrcWire(pip);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// cnt++;
|
|
||||||
//
|
|
||||||
// if (cnt % 100 == 0)
|
|
||||||
// fprintf(stderr, "Fuzzed %d arcs.\n", cnt);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||||
{
|
{
|
||||||
const auto &src_tw = torc_info->wire_to_tilewire[src.index];
|
const auto &src_tw = torc_info->wire_to_tilewire[src.index];
|
||||||
const auto &src_info = torc_info->tiles.getTileInfo(src_tw.getTileIndex());
|
const auto &src_loc = torc_info->tile_to_xy[src_tw.getTileIndex()];
|
||||||
const auto &dst_tw = torc_info->wire_to_tilewire[dst.index];
|
const auto &dst_tw = torc_info->wire_to_tilewire[dst.index];
|
||||||
const auto &dst_info = torc_info->tiles.getTileInfo(dst_tw.getTileIndex());
|
const auto &dst_loc = torc_info->tile_to_xy[dst_tw.getTileIndex()];
|
||||||
auto abs_delta_x = (abs(src_info.getCol() - dst_info.getCol()) + 1) /
|
|
||||||
2; // Divide by 2 because XDL coordinate space counts the INT tiles between CLBs
|
if (!torc_info->wire_is_global[src.index]) {
|
||||||
auto abs_delta_y = abs(src_info.getRow() - dst_info.getRow());
|
auto abs_delta_x = abs(dst_loc.first - src_loc.first);
|
||||||
#if 1
|
auto abs_delta_y = abs(dst_loc.second - src_loc.second);
|
||||||
auto div_LH = std::div(abs_delta_x, 12);
|
auto div_LH = std::div(abs_delta_x, 12);
|
||||||
auto div_LV = std::div(abs_delta_y, 18);
|
auto div_LV = std::div(abs_delta_y, 18);
|
||||||
auto div_LVB = std::div(div_LV.rem, 12);
|
auto div_LVB = std::div(div_LV.rem, 12);
|
||||||
@ -122,9 +49,14 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
|||||||
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
|
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
|
||||||
(div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
|
(div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
|
||||||
(num_H1 + num_V1) * 150;
|
(num_H1 + num_V1) * 150;
|
||||||
#else
|
}
|
||||||
return std::max(150, 33 * abs_delta_x + 66 * abs_delta_y);
|
else {
|
||||||
#endif
|
auto src_y = src_loc.second;
|
||||||
|
auto dst_y = dst_loc.second;
|
||||||
|
auto div_src_y = std::div(src_y, 52);
|
||||||
|
auto div_dst_y = std::div(dst_y, 52);
|
||||||
|
return abs(div_dst_y.quot - div_src_y.quot) * 52 + abs(div_dst_y.rem - div_src_y.rem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
|
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
|
||||||
@ -134,7 +66,6 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
|
|||||||
auto sink_loc = getBelLocation(sink.cell->bel);
|
auto sink_loc = getBelLocation(sink.cell->bel);
|
||||||
auto abs_delta_x = abs(driver_loc.x - sink_loc.x);
|
auto abs_delta_x = abs(driver_loc.x - sink_loc.x);
|
||||||
auto abs_delta_y = abs(driver_loc.y - sink_loc.y);
|
auto abs_delta_y = abs(driver_loc.y - sink_loc.y);
|
||||||
#if 1
|
|
||||||
auto div_LH = std::div(abs_delta_x, 12);
|
auto div_LH = std::div(abs_delta_x, 12);
|
||||||
auto div_LV = std::div(abs_delta_y, 18);
|
auto div_LV = std::div(abs_delta_y, 18);
|
||||||
auto div_LVB = std::div(div_LV.rem, 12);
|
auto div_LVB = std::div(div_LV.rem, 12);
|
||||||
@ -149,9 +80,6 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
|
|||||||
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
|
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
|
||||||
(div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
|
(div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
|
||||||
(num_H1 + num_V1) * 150;
|
(num_H1 + num_V1) * 150;
|
||||||
#else
|
|
||||||
return std::max(150, 33 * abs_delta_x + 66 * abs_delta_y);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
8
xc7/firmware.hex
Normal file
8
xc7/firmware.hex
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
@00000000
|
||||||
|
13 04 20 00 B7 04 00 02 93 09 00 10 13 04 14 00
|
||||||
|
63 44 34 01 13 04 20 00 13 09 20 00 63 5E 89 00
|
||||||
|
13 05 04 00 93 05 09 00 EF 00 C0 01 63 0A 05 00
|
||||||
|
13 09 19 00 6F F0 9F FE 23 A0 84 00 EF 00 80 01
|
||||||
|
6F F0 DF FC 93 02 10 00 33 05 B5 40 E3 5E 55 FE
|
||||||
|
67 80 00 00 B7 82 05 00 93 82 02 E4 93 82 F2 FF
|
||||||
|
E3 9E 02 FE 67 80 00 00
|
11
xc7/main.cc
11
xc7/main.cc
@ -51,8 +51,9 @@ Xc7CommandHandler::Xc7CommandHandler(int argc, char **argv) : CommandHandler(arg
|
|||||||
po::options_description Xc7CommandHandler::getArchOptions()
|
po::options_description Xc7CommandHandler::getArchOptions()
|
||||||
{
|
{
|
||||||
po::options_description specific("Architecture specific options");
|
po::options_description specific("Architecture specific options");
|
||||||
specific.add_options()("xc7z020", "set device type to xc7z020");
|
specific.add_options()("z020", "set device type to xc7z020");
|
||||||
// specific.add_options()("package", po::value<std::string>(), "set device package");
|
specific.add_options()("vx980", "set device type to xc7v980");
|
||||||
|
specific.add_options()("package", po::value<std::string>(), "set device package");
|
||||||
specific.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
|
specific.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
|
||||||
specific.add_options()("xdl", po::value<std::string>(), "XDL file to write");
|
specific.add_options()("xdl", po::value<std::string>(), "XDL file to write");
|
||||||
// specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
|
// specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
|
||||||
@ -97,6 +98,12 @@ std::unique_ptr<Context> Xc7CommandHandler::createContext()
|
|||||||
chipArgs.package = "clg400";
|
chipArgs.package = "clg400";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vm.count("vx980")) {
|
||||||
|
chipArgs.type = ArchArgs::VX980;
|
||||||
|
chipArgs.package = "ffg1926";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (chipArgs.type == ArchArgs::NONE) {
|
if (chipArgs.type == ArchArgs::NONE) {
|
||||||
chipArgs.type = ArchArgs::Z020;
|
chipArgs.type = ArchArgs::Z020;
|
||||||
chipArgs.package = "clg400";
|
chipArgs.package = "clg400";
|
||||||
|
640
xc7/pack.cc
640
xc7/pack.cc
@ -128,172 +128,15 @@ static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value)
|
|||||||
// Pack carry logic
|
// Pack carry logic
|
||||||
static void pack_carries(Context *ctx)
|
static void pack_carries(Context *ctx)
|
||||||
{
|
{
|
||||||
log_info("Packing carries..\n");
|
//log_info("Packing carries..\n");
|
||||||
std::unordered_set<IdString> exhausted_cells;
|
// TODO
|
||||||
std::unordered_set<IdString> packed_cells;
|
|
||||||
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
|
||||||
|
|
||||||
for (auto cell : sorted(ctx->cells)) {
|
|
||||||
CellInfo *ci = cell.second;
|
|
||||||
if (is_carry(ctx, ci)) {
|
|
||||||
packed_cells.insert(cell.first);
|
|
||||||
|
|
||||||
CellInfo *carry_ci_lc;
|
|
||||||
bool ci_value;
|
|
||||||
bool ci_const = net_is_constant(ctx, ci->ports.at(ctx->id("CI")).net, ci_value);
|
|
||||||
if (ci_const) {
|
|
||||||
carry_ci_lc = nullptr;
|
|
||||||
} else {
|
|
||||||
carry_ci_lc = net_only_drives(ctx, ci->ports.at(ctx->id("CI")).net, is_lc, ctx->id("I3"), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<IdString> i0_matches, i1_matches;
|
|
||||||
NetInfo *i0_net = ci->ports.at(ctx->id("I0")).net;
|
|
||||||
NetInfo *i1_net = ci->ports.at(ctx->id("I1")).net;
|
|
||||||
// Find logic cells connected to both I0 and I1
|
|
||||||
if (i0_net) {
|
|
||||||
for (auto usr : i0_net->users) {
|
|
||||||
if (is_lc(ctx, usr.cell) && usr.port == ctx->id("I1")) {
|
|
||||||
if (ctx->cells.find(usr.cell->name) != ctx->cells.end() &&
|
|
||||||
exhausted_cells.find(usr.cell->name) == exhausted_cells.end()) {
|
|
||||||
// This clause stops us double-packing cells
|
|
||||||
i0_matches.insert(usr.cell->name);
|
|
||||||
if (!i1_net) {
|
|
||||||
// I1 is don't care when disconnected, duplicate I0
|
|
||||||
i1_matches.insert(usr.cell->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i1_net) {
|
|
||||||
for (auto usr : i1_net->users) {
|
|
||||||
if (is_lc(ctx, usr.cell) && usr.port == ctx->id("I2")) {
|
|
||||||
if (ctx->cells.find(usr.cell->name) != ctx->cells.end() &&
|
|
||||||
exhausted_cells.find(usr.cell->name) == exhausted_cells.end()) {
|
|
||||||
// This clause stops us double-packing cells
|
|
||||||
i1_matches.insert(usr.cell->name);
|
|
||||||
if (!i0_net) {
|
|
||||||
// I0 is don't care when disconnected, duplicate I1
|
|
||||||
i0_matches.insert(usr.cell->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<IdString> carry_lcs;
|
|
||||||
std::set_intersection(i0_matches.begin(), i0_matches.end(), i1_matches.begin(), i1_matches.end(),
|
|
||||||
std::inserter(carry_lcs, carry_lcs.end()));
|
|
||||||
CellInfo *carry_lc = nullptr;
|
|
||||||
if (carry_ci_lc && carry_lcs.find(carry_ci_lc->name) != carry_lcs.end()) {
|
|
||||||
carry_lc = carry_ci_lc;
|
|
||||||
} else {
|
|
||||||
// No LC to pack into matching I0/I1, insert a new one
|
|
||||||
std::unique_ptr<CellInfo> created_lc =
|
|
||||||
create_xc7_cell(ctx, ctx->id("XC7_LC"), cell.first.str(ctx) + "$CARRY");
|
|
||||||
carry_lc = created_lc.get();
|
|
||||||
created_lc->ports.at(ctx->id("I1")).net = i0_net;
|
|
||||||
if (i0_net) {
|
|
||||||
PortRef pr;
|
|
||||||
pr.cell = created_lc.get();
|
|
||||||
pr.port = ctx->id("I1");
|
|
||||||
i0_net->users.push_back(pr);
|
|
||||||
}
|
|
||||||
created_lc->ports.at(ctx->id("I2")).net = i1_net;
|
|
||||||
if (i1_net) {
|
|
||||||
PortRef pr;
|
|
||||||
pr.cell = created_lc.get();
|
|
||||||
pr.port = ctx->id("I2");
|
|
||||||
i1_net->users.push_back(pr);
|
|
||||||
}
|
|
||||||
new_cells.push_back(std::move(created_lc));
|
|
||||||
}
|
|
||||||
carry_lc->params[ctx->id("CARRY_ENABLE")] = "1";
|
|
||||||
replace_port(ci, ctx->id("CI"), carry_lc, ctx->id("CIN"));
|
|
||||||
replace_port(ci, ctx->id("CO"), carry_lc, ctx->id("COUT"));
|
|
||||||
if (i0_net) {
|
|
||||||
auto &i0_usrs = i0_net->users;
|
|
||||||
i0_usrs.erase(std::remove_if(i0_usrs.begin(), i0_usrs.end(), [ci, ctx](const PortRef &pr) {
|
|
||||||
return pr.cell == ci && pr.port == ctx->id("I0");
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if (i1_net) {
|
|
||||||
auto &i1_usrs = i1_net->users;
|
|
||||||
i1_usrs.erase(std::remove_if(i1_usrs.begin(), i1_usrs.end(), [ci, ctx](const PortRef &pr) {
|
|
||||||
return pr.cell == ci && pr.port == ctx->id("I1");
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for constant driver on CIN
|
|
||||||
if (carry_lc->ports.at(ctx->id("CIN")).net != nullptr) {
|
|
||||||
IdString cin_net = carry_lc->ports.at(ctx->id("CIN")).net->name;
|
|
||||||
if (cin_net == ctx->id("$PACKER_GND_NET") || cin_net == ctx->id("$PACKER_VCC_NET")) {
|
|
||||||
carry_lc->params[ctx->id("CIN_CONST")] = "1";
|
|
||||||
carry_lc->params[ctx->id("CIN_SET")] = cin_net == ctx->id("$PACKER_VCC_NET") ? "1" : "0";
|
|
||||||
carry_lc->ports.at(ctx->id("CIN")).net = nullptr;
|
|
||||||
auto &cin_users = ctx->nets.at(cin_net)->users;
|
|
||||||
cin_users.erase(
|
|
||||||
std::remove_if(cin_users.begin(), cin_users.end(), [carry_lc, ctx](const PortRef &pr) {
|
|
||||||
return pr.cell == carry_lc && pr.port == ctx->id("CIN");
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exhausted_cells.insert(carry_lc->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto pcell : packed_cells) {
|
|
||||||
ctx->cells.erase(pcell);
|
|
||||||
}
|
|
||||||
for (auto &ncell : new_cells) {
|
|
||||||
ctx->cells[ncell->name] = std::move(ncell);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Pack" RAMs
|
// "Pack" RAMs
|
||||||
static void pack_ram(Context *ctx)
|
static void pack_ram(Context *ctx)
|
||||||
{
|
{
|
||||||
log_info("Packing RAMs..\n");
|
//log_info("Packing RAMs..\n");
|
||||||
|
// TODO
|
||||||
std::unordered_set<IdString> packed_cells;
|
|
||||||
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
|
||||||
|
|
||||||
for (auto cell : sorted(ctx->cells)) {
|
|
||||||
CellInfo *ci = cell.second;
|
|
||||||
if (is_ram(ctx, ci)) {
|
|
||||||
std::unique_ptr<CellInfo> packed =
|
|
||||||
create_xc7_cell(ctx, ctx->id("ICESTORM_RAM"), ci->name.str(ctx) + "_RAM");
|
|
||||||
packed_cells.insert(ci->name);
|
|
||||||
for (auto param : ci->params)
|
|
||||||
packed->params[param.first] = param.second;
|
|
||||||
packed->params[ctx->id("NEG_CLK_W")] =
|
|
||||||
std::to_string(ci->type == ctx->id("SB_RAM40_4KNW") || ci->type == ctx->id("SB_RAM40_4KNRNW"));
|
|
||||||
packed->params[ctx->id("NEG_CLK_R")] =
|
|
||||||
std::to_string(ci->type == ctx->id("SB_RAM40_4KNR") || ci->type == ctx->id("SB_RAM40_4KNRNW"));
|
|
||||||
packed->type = ctx->id("ICESTORM_RAM");
|
|
||||||
for (auto port : ci->ports) {
|
|
||||||
PortInfo &pi = port.second;
|
|
||||||
std::string newname = pi.name.str(ctx);
|
|
||||||
size_t bpos = newname.find('[');
|
|
||||||
if (bpos != std::string::npos) {
|
|
||||||
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
|
|
||||||
}
|
|
||||||
if (pi.name == ctx->id("RCLKN"))
|
|
||||||
newname = "RCLK";
|
|
||||||
else if (pi.name == ctx->id("WCLKN"))
|
|
||||||
newname = "WCLK";
|
|
||||||
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
|
||||||
}
|
|
||||||
new_cells.push_back(std::move(packed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto pcell : packed_cells) {
|
|
||||||
ctx->cells.erase(pcell);
|
|
||||||
}
|
|
||||||
for (auto &ncell : new_cells) {
|
|
||||||
ctx->cells[ncell->name] = std::move(ncell);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge a net into a constant net
|
// Merge a net into a constant net
|
||||||
@ -308,14 +151,6 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne
|
|||||||
if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
|
if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
|
||||||
!constval) {
|
!constval) {
|
||||||
uc->ports[user.port].net = nullptr;
|
uc->ports[user.port].net = nullptr;
|
||||||
} else if ((is_sb_mac16(ctx, uc) || uc->type == ctx->id("ICESTORM_DSP")) &&
|
|
||||||
(user.port != ctx->id("CLK") &&
|
|
||||||
((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) {
|
|
||||||
uc->ports[user.port].net = nullptr;
|
|
||||||
} else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") &&
|
|
||||||
user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") && user.port != ctx->id("RCLKE") &&
|
|
||||||
user.port != ctx->id("WCLKE")) {
|
|
||||||
uc->ports[user.port].net = nullptr;
|
|
||||||
} else {
|
} else {
|
||||||
uc->ports[user.port].net = constnet;
|
uc->ports[user.port].net = constnet;
|
||||||
constnet->users.push_back(user);
|
constnet->users.push_back(user);
|
||||||
@ -421,9 +256,9 @@ static void pack_io(Context *ctx)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create a IOBUF buffer
|
// Create a IOBUF buffer
|
||||||
std::unique_ptr<CellInfo> ice_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx));
|
std::unique_ptr<CellInfo> xc7_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx));
|
||||||
nxio_to_sb(ctx, ci, ice_cell.get());
|
nxio_to_sb(ctx, ci, xc7_cell.get());
|
||||||
new_cells.push_back(std::move(ice_cell));
|
new_cells.push_back(std::move(xc7_cell));
|
||||||
sb = new_cells.back().get();
|
sb = new_cells.back().get();
|
||||||
}
|
}
|
||||||
packed_cells.insert(ci->name);
|
packed_cells.insert(ci->name);
|
||||||
@ -443,8 +278,7 @@ static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
|
|||||||
{
|
{
|
||||||
if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port))
|
if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port))
|
||||||
return false;
|
return false;
|
||||||
return !is_sb_io(ctx, port.cell) && !is_sb_pll40(ctx, port.cell) && !is_sb_pll40_pad(ctx, port.cell) &&
|
return !is_sb_io(ctx, port.cell) && port.cell->type != id_BUFGCTRL;
|
||||||
port.cell->type != ctx->id("SB_GB");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
|
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
|
||||||
@ -631,242 +465,230 @@ static void pack_special(Context *ctx)
|
|||||||
|
|
||||||
for (auto cell : sorted(ctx->cells)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
CellInfo *ci = cell.second;
|
||||||
if (is_sb_lfosc(ctx, ci)) {
|
if (ci->type == id_BUFGCTRL) {
|
||||||
std::unique_ptr<CellInfo> packed =
|
ci->params.emplace(ctx->id("PRESELECT_I0"), "FALSE");
|
||||||
create_xc7_cell(ctx, ctx->id("ICESTORM_LFOSC"), ci->name.str(ctx) + "_OSC");
|
ci->params.emplace(ctx->id("CE0INV"), "CE0");
|
||||||
packed_cells.insert(ci->name);
|
ci->params.emplace(ctx->id("S0INV"), "S0");
|
||||||
replace_port(ci, ctx->id("CLKLFEN"), packed.get(), ctx->id("CLKLFEN"));
|
ci->params.emplace(ctx->id("IGNORE0INV"), "IGNORE0");
|
||||||
replace_port(ci, ctx->id("CLKLFPU"), packed.get(), ctx->id("CLKLFPU"));
|
ci->params.emplace(ctx->id("CE1INV"), "CE1");
|
||||||
if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME
|
ci->params.emplace(ctx->id("S1INV"), "S1");
|
||||||
replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF_FABRIC"));
|
ci->params.emplace(ctx->id("IGNORE1INV"), "IGNORE1");
|
||||||
} else {
|
} else if (ci->type == id_MMCME2_ADV) {
|
||||||
replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF"));
|
ci->params.emplace(ctx->id("BANDWIDTH"), "OPTIMIZED");
|
||||||
}
|
ci->params.emplace(ctx->id("CLKBURST_ENABLE"), "FALSE");
|
||||||
new_cells.push_back(std::move(packed));
|
ci->params.emplace(ctx->id("CLKBURST_REPEAT"), "FALSE");
|
||||||
} else if (is_sb_hfosc(ctx, ci)) {
|
ci->params.emplace(ctx->id("CLKFBIN_EDGE"), "FALSE");
|
||||||
std::unique_ptr<CellInfo> packed =
|
ci->params.emplace(ctx->id("CLKFBIN_NOCOUNT"), "TRUE");
|
||||||
create_xc7_cell(ctx, ctx->id("ICESTORM_HFOSC"), ci->name.str(ctx) + "_OSC");
|
ci->params.emplace(ctx->id("CLKFBOUT_EDGE"), "FALSE");
|
||||||
packed_cells.insert(ci->name);
|
ci->params.emplace(ctx->id("CLKFBOUT_EN"), "TRUE");
|
||||||
packed->params[ctx->id("CLKHF_DIV")] = str_or_default(ci->params, ctx->id("CLKHF_DIV"), "0b00");
|
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_EN"), "FALSE");
|
||||||
replace_port(ci, ctx->id("CLKHFEN"), packed.get(), ctx->id("CLKHFEN"));
|
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_WF_FALL"), "FALSE");
|
||||||
replace_port(ci, ctx->id("CLKHFPU"), packed.get(), ctx->id("CLKHFPU"));
|
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_WF_RISE"), "FALSE");
|
||||||
if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME
|
ci->params.emplace(ctx->id("CLKFBOUT_NOCOUNT"), "TRUE");
|
||||||
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF_FABRIC"));
|
ci->params.emplace(ctx->id("CLKFBOUT_USE_FINE_PS"), "FALSE");
|
||||||
} else {
|
ci->params.emplace(ctx->id("CLKINSELINV"), "CLKINSEL");
|
||||||
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF"));
|
ci->params.emplace(ctx->id("CLKOUT0_EDGE"), "FALSE");
|
||||||
}
|
ci->params.emplace(ctx->id("CLKOUT0_EN"), "FALSE");
|
||||||
new_cells.push_back(std::move(packed));
|
ci->params.emplace(ctx->id("CLKOUT0_FRAC_EN"), "FALSE");
|
||||||
} else if (is_sb_spram(ctx, ci)) {
|
ci->params.emplace(ctx->id("CLKOUT0_FRAC_WF_FALL"), "FALSE");
|
||||||
std::unique_ptr<CellInfo> packed =
|
ci->params.emplace(ctx->id("CLKOUT0_FRAC_WF_RISE"), "FALSE");
|
||||||
create_xc7_cell(ctx, ctx->id("ICESTORM_SPRAM"), ci->name.str(ctx) + "_RAM");
|
ci->params.emplace(ctx->id("CLKOUT0_NOCOUNT"), "TRUE");
|
||||||
packed_cells.insert(ci->name);
|
ci->params.emplace(ctx->id("CLKOUT0_USE_FINE_PS"), "FALSE");
|
||||||
for (auto port : ci->ports) {
|
ci->params.emplace(ctx->id("CLKOUT1_EDGE"), "FALSE");
|
||||||
PortInfo &pi = port.second;
|
ci->params.emplace(ctx->id("CLKOUT1_EN"), "FALSE");
|
||||||
std::string newname = pi.name.str(ctx);
|
ci->params.emplace(ctx->id("CLKOUT1_NOCOUNT"), "TRUE");
|
||||||
size_t bpos = newname.find('[');
|
ci->params.emplace(ctx->id("CLKOUT1_USE_FINE_PS"), "FALSE");
|
||||||
if (bpos != std::string::npos) {
|
ci->params.emplace(ctx->id("CLKOUT2_EDGE"), "FALSE");
|
||||||
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
|
ci->params.emplace(ctx->id("CLKOUT2_EN"), "FALSE");
|
||||||
}
|
ci->params.emplace(ctx->id("CLKOUT2_NOCOUNT"), "TRUE");
|
||||||
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
ci->params.emplace(ctx->id("CLKOUT2_USE_FINE_PS"), "FALSE");
|
||||||
}
|
ci->params.emplace(ctx->id("CLKOUT3_EDGE"), "FALSE");
|
||||||
new_cells.push_back(std::move(packed));
|
ci->params.emplace(ctx->id("CLKOUT3_EN"), "FALSE");
|
||||||
} else if (is_sb_mac16(ctx, ci)) {
|
ci->params.emplace(ctx->id("CLKOUT3_NOCOUNT"), "TRUE");
|
||||||
std::unique_ptr<CellInfo> packed =
|
ci->params.emplace(ctx->id("CLKOUT3_USE_FINE_PS"), "FALSE");
|
||||||
create_xc7_cell(ctx, ctx->id("ICESTORM_DSP"), ci->name.str(ctx) + "_DSP");
|
ci->params.emplace(ctx->id("CLKOUT4_CASCADE"), "FALSE");
|
||||||
packed_cells.insert(ci->name);
|
ci->params.emplace(ctx->id("CLKOUT4_EDGE"), "FALSE");
|
||||||
for (auto attr : ci->attrs)
|
ci->params.emplace(ctx->id("CLKOUT4_EN"), "FALSE");
|
||||||
packed->attrs[attr.first] = attr.second;
|
ci->params.emplace(ctx->id("CLKOUT4_NOCOUNT"), "TRUE");
|
||||||
for (auto param : ci->params)
|
ci->params.emplace(ctx->id("CLKOUT4_USE_FINE_PS"), "FALSE");
|
||||||
packed->params[param.first] = param.second;
|
ci->params.emplace(ctx->id("CLKOUT5_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("COMPENSATION"), "INTERNAL");
|
||||||
|
ci->params.emplace(ctx->id("DIRECT_PATH_CNTRL"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("EN_VCO_DIV1"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("EN_VCO_DIV6"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("GTS_WAIT"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("HVLF_CNT_TEST_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("INTERP_TEST"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("IN_DLY_EN"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("LF_LOW_SEL"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("MMCM_EN"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("PERF0_USE_CLK"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("PERF1_USE_CLK"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("PERF2_USE_CLK"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("PERF3_USE_CLK"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("PSENINV"), "PSEN");
|
||||||
|
ci->params.emplace(ctx->id("PSINCDECINV"), "PSINCDEC");
|
||||||
|
ci->params.emplace(ctx->id("PWRDWNINV"), "PWRDWN");
|
||||||
|
ci->params.emplace(ctx->id("RSTINV"), "RST");
|
||||||
|
ci->params.emplace(ctx->id("SEL_HV_NMOS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SEL_LV_NMOS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SEL_SLIPD"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SS_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SS_MODE"), "CENTER_HIGH");
|
||||||
|
ci->params.emplace(ctx->id("STARTUP_WAIT"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SUP_SEL_AREG"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SUP_SEL_DREG"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("TMUX_MUX_SEL"), "00");
|
||||||
|
ci->params.emplace(ctx->id("VLF_HIGH_DIS_B"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("VLF_HIGH_PWDN_B"), "TRUE");
|
||||||
|
//ci->params.emplace(ctx->id("MMCME2_ADV:mmcm_adv_inst:");
|
||||||
|
ci->params.emplace(ctx->id("ANALOG_MISC"), "0000");
|
||||||
|
ci->params.emplace(ctx->id("AVDD_COMP_SET"), "011");
|
||||||
|
ci->params.emplace(ctx->id("AVDD_VBG_PD"), "110");
|
||||||
|
ci->params.emplace(ctx->id("AVDD_VBG_SEL"), "1001");
|
||||||
|
ci->params.emplace(ctx->id("CLKBURST_CNT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBIN_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBIN_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBIN_MULT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_FRAC"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_MULT_F"), "40.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_PM_FALL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_PM_RISE"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKFB_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKIN1_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKIN1_PERIOD"), "8");
|
||||||
|
ci->params.emplace(ctx->id("CLKIN2_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKIN2_PERIOD"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_DIVIDE_F"), "16.875");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_FRAC"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_PM_FALL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_PM_RISE"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_0"), "1111001101111100");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_1"), "0111110101001101");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_2"), "0101000001000010");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_3"), "1110101111001000");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_4"), "1101010011011111");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_5"), "1010110111111011");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_6"), "1011001011000011");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_7"), "0100110000101110");
|
||||||
|
ci->params.emplace(ctx->id("CP"), "0000");
|
||||||
|
ci->params.emplace(ctx->id("CP_BIAS_TRIP_SET"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CP_RES"), "01");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_DIVIDE"), "5");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("DVDD_COMP_SET"), "011");
|
||||||
|
ci->params.emplace(ctx->id("DVDD_VBG_PD"), "110");
|
||||||
|
ci->params.emplace(ctx->id("DVDD_VBG_SEL"), "1001");
|
||||||
|
ci->params.emplace(ctx->id("EN_CURR_SINK"), "11");
|
||||||
|
ci->params.emplace(ctx->id("FINE_PS_FRAC"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK0"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK1"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK2"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK3"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_COMP"), "01");
|
||||||
|
ci->params.emplace(ctx->id("HROW_DLY_SET"), "0");
|
||||||
|
ci->params.emplace(ctx->id("HVLF_CNT_TEST"), "0");
|
||||||
|
ci->params.emplace(ctx->id("INTERP_EN"), "00010000");
|
||||||
|
ci->params.emplace(ctx->id("IN_DLY_MX_CVDD"), "011000");
|
||||||
|
ci->params.emplace(ctx->id("IN_DLY_MX_DVDD"), "000001");
|
||||||
|
ci->params.emplace(ctx->id("IN_DLY_SET"), "38");
|
||||||
|
ci->params.emplace(ctx->id("LFHF"), "11");
|
||||||
|
ci->params.emplace(ctx->id("LF_NEN"), "10");
|
||||||
|
ci->params.emplace(ctx->id("LF_PEN"), "00");
|
||||||
|
ci->params.emplace(ctx->id("LOCK_CNT"), "128");
|
||||||
|
ci->params.emplace(ctx->id("LOCK_FB_DLY"), "3");
|
||||||
|
ci->params.emplace(ctx->id("LOCK_REF_DLY"), "3");
|
||||||
|
ci->params.emplace(ctx->id("LOCK_SAT_HIGH"), "160");
|
||||||
|
ci->params.emplace(ctx->id("MAN_LF"), "000");
|
||||||
|
ci->params.emplace(ctx->id("MVDD_SEL"), "11");
|
||||||
|
ci->params.emplace(ctx->id("PERF0_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("PERF1_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("PERF2_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("PERF3_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("PFD"), "0100001");
|
||||||
|
ci->params.emplace(ctx->id("REF_JITTER1"), "0.01");
|
||||||
|
ci->params.emplace(ctx->id("REF_JITTER2"), "0.01");
|
||||||
|
ci->params.emplace(ctx->id("RES"), "0000");
|
||||||
|
ci->params.emplace(ctx->id("SKEW_FLOP_INV"), "0000");
|
||||||
|
ci->params.emplace(ctx->id("SPARE_ANALOG"), "00000");
|
||||||
|
ci->params.emplace(ctx->id("SPARE_DIGITAL"), "00000");
|
||||||
|
ci->params.emplace(ctx->id("SS_MOD_PERIOD"), "10000");
|
||||||
|
ci->params.emplace(ctx->id("SS_STEPS"), "011");
|
||||||
|
ci->params.emplace(ctx->id("SS_STEPS_INIT"), "010");
|
||||||
|
ci->params.emplace(ctx->id("SYNTH_CLK_DIV"), "11");
|
||||||
|
ci->params.emplace(ctx->id("UNLOCK_CNT"), "64");
|
||||||
|
ci->params.emplace(ctx->id("VREF_START"), "01");
|
||||||
|
|
||||||
for (auto port : ci->ports) {
|
ci->params[ctx->id("COMPENSATION")] = "INTERNAL";
|
||||||
PortInfo &pi = port.second;
|
|
||||||
std::string newname = pi.name.str(ctx);
|
|
||||||
size_t bpos = newname.find('[');
|
|
||||||
if (bpos != std::string::npos) {
|
|
||||||
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
|
|
||||||
}
|
|
||||||
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
|
||||||
}
|
|
||||||
new_cells.push_back(std::move(packed));
|
|
||||||
} else if (is_sb_pll40(ctx, ci)) {
|
|
||||||
bool is_pad = is_sb_pll40_pad(ctx, ci);
|
|
||||||
bool is_core = !is_pad;
|
|
||||||
|
|
||||||
std::unique_ptr<CellInfo> packed =
|
|
||||||
create_xc7_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL");
|
|
||||||
packed->attrs[ctx->id("TYPE")] = ci->type.str(ctx);
|
|
||||||
packed_cells.insert(ci->name);
|
|
||||||
|
|
||||||
for (auto attr : ci->attrs)
|
|
||||||
packed->attrs[attr.first] = attr.second;
|
|
||||||
for (auto param : ci->params)
|
|
||||||
packed->params[param.first] = param.second;
|
|
||||||
|
|
||||||
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
|
|
||||||
packed->params[ctx->id("FEEDBACK_PATH")] =
|
|
||||||
feedback_path == "DELAY"
|
|
||||||
? "0"
|
|
||||||
: feedback_path == "SIMPLE" ? "1"
|
|
||||||
: feedback_path == "PHASE_AND_DELAY"
|
|
||||||
? "2"
|
|
||||||
: feedback_path == "EXTERNAL" ? "6" : feedback_path;
|
|
||||||
packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
|
|
||||||
|
|
||||||
NetInfo *pad_packagepin_net = nullptr;
|
|
||||||
|
|
||||||
for (auto port : ci->ports) {
|
|
||||||
PortInfo &pi = port.second;
|
|
||||||
std::string newname = pi.name.str(ctx);
|
|
||||||
size_t bpos = newname.find('[');
|
|
||||||
if (bpos != std::string::npos) {
|
|
||||||
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
|
|
||||||
}
|
|
||||||
if (pi.name == ctx->id("PLLOUTCOREA"))
|
|
||||||
newname = "PLLOUT_A";
|
|
||||||
if (pi.name == ctx->id("PLLOUTCOREB"))
|
|
||||||
newname = "PLLOUT_B";
|
|
||||||
if (pi.name == ctx->id("PLLOUTCORE"))
|
|
||||||
newname = "PLLOUT_A";
|
|
||||||
|
|
||||||
if (pi.name == ctx->id("PACKAGEPIN")) {
|
|
||||||
if (!is_pad) {
|
|
||||||
log_error(" PLL '%s' has a PACKAGEPIN but is not a PAD PLL", ci->name.c_str(ctx));
|
|
||||||
} else {
|
|
||||||
// We drop this port and instead place the PLL adequately below.
|
|
||||||
pad_packagepin_net = port.second.net;
|
|
||||||
NPNR_ASSERT(pad_packagepin_net != nullptr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pi.name == ctx->id("REFERENCECLK")) {
|
|
||||||
if (!is_core)
|
|
||||||
log_error(" PLL '%s' has a REFERENCECLK but is not a CORE PLL", ci->name.c_str(ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If PLL is not constrained already, do that - we need this
|
|
||||||
// information to then constrain the LOCK LUT.
|
|
||||||
BelId pll_bel;
|
|
||||||
bool constrained = false;
|
|
||||||
if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) {
|
|
||||||
for (auto bel : ctx->getBels()) {
|
|
||||||
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared
|
|
||||||
// with PLLOUT_A.
|
|
||||||
if (is_pad) {
|
|
||||||
auto pll_sb_io_belpin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
|
|
||||||
NPNR_ASSERT(pad_packagepin_net != nullptr);
|
|
||||||
auto pll_packagepin_driver = pad_packagepin_net->driver;
|
|
||||||
NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
|
|
||||||
if (pll_packagepin_driver.cell->type != ctx->id("SB_IO")) {
|
|
||||||
log_error(" PLL '%s' has a PACKAGEPIN driven by "
|
|
||||||
"an %s, should be directly connected to an input SB_IO\n",
|
|
||||||
ci->name.c_str(ctx), pll_packagepin_driver.cell->type.c_str(ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto packagepin_cell = pll_packagepin_driver.cell;
|
|
||||||
auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
|
|
||||||
if (packagepin_bel_name == packagepin_cell->attrs.end()) {
|
|
||||||
log_error(" PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
|
|
||||||
packagepin_cell->name.c_str(ctx));
|
|
||||||
}
|
|
||||||
auto packagepin_bel = ctx->getBelByName(ctx->id(packagepin_bel_name->second));
|
|
||||||
if (pll_sb_io_belpin.bel != packagepin_bel) {
|
|
||||||
log_error(" PLL '%s' PACKAGEPIN is connected to pin %s, can only be pin %s\n",
|
|
||||||
ci->name.c_str(ctx), ctx->getBelPackagePin(packagepin_bel).c_str(),
|
|
||||||
ctx->getBelPackagePin(pll_sb_io_belpin.bel).c_str());
|
|
||||||
}
|
|
||||||
if (pad_packagepin_net->users.size() != 1) {
|
|
||||||
log_error(" PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx),
|
|
||||||
pad_packagepin_net->name.c_str(ctx));
|
|
||||||
}
|
|
||||||
// Set an attribute about this PLL's PAD SB_IO.
|
|
||||||
packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second;
|
|
||||||
// Remove the connection from the SB_IO to the PLL.
|
|
||||||
packagepin_cell->ports.erase(pll_packagepin_driver.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_info(" constrained '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx));
|
|
||||||
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
|
|
||||||
pll_bel = bel;
|
|
||||||
constrained = true;
|
|
||||||
}
|
|
||||||
if (!constrained) {
|
|
||||||
log_error(" could not constrain '%s' to any PLL Bel\n", packed->name.c_str(ctx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the original PACKAGEPIN net if needed.
|
|
||||||
if (pad_packagepin_net != nullptr) {
|
|
||||||
for (auto user : pad_packagepin_net->users) {
|
|
||||||
user.cell->ports.erase(user.port);
|
|
||||||
}
|
|
||||||
ctx->nets.erase(pad_packagepin_net->name);
|
|
||||||
pad_packagepin_net = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The LOCK signal on iCE40 PLLs goes through the neigh_op_bnl_1 wire.
|
|
||||||
// In practice, this means the LOCK signal can only directly reach LUT
|
|
||||||
// inputs.
|
|
||||||
// If we have a net connected to LOCK, make sure it only drives LUTs.
|
|
||||||
auto port = packed->ports[ctx->id("LOCK")];
|
|
||||||
if (port.net != nullptr) {
|
|
||||||
bool found_lut = false;
|
|
||||||
bool all_luts = true;
|
|
||||||
unsigned int lut_count = 0;
|
|
||||||
for (const auto &user : port.net->users) {
|
|
||||||
NPNR_ASSERT(user.cell != nullptr);
|
|
||||||
if (user.cell->type == ctx->id("XC7_LC")) {
|
|
||||||
found_lut = true;
|
|
||||||
lut_count++;
|
|
||||||
} else {
|
|
||||||
all_luts = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found_lut && all_luts) {
|
|
||||||
// Every user is a LUT, carry on now.
|
|
||||||
} else if (found_lut && !all_luts && lut_count < 8) {
|
|
||||||
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
|
|
||||||
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
|
|
||||||
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
|
|
||||||
new_cells.push_back(std::move(pt));
|
|
||||||
} else {
|
|
||||||
// Strategy: create a pass-through LUT, move every user behind it.
|
|
||||||
log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx));
|
|
||||||
auto pt = spliceLUT(ctx, packed.get(), port.name, false);
|
|
||||||
new_cells.push_back(std::move(pt));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find wire that will be driven by this port.
|
|
||||||
const auto pll_out_wire = ctx->getBelPinWire(pll_bel, port.name);
|
|
||||||
NPNR_ASSERT(pll_out_wire != WireId());
|
|
||||||
|
|
||||||
// Now, constrain all LUTs on the output of the signal to be at
|
|
||||||
// the correct Bel relative to the PLL Bel.
|
|
||||||
// int x = ctx->chip_info->wire_data[pll_out_wire.index].x;
|
|
||||||
// int y = ctx->chip_info->wire_data[pll_out_wire.index].y;
|
|
||||||
// int z = 0;
|
|
||||||
// for (const auto &user : port.net->users) {
|
|
||||||
// NPNR_ASSERT(user.cell != nullptr);
|
|
||||||
// NPNR_ASSERT(user.cell->type == ctx->id("XC7_LC"));
|
|
||||||
|
|
||||||
// // TODO(q3k): handle when the Bel might be already the
|
|
||||||
// // target of another constraint.
|
|
||||||
// NPNR_ASSERT(z < 8);
|
|
||||||
// auto target_bel = ctx->getBelByLocation(Loc(x, y, z++));
|
|
||||||
// auto target_bel_name = ctx->getBelName(target_bel).str(ctx);
|
|
||||||
// user.cell->attrs[ctx->id("BEL")] = target_bel_name;
|
|
||||||
// log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str());
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_cells.push_back(std::move(packed));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
NET "clk" PERIOD = 8 nS ;
|
NET "pll.clkin1" PERIOD = 8 nS ;
|
||||||
PIN "clk_pin" = BEL "clk.PAD" PINNAME PAD;
|
#PIN "clk_pin" = BEL "clk.PAD" PINNAME PAD;
|
||||||
PIN "clk_pin" CLOCK_DEDICATED_ROUTE = FALSE;
|
#PIN "clk_pin" CLOCK_DEDICATED_ROUTE = FALSE;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
read_verilog picorv32.v
|
read_verilog picorv32.v
|
||||||
read_verilog picorv32_top.v
|
read_verilog picorv32_top.v
|
||||||
|
read_verilog 125MHz_to_60MHz.v
|
||||||
|
|
||||||
#synth_xilinx -top picorv32
|
#synth_xilinx -top picorv32
|
||||||
|
|
||||||
|
@ -12,13 +12,7 @@ module top (
|
|||||||
input [31:0] mem_rdata
|
input [31:0] mem_rdata
|
||||||
);
|
);
|
||||||
|
|
||||||
wire gclk;
|
clk_wiz_v3_6 pll(.CLK_IN1(clk), .CLK_OUT1(gclk));
|
||||||
BUFGCTRL clk_gb (
|
|
||||||
.I0(clk),
|
|
||||||
.CE0(1'b1),
|
|
||||||
.S0(1'b1),
|
|
||||||
.O(gclk)
|
|
||||||
);
|
|
||||||
|
|
||||||
picorv32 #(
|
picorv32 #(
|
||||||
.ENABLE_COUNTERS(0),
|
.ENABLE_COUNTERS(0),
|
||||||
|
63
xc7/xdl.cc
63
xc7/xdl.cc
@ -25,6 +25,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
#include "torc/Physical.hpp"
|
#include "torc/Physical.hpp"
|
||||||
using namespace torc::architecture::xilinx;
|
using namespace torc::architecture::xilinx;
|
||||||
@ -32,9 +33,8 @@ using namespace torc::physical;
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
void write_xdl(const Context *ctx, std::ostream &out)
|
DesignSharedPtr create_torc_design(const Context *ctx)
|
||||||
{
|
{
|
||||||
XdlExporter exporter(out);
|
|
||||||
auto designPtr = Factory::newDesignPtr("name", torc_info->ddb->getDeviceName(), ctx->args.package, "-1", "");
|
auto designPtr = Factory::newDesignPtr("name", torc_info->ddb->getDeviceName(), ctx->args.package, "-1", "");
|
||||||
|
|
||||||
std::unordered_map<int32_t, InstanceSharedPtr> site_to_instance;
|
std::unordered_map<int32_t, InstanceSharedPtr> site_to_instance;
|
||||||
@ -68,7 +68,7 @@ void write_xdl(const Context *ctx, std::ostream &out)
|
|||||||
const char *type;
|
const char *type;
|
||||||
if (cell.second->type == id_SLICE_LUT6)
|
if (cell.second->type == id_SLICE_LUT6)
|
||||||
type = "SLICEL";
|
type = "SLICEL";
|
||||||
else if (cell.second->type == id_IOB33 || cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7)
|
else if (cell.second->type == id_IOB33 || cell.second->type == id_IOB18 || cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV)
|
||||||
type = cell.second->type.c_str(ctx);
|
type = cell.second->type.c_str(ctx);
|
||||||
else
|
else
|
||||||
log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
|
log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
|
||||||
@ -111,7 +111,7 @@ void write_xdl(const Context *ctx, std::ostream &out)
|
|||||||
// Assume from Yosys that INIT masks of less than 32 bits are output as uint32_t
|
// Assume from Yosys that INIT masks of less than 32 bits are output as uint32_t
|
||||||
if (lut_inputs.size() < 6) {
|
if (lut_inputs.size() < 6) {
|
||||||
auto init_as_uint = boost::lexical_cast<uint32_t>(init);
|
auto init_as_uint = boost::lexical_cast<uint32_t>(init);
|
||||||
NPNR_ASSERT(init_as_uint < (1ull << (1u << lut_inputs.size())));
|
NPNR_ASSERT(init_as_uint <= ((1ull << (1u << lut_inputs.size())) - 1));
|
||||||
if (lut_inputs.empty())
|
if (lut_inputs.empty())
|
||||||
value += init;
|
value += init;
|
||||||
else {
|
else {
|
||||||
@ -173,16 +173,21 @@ void write_xdl(const Context *ctx, std::ostream &out)
|
|||||||
boost::replace_all(name, ":", "\\:");
|
boost::replace_all(name, ":", "\\:");
|
||||||
instPtr->setConfig(setting, name, "#FF");
|
instPtr->setConfig(setting, name, "#FF");
|
||||||
instPtr->setConfig(setting + "MUX", "", "O6");
|
instPtr->setConfig(setting + "MUX", "", "O6");
|
||||||
instPtr->setConfig(setting + "INIT", "", "INIT" + cell.second->params.at(ctx->id("DFF_INIT")));
|
instPtr->setConfig(setting + "INIT", "", cell.second->params.at(ctx->id("FFINIT")));
|
||||||
assert(cell.second->params.at(ctx->id("SET_NORESET")) == "0"); // TODO
|
|
||||||
instPtr->setConfig(setting + "SR", "", "SRLOW");
|
|
||||||
|
|
||||||
NPNR_ASSERT(!cell.second->lcInfo.negClk); // TODO
|
if (cell.second->lcInfo.negClk)
|
||||||
|
instPtr->setConfig("CLKINV", "", "CLK_B");
|
||||||
|
else
|
||||||
instPtr->setConfig("CLKINV", "", "CLK");
|
instPtr->setConfig("CLKINV", "", "CLK");
|
||||||
|
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_SR)) {
|
||||||
|
instPtr->setConfig(setting + "SR", "", cell.second->params.at(id_SR));
|
||||||
|
instPtr->setConfig("SYNC_ATTR", "", cell.second->params.at(ctx->id("SYNC_ATTR")));
|
||||||
instPtr->setConfig("SRUSEDMUX", "", "IN");
|
instPtr->setConfig("SRUSEDMUX", "", "IN");
|
||||||
|
}
|
||||||
|
if (get_net_or_empty(cell.second.get(), ctx->id("CE")))
|
||||||
instPtr->setConfig("CEUSEDMUX", "", "IN");
|
instPtr->setConfig("CEUSEDMUX", "", "IN");
|
||||||
instPtr->setConfig("SYNC_ATTR", "", "ASYNC");
|
|
||||||
}
|
}
|
||||||
} else if (cell.second->type == id_IOB33) {
|
} else if (cell.second->type == id_IOB33) {
|
||||||
if (get_net_or_empty(cell.second.get(), id_I)) {
|
if (get_net_or_empty(cell.second.get(), id_I)) {
|
||||||
@ -195,17 +200,20 @@ void write_xdl(const Context *ctx, std::ostream &out)
|
|||||||
instPtr->setConfig("DRIVE", "", "12");
|
instPtr->setConfig("DRIVE", "", "12");
|
||||||
instPtr->setConfig("SLEW", "", "SLOW");
|
instPtr->setConfig("SLEW", "", "SLOW");
|
||||||
}
|
}
|
||||||
} else if (cell.second->type == id_BUFGCTRL) {
|
} else if (cell.second->type == id_IOB18) {
|
||||||
auto it = cell.second->params.find(ctx->id("PRESELECT_I0"));
|
if (get_net_or_empty(cell.second.get(), id_I)) {
|
||||||
instPtr->setConfig("PRESELECT_I0", "", it != cell.second->params.end() ? it->second : "FALSE");
|
instPtr->setConfig("IUSED", "", "0");
|
||||||
|
instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE");
|
||||||
instPtr->setConfig("CE0INV", "", "CE0");
|
instPtr->setConfig("ISTANDARD", "", "LVCMOS18");
|
||||||
instPtr->setConfig("S0INV", "", "S0");
|
} else {
|
||||||
instPtr->setConfig("IGNORE0INV", "", "IGNORE0");
|
instPtr->setConfig("OUSED", "", "0");
|
||||||
instPtr->setConfig("CE1INV", "", "CE1");
|
instPtr->setConfig("OSTANDARD", "", "LVCMOS18");
|
||||||
instPtr->setConfig("S1INV", "", "S1");
|
instPtr->setConfig("DRIVE", "", "12");
|
||||||
instPtr->setConfig("IGNORE1INV", "", "IGNORE1");
|
instPtr->setConfig("SLEW", "", "SLOW");
|
||||||
} else if (cell.second->type == id_PS7) {
|
}
|
||||||
|
} else if (cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV) {
|
||||||
|
for (const auto& i : cell.second->params)
|
||||||
|
instPtr->setConfig(i.first.str(ctx), "", i.second);
|
||||||
} else
|
} else
|
||||||
log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
|
log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
|
||||||
}
|
}
|
||||||
@ -224,9 +232,12 @@ void write_xdl(const Context *ctx, std::ostream &out)
|
|||||||
const auto lut = bel_to_lut(driver.cell->bel);
|
const auto lut = bel_to_lut(driver.cell->bel);
|
||||||
pin_name[0] = lut[0];
|
pin_name[0] = lut[0];
|
||||||
}
|
}
|
||||||
|
// e.g. Convert DDRARB[0] -> DDRARB0
|
||||||
|
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
|
||||||
auto pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
|
auto pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
|
||||||
netPtr->addSource(pinPtr);
|
netPtr->addSource(pinPtr);
|
||||||
|
|
||||||
|
if (!net.second->users.empty()) {
|
||||||
for (const auto &user : net.second->users) {
|
for (const auto &user : net.second->users) {
|
||||||
site_index = torc_info->bel_to_site_index[user.cell->bel.index];
|
site_index = torc_info->bel_to_site_index[user.cell->bel.index];
|
||||||
instPtr = site_to_instance.at(site_index);
|
instPtr = site_to_instance.at(site_index);
|
||||||
@ -237,6 +248,10 @@ void write_xdl(const Context *ctx, std::ostream &out)
|
|||||||
const auto lut = bel_to_lut(user.cell->bel);
|
const auto lut = bel_to_lut(user.cell->bel);
|
||||||
pin_name[0] = lut[0];
|
pin_name[0] = lut[0];
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// e.g. Convert DDRARB[0] -> DDRARB0
|
||||||
|
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
|
||||||
|
}
|
||||||
pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
|
pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
|
||||||
netPtr->addSink(pinPtr);
|
netPtr->addSink(pinPtr);
|
||||||
}
|
}
|
||||||
@ -255,7 +270,15 @@ void write_xdl(const Context *ctx, std::ostream &out)
|
|||||||
netPtr->addPip(p);
|
netPtr->addPip(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return designPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_xdl(const Context *ctx, std::ostream &out)
|
||||||
|
{
|
||||||
|
XdlExporter exporter(out);
|
||||||
|
auto designPtr = create_torc_design(ctx);
|
||||||
exporter(designPtr);
|
exporter(designPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user