diff --git a/.gitignore b/.gitignore index 74ff0107..32fe798d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ CTestTestfile.cmake install_manifest.txt /bbasm /ImportExecutables.cmake +*-coverage/ +*-coverage.info diff --git a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp index 523856eb..bdca7dd5 100644 --- a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp +++ b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp @@ -82,6 +82,7 @@ public: void slotCollapsed(const QModelIndex &index); void slotExpanded(const QModelIndex &index); + void onHoverPropertyChanged(QtBrowserItem *item); QColor calculatedBackgroundColor(QtBrowserItem *item) const; @@ -129,12 +130,17 @@ public: { return itemFromIndex(index); } protected: + void mouseMoveEvent(QMouseEvent *event) override; + void leaveEvent(QEvent *event) override; void keyPressEvent(QKeyEvent *event); void mousePressEvent(QMouseEvent *event); void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; +Q_SIGNALS: + void hoverPropertyChanged(QtBrowserItem *item); private: QtTreePropertyBrowserPrivate *m_editorPrivate; + QModelIndex current; }; QtPropertyEditorView::QtPropertyEditorView(QWidget *parent) : @@ -172,6 +178,21 @@ void QtPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem painter->restore(); } +void QtPropertyEditorView::mouseMoveEvent(QMouseEvent *event) +{ + QModelIndex index = indexAt(event->pos()); + if (index!=current) { + current = index; + Q_EMIT hoverPropertyChanged(m_editorPrivate->indexToBrowserItem(index)); + } + QTreeWidget::mouseMoveEvent(event); +} + +void QtPropertyEditorView::leaveEvent(QEvent *event) +{ + Q_EMIT hoverPropertyChanged(nullptr); +} + void QtPropertyEditorView::keyPressEvent(QKeyEvent *event) { switch (event->key()) { @@ -489,6 +510,7 @@ void QtTreePropertyBrowserPrivate::init(QWidget *parent) QObject::connect(m_treeWidget, SIGNAL(collapsed(const QModelIndex &)), q_ptr, SLOT(slotCollapsed(const QModelIndex &))); QObject::connect(m_treeWidget, SIGNAL(expanded(const QModelIndex &)), q_ptr, SLOT(slotExpanded(const QModelIndex &))); QObject::connect(m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), q_ptr, SLOT(slotCurrentTreeItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); + QObject::connect(m_treeWidget, SIGNAL(hoverPropertyChanged(QtBrowserItem *)), q_ptr, SLOT(onHoverPropertyChanged(QtBrowserItem *))); } QtBrowserItem *QtTreePropertyBrowserPrivate::currentItem() const @@ -688,6 +710,12 @@ void QtTreePropertyBrowserPrivate::slotExpanded(const QModelIndex &index) emit q_ptr->expanded(idx); } +void QtTreePropertyBrowserPrivate::onHoverPropertyChanged(QtBrowserItem *item) +{ + emit q_ptr->hoverPropertyChanged(item); +} + + void QtTreePropertyBrowserPrivate::slotCurrentBrowserItemChanged(QtBrowserItem *item) { if (!m_browserChangedBlocked && item != currentItem()) diff --git a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.h b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.h index 7bc96b69..194ce627 100644 --- a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.h +++ b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.h @@ -115,7 +115,7 @@ Q_SIGNALS: void collapsed(QtBrowserItem *item); void expanded(QtBrowserItem *item); - + void hoverPropertyChanged(QtBrowserItem *item); protected: virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); virtual void itemRemoved(QtBrowserItem *item); @@ -130,7 +130,8 @@ private: Q_PRIVATE_SLOT(d_func(), void slotCollapsed(const QModelIndex &)) Q_PRIVATE_SLOT(d_func(), void slotExpanded(const QModelIndex &)) Q_PRIVATE_SLOT(d_func(), void slotCurrentBrowserItemChanged(QtBrowserItem *)) - Q_PRIVATE_SLOT(d_func(), void slotCurrentTreeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentTreeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void onHoverPropertyChanged(QtBrowserItem *)) }; diff --git a/CMakeLists.txt b/CMakeLists.txt index 67029d5b..76fc15f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,16 @@ project(nextpnr) option(BUILD_GUI "Build GUI" ON) option(BUILD_PYTHON "Build Python Integration" ON) option(BUILD_TESTS "Build GUI" OFF) +option(COVERAGE "Add code coverage info" OFF) +option(STATIC_BUILD "Create static build" OFF) + +set(link_param "") +if (STATIC_BUILD) + set(Boost_USE_STATIC_LIBS ON) + if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(link_param "-static") + endif() +endif() # List of families to build set(FAMILIES generic ice40 ecp5 xc7) @@ -43,7 +53,11 @@ set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O3 -g") endif() set(CMAKE_DEFIN) -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/3rdparty/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/3rdparty/sanitizers-cmake/cmake;." ${CMAKE_MODULE_PATH}) + +if (COVERAGE) + include(CodeCoverage) +endif() if(NOT DEFINED CMAKE_SUPPRESS_DEVELOPER_WARNINGS) set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE INTERNAL "No dev warnings") @@ -185,6 +199,16 @@ foreach (family ${ARCH}) # Add any new per-architecture targets here if (BUILD_TESTS) + if (COVERAGE) + APPEND_COVERAGE_COMPILER_FLAGS() + set(COVERAGE_LCOV_EXCLUDES '/usr/include/*' '3rdparty/*' 'generated/*' 'bba/*' 'tests/*') + SETUP_TARGET_FOR_COVERAGE_LCOV( + NAME ${family}-coverage + EXECUTABLE nextpnr-${family}-test + DEPENDENCIES nextpnr-${family}-test + ) + endif() + aux_source_directory(tests/${family}/ ${ufamily}_TEST_FILES) if (BUILD_GUI) aux_source_directory(tests/gui/ GUI_TEST_FILES) @@ -211,7 +235,7 @@ foreach (family ${ARCH}) # Include family-specific source files to all family targets and set defines appropriately target_include_directories(${target} PRIVATE ${family}/ ${CMAKE_CURRENT_BINARY_DIR}/generated/) target_compile_definitions(${target} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family}) - target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES}) + target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES} ${link_param}) if (NOT MSVC) target_link_libraries(${target} LINK_PUBLIC pthread) endif() diff --git a/CodeCoverage.cmake b/CodeCoverage.cmake new file mode 100644 index 00000000..10285228 --- /dev/null +++ b/CodeCoverage.cmake @@ -0,0 +1,303 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt: +# include(CodeCoverage) +# +# 3. Append necessary compiler flags: +# APPEND_COVERAGE_COMPILER_FLAGS() +# +# 4. If you need to exclude additional directories from the report, specify them +# using the COVERAGE_LCOV_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE_LCOV. +# Example: +# set(COVERAGE_LCOV_EXCLUDES 'dir1/*' 'dir2/*') +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# + +include(CMakeParseArguments) + +# Check prereqs +find_program( GCOV_PATH gcov ) +find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) +find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( SIMPLE_PYTHON_EXECUTABLE python ) + +if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") +endif() # NOT GCOV_PATH + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() +elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") +endif() + +set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage" + CACHE INTERNAL "") + +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE ) +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE ) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE ) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE ) +mark_as_advanced( + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") +endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") +endif() + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# SETUP_TARGET_FOR_COVERAGE_LCOV( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# ) +function(SETUP_TARGET_FOR_COVERAGE_LCOV) + + set(options NONE) + set(oneValueArgs NAME) + set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() # NOT LCOV_PATH + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() # NOT GENHTML_PATH + + # Setup target + add_custom_target(${Coverage_NAME} + + # Cleanup lcov + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} -directory . --zerocounters + # Create baseline to make sure untouched files show up in the report + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base + + # Run tests + COMMAND ${Coverage_EXECUTABLE} + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info + # add baseline counters + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_LCOV_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned + COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned + COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # SETUP_TARGET_FOR_COVERAGE_LCOV + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# SETUP_TARGET_FOR_COVERAGE_GCOVR_XML( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# ) +function(SETUP_TARGET_FOR_COVERAGE_GCOVR_XML) + + set(options NONE) + set(oneValueArgs NAME) + set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT SIMPLE_PYTHON_EXECUTABLE) + message(FATAL_ERROR "python not found! Aborting...") + endif() # NOT SIMPLE_PYTHON_EXECUTABLE + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDES "-e") + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} + + # Running gcovr + COMMAND ${GCOVR_PATH} --xml + -r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) + +endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_XML + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# ) +function(SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML) + + set(options NONE) + set(oneValueArgs NAME) + set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT SIMPLE_PYTHON_EXECUTABLE) + message(FATAL_ERROR "python not found! Aborting...") + endif() # NOT SIMPLE_PYTHON_EXECUTABLE + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDES "-e") + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} + + # Create folder + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + + # Running gcovr + COMMAND ${GCOVR_PATH} --html --html-details + -r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}/index.html + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML + +function(APPEND_COVERAGE_COMPILER_FLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # APPEND_COVERAGE_COMPILER_FLAGS diff --git a/README.md b/README.md index e9f197cd..010acd8b 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,13 @@ cmake -DARCH=ice40 -DCMAKE_BUILD_TYPE=Debug -DBUILD_PYTHON=OFF -DBUILD_GUI=OFF - make -j$(nproc) ``` +To make static build relase for iCE40 architecture use the following: + +``` +cmake -DARCH=ice40 -DBUILD_PYTHON=OFF -DBUILD_GUI=OFF -DSTATIC_BUILD=ON . +make -j$(nproc) +``` + Notes for developers -------------------- @@ -145,6 +152,9 @@ Testing - `-DSANITIZE_THREAD=ON` - `-DSANITIZE_UNDEFINED=ON` - Running valgrind example `valgrind --leak-check=yes --tool=memcheck ./nextpnr-ice40 --json ice40/blinky.json` +- Running tests with code coverage use `-DBUILD_TESTS=ON -DCOVERAGE` and after `make` run `make ice40-coverage` +- After that open `ice40-coverage/index.html` in your browser to view the coverage report +- Note that `lcov` is needed in order to generate reports Links and references -------------------- @@ -167,6 +177,6 @@ Links and references - [Arachne PNR](https://github.com/cseed/arachne-pnr) - [VPR/VTR](https://verilogtorouting.org/) - [SymbiFlow](https://github.com/SymbiFlow/symbiflow-arch-defs) -- [Gaffe](https://github.com/kc8apf/gaffe) +- [Gaffe](https://github.com/gaffe-logic/gaffe) - [KinglerPAR](https://github.com/rqou/KinglerPAR) diff --git a/common/command.cc b/common/command.cc index d5639e9a..ab0c92f2 100644 --- a/common/command.cc +++ b/common/command.cc @@ -144,7 +144,7 @@ void CommandHandler::setupContext(Context *ctx) } if (vm.count("cstrweight")) { - // ctx->placer_constraintWeight = vm["cstrweight"].as(); + settings->set("placer1/constraintWeight", vm["cstrweight"].as()); } if (vm.count("freq")) { @@ -261,6 +261,7 @@ int CommandHandler::exec() } else { ctx = createContext(); } + settings = std::unique_ptr(new Settings(ctx.get())); setupContext(ctx.get()); setupArchContext(ctx.get()); return executeMain(std::move(ctx)); diff --git a/common/command.h b/common/command.h index 13d5a250..900cf74b 100644 --- a/common/command.h +++ b/common/command.h @@ -24,6 +24,7 @@ #include #include "nextpnr.h" #include "project.h" +#include "settings.h" NEXTPNR_NAMESPACE_BEGIN @@ -56,6 +57,7 @@ class CommandHandler protected: po::variables_map vm; ArchArgs chipArgs; + std::unique_ptr settings; private: po::options_description options; diff --git a/common/log.cc b/common/log.cc index df5430bb..e30449ad 100644 --- a/common/log.cc +++ b/common/log.cc @@ -46,6 +46,18 @@ void (*log_error_atexit)() = NULL; // static bool next_print_log = false; static int log_newline_count = 0; +std::string stringf(const char *fmt, ...) +{ + std::string string; + va_list ap; + + va_start(ap, fmt); + string = vstringf(fmt, ap); + va_end(ap); + + return string; +} + std::string vstringf(const char *fmt, va_list ap) { std::string string; diff --git a/common/log.h b/common/log.h index 74b9f0f5..b5fddf53 100644 --- a/common/log.h +++ b/common/log.h @@ -51,6 +51,9 @@ extern bool log_quiet_warnings; extern std::string log_last_error; extern void (*log_error_atexit)(); +std::string stringf(const char *fmt, ...); +std::string vstringf(const char *fmt, va_list ap); + extern std::ostream clog; void log(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2)); void log_always(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2)); diff --git a/common/placer1.cc b/common/placer1.cc index 88f2fc47..363b4d58 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -23,6 +23,7 @@ #include "placer1.h" #include +#include #include #include #include @@ -108,14 +109,12 @@ class SAPlacer if (bel_type != cell->type) { log_error("Bel \'%s\' of type \'%s\' does not match cell " "\'%s\' of type \'%s\'\n", - loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), - cell->type.c_str(ctx)); + loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx)); } if (!ctx->isValidBelForCell(cell, bel)) { log_error("Bel \'%s\' of type \'%s\' is not valid for cell " "\'%s\' of type \'%s\'\n", - loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), - cell->type.c_str(ctx)); + loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx)); } ctx->bindBel(bel, cell, STRENGTH_USER); @@ -492,6 +491,8 @@ class SAPlacer std::vector old_udata; }; +Placer1Cfg::Placer1Cfg(Context *ctx) : Settings(ctx) { constraintWeight = get("placer1/constraintWeight", 10); } + bool placer1(Context *ctx, Placer1Cfg cfg) { try { diff --git a/common/placer1.h b/common/placer1.h index d8f64b84..55db1fa5 100644 --- a/common/placer1.h +++ b/common/placer1.h @@ -20,12 +20,14 @@ #define PLACE_H #include "nextpnr.h" +#include "settings.h" NEXTPNR_NAMESPACE_BEGIN -struct Placer1Cfg +struct Placer1Cfg : public Settings { - float constraintWeight = 10; + Placer1Cfg(Context *ctx); + float constraintWeight; }; extern bool placer1(Context *ctx, Placer1Cfg cfg); diff --git a/common/project.cc b/common/project.cc index 949f6878..b0ebe961 100644 --- a/common/project.cc +++ b/common/project.cc @@ -18,6 +18,7 @@ */ #include "project.h" +#include #include #include #include @@ -64,17 +65,36 @@ void ProjectHandler::save(Context *ctx, std::string filename) root.put("project.name", boost::filesystem::basename(filename)); root.put("project.arch.name", ctx->archId().c_str(ctx)); root.put("project.arch.type", ctx->archArgsToId(ctx->archArgs()).c_str(ctx)); - std::string fn = ctx->settings[ctx->id("project/input/json")]; + std::string fn = ctx->settings[ctx->id("input/json")]; root.put("project.input.json", make_relative(fn, proj.parent_path()).string()); root.put("project.params.freq", int(ctx->target_freq / 1e6)); root.put("project.params.seed", ctx->rngstate); saveArch(ctx, root, proj.parent_path().string()); + for (auto const &item : ctx->settings) { + std::string path = "project.settings."; + path += item.first.c_str(ctx); + std::replace(path.begin(), path.end(), '/', '.'); + root.put(path, item.second); + } pt::write_json(f, root); } catch (...) { log_error("Error saving project file.\n"); } } +void addSettings(Context *ctx, std::string path, pt::ptree sub) +{ + for (pt::ptree::value_type &v : sub) { + const std::string &key = v.first; + const boost::property_tree::ptree &subtree = v.second; + if (subtree.empty()) { + ctx->settings.emplace(ctx->id(path + key), subtree.get_value().c_str()); + } else { + addSettings(ctx, path + key + "/", subtree); + } + } +} + std::unique_ptr ProjectHandler::load(std::string filename) { std::unique_ptr ctx; @@ -110,6 +130,10 @@ std::unique_ptr ProjectHandler::load(std::string filename) if (params.count("seed")) ctx->rngseed(params.get("seed")); } + if (project.count("settings")) { + addSettings(ctx.get(), "", project.get_child("settings")); + } + loadArch(ctx.get(), root, proj.parent_path().string()); } catch (...) { log_error("Error loading project file.\n"); diff --git a/common/router1.cc b/common/router1.cc index 0733a61e..5cd4414c 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -682,6 +682,14 @@ void cleanupReroute(Context *ctx, const Router1Cfg &cfg, RipupScoreboard &scores NEXTPNR_NAMESPACE_BEGIN +Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) +{ + maxIterCnt = get("router1/maxIterCnt", 200); + cleanupReroute = get("router1/cleanupReroute", true); + fullCleanupReroute = get("router1/fullCleanupReroute", true); + useEstimate = get("router1/useEstimate", true); +} + bool router1(Context *ctx, const Router1Cfg &cfg) { try { @@ -953,7 +961,7 @@ bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *del std::unordered_map *route, bool useEstimate) { RipupScoreboard scores; - Router1Cfg cfg; + Router1Cfg cfg(this); cfg.useEstimate = useEstimate; Router router(this, cfg, scores, src_wire, dst_wire); diff --git a/common/router1.h b/common/router1.h index 0380adc2..a184cbe7 100644 --- a/common/router1.h +++ b/common/router1.h @@ -21,15 +21,18 @@ #define ROUTER1_H #include "nextpnr.h" +#include "settings.h" NEXTPNR_NAMESPACE_BEGIN -struct Router1Cfg +struct Router1Cfg : Settings { - int maxIterCnt = 200; - bool cleanupReroute = true; - bool fullCleanupReroute = true; - bool useEstimate = true; + Router1Cfg(Context *ctx); + + int maxIterCnt; + bool cleanupReroute; + bool fullCleanupReroute; + bool useEstimate; }; extern bool router1(Context *ctx, const Router1Cfg &cfg); diff --git a/common/settings.h b/common/settings.h new file mode 100644 index 00000000..e1f1166a --- /dev/null +++ b/common/settings.h @@ -0,0 +1,63 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include +#include "log.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +class Settings +{ + public: + explicit Settings(Context *ctx) : ctx(ctx) {} + + template T get(const char *name, T defaultValue) + { + try { + IdString id = ctx->id(name); + auto pair = ctx->settings.emplace(id, std::to_string(defaultValue)); + if (!pair.second) { + return boost::lexical_cast(pair.first->second); + } + + } catch (boost::bad_lexical_cast &) { + log_error("Problem reading setting %s, using default value\n", name); + } + return defaultValue; + } + + template void set(const char *name, T value) + { + IdString id = ctx->id(name); + auto pair = ctx->settings.emplace(id, std::to_string(value)); + if (!pair.second) { + ctx->settings[pair.first->first] = value; + } + } + + private: + Context *ctx; +}; + +NEXTPNR_NAMESPACE_END + +#endif // SETTINGS_H diff --git a/common/timing.cc b/common/timing.cc index 5e45f8a2..62697353 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -410,12 +410,13 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) log_info("estimated Fmax = %.2f MHz\n", 1e3 / ctx->getDelayNS(default_slack - min_slack)); if (print_histogram && slack_histogram.size() > 0) { - constexpr unsigned num_bins = 20; + unsigned num_bins = 20; unsigned bar_width = 60; auto min_slack = slack_histogram.begin()->first; auto max_slack = slack_histogram.rbegin()->first; - auto bin_size = (max_slack - min_slack) / num_bins; - std::vector bins(num_bins + 1); + auto bin_size = std::max(1u, (max_slack - min_slack) / num_bins); + num_bins = std::min((max_slack - min_slack) / bin_size, num_bins) + 1; + std::vector bins(num_bins); unsigned max_freq = 0; for (const auto &i : slack_histogram) { auto &bin = bins[(i.first - min_slack) / bin_size]; @@ -428,7 +429,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) log_info("Slack histogram:\n"); log_info(" legend: * represents %d endpoint(s)\n", max_freq / bar_width); log_info(" + represents [1,%d) endpoint(s)\n", max_freq / bar_width); - for (unsigned i = 0; i < bins.size(); ++i) + for (unsigned i = 0; i < num_bins; ++i) log_info("[%6d, %6d) |%s%c\n", min_slack + bin_size * i, min_slack + bin_size * (i + 1), std::string(bins[i] * bar_width / max_freq, '*').c_str(), (bins[i] * bar_width) % max_freq > 0 ? '+' : ' '); diff --git a/docs/archapi.md b/docs/archapi.md index 473cdd2e..73443c15 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -151,6 +151,11 @@ Return a list of all bels on the device. Return the type of a given bel. +### const\_range\\> getBelAttrs(BelId bel) const + +Return the attributes for that bel. Bel attributes are only informal. They are displayed by the GUI but are otherwise +unused. An implementation may simply return an empty range. + ### WireId getBelPinWire(BelId bel, IdString pin) const Return the wire connected to the given bel pin. @@ -180,6 +185,11 @@ Get the type of a wire. The wire type is purely informal and isn't used by any of the core algorithms. Implementations may simply return `IdString()`. +### const\_range\\> getWireAttrs(WireId wire) const + +Return the attributes for that wire. Wire attributes are only informal. They are displayed by the GUI but are otherwise +unused. An implementation may simply return an empty range. + ### uint32\_t getWireChecksum(WireId wire) const Return a (preferably unique) number that represents this wire. This is used in design state checksum calculations. @@ -242,6 +252,11 @@ Get the name for a pip. (Pip names must be unique.) Get the type of a pip. Pip types are purely informal and implementations may simply return `IdString()`. +### const\_range\\> getPipAttrs(PipId pip) const + +Return the attributes for that pip. Pip attributes are only informal. They are displayed by the GUI but are otherwise +unused. An implementation may simply return an empty range. + ### Loc getPipLocation(PipId pip) const Get the X/Y/Z location of a given pip. Pip locations do not need to be unique, and in most cases they aren't. So diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 4358fdaf..82ebfba1 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -46,7 +46,9 @@ static std::tuple split_identifier_name(const std::string void IdString::initialize_arch(const BaseCtx *ctx) { #define X(t) initialize_add(ctx, #t, ID_##t); + #include "constids.inc" + #undef X } @@ -92,6 +94,8 @@ Arch::Arch(ArchArgs args) : args(args) if (!package_info) log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str()); + + bel_to_cell.resize(chip_info->height * chip_info->width * max_loc_bels, nullptr); } // ----------------------------------------------------------------------- @@ -367,7 +371,7 @@ BelId Arch::getBelByLocation(Loc loc) const delay_t Arch::estimateDelay(WireId src, WireId dst) const { - return 200 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y)); + return 100 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y)); } delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const @@ -376,20 +380,16 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const auto driver_loc = getBelLocation(driver.cell->bel); auto sink_loc = getBelLocation(sink.cell->bel); - return 200 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y)); + return 100 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y)); } bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } // ----------------------------------------------------------------------- -bool Arch::place() { return placer1(getCtx(), Placer1Cfg()); } +bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } -bool Arch::route() -{ - Router1Cfg cfg; - return router1(getCtx(), cfg); -} +bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } // ----------------------------------------------------------------------- @@ -436,7 +436,7 @@ DecalXY Arch::getBelDecal(BelId bel) const decalxy.decal.type = DecalId::TYPE_BEL; decalxy.decal.location = bel.location; decalxy.decal.z = bel.index; - decalxy.decal.active = bel_to_cell.count(bel) && (bel_to_cell.at(bel) != nullptr); + decalxy.decal.active = (bel_to_cell.at(getBelFlatIndex(bel)) != nullptr); return decalxy; } @@ -450,12 +450,122 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { - return false; + // Data for -8 grade + if (cell->type == id_TRELLIS_SLICE) { + bool has_carry = str_or_default(cell->params, id("MODE"), "LOGIC") == "CCU2"; + if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0) { + if (toPort == id_F0) { + delay.delay = 180; + return true; + } else if (has_carry && toPort == id_F1) { + delay.delay = 500; + return true; + } else if (has_carry && toPort == id_FCO) { + delay.delay = 355; + return true; + } else if (toPort == id_OFX0) { + delay.delay = 306; + return true; + } + } + + if (fromPort == id_A1 || fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1) { + if (toPort == id_F1) { + delay.delay = 180; + return true; + } else if (has_carry && toPort == id_FCO) { + delay.delay = 355; + return true; + } else if (toPort == id_OFX0) { + delay.delay = 306; + return true; + } + } + + if (has_carry && fromPort == id_FCI) { + if (toPort == id_F0) { + delay.delay = 328; + return true; + } else if (toPort == id_F1) { + delay.delay = 349; + return true; + } else if (toPort == id_FCO) { + delay.delay = 56; + return true; + } + } + + if (fromPort == id_CLK && (toPort == id_Q0 || toPort == id_Q1)) { + delay.delay = 395; + return true; + } + + if (fromPort == id_M0 && toPort == id_OFX0) { + delay.delay = 193; + return true; + } + + if (fromPort == id_WCK && (toPort == id_F0 || toPort == id_F1)) { + delay.delay = 717; + return true; + } + + if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) || + (fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) || + (fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) || + (fromPort == id_D0 && toPort == id_WADO0) || (fromPort == id_D1 && toPort == id_WDO2)) { + delay.delay = 0; + return true; + } + return false; + } else { + return false; + } } TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const { - return TMG_IGNORE; + if (cell->type == id_TRELLIS_SLICE) { + int sd0 = int_or_default(cell->params, id("REG0_SD"), 0), sd1 = int_or_default(cell->params, id("REG1_SD"), 0); + if (port == id_CLK || port == id_WCK) + return TMG_CLOCK_INPUT; + if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 || + port == id_D0 || port == id_D1 || port == id_FCI || port == id_FXA || port == id_FXB) + return TMG_COMB_INPUT; + if (port == id_F0 || port == id_F1 || port == id_FCO || port == id_OFX0 || port == id_OFX1) + return TMG_COMB_OUTPUT; + if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 1 && port == id_M0) || + (sd1 == 1 && port == id_M1)) { + clockPort = id_CLK; + return TMG_REGISTER_INPUT; + } + if (port == id_M0 || port == id_M1) + return TMG_COMB_INPUT; + if (port == id_Q0 || port == id_Q1) { + clockPort = id_CLK; + return TMG_REGISTER_OUTPUT; + } + + if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3 || port == id_WADO0 || + port == id_WADO1 || port == id_WADO2 || port == id_WADO3) + return TMG_COMB_OUTPUT; + + if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || + port == id_WAD3 || port == id_WRE) { + clockPort = id_WCK; + return TMG_REGISTER_INPUT; + } + + NPNR_ASSERT_FALSE_STR("no timing type for slice port '" + port.str(this) + "'"); + } else if (cell->type == id_TRELLIS_IO) { + if (port == id_T || port == id_I) + return TMG_ENDPOINT; + if (port == id_O) + return TMG_STARTPOINT; + return TMG_IGNORE; + } else { + NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'"); + } } std::vector> Arch::getTilesAtLocation(int row, int col) diff --git a/ecp5/arch.h b/ecp5/arch.h index 36792625..da86d4e2 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -404,7 +404,7 @@ struct Arch : BaseCtx mutable std::unordered_map wire_by_name; mutable std::unordered_map pip_by_name; - std::unordered_map bel_to_cell; + std::vector bel_to_cell; std::unordered_map wire_to_net; std::unordered_map pip_to_net; @@ -443,11 +443,18 @@ struct Arch : BaseCtx uint32_t getBelChecksum(BelId bel) const { return bel.index; } + const int max_loc_bels = 20; + int getBelFlatIndex(BelId bel) const + { + return (bel.location.y * chip_info->width + bel.location.x) * max_loc_bels + bel.index; + } + void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) { NPNR_ASSERT(bel != BelId()); - NPNR_ASSERT(bel_to_cell[bel] == nullptr); - bel_to_cell[bel] = cell; + int idx = getBelFlatIndex(bel); + NPNR_ASSERT(bel_to_cell.at(idx) == nullptr); + bel_to_cell[idx] = cell; cell->bel = bel; cell->belStrength = strength; refreshUiBel(bel); @@ -456,10 +463,11 @@ struct Arch : BaseCtx void unbindBel(BelId bel) { NPNR_ASSERT(bel != BelId()); - NPNR_ASSERT(bel_to_cell[bel] != nullptr); - bel_to_cell[bel]->bel = BelId(); - bel_to_cell[bel]->belStrength = STRENGTH_NONE; - bel_to_cell[bel] = nullptr; + int idx = getBelFlatIndex(bel); + NPNR_ASSERT(bel_to_cell.at(idx) != nullptr); + bel_to_cell[idx]->bel = BelId(); + bel_to_cell[idx]->belStrength = STRENGTH_NONE; + bel_to_cell[idx] = nullptr; refreshUiBel(bel); } @@ -480,25 +488,19 @@ struct Arch : BaseCtx bool checkBelAvail(BelId bel) const { NPNR_ASSERT(bel != BelId()); - return bel_to_cell.find(bel) == bel_to_cell.end() || bel_to_cell.at(bel) == nullptr; + return bel_to_cell[getBelFlatIndex(bel)] == nullptr; } CellInfo *getBoundBelCell(BelId bel) const { NPNR_ASSERT(bel != BelId()); - if (bel_to_cell.find(bel) == bel_to_cell.end()) - return nullptr; - else - return bel_to_cell.at(bel); + return bel_to_cell[getBelFlatIndex(bel)]; } CellInfo *getConflictingBelCell(BelId bel) const { NPNR_ASSERT(bel != BelId()); - if (bel_to_cell.find(bel) == bel_to_cell.end()) - return nullptr; - else - return bel_to_cell.at(bel); + return bel_to_cell[getBelFlatIndex(bel)]; } BelRange getBels() const @@ -522,6 +524,12 @@ struct Arch : BaseCtx return id; } + std::vector> getBelAttrs(BelId) const + { + std::vector> ret; + return ret; + } + WireId getBelPinWire(BelId bel, IdString pin) const; BelPinRange getWireBelPins(WireId wire) const @@ -553,6 +561,12 @@ struct Arch : BaseCtx IdString getWireType(WireId wire) const { return IdString(); } + std::vector> getWireAttrs(WireId) const + { + std::vector> ret; + return ret; + } + uint32_t getWireChecksum(WireId wire) const { return wire.index; } void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) @@ -633,6 +647,12 @@ struct Arch : BaseCtx IdString getPipType(PipId pip) const { return IdString(); } + std::vector> getPipAttrs(PipId) const + { + std::vector> ret; + return ret; + } + uint32_t getPipChecksum(PipId pip) const { return pip.index; } void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) @@ -725,7 +745,7 @@ struct Arch : BaseCtx { DelayInfo delay; NPNR_ASSERT(pip != PipId()); - delay.delay = locInfo(pip)->pip_data[pip.index].delay * 100; + delay.delay = locInfo(pip)->pip_data[pip.index].delay; return delay; } @@ -848,6 +868,8 @@ struct Arch : BaseCtx // Helper function for above bool slicesCompatible(const std::vector &cells) const; + void assignArchInfo(); + std::vector> getTilesAtLocation(int row, int col); std::string getTileByTypeAndLocation(int row, int col, std::string type) const { diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index 83af6b5a..55fff73d 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -35,26 +35,26 @@ bool Arch::slicesCompatible(const std::vector &cells) const { // TODO: allow different LSR/CLK and MUX/SRMODE settings once // routing details are worked out - NetInfo *clk_sig = nullptr, *lsr_sig = nullptr; - std::string CLKMUX, LSRMUX, SRMODE; + IdString clk_sig, lsr_sig; + IdString CLKMUX, LSRMUX, SRMODE; bool first = true; for (auto cell : cells) { if (first) { - clk_sig = port_or_nullptr(cell, id_CLK); - lsr_sig = port_or_nullptr(cell, id_LSR); - CLKMUX = str_or_default(cell->params, id_CLKMUX, "CLK"); - LSRMUX = str_or_default(cell->params, id_LSRMUX, "LSR"); - SRMODE = str_or_default(cell->params, id_SRMODE, "CE_OVER_LSR"); + clk_sig = cell->sliceInfo.clk_sig; + lsr_sig = cell->sliceInfo.lsr_sig; + CLKMUX = cell->sliceInfo.clkmux; + LSRMUX = cell->sliceInfo.lsrmux; + SRMODE = cell->sliceInfo.srmode; } else { - if (port_or_nullptr(cell, id_CLK) != clk_sig) + if (cell->sliceInfo.clk_sig != clk_sig) return false; - if (port_or_nullptr(cell, id_LSR) != lsr_sig) + if (cell->sliceInfo.lsr_sig != lsr_sig) return false; - if (str_or_default(cell->params, id_CLKMUX, "CLK") != CLKMUX) + if (cell->sliceInfo.clkmux != CLKMUX) return false; - if (str_or_default(cell->params, id_LSRMUX, "LSR") != LSRMUX) + if (cell->sliceInfo.lsrmux != LSRMUX) return false; - if (str_or_default(cell->params, id_SRMODE, "CE_OVER_LSR") != SRMODE) + if (cell->sliceInfo.srmode != SRMODE) return false; } first = false; diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index 1493c691..c4e1413f 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -139,6 +139,10 @@ struct ArchNetInfo }; struct ArchCellInfo { + struct + { + IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode; + } sliceInfo; }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 786f543e..a2077204 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -536,10 +536,34 @@ bool Arch::pack() log_break(); Ecp5Packer(ctx).pack(); log_info("Checksum: 0x%08x\n", ctx->checksum()); + assignArchInfo(); return true; } catch (log_execution_error_exception) { + assignArchInfo(); return false; } } +void Arch::assignArchInfo() +{ + for (auto cell : sorted(cells)) { + CellInfo *ci = cell.second; + if (ci->type == id_TRELLIS_SLICE) { + if (ci->ports.count(id_CLK) && ci->ports[id_CLK].net != nullptr) + ci->sliceInfo.clk_sig = ci->ports[id_CLK].net->name; + else + ci->sliceInfo.clk_sig = IdString(); + + if (ci->ports.count(id_LSR) && ci->ports[id_LSR].net != nullptr) + ci->sliceInfo.lsr_sig = ci->ports[id_LSR].net->name; + else + ci->sliceInfo.lsr_sig = IdString(); + + ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK")); + ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR")); + ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); + } + } +} + NEXTPNR_NAMESPACE_END diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 7c7c9e15..de8e9958 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -130,11 +130,54 @@ def process_loc_globals(chip): tapdrv = chip.global_data.get_tap_driver(y, x) global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col) +def get_wire_type(name): + if "H00" in name or "V00" in name: + return "X0" + if "H01" in name or "V01" in name: + return "X1" + if "H02" in name or "V02" in name: + return "X2" + if "H06" in name or "V06" in name: + return "X6" + if "_SLICE" in name or "_EBR" in name: + return "SLICE" + return "LOCAL" + +def get_pip_delay(wire_from, wire_to): + # ECP5 timings WIP!!! + type_from = get_wire_type(wire_from) + type_to = get_wire_type(wire_to) + if type_from == "X2" and type_to == "X2": + return 170 + if type_from == "SLICE" or type_to == "SLICE": + return 205 + if type_from in ("LOCAL", "X0") and type_to in ("X1", "X2", "X6"): + return 90 + if type_from == "X6" or type_to == "X6": + return 200 + if type_from in ("X1", "X2", "X6") and type_to in ("LOCAL", "X0"): + return 90 + return 100 + + + def write_database(dev_name, chip, ddrg, endianness): def write_loc(loc, sym_name): bba.u16(loc.x, "%s.x" % sym_name) bba.u16(loc.y, "%s.y" % sym_name) + loctypes = list([_.key() for _ in ddrg.locationTypes]) + loc_with_type = {} + for y in range(0, max_row+1): + for x in range(0, max_col+1): + loc_with_type[loctypes.index(ddrg.typeAtLocation[pytrellis.Location(x, y)])] = (x, y) + + def get_wire_name(arc_loctype, rel, idx): + loc = loc_with_type[arc_loctype] + lt = ddrg.typeAtLocation[pytrellis.Location(loc[0] + rel.x, loc[1] + rel.y)] + wire = ddrg.locationTypes[lt].wires[idx] + return ddrg.to_str(wire.name) + bba = BinaryBlobAssembler() bba.pre('#include "nextpnr.h"') bba.pre('NEXTPNR_NAMESPACE_BEGIN') @@ -142,7 +185,6 @@ def write_database(dev_name, chip, ddrg, endianness): bba.push("chipdb_blob_%s" % dev_name) bba.r("chip_info", "chip_info") - loctypes = list([_.key() for _ in ddrg.locationTypes]) for idx in range(len(loctypes)): loctype = ddrg.locationTypes[loctypes[idx]] @@ -153,7 +195,7 @@ def write_database(dev_name, chip, ddrg, endianness): write_loc(arc.sinkWire.rel, "dst") bba.u32(arc.srcWire.id, "src_idx") bba.u32(arc.sinkWire.id, "dst_idx") - bba.u32(arc.delay, "delay") # TODO:delay + bba.u32(get_pip_delay(get_wire_name(idx, arc.srcWire.rel, arc.srcWire.id), get_wire_name(idx, arc.sinkWire.rel, arc.sinkWire.id)), "delay") # TODO:delay bba.u16(get_tiletype_index(ddrg.to_str(arc.tiletype)), "tile_type") bba.u8(int(arc.cls), "pip_type") bba.u8(0, "padding") diff --git a/generic/arch.cc b/generic/arch.cc index d306a9ec..583c74d8 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -184,6 +184,21 @@ void Arch::setGroupDecal(GroupId group, DecalXY decalxy) refreshUiGroup(group); } +void Arch::setWireAttr(IdString wire, IdString key, const std::string &value) +{ + wires.at(wire).attrs[key] = value; +} + +void Arch::setPipAttr(IdString pip, IdString key, const std::string &value) +{ + pips.at(pip).attrs[key] = value; +} + +void Arch::setBelAttr(IdString bel, IdString key, const std::string &value) +{ + bels.at(bel).attrs[key] = value; +} + // --------------------------------------------------------------- Arch::Arch(ArchArgs args) : chipName("generic"), args(args) {} @@ -251,6 +266,8 @@ const std::vector &Arch::getBels() const { return bel_ids; } IdString Arch::getBelType(BelId bel) const { return bels.at(bel).type; } +const std::map &Arch::getBelAttrs(BelId bel) const { return bels.at(bel).attrs; } + WireId Arch::getBelPinWire(BelId bel, IdString pin) const { return bels.at(bel).pins.at(pin).wire; } PortType Arch::getBelPinType(BelId bel, IdString pin) const { return bels.at(bel).pins.at(pin).type; } @@ -276,6 +293,8 @@ IdString Arch::getWireName(WireId wire) const { return wire; } IdString Arch::getWireType(WireId wire) const { return wires.at(wire).type; } +const std::map &Arch::getWireAttrs(WireId wire) const { return wires.at(wire).attrs; } + uint32_t Arch::getWireChecksum(WireId wire) const { // FIXME @@ -328,6 +347,8 @@ IdString Arch::getPipName(PipId pip) const { return pip; } IdString Arch::getPipType(PipId pip) const { return pips.at(pip).type; } +const std::map &Arch::getPipAttrs(PipId pip) const { return pips.at(pip).attrs; } + uint32_t Arch::getPipChecksum(PipId wire) const { // FIXME @@ -425,9 +446,9 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay // --------------------------------------------------------------- -bool Arch::place() { return placer1(getCtx(), Placer1Cfg()); } +bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } -bool Arch::route() { return router1(getCtx(), Router1Cfg()); } +bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } // --------------------------------------------------------------- diff --git a/generic/arch.h b/generic/arch.h index 7549a75b..22966e2a 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -32,6 +32,7 @@ struct WireInfo; struct PipInfo { IdString name, type; + std::map attrs; NetInfo *bound_net; WireId srcWire, dstWire; DelayInfo delay; @@ -42,6 +43,7 @@ struct PipInfo struct WireInfo { IdString name, type; + std::map attrs; NetInfo *bound_net; std::vector downhill, uphill, aliases; BelPin uphill_bel_pin; @@ -61,6 +63,7 @@ struct PinInfo struct BelInfo { IdString name, type; + std::map attrs; CellInfo *bound_cell; std::unordered_map pins; DecalXY decalxy; @@ -120,6 +123,10 @@ struct Arch : BaseCtx void setBelDecal(BelId bel, DecalXY decalxy); void setGroupDecal(GroupId group, DecalXY decalxy); + void setWireAttr(IdString wire, IdString key, const std::string &value); + void setPipAttr(IdString pip, IdString key, const std::string &value); + void setBelAttr(IdString bel, IdString key, const std::string &value); + // --------------------------------------------------------------- // Common Arch API. Every arch must provide the following methods. @@ -151,6 +158,7 @@ struct Arch : BaseCtx CellInfo *getConflictingBelCell(BelId bel) const; const std::vector &getBels() const; IdString getBelType(BelId bel) const; + const std::map &getBelAttrs(BelId bel) const; WireId getBelPinWire(BelId bel, IdString pin) const; PortType getBelPinType(BelId bel, IdString pin) const; std::vector getBelPins(BelId bel) const; @@ -158,6 +166,7 @@ struct Arch : BaseCtx WireId getWireByName(IdString name) const; IdString getWireName(WireId wire) const; IdString getWireType(WireId wire) const; + const std::map &getWireAttrs(WireId wire) const; uint32_t getWireChecksum(WireId wire) const; void bindWire(WireId wire, NetInfo *net, PlaceStrength strength); void unbindWire(WireId wire); @@ -171,6 +180,7 @@ struct Arch : BaseCtx PipId getPipByName(IdString name) const; IdString getPipName(PipId pip) const; IdString getPipType(PipId pip) const; + const std::map &getPipAttrs(PipId pip) const; uint32_t getPipChecksum(PipId pip) const; void bindPip(PipId pip, NetInfo *net, PlaceStrength strength); void unbindPip(PipId pip); diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 66df3ca4..92812285 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -109,6 +109,7 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr context, ArchArgs args, connect(designview, &DesignWidget::selected, fpgaView, &FPGAViewWidget::onSelectedArchItem); connect(designview, &DesignWidget::zoomSelected, fpgaView, &FPGAViewWidget::zoomSelected); connect(designview, &DesignWidget::highlight, fpgaView, &FPGAViewWidget::onHighlightGroupChanged); + connect(designview, &DesignWidget::hover, fpgaView, &FPGAViewWidget::onHoverItemChanged); // Click event on device view connect(fpgaView, &FPGAViewWidget::clickedBel, designview, &DesignWidget::onClickedBel); @@ -420,7 +421,7 @@ void BaseMainWindow::disableActions() actionNew->setEnabled(true); actionOpen->setEnabled(true); - if (ctx->settings.find(ctx->id("project/input/json")) != ctx->settings.end()) + if (ctx->settings.find(ctx->id("input/json")) != ctx->settings.end()) actionSave->setEnabled(true); else actionSave->setEnabled(false); diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 896ef071..c49df085 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -30,15 +30,34 @@ NEXTPNR_NAMESPACE_BEGIN +TreeView::TreeView(QWidget *parent) : QTreeView(parent) {} + +TreeView::~TreeView() {} + +void TreeView::mouseMoveEvent(QMouseEvent *event) +{ + QModelIndex index = indexAt(event->pos()); + if (index!=current) { + current = index; + Q_EMIT hoverIndexChanged(index); + } + QTreeView::mouseMoveEvent(event); +} + +void TreeView::leaveEvent(QEvent *event) +{ + Q_EMIT hoverIndexChanged(QModelIndex()); +} + DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), selectionModel(nullptr) { // Add tree view - treeView = new QTreeView(); + treeView = new TreeView(); treeModel = new TreeModel::Model(); treeView->setModel(treeModel); treeView->setContextMenuPolicy(Qt::CustomContextMenu); treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); - + treeView->viewport()->setMouseTracking(true); // Add property view variantManager = new QtVariantPropertyManager(this); readOnlyManager = new QtVariantPropertyManager(this); @@ -50,6 +69,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel propertyEditor->show(); propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu); propertyEditor->treeWidget()->setSelectionMode(QAbstractItemView::ExtendedSelection); + propertyEditor->treeWidget()->viewport()->setMouseTracking(true); searchEdit = new QLineEdit(); searchEdit->setClearButtonEnabled(true); @@ -158,9 +178,11 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel connect(propertyEditor->treeWidget(), &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenuProperty); connect(propertyEditor->treeWidget(), &QTreeWidget::itemDoubleClicked, this, &DesignWidget::onItemDoubleClicked); + connect(propertyEditor, &QtTreePropertyBrowser::hoverPropertyChanged, this, &DesignWidget::onHoverPropertyChanged); - connect(treeView, &QTreeView::customContextMenuRequested, this, &DesignWidget::prepareMenuTree); - connect(treeView, &QTreeView::doubleClicked, this, &DesignWidget::onDoubleClicked); + connect(treeView, &TreeView::customContextMenuRequested, this, &DesignWidget::prepareMenuTree); + connect(treeView, &TreeView::doubleClicked, this, &DesignWidget::onDoubleClicked); + connect(treeView, &TreeView::hoverIndexChanged, this, &DesignWidget::onHoverIndexChanged); selectionModel = treeView->selectionModel(); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &DesignWidget::onSelectionChanged); @@ -388,7 +410,6 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti ElementType type = clickItem->type(); if (type == ElementType::NONE) return; - std::vector decals; addToHistory(index); @@ -411,6 +432,11 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->nameOf(ctx->getConflictingBelCell(bel)), ElementType::CELL); + QtProperty *attrsItem = addSubGroup(topItem, "Attributes"); + for (auto &item : ctx->getBelAttrs(bel)) { + addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str()); + } + QtProperty *belpinsItem = addSubGroup(topItem, "Ports"); for (const auto &item : ctx->getBelPins(bel)) { QtProperty *portInfoItem = addSubGroup(belpinsItem, item.c_str(ctx)); @@ -433,6 +459,11 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingWireNet(wire)), ElementType::NET); + QtProperty *attrsItem = addSubGroup(topItem, "Attributes"); + for (auto &item : ctx->getWireAttrs(wire)) { + addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str()); + } + DelayInfo delay = ctx->getWireDelay(wire); QtProperty *delayItem = addSubGroup(topItem, "Delay"); @@ -492,6 +523,11 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti addProperty(topItem, QVariant::String, "Dest Wire", ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx), ElementType::WIRE); + QtProperty *attrsItem = addSubGroup(topItem, "Attributes"); + for (auto &item : ctx->getPipAttrs(pip)) { + addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str()); + } + DelayInfo delay = ctx->getPipDelay(pip); QtProperty *delayItem = addSubGroup(topItem, "Delay"); @@ -785,4 +821,35 @@ void DesignWidget::onSearchInserted() if (currentSearchIndexes.size() > 0 && currentIndex < currentSearchIndexes.size()) selectionModel->setCurrentIndex(currentSearchIndexes.at(currentIndex), QItemSelectionModel::ClearAndSelect); } + +void DesignWidget::onHoverIndexChanged(QModelIndex index) +{ + if (index.isValid()) { + TreeModel::Item *item = treeModel->nodeFromIndex(index); + if (item->type() != ElementType::NONE) { + Q_EMIT hover(getDecals(item->type(), item->id()).at(0)); + return; + } + } + Q_EMIT hover(DecalXY()); +} + +void DesignWidget::onHoverPropertyChanged(QtBrowserItem *item) +{ + if (item!=nullptr) { + QtProperty *selectedProperty = item->property(); + ElementType type = getElementTypeByName(selectedProperty->propertyId()); + if (type != ElementType::NONE) { + IdString value = ctx->id(selectedProperty->valueText().toStdString()); + if (value!=IdString()) { + auto node = treeModel->nodeForIdType(type, value); + if (node) { + Q_EMIT hover(getDecals((*node)->type(), (*node)->id()).at(0)); + return; + } + } + } + } + Q_EMIT hover(DecalXY()); +} NEXTPNR_NAMESPACE_END diff --git a/gui/designwidget.h b/gui/designwidget.h index c78d7232..91da556a 100644 --- a/gui/designwidget.h +++ b/gui/designwidget.h @@ -22,6 +22,7 @@ #include #include +#include #include "nextpnr.h" #include "qtgroupboxpropertybrowser.h" #include "qtpropertymanager.h" @@ -31,6 +32,22 @@ NEXTPNR_NAMESPACE_BEGIN +class TreeView : public QTreeView +{ + Q_OBJECT + + public: + explicit TreeView(QWidget *parent = 0); + ~TreeView(); + void mouseMoveEvent(QMouseEvent *event) override; + void leaveEvent(QEvent *event) override; + + Q_SIGNALS: + void hoverIndexChanged(QModelIndex index); + private: + QModelIndex current; +}; + class DesignWidget : public QWidget { Q_OBJECT @@ -55,6 +72,7 @@ class DesignWidget : public QWidget Q_SIGNALS: void selected(std::vector decal, bool keep); void highlight(std::vector decal, int group); + void hover(DecalXY decal); void zoomSelected(); private Q_SLOTS: @@ -64,6 +82,8 @@ class DesignWidget : public QWidget void onItemDoubleClicked(QTreeWidgetItem *item, int column); void onDoubleClicked(const QModelIndex &index); void onSearchInserted(); + void onHoverIndexChanged(QModelIndex index); + void onHoverPropertyChanged(QtBrowserItem *item); public Q_SLOTS: void newContext(Context *ctx); void updateTree(); @@ -74,7 +94,7 @@ class DesignWidget : public QWidget private: Context *ctx; - QTreeView *treeView; + TreeView *treeView; QItemSelectionModel *selectionModel; TreeModel::Model *treeModel; QLineEdit *searchEdit; diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 66739b28..53ee7282 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -240,9 +240,10 @@ void FPGAViewWidget::populateQuadTree(RendererData *data, const DecalXY &decal, continue; } + bool res = true; if (el.type == GraphicElement::TYPE_BOX) { // Boxes are bounded by themselves. - data->qt->insert(PickQuadTree::BoundingBox(x + el.x1, y + el.y1, x + el.x2, y + el.y2), element); + res = data->qt->insert(PickQuadTree::BoundingBox(x + el.x1, y + el.y1, x + el.x2, y + el.y2), element); } if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) { @@ -261,7 +262,11 @@ void FPGAViewWidget::populateQuadTree(RendererData *data, const DecalXY &decal, x1 += 0.01; y1 += 0.01; - data->qt->insert(PickQuadTree::BoundingBox(x0, y0, x1, y1), element); + res = data->qt->insert(PickQuadTree::BoundingBox(x0, y0, x1, y1), element); + } + + if (!res) { + NPNR_ASSERT_FALSE("populateQuadTree: could not insert element"); } } } @@ -450,8 +455,17 @@ void FPGAViewWidget::renderLines(void) NPNR_ASSERT(data->bbGlobal.w() != 0); NPNR_ASSERT(data->bbGlobal.h() != 0); + // Enlarge the bounding box slightly for the picking - when we insert + // elements into it, we enlarge their bounding boxes slightly, so + // we need to give ourselves some sagery margin here. + auto bb = data->bbGlobal; + bb.setX0(bb.x0() - 1); + bb.setY0(bb.y0() - 1); + bb.setX1(bb.x1() + 1); + bb.setY1(bb.y1() + 1); + // Populate picking quadtree. - data->qt = std::unique_ptr(new PickQuadTree(data->bbGlobal)); + data->qt = std::unique_ptr(new PickQuadTree(bb)); for (auto const &decal : belDecals) { populateQuadTree(data.get(), decal.first, PickedElement::fromBel(decal.second, decal.first.x, decal.first.y)); @@ -545,6 +559,14 @@ void FPGAViewWidget::onHighlightGroupChanged(std::vector decals, int gr pokeRenderer(); } +void FPGAViewWidget::onHoverItemChanged(DecalXY decal) +{ + QMutexLocker locked(&rendererArgsLock_); + rendererArgs_->hoveredDecal = decal; + rendererArgs_->changed = true; + pokeRenderer(); +} + void FPGAViewWidget::resizeGL(int width, int height) {} boost::optional FPGAViewWidget::pickElement(float worldx, float worldy) @@ -736,7 +758,7 @@ void FPGAViewWidget::zoomOut() { zoom(-10); } void FPGAViewWidget::zoomToBB(const PickQuadTree::BoundingBox &bb, float margin) { - if (bb.w() < 0.00005 && bb.h() < 0.00005) + if (fabs(bb.w()) < 0.00005 && fabs(bb.h()) < 0.00005) return; viewMove_.setToIdentity(); @@ -767,4 +789,12 @@ void FPGAViewWidget::zoomOutbound() } } +void FPGAViewWidget::leaveEvent(QEvent *event) +{ + QMutexLocker locked(&rendererArgsLock_); + rendererArgs_->hoveredDecal = DecalXY(); + rendererArgs_->changed = true; + pokeRenderer(); +} + NEXTPNR_NAMESPACE_END diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index a40a0153..51a038fc 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -107,11 +107,13 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; QSize minimumSizeHint() const override; QSize sizeHint() const override; + void leaveEvent(QEvent *event) override; public Q_SLOTS: void newContext(Context *ctx); void onSelectedArchItem(std::vector decals, bool keep); void onHighlightGroupChanged(std::vector decals, int group); + void onHoverItemChanged(DecalXY decal); void pokeRenderer(void); void zoomIn(); void zoomOut(); diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index 9fe80717..f270b112 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -191,7 +191,7 @@ void MainWindow::onRouteFinished() { actionSaveAsc->setEnabled(true); } void MainWindow::onProjectLoaded() { - if (ctx->settings.find(ctx->id("project/input/pcf")) != ctx->settings.end()) + if (ctx->settings.find(ctx->id("input/pcf")) != ctx->settings.end()) actionLoadPCF->setEnabled(false); } diff --git a/ice40/arch.cc b/ice40/arch.cc index 7e2dd7f7..3983a24e 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -226,6 +226,15 @@ PortType Arch::getBelPinType(BelId bel, IdString pin) const return PORT_INOUT; } +std::vector> Arch::getBelAttrs(BelId bel) const +{ + std::vector> ret; + + ret.push_back(std::make_pair(id("INDEX"), stringf("%d", bel.index))); + + return ret; +} + WireId Arch::getBelPinWire(BelId bel, IdString pin) const { WireId ret; @@ -331,6 +340,28 @@ IdString Arch::getWireType(WireId wire) const return IdString(); } +std::vector> Arch::getWireAttrs(WireId wire) const +{ + std::vector> ret; + auto &wi = chip_info->wire_data[wire.index]; + + ret.push_back(std::make_pair(id("INDEX"), stringf("%d", wire.index))); + + ret.push_back(std::make_pair(id("GRID_X"), stringf("%d", wi.x))); + ret.push_back(std::make_pair(id("GRID_Y"), stringf("%d", wi.y))); + ret.push_back(std::make_pair(id("GRID_Z"), stringf("%d", wi.z))); + +#if 0 + for (int i = 0; i < wi.num_segments; i++) { + auto &si = wi.segments[i]; + ret.push_back(std::make_pair(id(stringf("segment[%d]", i)), + stringf("X%d/Y%d/%s", si.x, si.y, chip_info->tile_wire_names[si.index].get()))); + } +#endif + + return ret; +} + // ----------------------------------------------------------------------- PipId Arch::getPipByName(IdString name) const @@ -372,6 +403,18 @@ IdString Arch::getPipName(PipId pip) const #endif } +IdString Arch::getPipType(PipId pip) const { return IdString(); } + +std::vector> Arch::getPipAttrs(PipId pip) const +{ + std::vector> ret; + + ret.push_back(std::make_pair(id("INDEX"), stringf("%d", pip.index))); + + return ret; +} + + // ----------------------------------------------------------------------- BelId Arch::getPackagePinBel(const std::string &pin) const @@ -565,18 +608,9 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay // ----------------------------------------------------------------------- -bool Arch::place() -{ - Placer1Cfg cfg; - cfg.constraintWeight = placer_constraintWeight; - return placer1(getCtx(), cfg); -} +bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } -bool Arch::route() -{ - Router1Cfg cfg; - return router1(getCtx(), cfg); -} +bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } // ----------------------------------------------------------------------- @@ -695,13 +729,29 @@ std::vector Arch::getDecalGraphics(DecalId decal) const GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; for (int i = 0; i < n; i++) - gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style); + gfxTileWire(ret, p[i].x, p[i].y, chip_info->width, chip_info->height, GfxTileWireId(p[i].index), style); + +#if 0 + if (ret.empty()) { + WireId wire; + wire.index = decal.index; + log_warning("No gfx decal for wire %s (%d).\n", getWireName(wire).c_str(getCtx()), decal.index); + } +#endif } if (decal.type == DecalId::TYPE_PIP) { const PipInfoPOD &p = chip_info->pip_data[decal.index]; GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_HIDDEN; gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style); + +#if 0 + if (ret.empty()) { + PipId pip; + pip.index = decal.index; + log_warning("No gfx decal for pip %s (%d).\n", getPipName(pip).c_str(getCtx()), decal.index); + } +#endif } if (decal.type == DecalId::TYPE_BEL) { @@ -727,7 +777,7 @@ std::vector Arch::getDecalGraphics(DecalId decal) const GraphicElement el; el.type = GraphicElement::TYPE_BOX; el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; - el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1; + el.x1 = chip_info->bel_data[bel.index].x + lut_swbox_x1; el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2; el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + (4 * chip_info->bel_data[bel.index].z) * logic_cell_pitch; @@ -741,13 +791,43 @@ std::vector Arch::getDecalGraphics(DecalId decal) const GraphicElement el; el.type = GraphicElement::TYPE_BOX; el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; - el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1; + el.x1 = chip_info->bel_data[bel.index].x + lut_swbox_x1; el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2; el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i; el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + i + 7 * logic_cell_pitch; ret.push_back(el); } } + + if (bel_type == id_SB_GB) { + GraphicElement el; + el.type = GraphicElement::TYPE_BOX; + el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; + el.x1 = chip_info->bel_data[bel.index].x + local_swbox_x1 + 0.05; + el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2 - 0.05; + el.y1 = chip_info->bel_data[bel.index].y + main_swbox_y2 - 0.05; + el.y2 = chip_info->bel_data[bel.index].y + main_swbox_y2 - 0.10; + ret.push_back(el); + } + + if (bel_type == id_ICESTORM_PLL || bel_type == id_SB_WARMBOOT) { + GraphicElement el; + el.type = GraphicElement::TYPE_BOX; + el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; + el.x1 = chip_info->bel_data[bel.index].x + local_swbox_x1 + 0.05; + el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2 - 0.05; + el.y1 = chip_info->bel_data[bel.index].y + main_swbox_y2; + el.y2 = chip_info->bel_data[bel.index].y + main_swbox_y2 + 0.05; + ret.push_back(el); + } + +#if 0 + if (ret.empty()) { + BelId bel; + bel.index = decal.index; + log_warning("No gfx decal for bel %s (%d).\n", getBelName(bel).c_str(getCtx()), decal.index); + } +#endif } return ret; diff --git a/ice40/arch.h b/ice40/arch.h index 8fd1af23..37f663d9 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -225,6 +225,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { RelPtr bel_config; RelPtr packages_data; RelPtr cell_timing; + RelPtr> tile_wire_names; }); #if defined(_MSC_VER) @@ -502,6 +503,8 @@ struct Arch : BaseCtx return IdString(chip_info->bel_data[bel.index].type); } + std::vector> getBelAttrs(BelId bel) const; + WireId getBelPinWire(BelId bel, IdString pin) const; PortType getBelPinType(BelId bel, IdString pin) const; std::vector getBelPins(BelId bel) const; @@ -517,6 +520,7 @@ struct Arch : BaseCtx } IdString getWireType(WireId wire) const; + std::vector> getWireAttrs(WireId wire) const; uint32_t getWireChecksum(WireId wire) const { return wire.index; } @@ -692,7 +696,8 @@ struct Arch : BaseCtx IdString getPipName(PipId pip) const; - IdString getPipType(PipId pip) const { return IdString(); } + IdString getPipType(PipId pip) const; + std::vector> getPipAttrs(PipId pip) const; uint32_t getPipChecksum(PipId pip) const { return pip.index; } @@ -832,8 +837,6 @@ struct Arch : BaseCtx } NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}"); } - - float placer_constraintWeight = 10; }; void ice40DelayFuzzerMain(Context *ctx); diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index ee276e49..4ea91011 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -870,10 +870,12 @@ bool read_asc(Context *ctx, std::istream &in) } if (isUsed) { NetInfo *net = ctx->wire_to_net[pi.dst]; - WireId wire; - wire.index = pi.dst; - ctx->unbindWire(wire); - ctx->bindPip(pip, net, STRENGTH_WEAK); + if (net!=nullptr) { + WireId wire; + wire.index = pi.dst; + ctx->unbindWire(wire); + ctx->bindPip(pip, net, STRENGTH_WEAK); + } } } for (auto bel : ctx->getBels()) { diff --git a/ice40/cells.cc b/ice40/cells.cc index 5bdc7990..e79a1fda 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -28,6 +28,7 @@ NEXTPNR_NAMESPACE_BEGIN void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir) { IdString id = ctx->id(name); + NPNR_ASSERT(cell->ports.count(id) == 0); cell->ports[id] = PortInfo{id, nullptr, dir}; } @@ -237,7 +238,7 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri 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(), "SDO", PORT_OUT); add_port(ctx, new_cell.get(), "LOCK", PORT_OUT); add_port(ctx, new_cell.get(), "PLLOUT_A", PORT_OUT); diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 7bdf82f0..5b2f3e57 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -50,6 +50,7 @@ tiletypes = dict() wiretypes = dict() gfx_wire_ids = dict() +gfx_wire_names = list() wire_segments = dict() fast_timings = None @@ -93,6 +94,136 @@ with open(args.gfxh) as f: idx = len(gfx_wire_ids) name = line.strip().rstrip(",") gfx_wire_ids[name] = idx + gfx_wire_names.append(name) + +def gfx_wire_alias(old, new): + assert old in gfx_wire_ids + assert new not in gfx_wire_ids + gfx_wire_ids[new] = gfx_wire_ids[old] + +# GFX aliases for RAM tiles + +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_0", "TILE_WIRE_RAM_RADDR_0") +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_1", "TILE_WIRE_RAM_RADDR_1") +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_2", "TILE_WIRE_RAM_RADDR_2") +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_3", "TILE_WIRE_RAM_RADDR_3") + +gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_0", "TILE_WIRE_RAM_RADDR_4") +gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_1", "TILE_WIRE_RAM_RADDR_5") +gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_2", "TILE_WIRE_RAM_RADDR_6") +gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_3", "TILE_WIRE_RAM_RADDR_7") + +gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_0", "TILE_WIRE_RAM_RADDR_8") +gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_1", "TILE_WIRE_RAM_RADDR_9") +gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_2", "TILE_WIRE_RAM_RADDR_10") + +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_0", "TILE_WIRE_RAM_WADDR_0") +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_1", "TILE_WIRE_RAM_WADDR_1") +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_2", "TILE_WIRE_RAM_WADDR_2") +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_3", "TILE_WIRE_RAM_WADDR_3") + +gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_0", "TILE_WIRE_RAM_WADDR_4") +gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_1", "TILE_WIRE_RAM_WADDR_5") +gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_2", "TILE_WIRE_RAM_WADDR_6") +gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_3", "TILE_WIRE_RAM_WADDR_7") + +gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_0", "TILE_WIRE_RAM_WADDR_8") +gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_1", "TILE_WIRE_RAM_WADDR_9") +gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_2", "TILE_WIRE_RAM_WADDR_10") + +gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_0", "TILE_WIRE_RAM_MASK_0") +gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_1", "TILE_WIRE_RAM_MASK_1") +gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_2", "TILE_WIRE_RAM_MASK_2") +gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_3", "TILE_WIRE_RAM_MASK_3") + +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_0", "TILE_WIRE_RAM_MASK_4") +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_1", "TILE_WIRE_RAM_MASK_5") +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_2", "TILE_WIRE_RAM_MASK_6") +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_3", "TILE_WIRE_RAM_MASK_7") + +gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_0", "TILE_WIRE_RAM_MASK_8") +gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_1", "TILE_WIRE_RAM_MASK_9") +gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_2", "TILE_WIRE_RAM_MASK_10") +gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_3", "TILE_WIRE_RAM_MASK_11") + +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_0", "TILE_WIRE_RAM_MASK_12") +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_1", "TILE_WIRE_RAM_MASK_13") +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_2", "TILE_WIRE_RAM_MASK_14") +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_3", "TILE_WIRE_RAM_MASK_15") + +gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_0", "TILE_WIRE_RAM_WDATA_0") +gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_1", "TILE_WIRE_RAM_WDATA_1") +gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_2", "TILE_WIRE_RAM_WDATA_2") +gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_3", "TILE_WIRE_RAM_WDATA_3") + +gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_0", "TILE_WIRE_RAM_WDATA_4") +gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_1", "TILE_WIRE_RAM_WDATA_5") +gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_2", "TILE_WIRE_RAM_WDATA_6") +gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_3", "TILE_WIRE_RAM_WDATA_7") + +gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_0", "TILE_WIRE_RAM_WDATA_8") +gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_1", "TILE_WIRE_RAM_WDATA_9") +gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_2", "TILE_WIRE_RAM_WDATA_10") +gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_3", "TILE_WIRE_RAM_WDATA_11") + +gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_0", "TILE_WIRE_RAM_WDATA_12") +gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_1", "TILE_WIRE_RAM_WDATA_13") +gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_2", "TILE_WIRE_RAM_WDATA_14") +gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_3", "TILE_WIRE_RAM_WDATA_15") + +gfx_wire_alias("TILE_WIRE_LUTFF_0_OUT", "TILE_WIRE_RAM_RDATA_0") +gfx_wire_alias("TILE_WIRE_LUTFF_1_OUT", "TILE_WIRE_RAM_RDATA_1") +gfx_wire_alias("TILE_WIRE_LUTFF_2_OUT", "TILE_WIRE_RAM_RDATA_2") +gfx_wire_alias("TILE_WIRE_LUTFF_3_OUT", "TILE_WIRE_RAM_RDATA_3") +gfx_wire_alias("TILE_WIRE_LUTFF_4_OUT", "TILE_WIRE_RAM_RDATA_4") +gfx_wire_alias("TILE_WIRE_LUTFF_5_OUT", "TILE_WIRE_RAM_RDATA_5") +gfx_wire_alias("TILE_WIRE_LUTFF_6_OUT", "TILE_WIRE_RAM_RDATA_6") +gfx_wire_alias("TILE_WIRE_LUTFF_7_OUT", "TILE_WIRE_RAM_RDATA_7") + +gfx_wire_alias("TILE_WIRE_LUTFF_0_OUT", "TILE_WIRE_RAM_RDATA_8") +gfx_wire_alias("TILE_WIRE_LUTFF_1_OUT", "TILE_WIRE_RAM_RDATA_9") +gfx_wire_alias("TILE_WIRE_LUTFF_2_OUT", "TILE_WIRE_RAM_RDATA_10") +gfx_wire_alias("TILE_WIRE_LUTFF_3_OUT", "TILE_WIRE_RAM_RDATA_11") +gfx_wire_alias("TILE_WIRE_LUTFF_4_OUT", "TILE_WIRE_RAM_RDATA_12") +gfx_wire_alias("TILE_WIRE_LUTFF_5_OUT", "TILE_WIRE_RAM_RDATA_13") +gfx_wire_alias("TILE_WIRE_LUTFF_6_OUT", "TILE_WIRE_RAM_RDATA_14") +gfx_wire_alias("TILE_WIRE_LUTFF_7_OUT", "TILE_WIRE_RAM_RDATA_15") + +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CEN", "TILE_WIRE_RAM_RCLKE") +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CEN", "TILE_WIRE_RAM_WCLKE") +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CLK", "TILE_WIRE_RAM_RCLK") +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CLK", "TILE_WIRE_RAM_WCLK") +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_S_R", "TILE_WIRE_RAM_RE") +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_S_R", "TILE_WIRE_RAM_WE") + +# GFX aliases for IO tiles + +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_0", "TILE_WIRE_IO_0_D_OUT_0") +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_1", "TILE_WIRE_IO_0_D_OUT_1") +gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_3", "TILE_WIRE_IO_0_OUT_ENB") + +gfx_wire_alias("TILE_WIRE_LUTFF_0_OUT", "TILE_WIRE_IO_0_D_IN_0") +gfx_wire_alias("TILE_WIRE_LUTFF_1_OUT", "TILE_WIRE_IO_0_D_IN_1") + +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_0", "TILE_WIRE_IO_1_D_OUT_0") +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_1", "TILE_WIRE_IO_1_D_OUT_1") +gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_3", "TILE_WIRE_IO_1_OUT_ENB") + +gfx_wire_alias("TILE_WIRE_LUTFF_4_OUT", "TILE_WIRE_IO_1_D_IN_0") +gfx_wire_alias("TILE_WIRE_LUTFF_5_OUT", "TILE_WIRE_IO_1_D_IN_1") + +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CEN", "TILE_WIRE_IO_GLOBAL_CEN") +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CLK", "TILE_WIRE_IO_GLOBAL_INCLK") +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_S_R", "TILE_WIRE_IO_GLOBAL_OUTCLK") + +gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_G0", "TILE_WIRE_IO_GLOBAL_LATCH") + +for neigh in "BNL BNR BOT LFT RGT TNL TNR TOP".split(): + for i in range(8): + gfx_wire_alias("TILE_WIRE_NEIGH_OP_%s_%d" % (neigh, i), "TILE_WIRE_LOGIC_OP_%s_%d" % (neigh, i)) + +# End of GFX aliases + def read_timings(filename): db = dict() @@ -165,6 +296,18 @@ def maj_wire_name(name): return name[2] in ("sp12_v_b_0", "sp12_v_b_1") return False +def norm_wire_xy(x, y, name): + if name.startswith("glb_netwk_"): + return None + if name.startswith("neigh_op_"): + return None + if name.startswith("logic_op_"): + return None + if name.startswith("io_global/latch"): + return None + return None # FIXME + return (x, y) + def cmp_wire_names(newname, oldname): if maj_wire_name(newname): return True @@ -508,11 +651,13 @@ with open(args.filename, "r") as f: wire_names_r[mode[1]] = wname if mode[1] not in wire_xy: wire_xy[mode[1]] = list() - wire_xy[mode[1]].append((int(line[0]), int(line[1]))) + wire_xy[mode[1]].append(wname) if mode[1] not in wire_segments: wire_segments[mode[1]] = dict() if ("TILE_WIRE_" + wname[2].upper().replace("/", "_")) in gfx_wire_ids: - wire_segments[mode[1]][(wname[0], wname[1])] = wname[2] + if (wname[0], wname[1]) not in wire_segments[mode[1]]: + wire_segments[mode[1]][(wname[0], wname[1])] = list() + wire_segments[mode[1]][(wname[0], wname[1])].append(wname[2]) continue if mode[0] in ("buffer", "routing"): @@ -561,7 +706,9 @@ def add_wire(x, y, name): wire_names_r[wire_idx] = wname wire_segments[wire_idx] = dict() if ("TILE_WIRE_" + wname[2].upper().replace("/", "_")) in gfx_wire_ids: - wire_segments[wire_idx][(wname[0], wname[1])] = wname[2] + if (wname[0], wname[1]) not in wire_segments[wire_idx]: + wire_segments[wire_idx][(wname[0], wname[1])] = list() + wire_segments[wire_idx][(wname[0], wname[1])].append(wname[2]) return wire_idx def add_switch(x, y, bel=-1): @@ -580,10 +727,6 @@ def add_pip(src, dst, flags=0): pip_xy[(src, dst)] = (x, y, 0, len(switches) - 1, flags) -# Add virtual padin wires -for i in range(8): - add_wire(0, 0, "padin_%d" % i) - def add_bel_input(bel, wire, port): if wire not in wire_belports: wire_belports[wire] = set() @@ -932,6 +1075,10 @@ bba.post('NEXTPNR_NAMESPACE_END') bba.push("chipdb_blob_%s" % dev_name) bba.r("chip_info_%s" % dev_name, "chip_info") +bba.l("tile_wire_names") +for name in gfx_wire_names: + bba.s(name, name) + for bel in range(len(bel_name)): bba.l("bel_wires_%d" % bel, "BelWirePOD") for data in sorted(bel_wires[bel]): @@ -1028,20 +1175,32 @@ for wire in range(num_wires): info["num_bel_pins"] = num_bel_pins info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None + pos_xy = None + first = None + if wire in wire_xy: - avg_x, avg_y = 0, 0 + for x, y, n in wire_xy[wire]: + norm_xy = norm_wire_xy(x, y, n) + if norm_xy is None: + continue + if pos_xy is None: + pos_xy = norm_xy + first = (x, y, n) + elif pos_xy != norm_xy: + print("Conflicting positions for wire %s: (%d, %d, %s) -> (%d, %d), (%d, %d, %s) -> (%d, %d)" % \ + ((info["name"],) + first + pos_xy + (x, y, n) + norm_xy), file=sys.stderr) + assert 0 + if (pos_xy is None) and (len(wire_xy[wire]) > 1): + # print("Only 'None' positions for wire %s." % info["name"], file=sys.stderr) + # assert 0 + pass - for x, y in wire_xy[wire]: - avg_x += x - avg_y += y - avg_x /= len(wire_xy[wire]) - avg_y /= len(wire_xy[wire]) - - info["x"] = int(round(avg_x)) - info["y"] = int(round(avg_y)) - else: + if pos_xy is None: info["x"] = wire_names_r[wire][0] info["y"] = wire_names_r[wire][1] + else: + info["x"] = pos_xy[0] + info["y"] = pos_xy[1] wireinfo.append(info) @@ -1108,8 +1267,13 @@ for wire, info in enumerate(wireinfo): bba.r(info["list_downhill"], "pips_downhill") bba.u32(info["num_bel_pins"], "num_bel_pins") bba.r(info["list_bel_pins"], "bel_pins") - bba.u32(len(wire_segments[wire]), "num_segments") - if len(wire_segments[wire]): + + num_segments = 0 + for segs in wire_segments[wire].values(): + num_segments += len(segs) + bba.u32(num_segments, "num_segments") + + if num_segments: bba.r("wire_segments_%d" % wire, "segments") else: bba.u32(0, "segments") @@ -1125,24 +1289,25 @@ for wire, info in enumerate(wireinfo): for wire in range(num_wires): if len(wire_segments[wire]): bba.l("wire_segments_%d" % wire, "WireSegmentPOD") - for xy, seg in sorted(wire_segments[wire].items()): - bba.u8(xy[0], "x") - bba.u8(xy[1], "y") - bba.u16(gfx_wire_ids["TILE_WIRE_" + seg.upper().replace("/", "_")], "index") + for xy, segs in sorted(wire_segments[wire].items()): + for seg in segs: + bba.u8(xy[0], "x") + bba.u8(xy[1], "y") + bba.u16(gfx_wire_ids["TILE_WIRE_" + seg.upper().replace("/", "_")], "index") bba.l("pip_data_%s" % dev_name, "PipInfoPOD") for info in pipinfo: src_seg = -1 src_segname = wire_names_r[info["src"]] if (info["x"], info["y"]) in wire_segments[info["src"]]: - src_segname = wire_segments[info["src"]][(info["x"], info["y"])] + src_segname = wire_segments[info["src"]][(info["x"], info["y"])][0] src_seg = gfx_wire_ids["TILE_WIRE_" + src_segname.upper().replace("/", "_")] src_segname = src_segname.replace("/", ".") dst_seg = -1 dst_segname = wire_names_r[info["dst"]] if (info["x"], info["y"]) in wire_segments[info["dst"]]: - dst_segname = wire_segments[info["dst"]][(info["x"], info["y"])] + dst_segname = wire_segments[info["dst"]][(info["x"], info["y"])][0] dst_seg = gfx_wire_ids["TILE_WIRE_" + dst_segname.upper().replace("/", "_")] dst_segname = dst_segname.replace("/", ".") @@ -1276,5 +1441,6 @@ bba.r("bits_info_%s" % dev_name, "bits_info") bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config") bba.r("package_info_%s" % dev_name, "packages_data") bba.r("cell_timings_%s" % dev_name, "cell_timing") +bba.r("tile_wire_names", "tile_wire_names") bba.pop() diff --git a/ice40/family.cmake b/ice40/family.cmake index f558a14d..0e1d36e6 100644 --- a/ice40/family.cmake +++ b/ice40/family.cmake @@ -36,7 +36,7 @@ if (MSVC) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) add_custom_command(OUTPUT ${DEV_CC_BBA_DB} COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} -g ${DEV_GFXH} ${OPT_FAST} ${OPT_SLOW} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB} - DEPENDS ${DEV_TXT_DB} ${DB_PY} + DEPENDS ${DEV_CONSTIDS_INC} ${DEV_GFXH} ${DEV_TXT_DB} ${DB_PY} ) add_custom_command(OUTPUT ${DEV_CC_DB} COMMAND bbasm ${DEV_CC_BBA_DB} ${DEV_CC_DB} @@ -69,7 +69,7 @@ else() add_custom_command(OUTPUT ${DEV_CC_BBA_DB} COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} -g ${DEV_GFXH} ${OPT_FAST} ${OPT_SLOW} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB} - DEPENDS ${DEV_TXT_DB} ${DB_PY} + DEPENDS ${DEV_CONSTIDS_INC} ${DEV_GFXH} ${DEV_TXT_DB} ${DB_PY} ) add_custom_command(OUTPUT ${DEV_CC_DB} COMMAND bbasm --c ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new diff --git a/ice40/gfx.cc b/ice40/gfx.cc index 1ab2fb3c..74338b8d 100644 --- a/ice40/gfx.cc +++ b/ice40/gfx.cc @@ -21,7 +21,7 @@ NEXTPNR_NAMESPACE_BEGIN -void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style) +void gfxTileWire(std::vector &g, int x, int y, int w, int h, GfxTileWireId id, GraphicElement::style_t style) { GraphicElement el; el.type = GraphicElement::TYPE_LINE; @@ -339,6 +339,168 @@ void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, g.push_back(el); } + // IO Span-4 Wires connecting to fabric + + if (id >= TILE_WIRE_SPAN4_HORZ_0 && id <= TILE_WIRE_SPAN4_HORZ_47) { + int idx = id - TILE_WIRE_SPAN4_HORZ_0; + float y1 = y + 1.0 - (0.03 + 0.0025 * (48 - (idx ^ 1))); + + el.x1 = x; + el.x2 = x + 1.0; + el.y1 = y1; + el.y2 = y1; + g.push_back(el); + + el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35); + el.x2 = el.x1; + el.y1 = y1; + el.y2 = y + main_swbox_y2; + g.push_back(el); + } + + if (id >= TILE_WIRE_SPAN4_VERT_0 && id <= TILE_WIRE_SPAN4_VERT_47) { + int idx = id - TILE_WIRE_SPAN4_VERT_0; + float x1 = x + 0.03 + 0.0025 * (48 - (idx ^ 1)); + + el.x1 = x1; + el.x2 = x1; + el.y1 = y; + el.y2 = y + 1.0; + g.push_back(el); + + el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1))); + el.y2 = el.y1; + el.x1 = x1; + el.x2 = x + main_swbox_x1; + g.push_back(el); + } + + // IO Span-12 Wires connecting to fabric + + if (id >= TILE_WIRE_SPAN12_HORZ_0 && id <= TILE_WIRE_SPAN12_HORZ_23) { + int idx = id - TILE_WIRE_SPAN12_HORZ_0; + float y1 = y + 1.0 - (0.03 + 0.0025 * (88 - (idx ^ 1))); + + el.x1 = x; + el.x2 = x + 1.0; + el.y1 = y1; + el.y2 = y1; + g.push_back(el); + + el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5); + el.x2 = el.x1; + el.y1 = y1; + el.y2 = y + main_swbox_y2; + g.push_back(el); + } + + if (id >= TILE_WIRE_SPAN12_VERT_0 && id <= TILE_WIRE_SPAN12_VERT_23) { + int idx = id - TILE_WIRE_SPAN12_VERT_0; + float x1 = x + 0.03 + 0.0025 * (88 - (idx ^ 1)); + + el.x1 = x1; + el.x2 = x1; + el.y1 = y; + el.y2 = y + 1.0; + g.push_back(el); + + el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1))); + el.y2 = el.y1; + el.x1 = x1; + el.x2 = x + main_swbox_x1; + g.push_back(el); + } + + // Horizontal IO Span-4 Wires + + if (id >= TILE_WIRE_SPAN4_HORZ_R_0 && id <= TILE_WIRE_SPAN4_HORZ_L_15) { + int idx = id - TILE_WIRE_SPAN4_HORZ_R_0; + + float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - idx)); + float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - idx - 4)); + + el.x1 = x; + el.x2 = x + 0.9; + el.y1 = y1; + el.y2 = y1; + g.push_back(el); + + if (idx <= 15) { + el.x1 = x + 0.9; + el.x2 = x + 1.0; + el.y1 = y1; + el.y2 = y2; + g.push_back(el); + } + + el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 35); + el.x2 = el.x1; + el.y1 = y1; + el.y2 = y + main_swbox_y2; + g.push_back(el); + } + + // Vertical IO Span-4 Wires + + if (id >= TILE_WIRE_SPAN4_VERT_B_0 && id <= TILE_WIRE_SPAN4_VERT_T_15) { + int idx = id - TILE_WIRE_SPAN4_VERT_B_0; + + float x1 = x + 0.03 + 0.0025 * (60 - idx); + float x2 = x + 0.03 + 0.0025 * (60 - idx - 4); + + el.y1 = y + 1.00; + el.y2 = y + 0.10; + el.x1 = x1; + el.x2 = x1; + g.push_back(el); + + if (idx <= 15) { + el.y1 = y + 0.10; + el.y2 = y; + el.x1 = x1; + el.x2 = x2; + g.push_back(el); + } + + if (idx <= 15 && (x == 0 || x == w-1) && y == 1) { + float y1 = y - (0.03 + 0.0025 * (60 - idx - 4)); + + el.x1 = x2; + el.y1 = y; + el.x2 = x2; + el.y2 = y1; + g.push_back(el); + + el.x1 = x2; + el.y1 = y1; + el.x2 = x + (x == 0); + el.y2 = y1; + g.push_back(el); + } + + if (idx >= 4 && (x == 0 || x == w-1) && y == h-2) { + float y1 = y + 2.0 - (0.03 + 0.0025 * (60 - idx)); + + el.x1 = x1; + el.y1 = y + 1.0; + el.x2 = x1; + el.y2 = y1; + g.push_back(el); + + el.x1 = x1; + el.y1 = y1; + el.x2 = x + (x == 0); + el.y2 = y1; + g.push_back(el); + } + + el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - idx)); + el.y2 = el.y1; + el.x1 = x1; + el.x2 = x + main_swbox_x1; + g.push_back(el); + } + // Global2Local if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) { @@ -392,7 +554,7 @@ void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, int input = idx % 4; el.x1 = x + local_swbox_x2; el.x2 = x + lut_swbox_x1; - el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch; + el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * input) + z * logic_cell_pitch; el.y2 = el.y1; g.push_back(el); } @@ -403,7 +565,7 @@ void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, int input = idx % 4; el.x1 = x + lut_swbox_x2; el.x2 = x + logic_cell_x1; - el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch; + el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * input) + z * logic_cell_pitch; el.y2 = el.y1; g.push_back(el); } @@ -457,6 +619,42 @@ void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, } } + // LC Control for IO and BRAM + + if (id >= TILE_WIRE_FUNC_GLOBAL_CEN && id <= TILE_WIRE_FUNC_GLOBAL_S_R) { + int idx = id - TILE_WIRE_FUNC_GLOBAL_CEN; + + el.x1 = x + main_swbox_x2 - 0.005 * (idx + 5); + el.x2 = el.x1; + el.y1 = y + main_swbox_y1; + el.y2 = el.y1 - 0.005 * (idx + 2); + g.push_back(el); + + el.y1 = el.y2; + el.x2 = x + logic_cell_x2 - 0.005 * (2 - idx + 5); + g.push_back(el); + + el.y2 = y + logic_cell_y1; + el.x1 = el.x2; + g.push_back(el); + } + + if (id == TILE_WIRE_FABOUT) { + el.y1 = y + main_swbox_y1; + el.y2 = el.y1 - 0.005 * 4; + el.x1 = x + main_swbox_x2 - 0.005 * 9; + el.x2 = el.x1; + g.push_back(el); + } + + if (id == TILE_WIRE_FUNC_GLOBAL_G0) { + el.y1 = y + logic_cell_y1; + el.y2 = el.y1 - 0.005 * 4; + el.x1 = x + logic_cell_x2 - 0.005 * 3; + el.x2 = el.x1; + g.push_back(el); + } + // LC Cascade if (id >= TILE_WIRE_LUTFF_0_LOUT && id <= TILE_WIRE_LUTFF_6_LOUT) { @@ -571,6 +769,54 @@ static bool getWireXY_main(GfxTileWireId id, float &x, float &y) return true; } + // IO Span-4 Wires connecting to fabric + + if (id >= TILE_WIRE_SPAN4_HORZ_0 && id <= TILE_WIRE_SPAN4_HORZ_47) { + int idx = id - TILE_WIRE_SPAN4_HORZ_0; + x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35); + y = main_swbox_y2; + return true; + } + + if (id >= TILE_WIRE_SPAN4_VERT_0 && id <= TILE_WIRE_SPAN4_VERT_47) { + int idx = id - TILE_WIRE_SPAN4_VERT_0; + y = 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1))); + x = main_swbox_x1; + return true; + } + + // IO Span-12 Wires connecting to fabric + + if (id >= TILE_WIRE_SPAN12_HORZ_0 && id <= TILE_WIRE_SPAN12_HORZ_23) { + int idx = id - TILE_WIRE_SPAN12_HORZ_0; + x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5); + y = main_swbox_y2; + return true; + } + + if (id >= TILE_WIRE_SPAN12_VERT_0 && id <= TILE_WIRE_SPAN12_VERT_23) { + int idx = id - TILE_WIRE_SPAN12_VERT_0; + y = 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1))); + x = main_swbox_x1; + return true; + } + + // IO Span-4 Wires + + if (id >= TILE_WIRE_SPAN4_HORZ_R_0 && id <= TILE_WIRE_SPAN4_HORZ_L_15) { + int idx = id - TILE_WIRE_SPAN4_HORZ_R_0; + y = main_swbox_y2; + x = main_swbox_x1 + 0.0025 * (idx + 35); + return true; + } + + if (id >= TILE_WIRE_SPAN4_VERT_B_0 && id <= TILE_WIRE_SPAN4_VERT_T_15) { + int idx = id - TILE_WIRE_SPAN4_VERT_B_0; + y = 1.0 - (0.03 + 0.0025 * (270 - idx)); + x = main_swbox_x1; + return true; + } + // Global2Local if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) { @@ -626,6 +872,19 @@ static bool getWireXY_main(GfxTileWireId id, float &x, float &y) return true; } + if (id >= TILE_WIRE_FUNC_GLOBAL_CEN && id <= TILE_WIRE_FUNC_GLOBAL_S_R) { + int idx = id - TILE_WIRE_FUNC_GLOBAL_CEN; + x = main_swbox_x2 - 0.005 * (idx + 5); + y = main_swbox_y1; + return true; + } + + if (id == TILE_WIRE_FABOUT) { + x = main_swbox_x2 - 0.005 * 9; + y = main_swbox_y1; + return true; + } + return false; } @@ -662,22 +921,22 @@ void pipGfx(std::vector &g, int x, int y, float x1, float y1, fl el.style = style; if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) { - tx = x1 + 0.25 * fabsf(y1 - y2); + tx = swx1 + 0.25 * fabsf(y1 - y2); goto edge_pip; } if (fabsf(x1 - swx2) < 0.001 && fabsf(x2 - swx2) < 0.001) { - tx = x1 - 0.25 * fabsf(y1 - y2); + tx = swx2 - 0.25 * fabsf(y1 - y2); goto edge_pip; } if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) { - ty = y1 + 0.25 * fabsf(x1 - x2); + ty = swy1 + 0.25 * fabsf(x1 - x2); goto edge_pip; } - if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) { - ty = y1 + 0.25 * fabsf(x1 - x2); + if (fabsf(y1 - swy2) < 0.001 && fabsf(y2 - swy2) < 0.001) { + ty = swy2 - 0.25 * fabsf(x1 - x2); goto edge_pip; } @@ -727,7 +986,7 @@ void gfxTilePip(std::vector &g, int x, int y, GfxTileWireId src, el.style = style; el.x1 = x + logic_cell_x1; el.x2 = x + logic_cell_x2; - el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch; + el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * in_idx) + lut_idx * logic_cell_pitch; el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + lut_idx * logic_cell_pitch; g.push_back(el); return; @@ -744,8 +1003,46 @@ void gfxTilePip(std::vector &g, int x, int y, GfxTileWireId src, el.style = style; el.x1 = x + lut_swbox_x1; el.x2 = x + lut_swbox_x2; - el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch; - el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * out_idx) + lut_idx * logic_cell_pitch; + el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * in_idx) + lut_idx * logic_cell_pitch; + el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * out_idx) + lut_idx * logic_cell_pitch; + g.push_back(el); + return; + } + + if ((src == TILE_WIRE_CARRY_IN_MUX || (src >= TILE_WIRE_LUTFF_0_COUT && src <= TILE_WIRE_LUTFF_6_COUT)) && + (dst >= TILE_WIRE_LUTFF_0_IN_0 && dst <= TILE_WIRE_LUTFF_7_IN_3 && (dst - TILE_WIRE_LUTFF_0_IN_0) % 4 == 3)) { + int lut_idx = (dst - TILE_WIRE_LUTFF_0_IN_0) / 4; + + GraphicElement el; + el.type = GraphicElement::TYPE_ARROW; + el.style = style; + el.x1 = x + (local_swbox_x2 + lut_swbox_x1) / 2; + el.x2 = el.x1; + el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * 3) + lut_idx * logic_cell_pitch; + el.y2 = y + (logic_cell_y1 + logic_cell_y2 - logic_cell_pitch) / 2 + lut_idx * logic_cell_pitch; + g.push_back(el); + + el.x1 = x + logic_cell_x1 + 0.005 * 3; + el.y1 = el.y2; + g.push_back(el); + return; + } + + if ((src >= TILE_WIRE_LUTFF_0_LOUT && src <= TILE_WIRE_LUTFF_6_LOUT) && + (dst >= TILE_WIRE_LUTFF_0_IN_0 && dst <= TILE_WIRE_LUTFF_7_IN_3 && (dst - TILE_WIRE_LUTFF_0_IN_0) % 4 == 2)) { + int lut_idx = (dst - TILE_WIRE_LUTFF_0_IN_0) / 4; + + GraphicElement el; + el.type = GraphicElement::TYPE_ARROW; + el.style = style; + el.x1 = x + (local_swbox_x2 + lut_swbox_x1) / 2 + 0.005; + el.x2 = el.x1; + el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * 2) + lut_idx * logic_cell_pitch; + el.y2 = y + (logic_cell_y1 + logic_cell_y2 - logic_cell_pitch) / 2 + lut_idx * logic_cell_pitch + 0.003; + g.push_back(el); + + el.x1 = x + logic_cell_x1 + 0.005 * 5; + el.y1 = el.y2; g.push_back(el); return; } diff --git a/ice40/gfx.h b/ice40/gfx.h index 8ee7b0b6..4fb6e147 100644 --- a/ice40/gfx.h +++ b/ice40/gfx.h @@ -205,6 +205,13 @@ enum GfxTileWireId TILE_WIRE_LUTFF_GLOBAL_CLK, TILE_WIRE_LUTFF_GLOBAL_S_R, + TILE_WIRE_FUNC_GLOBAL_CEN, + TILE_WIRE_FUNC_GLOBAL_CLK, + TILE_WIRE_FUNC_GLOBAL_S_R, + + TILE_WIRE_FUNC_GLOBAL_G0, + TILE_WIRE_FABOUT, + TILE_WIRE_CARRY_IN, TILE_WIRE_CARRY_IN_MUX, @@ -509,12 +516,205 @@ enum GfxTileWireId TILE_WIRE_SP12_H_L_22, TILE_WIRE_SP12_H_L_23, + TILE_WIRE_SPAN4_VERT_0, + TILE_WIRE_SPAN4_VERT_1, + TILE_WIRE_SPAN4_VERT_2, + TILE_WIRE_SPAN4_VERT_3, + TILE_WIRE_SPAN4_VERT_4, + TILE_WIRE_SPAN4_VERT_5, + TILE_WIRE_SPAN4_VERT_6, + TILE_WIRE_SPAN4_VERT_7, + TILE_WIRE_SPAN4_VERT_8, + TILE_WIRE_SPAN4_VERT_9, + TILE_WIRE_SPAN4_VERT_10, + TILE_WIRE_SPAN4_VERT_11, + TILE_WIRE_SPAN4_VERT_12, + TILE_WIRE_SPAN4_VERT_13, + TILE_WIRE_SPAN4_VERT_14, + TILE_WIRE_SPAN4_VERT_15, + TILE_WIRE_SPAN4_VERT_16, + TILE_WIRE_SPAN4_VERT_17, + TILE_WIRE_SPAN4_VERT_18, + TILE_WIRE_SPAN4_VERT_19, + TILE_WIRE_SPAN4_VERT_20, + TILE_WIRE_SPAN4_VERT_21, + TILE_WIRE_SPAN4_VERT_22, + TILE_WIRE_SPAN4_VERT_23, + TILE_WIRE_SPAN4_VERT_24, + TILE_WIRE_SPAN4_VERT_25, + TILE_WIRE_SPAN4_VERT_26, + TILE_WIRE_SPAN4_VERT_27, + TILE_WIRE_SPAN4_VERT_28, + TILE_WIRE_SPAN4_VERT_29, + TILE_WIRE_SPAN4_VERT_30, + TILE_WIRE_SPAN4_VERT_31, + TILE_WIRE_SPAN4_VERT_32, + TILE_WIRE_SPAN4_VERT_33, + TILE_WIRE_SPAN4_VERT_34, + TILE_WIRE_SPAN4_VERT_35, + TILE_WIRE_SPAN4_VERT_36, + TILE_WIRE_SPAN4_VERT_37, + TILE_WIRE_SPAN4_VERT_38, + TILE_WIRE_SPAN4_VERT_39, + TILE_WIRE_SPAN4_VERT_40, + TILE_WIRE_SPAN4_VERT_41, + TILE_WIRE_SPAN4_VERT_42, + TILE_WIRE_SPAN4_VERT_43, + TILE_WIRE_SPAN4_VERT_44, + TILE_WIRE_SPAN4_VERT_45, + TILE_WIRE_SPAN4_VERT_46, + TILE_WIRE_SPAN4_VERT_47, + + TILE_WIRE_SPAN4_HORZ_0, + TILE_WIRE_SPAN4_HORZ_1, + TILE_WIRE_SPAN4_HORZ_2, + TILE_WIRE_SPAN4_HORZ_3, + TILE_WIRE_SPAN4_HORZ_4, + TILE_WIRE_SPAN4_HORZ_5, + TILE_WIRE_SPAN4_HORZ_6, + TILE_WIRE_SPAN4_HORZ_7, + TILE_WIRE_SPAN4_HORZ_8, + TILE_WIRE_SPAN4_HORZ_9, + TILE_WIRE_SPAN4_HORZ_10, + TILE_WIRE_SPAN4_HORZ_11, + TILE_WIRE_SPAN4_HORZ_12, + TILE_WIRE_SPAN4_HORZ_13, + TILE_WIRE_SPAN4_HORZ_14, + TILE_WIRE_SPAN4_HORZ_15, + TILE_WIRE_SPAN4_HORZ_16, + TILE_WIRE_SPAN4_HORZ_17, + TILE_WIRE_SPAN4_HORZ_18, + TILE_WIRE_SPAN4_HORZ_19, + TILE_WIRE_SPAN4_HORZ_20, + TILE_WIRE_SPAN4_HORZ_21, + TILE_WIRE_SPAN4_HORZ_22, + TILE_WIRE_SPAN4_HORZ_23, + TILE_WIRE_SPAN4_HORZ_24, + TILE_WIRE_SPAN4_HORZ_25, + TILE_WIRE_SPAN4_HORZ_26, + TILE_WIRE_SPAN4_HORZ_27, + TILE_WIRE_SPAN4_HORZ_28, + TILE_WIRE_SPAN4_HORZ_29, + TILE_WIRE_SPAN4_HORZ_30, + TILE_WIRE_SPAN4_HORZ_31, + TILE_WIRE_SPAN4_HORZ_32, + TILE_WIRE_SPAN4_HORZ_33, + TILE_WIRE_SPAN4_HORZ_34, + TILE_WIRE_SPAN4_HORZ_35, + TILE_WIRE_SPAN4_HORZ_36, + TILE_WIRE_SPAN4_HORZ_37, + TILE_WIRE_SPAN4_HORZ_38, + TILE_WIRE_SPAN4_HORZ_39, + TILE_WIRE_SPAN4_HORZ_40, + TILE_WIRE_SPAN4_HORZ_41, + TILE_WIRE_SPAN4_HORZ_42, + TILE_WIRE_SPAN4_HORZ_43, + TILE_WIRE_SPAN4_HORZ_44, + TILE_WIRE_SPAN4_HORZ_45, + TILE_WIRE_SPAN4_HORZ_46, + TILE_WIRE_SPAN4_HORZ_47, + + TILE_WIRE_SPAN12_VERT_0, + TILE_WIRE_SPAN12_VERT_1, + TILE_WIRE_SPAN12_VERT_2, + TILE_WIRE_SPAN12_VERT_3, + TILE_WIRE_SPAN12_VERT_4, + TILE_WIRE_SPAN12_VERT_5, + TILE_WIRE_SPAN12_VERT_6, + TILE_WIRE_SPAN12_VERT_7, + TILE_WIRE_SPAN12_VERT_8, + TILE_WIRE_SPAN12_VERT_9, + TILE_WIRE_SPAN12_VERT_10, + TILE_WIRE_SPAN12_VERT_11, + TILE_WIRE_SPAN12_VERT_12, + TILE_WIRE_SPAN12_VERT_13, + TILE_WIRE_SPAN12_VERT_14, + TILE_WIRE_SPAN12_VERT_15, + TILE_WIRE_SPAN12_VERT_16, + TILE_WIRE_SPAN12_VERT_17, + TILE_WIRE_SPAN12_VERT_18, + TILE_WIRE_SPAN12_VERT_19, + TILE_WIRE_SPAN12_VERT_20, + TILE_WIRE_SPAN12_VERT_21, + TILE_WIRE_SPAN12_VERT_22, + TILE_WIRE_SPAN12_VERT_23, + + TILE_WIRE_SPAN12_HORZ_0, + TILE_WIRE_SPAN12_HORZ_1, + TILE_WIRE_SPAN12_HORZ_2, + TILE_WIRE_SPAN12_HORZ_3, + TILE_WIRE_SPAN12_HORZ_4, + TILE_WIRE_SPAN12_HORZ_5, + TILE_WIRE_SPAN12_HORZ_6, + TILE_WIRE_SPAN12_HORZ_7, + TILE_WIRE_SPAN12_HORZ_8, + TILE_WIRE_SPAN12_HORZ_9, + TILE_WIRE_SPAN12_HORZ_10, + TILE_WIRE_SPAN12_HORZ_11, + TILE_WIRE_SPAN12_HORZ_12, + TILE_WIRE_SPAN12_HORZ_13, + TILE_WIRE_SPAN12_HORZ_14, + TILE_WIRE_SPAN12_HORZ_15, + TILE_WIRE_SPAN12_HORZ_16, + TILE_WIRE_SPAN12_HORZ_17, + TILE_WIRE_SPAN12_HORZ_18, + TILE_WIRE_SPAN12_HORZ_19, + TILE_WIRE_SPAN12_HORZ_20, + TILE_WIRE_SPAN12_HORZ_21, + TILE_WIRE_SPAN12_HORZ_22, + TILE_WIRE_SPAN12_HORZ_23, + + TILE_WIRE_SPAN4_VERT_B_0, + TILE_WIRE_SPAN4_VERT_B_1, + TILE_WIRE_SPAN4_VERT_B_2, + TILE_WIRE_SPAN4_VERT_B_3, + TILE_WIRE_SPAN4_VERT_B_4, + TILE_WIRE_SPAN4_VERT_B_5, + TILE_WIRE_SPAN4_VERT_B_6, + TILE_WIRE_SPAN4_VERT_B_7, + TILE_WIRE_SPAN4_VERT_B_8, + TILE_WIRE_SPAN4_VERT_B_9, + TILE_WIRE_SPAN4_VERT_B_10, + TILE_WIRE_SPAN4_VERT_B_11, + TILE_WIRE_SPAN4_VERT_B_12, + TILE_WIRE_SPAN4_VERT_B_13, + TILE_WIRE_SPAN4_VERT_B_14, + TILE_WIRE_SPAN4_VERT_B_15, + + TILE_WIRE_SPAN4_VERT_T_12, + TILE_WIRE_SPAN4_VERT_T_13, + TILE_WIRE_SPAN4_VERT_T_14, + TILE_WIRE_SPAN4_VERT_T_15, + + TILE_WIRE_SPAN4_HORZ_R_0, + TILE_WIRE_SPAN4_HORZ_R_1, + TILE_WIRE_SPAN4_HORZ_R_2, + TILE_WIRE_SPAN4_HORZ_R_3, + TILE_WIRE_SPAN4_HORZ_R_4, + TILE_WIRE_SPAN4_HORZ_R_5, + TILE_WIRE_SPAN4_HORZ_R_6, + TILE_WIRE_SPAN4_HORZ_R_7, + TILE_WIRE_SPAN4_HORZ_R_8, + TILE_WIRE_SPAN4_HORZ_R_9, + TILE_WIRE_SPAN4_HORZ_R_10, + TILE_WIRE_SPAN4_HORZ_R_11, + TILE_WIRE_SPAN4_HORZ_R_12, + TILE_WIRE_SPAN4_HORZ_R_13, + TILE_WIRE_SPAN4_HORZ_R_14, + TILE_WIRE_SPAN4_HORZ_R_15, + + TILE_WIRE_SPAN4_HORZ_L_12, + TILE_WIRE_SPAN4_HORZ_L_13, + TILE_WIRE_SPAN4_HORZ_L_14, + TILE_WIRE_SPAN4_HORZ_L_15, + TILE_WIRE_PLLIN, TILE_WIRE_PLLOUT_A, TILE_WIRE_PLLOUT_B }; -void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style); +void gfxTileWire(std::vector &g, int x, int y, int w, int h, GfxTileWireId id, + GraphicElement::style_t style); void gfxTilePip(std::vector &g, int x, int y, GfxTileWireId src, GfxTileWireId dst, GraphicElement::style_t style); diff --git a/ice40/pack.cc b/ice40/pack.cc index 05338713..7c853e0e 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -733,7 +733,7 @@ static void pack_special(Context *ctx) 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)); + 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; @@ -743,9 +743,22 @@ static void pack_special(Context *ctx) } 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)); + log_error("PLL '%s' has a REFERENCECLK but is not a CORE PLL", ci->name.c_str(ctx)); } + if (packed->ports.count(ctx->id(newname)) == 0) { + if (ci->ports[pi.name].net == nullptr) { + log_warning("PLL '%s' has unknown unconnected port '%s' - ignoring\n", ci->name.c_str(ctx), pi.name.c_str(ctx)); + continue; + } else { + if (ctx->force) { + log_error("PLL '%s' has unknown connected port '%s'\n", ci->name.c_str(ctx), pi.name.c_str(ctx)); + } else { + log_warning("PLL '%s' has unknown connected port '%s' - ignoring\n", ci->name.c_str(ctx), pi.name.c_str(ctx)); + continue; + } + } + } replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); } @@ -766,7 +779,7 @@ static void pack_special(Context *ctx) 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 " + 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)); } @@ -774,17 +787,17 @@ static void pack_special(Context *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), + 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", + 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), + 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. @@ -793,13 +806,13 @@ static void pack_special(Context *ctx) 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)); + log_info(" constrained PLL '%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)); + log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n", packed->name.c_str(ctx)); } } @@ -818,6 +831,8 @@ static void pack_special(Context *ctx) // 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) { + 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 all_luts = true; unsigned int lut_count = 0; @@ -835,12 +850,12 @@ static void pack_special(Context *ctx) // 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)); + 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)); + 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)); } @@ -864,7 +879,7 @@ static void pack_special(Context *ctx) 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()); + log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str()); } } diff --git a/ice40/pcf.cc b/ice40/pcf.cc index d9fc4e68..af5b3e17 100644 --- a/ice40/pcf.cc +++ b/ice40/pcf.cc @@ -66,7 +66,7 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in) log_error("unsupported pcf command '%s'\n", cmd.c_str()); } } - ctx->settings.emplace(ctx->id("project/input/pcf"), filename); + ctx->settings.emplace(ctx->id("input/pcf"), filename); return true; } catch (log_execution_error_exception) { return false; diff --git a/ice40/project.cc b/ice40/project.cc index 8ca10e36..47c0903d 100644 --- a/ice40/project.cc +++ b/ice40/project.cc @@ -28,8 +28,8 @@ NEXTPNR_NAMESPACE_BEGIN void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) { root.put("project.arch.package", ctx->archArgs().package); - if (ctx->settings.find(ctx->id("project/input/pcf")) != ctx->settings.end()) { - std::string fn = ctx->settings[ctx->id("project/input/pcf")]; + if (ctx->settings.find(ctx->id("input/pcf")) != ctx->settings.end()) { + std::string fn = ctx->settings[ctx->id("input/pcf")]; root.put("project.input.pcf", make_relative(fn, path).string()); } } diff --git a/json/jsonparse.cc b/json/jsonparse.cc index 9e86f93e..a690bf18 100644 --- a/json/jsonparse.cc +++ b/json/jsonparse.cc @@ -52,7 +52,7 @@ struct JsonNode std::map data_dict; std::vector data_dict_keys; - JsonNode(std::istream &f) + JsonNode(std::istream &f, int &lineno) { type = 0; data_number = 0; @@ -63,6 +63,8 @@ struct JsonNode if (ch == EOF) log_error("Unexpected EOF in JSON file.\n"); + if (ch == '\n') + lineno++; if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') continue; @@ -91,9 +93,12 @@ struct JsonNode break; } - if ('0' <= ch && ch <= '9') { + if (('0' <= ch && ch <= '9') || ('-' == ch)) { type = 'N'; - data_number = ch - '0'; + if (ch == '-') + data_number = 0; + else + data_number = ch - '0'; data_string += ch; while (1) { @@ -114,6 +119,8 @@ struct JsonNode data_string += ch; } + if (data_string[0] == '-') + data_number = -data_number; data_string = ""; break; @@ -148,6 +155,8 @@ struct JsonNode if (ch == EOF) log_error("Unexpected EOF in JSON file.\n"); + if (ch == '\n') + lineno++; if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') continue; @@ -155,7 +164,7 @@ struct JsonNode break; f.unget(); - data_array.push_back(new JsonNode(f)); + data_array.push_back(new JsonNode(f, lineno)); } break; @@ -170,6 +179,8 @@ struct JsonNode if (ch == EOF) log_error("Unexpected EOF in JSON file.\n"); + if (ch == '\n') + lineno++; if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') continue; @@ -177,7 +188,7 @@ struct JsonNode break; f.unget(); - JsonNode key(f); + JsonNode key(f, lineno); while (1) { ch = f.get(); @@ -185,6 +196,8 @@ struct JsonNode if (ch == EOF) log_error("Unexpected EOF in JSON file.\n"); + if (ch == '\n') + lineno++; if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ':') continue; @@ -192,10 +205,10 @@ struct JsonNode break; } - JsonNode *value = new JsonNode(f); + JsonNode *value = new JsonNode(f, lineno); if (key.type != 'S') - log_error("Unexpected non-string key in JSON dict.\n"); + log_error("Unexpected non-string key in JSON dict, line %d.\n", lineno); data_dict[key.data_string] = value; data_dict_keys.push_back(key.data_string); @@ -204,7 +217,7 @@ struct JsonNode break; } - log_error("Unexpected character in JSON file: '%c'\n", ch); + log_error("Unexpected character in JSON file, line %d: '%c'\n", lineno, ch); } } @@ -736,8 +749,9 @@ bool parse_json_file(std::istream &f, std::string &filename, Context *ctx) { try { using namespace JsonParser; + int lineno = 1; - JsonNode root(f); + JsonNode root(f, lineno); if (root.type != 'D') log_error("JSON root node is not a dictionary.\n"); @@ -754,7 +768,7 @@ bool parse_json_file(std::istream &f, std::string &filename, Context *ctx) log_info("Checksum: 0x%08x\n", ctx->checksum()); log_break(); - ctx->settings.emplace(ctx->id("project/input/json"), filename); + ctx->settings.emplace(ctx->id("input/json"), filename); return true; } catch (log_execution_error_exception) { return false; diff --git a/tests/ice40/hx1k.cc b/tests/ice40/hx1k.cc index b7990a82..741c954b 100644 --- a/tests/ice40/hx1k.cc +++ b/tests/ice40/hx1k.cc @@ -59,7 +59,7 @@ TEST_F(HX1KTest, wire_names) assert(wire == ctx->getWireByName(name)); wire_count++; } - ASSERT_EQ(wire_count, 27690); + ASSERT_EQ(wire_count, 32802); } TEST_F(HX1KTest, pip_names) @@ -70,7 +70,7 @@ TEST_F(HX1KTest, pip_names) assert(pip == ctx->getPipByName(name)); pip_count++; } - ASSERT_EQ(pip_count, 319904); + ASSERT_EQ(pip_count, 345504); } TEST_F(HX1KTest, uphill_to_downhill) diff --git a/tests/ice40/hx8k.cc b/tests/ice40/hx8k.cc index d5b489eb..517df0d6 100644 --- a/tests/ice40/hx8k.cc +++ b/tests/ice40/hx8k.cc @@ -59,7 +59,7 @@ TEST_F(HX8KTest, wire_names) assert(wire == ctx->getWireByName(name)); wire_count++; } - ASSERT_EQ(wire_count, 135182); + ASSERT_EQ(wire_count, 165894); } TEST_F(HX8KTest, pip_names) @@ -70,7 +70,7 @@ TEST_F(HX8KTest, pip_names) assert(pip == ctx->getPipByName(name)); pip_count++; } - ASSERT_EQ(pip_count, 1652480); + ASSERT_EQ(pip_count, 1806080); } TEST_F(HX8KTest, uphill_to_downhill) diff --git a/tests/ice40/lp1k.cc b/tests/ice40/lp1k.cc index b258d115..2fdba08b 100644 --- a/tests/ice40/lp1k.cc +++ b/tests/ice40/lp1k.cc @@ -59,7 +59,7 @@ TEST_F(LP1KTest, wire_names) assert(wire == ctx->getWireByName(name)); wire_count++; } - ASSERT_EQ(wire_count, 27690); + ASSERT_EQ(wire_count, 32802); } TEST_F(LP1KTest, pip_names) @@ -70,7 +70,7 @@ TEST_F(LP1KTest, pip_names) assert(pip == ctx->getPipByName(name)); pip_count++; } - ASSERT_EQ(pip_count, 319904); + ASSERT_EQ(pip_count, 345504); } TEST_F(LP1KTest, uphill_to_downhill) diff --git a/tests/ice40/lp384.cc b/tests/ice40/lp384.cc index a2d91e7d..a030b77b 100644 --- a/tests/ice40/lp384.cc +++ b/tests/ice40/lp384.cc @@ -59,7 +59,7 @@ TEST_F(LP384Test, wire_names) assert(wire == ctx->getWireByName(name)); wire_count++; } - ASSERT_EQ(wire_count, 8302); + ASSERT_EQ(wire_count, 9830); } TEST_F(LP384Test, pip_names) @@ -70,7 +70,7 @@ TEST_F(LP384Test, pip_names) assert(pip == ctx->getPipByName(name)); pip_count++; } - ASSERT_EQ(pip_count, 86864); + ASSERT_EQ(pip_count, 94544); } TEST_F(LP384Test, uphill_to_downhill) diff --git a/tests/ice40/lp8k.cc b/tests/ice40/lp8k.cc index a1c8c88c..7fe6ac37 100644 --- a/tests/ice40/lp8k.cc +++ b/tests/ice40/lp8k.cc @@ -59,7 +59,7 @@ TEST_F(LP8KTest, wire_names) assert(wire == ctx->getWireByName(name)); wire_count++; } - ASSERT_EQ(wire_count, 135182); + ASSERT_EQ(wire_count, 165894); } TEST_F(LP8KTest, pip_names) @@ -70,7 +70,7 @@ TEST_F(LP8KTest, pip_names) assert(pip == ctx->getPipByName(name)); pip_count++; } - ASSERT_EQ(pip_count, 1652480); + ASSERT_EQ(pip_count, 1806080); } TEST_F(LP8KTest, uphill_to_downhill) diff --git a/tests/ice40/up5k.cc b/tests/ice40/up5k.cc index 6761db2e..582876e8 100644 --- a/tests/ice40/up5k.cc +++ b/tests/ice40/up5k.cc @@ -59,7 +59,7 @@ TEST_F(UP5KTest, wire_names) assert(wire == ctx->getWireByName(name)); wire_count++; } - ASSERT_EQ(wire_count, 103391); + ASSERT_EQ(wire_count, 124503); } TEST_F(UP5KTest, pip_names) @@ -70,7 +70,7 @@ TEST_F(UP5KTest, pip_names) assert(pip == ctx->getPipByName(name)); pip_count++; } - ASSERT_EQ(pip_count, 1219104); + ASSERT_EQ(pip_count, 1324704); } TEST_F(UP5KTest, uphill_to_downhill)