Merge branch 'master' into 'master'

Master

See merge request eddiehung/nextpnr!1
This commit is contained in:
Eddie Hung 2018-07-15 19:22:57 +00:00
commit 0bae7f5606
87 changed files with 6485 additions and 1179 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
/objs/ /objs/
/nextpnr-generic* /nextpnr-generic*
/nextpnr-ice40* /nextpnr-ice40*
/nextpnr-ecp5*
cmake-build-*/ cmake-build-*/
Makefile Makefile
cmake_install.cmake cmake_install.cmake

View File

@ -74,6 +74,7 @@ public:
QtProperty *indexToProperty(const QModelIndex &index) const; QtProperty *indexToProperty(const QModelIndex &index) const;
QTreeWidgetItem *indexToItem(const QModelIndex &index) const; QTreeWidgetItem *indexToItem(const QModelIndex &index) const;
QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const; QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const;
QtBrowserItem *itemToBrowserItem(QTreeWidgetItem *item) const { return m_itemToIndex.value(item); };
bool lastColumn(int column) const; bool lastColumn(int column) const;
void disableItem(QTreeWidgetItem *item) const; void disableItem(QTreeWidgetItem *item) const;
void enableItem(QTreeWidgetItem *item) const; void enableItem(QTreeWidgetItem *item) const;
@ -1068,6 +1069,16 @@ void QtTreePropertyBrowser::editItem(QtBrowserItem *item)
d_ptr->editItem(item); d_ptr->editItem(item);
} }
QTreeWidget *QtTreePropertyBrowser::treeWidget() const
{
return d_ptr->treeWidget();
}
QtBrowserItem *QtTreePropertyBrowser::itemToBrowserItem(QTreeWidgetItem *item)
{
return d_ptr->itemToBrowserItem(item);
}
#if QT_VERSION >= 0x040400 #if QT_VERSION >= 0x040400
QT_END_NAMESPACE QT_END_NAMESPACE
#endif #endif

View File

@ -47,6 +47,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#endif #endif
class QTreeWidget;
class QTreeWidgetItem; class QTreeWidgetItem;
class QtTreePropertyBrowserPrivate; class QtTreePropertyBrowserPrivate;
@ -107,6 +108,9 @@ public:
void editItem(QtBrowserItem *item); void editItem(QtBrowserItem *item);
//ADDED:miodrag
QTreeWidget *treeWidget() const;
QtBrowserItem *itemToBrowserItem(QTreeWidgetItem *item);
Q_SIGNALS: Q_SIGNALS:
void collapsed(QtBrowserItem *item); void collapsed(QtBrowserItem *item);

View File

@ -1,322 +0,0 @@
#include "pyconsole.h"
#include "pyinterpreter.h"
#include "ColumnFormatter.h"
#include <iostream>
#include <QKeyEvent>
#include <QFont>
#include "Utils.h"
const QString PythonConsole::PROMPT = ">>> ";
const QString PythonConsole::MULTILINE_PROMPT = "... ";
const QColor PythonConsole::NORMAL_COLOR = QColor::fromRgbF( 0, 0, 0 );
const QColor PythonConsole::ERROR_COLOR = QColor::fromRgbF( 1.0, 0, 0 );
const QColor PythonConsole::OUTPUT_COLOR = QColor::fromRgbF( 0, 0, 1.0 );
PythonConsole::PythonConsole( QWidget* parent ):
QTextEdit( parent )
{
QFont font("unexistent");
font.setStyleHint(QFont::Monospace);
setFont(font);
m_parseHelper.subscribe( this );
}
void PythonConsole::keyPressEvent( QKeyEvent* e )
{
switch ( e->key() )
{
case Qt::Key_Return:
handleReturnKeyPress( );
return;
case Qt::Key_Tab:
autocomplete( );
return;
case Qt::Key_Backspace:
if ( ! canBackspace( ) )
return;
break;
case Qt::Key_Up:
previousHistory( );
return;
case Qt::Key_Down:
nextHistory( );
return;
case Qt::Key_Left:
if ( ! canGoLeft( ) )
return;
}
if (!cursorIsOnInputLine()) return;
if (textCursor().columnNumber() < PythonConsole::PROMPT.size()) return;
QTextEdit::keyPressEvent( e );
}
void PythonConsole::handleReturnKeyPress( )
{
if ( ! cursorIsOnInputLine( ) )
{
return;
}
QString line = getLine( );
m_parseHelper.process( line.toStdString( ) );
if ( m_parseHelper.buffered( ) )
{
append("");
displayPrompt( );
}
if ( line.size( ) )
{
m_historyBuffer.push_back( line.toStdString( ) );
m_historyIt = m_historyBuffer.end();
}
moveCursorToEnd( );
}
void PythonConsole::parseEvent( const ParseMessage& message )
{
// handle invalid user input
if ( message.errorCode )
{
setTextColor( ERROR_COLOR );
append(message.message.c_str());
setTextColor( NORMAL_COLOR );
append("");
displayPrompt( );
return;
}
// interpret valid user input
int errorCode = 0;
std::string res;
if ( message.message.size() )
res = pyinterpreter_execute( message.message, &errorCode );
if ( errorCode )
{
setTextColor( ERROR_COLOR );
}
else
{
setTextColor( OUTPUT_COLOR );
}
if ( res.size( ) )
{
append(res.c_str());
}
setTextColor( NORMAL_COLOR );
// set up the next line on the console
append("");
displayPrompt( );
}
QString PythonConsole::getLine( )
{
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::StartOfLine );
cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
QString line = cursor.selectedText( );
cursor.clearSelection( );
return line;
}
bool PythonConsole::cursorIsOnInputLine( )
{
int cursorBlock = textCursor( ).blockNumber( );
QTextCursor bottomCursor = textCursor( );
bottomCursor.movePosition( QTextCursor::End );
int bottomBlock = bottomCursor.blockNumber( );
return ( cursorBlock == bottomBlock );
}
bool PythonConsole::inputLineIsEmpty( )
{
QTextCursor bottomCursor = textCursor( );
bottomCursor.movePosition( QTextCursor::End );
int col = bottomCursor.columnNumber( );
return ( col == PythonConsole::PROMPT.size( ) );
}
bool PythonConsole::canBackspace( )
{
if ( ! cursorIsOnInputLine( ) )
{
return false;
}
if ( inputLineIsEmpty( ) )
{
return false;
}
return true;
}
bool PythonConsole::canGoLeft( )
{
if ( cursorIsOnInputLine( ) )
{
QTextCursor bottomCursor = textCursor( );
int col = bottomCursor.columnNumber( );
return (col > PythonConsole::PROMPT.size( ));
}
return true;
}
void PythonConsole::displayPrompt( )
{
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::End );
if ( m_parseHelper.buffered( ) )
{
cursor.insertText( PythonConsole::MULTILINE_PROMPT );
}
else
{
cursor.insertText( PythonConsole::PROMPT );
}
cursor.movePosition( QTextCursor::EndOfLine );
}
void PythonConsole::displayString(QString text)
{
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::End );
cursor.insertText( text );
cursor.movePosition( QTextCursor::EndOfLine );
}
void PythonConsole::autocomplete( )
{
if ( ! cursorIsOnInputLine( ) )
return;
QString line = getLine( );
const std::list<std::string>& suggestions =
pyinterpreter_suggest( line.toStdString( ) );
if (suggestions.size() == 1)
{
line = suggestions.back().c_str();
}
else
{
// try to complete to longest common prefix
std::string prefix =
LongestCommonPrefix(suggestions.begin(), suggestions.end());
if (prefix.size() > (size_t)line.size())
{
line = prefix.c_str();
}
else
{
ColumnFormatter fmt;
fmt.setItems(suggestions.begin(), suggestions.end());
fmt.format(width() / 10);
setTextColor( OUTPUT_COLOR );
const std::list<std::string>& formatted = fmt.formattedOutput();
for (std::list<std::string>::const_iterator it = formatted.begin();
it != formatted.end(); ++it)
{
append(it->c_str());
}
setTextColor( NORMAL_COLOR );
}
}
// set up the next line on the console
append("");
displayPrompt( );
moveCursorToEnd( );
QTextCursor cursor = textCursor();
cursor.insertText( line );
moveCursorToEnd( );
}
void PythonConsole::previousHistory( )
{
if ( ! cursorIsOnInputLine( ) )
return;
if ( ! m_historyBuffer.size( ) )
return;
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::StartOfLine );
cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
cursor.removeSelectedText( );
if ( m_historyIt != m_historyBuffer.begin( ) )
{
--m_historyIt;
}
cursor.insertText( m_historyIt->c_str() );
}
void PythonConsole::nextHistory( )
{
if ( ! cursorIsOnInputLine( ) )
return;
if ( ! m_historyBuffer.size( ) )
return;
if ( m_historyIt == m_historyBuffer.end( ) )
{
return;
}
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::StartOfLine );
cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
cursor.removeSelectedText( );
++m_historyIt;
if ( m_historyIt == m_historyBuffer.end( ) )
{
return;
}
cursor.insertText( m_historyIt->c_str() );
}
void PythonConsole::moveCursorToEnd( )
{
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::End );
setTextCursor( cursor );
}
void PythonConsole::insertFromMimeData(const QMimeData *src)
{
if (src->hasText()) {
QStringList list = src->text().split("\n",QString::KeepEmptyParts);
bool lastends = src->text().endsWith("\n");
for (int i=0;i<list.size();i++)
{
QString line = list.at(i);
displayString(line);
if (!lastends && (i==list.size()-1)) break;
m_parseHelper.process( line.toStdString( ) );
if ( m_parseHelper.buffered( ) )
{
append("");
displayPrompt( );
}
if ( line.size( ) )
{
m_historyBuffer.push_back( line.toStdString( ) );
m_historyIt = m_historyBuffer.end();
}
moveCursorToEnd( );
}
}
}

View File

@ -1,80 +0,0 @@
/**
python-console
Copyright (C) 2018 Alex Tsui
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef PYCONSOLE_H
#define PYCONSOLE_H
#include <QColor>
#include <QTextEdit>
#include <QMimeData>
#include "ParseHelper.h"
#include "ParseListener.h"
class QWidget;
class QKeyEvent;
class PythonConsole : public QTextEdit, ParseListener
{
Q_OBJECT
public:
PythonConsole(QWidget *parent = 0);
void displayPrompt();
void displayString(QString text);
protected:
// override QTextEdit
virtual void keyPressEvent(QKeyEvent *e);
virtual void handleReturnKeyPress();
virtual void insertFromMimeData(const QMimeData *src);
/**
Handle a compilable chunk of Python user input.
*/
virtual void parseEvent(const ParseMessage &message);
QString getLine();
bool cursorIsOnInputLine();
bool inputLineIsEmpty();
bool canBackspace();
bool canGoLeft();
void autocomplete();
void previousHistory();
void nextHistory();
void moveCursorToEnd();
static const QString PROMPT;
static const QString MULTILINE_PROMPT;
static const QColor NORMAL_COLOR;
static const QColor ERROR_COLOR;
static const QColor OUTPUT_COLOR;
ParseHelper m_parseHelper;
std::list<std::string> m_historyBuffer;
std::list<std::string>::const_iterator m_historyIt;
};
#endif // PYCONSOLE_H

View File

@ -7,7 +7,31 @@ option(BUILD_PYTHON "Build Python Integration" ON)
option(BUILD_TESTS "Build GUI" OFF) option(BUILD_TESTS "Build GUI" OFF)
# List of families to build # List of families to build
set(FAMILIES generic ice40) set(FAMILIES generic ice40 ecp5)
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
if (NOT ARCH)
message(STATUS "Architecture needs to be set, set desired one with -DARCH=xxx")
message(STATUS "Supported architectures are :")
message(STATUS " all")
foreach(item ${FAMILIES})
message(STATUS " ${item}")
endforeach()
message(FATAL_ERROR "Architecture setting is mandatory")
endif ()
if (ARCH STREQUAL "all")
SET(ARCH ${FAMILIES})
endif()
foreach(item ${ARCH})
if (NOT item IN_LIST FAMILIES)
message(FATAL_ERROR "Architecture '${item}' not in list of supported architectures")
endif()
endforeach()
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
if (MSVC) if (MSVC)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE) set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
@ -30,6 +54,10 @@ find_package(Sanitizers)
# List of Boost libraries to include # List of Boost libraries to include
set(boost_libs filesystem thread program_options) set(boost_libs filesystem thread program_options)
if (BUILD_GUI AND NOT BUILD_PYTHON)
message(FATAL_ERROR "GUI requires Python to build")
endif()
if (BUILD_PYTHON) if (BUILD_PYTHON)
# TODO: sensible minimum Python version # TODO: sensible minimum Python version
find_package(PythonInterp 3.5 REQUIRED) find_package(PythonInterp 3.5 REQUIRED)
@ -62,7 +90,7 @@ if (BUILD_TESTS)
endif() endif()
if (BUILD_GUI) if (BUILD_GUI)
add_subdirectory(3rdparty/QtPropertyBrowser ${CMAKE_CURRENT_BINARY_DIR}/generated/3rdparty/QtPropertyBrowser) add_subdirectory(3rdparty/QtPropertyBrowser ${CMAKE_CURRENT_BINARY_DIR}/generated/3rdparty/QtPropertyBrowser EXCLUDE_FROM_ALL)
endif() endif()
add_definitions("-DGIT_COMMIT_HASH=${GIT_COMMIT_HASH}") add_definitions("-DGIT_COMMIT_HASH=${GIT_COMMIT_HASH}")
@ -109,6 +137,13 @@ if (BUILD_PYTHON)
endif () endif ()
endif () endif ()
if (NOT Boost_PYTHON_FOUND)
find_package(Boost COMPONENTS python37 ${boost_libs})
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
endif ()
if (NOT Boost_PYTHON_FOUND) if (NOT Boost_PYTHON_FOUND)
STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING}) STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING})
find_package(Boost COMPONENTS python-${gentoo_version} ${boost_libs}) find_package(Boost COMPONENTS python-${gentoo_version} ${boost_libs})
@ -132,22 +167,23 @@ if(MINGW)
add_definitions("-Wa,-mbig-obj") add_definitions("-Wa,-mbig-obj")
endif(MINGW) endif(MINGW)
foreach (family ${FAMILIES}) foreach (family ${ARCH})
message(STATUS "Configuring architecture : ${family}")
string(TOUPPER ${family} ufamily) string(TOUPPER ${family} ufamily)
aux_source_directory(${family}/ ${ufamily}_FILES) aux_source_directory(${family}/ ${ufamily}_FILES)
if (BUILD_GUI) if (BUILD_GUI)
add_subdirectory(gui ${CMAKE_CURRENT_BINARY_DIR}/generated/gui/${family}) add_subdirectory(gui ${CMAKE_CURRENT_BINARY_DIR}/generated/gui/${family} EXCLUDE_FROM_ALL)
endif() endif()
# Add the CLI binary target # Add the CLI binary target
add_executable(nextpnr-${family} ${COMMON_FILES} ${${ufamily}_FILES} ) add_executable(nextpnr-${family} ${COMMON_FILES} ${${ufamily}_FILES})
install(TARGETS nextpnr-${family} RUNTIME DESTINATION bin) install(TARGETS nextpnr-${family} RUNTIME DESTINATION bin)
target_compile_definitions(nextpnr-${family} PRIVATE MAIN_EXECUTABLE) target_compile_definitions(nextpnr-${family} PRIVATE MAIN_EXECUTABLE)
if (BUILD_PYTHON) if (BUILD_PYTHON)
# Add the importable Python module target # Add the importable Python module target
PYTHON_ADD_MODULE(nextpnrpy_${family} EXCLUDE_FROM_ALL ${COMMON_FILES} ${${ufamily}_FILES}) PYTHON_ADD_MODULE(nextpnrpy_${family} ${COMMON_FILES} ${${ufamily}_FILES})
endif() endif()
# Add any new per-architecture targets here # Add any new per-architecture targets here
@ -178,7 +214,7 @@ foreach (family ${FAMILIES})
# Include family-specific source files to all family targets and set defines appropriately # 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_include_directories(${target} PRIVATE ${family}/ ${CMAKE_CURRENT_BINARY_DIR}/generated/)
target_compile_definitions(${target} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family}) 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} pthread)
add_sanitizers(${target}) add_sanitizers(${target})
if (BUILD_GUI) if (BUILD_GUI)
target_include_directories(${target} PRIVATE gui/${family}/ gui/) target_include_directories(${target} PRIVATE gui/${family}/ gui/)
@ -193,6 +229,7 @@ endforeach (family)
file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h) file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h)
string(REGEX REPLACE "[^;]*/ice40/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") string(REGEX REPLACE "[^;]*/ice40/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
string(REGEX REPLACE "[^;]*/ecp5/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
string(REGEX REPLACE "[^;]*/3rdparty[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") string(REGEX REPLACE "[^;]*/3rdparty[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
string(REGEX REPLACE "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}") string(REGEX REPLACE "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")

View File

@ -5,6 +5,7 @@ Supported Architectures
----------------------- -----------------------
- iCE40 - iCE40
- ECP5
Prequisites Prequisites
----------- -----------
@ -23,23 +24,30 @@ Prequisites
- For building on macOS, brew utility is needed. - For building on macOS, brew utility is needed.
- Install all needed packages `brew install cmake python boost boost-python3 qt5` - Install all needed packages `brew install cmake python boost boost-python3 qt5`
- Do not forget to add qt5 in path as well `echo 'export PATH="/usr/local/opt/qt/bin:$PATH"' >> ~/.bash_profile` - Do not forget to add qt5 in path as well `echo 'export PATH="/usr/local/opt/qt/bin:$PATH"' >> ~/.bash_profile`
- For ECP5 support, you must download [Project Trellis](https://github.com/SymbiFlow/prjtrellis), then follow its instructions to
download the latest database and build _libtrellis_.
Building Building
-------- --------
- Specifying target architecture is mandatory use ARCH parameter to set it. It is semicolon separated list.
- Use `cmake . -DARCH=all` to build all supported targets
- For example `cmake . -DARCH=ice40` would build just ICE40 support
- Use CMake to generate the Makefiles (only needs to be done when `CMakeLists.txt` changes) - Use CMake to generate the Makefiles (only needs to be done when `CMakeLists.txt` changes)
- For a debug build, run `cmake -DCMAKE_BUILD_TYPE=Debug .` - For an iCE40 debug build, run `cmake -DARCH=ice40 -DCMAKE_BUILD_TYPE=Debug .`
- For a debug build with HX1K support only, run ` cmake -DCMAKE_BUILD_TYPE=Debug -DICE40_HX1K_ONLY=1 .` - For an iCE40 debug build with HX1K support only, run `cmake -DARCH=ice40 -DCMAKE_BUILD_TYPE=Debug -DICE40_HX1K_ONLY=1 .`
- For a release build, run `cmake .` - For an iCE40 and ECP5 release build, run `cmake -DARCH="ice40;ecp5" .`
- Add `-DCMAKE_INSTALL_PREFIX=/your/install/prefix` to use a different install prefix to the default `/usr/local` - Add `-DCMAKE_INSTALL_PREFIX=/your/install/prefix` to use a different install prefix to the default `/usr/local`
- For MSVC build with vcpkg use `cmake . -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake` using your vcpkg location - For MSVC build with vcpkg use `-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake` using your vcpkg location
- For MSVC x64 build adding `-G"Visual Studio 14 2015 Win64"` is needed. - For MSVC x64 build adding `-G"Visual Studio 14 2015 Win64"` is needed.
- For ECP5 support, you must also specify the path to Project Trellis using `-DTRELLIS_ROOT=/path/trellis`
- Use Make to run the build itself - Use Make to run the build itself
- For all binary targets, just run `make` - For all binary targets, just run `make`
- For just the iCE40 CLI&GUI binary, run `make nextpnr-ice40` - For just the iCE40 CLI&GUI binary, run `make nextpnr-ice40`
- To build binary without Python support, run `cmake -DBUILD_PYTHON=OFF .` - To build binary without Python support, use `-DBUILD_PYTHON=OFF`
- To build binary without GUI, run `cmake -DBUILD_GUI=OFF .` - To build binary without GUI, use `-DBUILD_GUI=OFF`
- For minimal binary without Python and GUI, run `cmake -DBUILD_PYTHON=OFF -DBUILD_GUI=OFF .` - For minimal binary without Python and GUI, use `-DBUILD_PYTHON=OFF -DBUILD_GUI=OFF`
- For just the iCE40 Python module, run `make nextpnrpy_ice40` - For just the iCE40 Python module, run `make nextpnrpy_ice40`
- Using too many parallel jobs may lead to out-of-memory issues due to the significant memory needed to build the chipdbs - Using too many parallel jobs may lead to out-of-memory issues due to the significant memory needed to build the chipdbs
- To install nextpnr, run `make install` - To install nextpnr, run `make install`
@ -47,12 +55,12 @@ Building
Testing Testing
------- -------
- To build test binaries as well, run `cmake -DBUILD_TESTS=OFF .` and after run `make tests` to run them, or you can run separate binaries. - To build test binaries as well, use `-DBUILD_TESTS=OFF` and after run `make tests` to run them, or you can run separate binaries.
- To use code sanitizers use: - To use code sanitizers use the `cmake` options:
- `cmake . -DSANITIZE_ADDRESS=ON` - `-DSANITIZE_ADDRESS=ON`
- `cmake . -DSANITIZE_MEMORY=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++` - `-DSANITIZE_MEMORY=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++`
- `cmake . -DSANITIZE_THREAD=ON` - `-DSANITIZE_THREAD=ON`
- `cmake . -DSANITIZE_UNDEFINED=ON` - `-DSANITIZE_UNDEFINED=ON`
- Running valgrind example `valgrind --leak-check=yes --tool=memcheck ./nextpnr-ice40 --json ice40/blinky.json` - Running valgrind example `valgrind --leak-check=yes --tool=memcheck ./nextpnr-ice40 --json ice40/blinky.json`
Running Running
@ -65,6 +73,12 @@ Running
produce `blinky.json`. produce `blinky.json`.
- To place-and-route the blinky using nextpnr, run `./nextpnr-ice40 --hx1k --json ice40/blinky.json --pcf ice40/blinky.pcf --asc blinky.asc` - To place-and-route the blinky using nextpnr, run `./nextpnr-ice40 --hx1k --json ice40/blinky.json --pcf ice40/blinky.pcf --asc blinky.asc`
- For an ECP5 blinky, first synthesise using `yosys blinky.ys` in `ecp5/synth`.
- Then run ECP5 place-and route using
`./nextpnr-ecp5 --json ecp5/synth/blinky.json --basecfg ecp5/synth/ulx3s_empty.config --bit ecp5/synth/ulx3s.bit`
- Note that `ulx3s_empty.config` contains fixed/unknown bits to be copied to the output bitstream
- You can also use `--textcfg out.config` to write a text file describing the bitstream for debugging
Notes Notes
------- -------

View File

@ -136,7 +136,7 @@ NEXTPNR_NAMESPACE_BEGIN
struct GraphicElement struct GraphicElement
{ {
enum enum type_t
{ {
G_NONE, G_NONE,
G_LINE, G_LINE,
@ -145,6 +145,14 @@ struct GraphicElement
G_LABEL G_LABEL
} type = G_NONE; } type = G_NONE;
enum style_t
{
G_FRAME,
G_HIDDEN,
G_INACTIVE,
G_ACTIVE,
} style = G_FRAME;
float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0; float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
std::string text; std::string text;
}; };
@ -155,6 +163,12 @@ NEXTPNR_NAMESPACE_END
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
struct DecalXY
{
DecalId decal;
float x = 0, y = 0;
};
struct BelPin struct BelPin
{ {
BelId bel; BelId bel;
@ -253,6 +267,31 @@ struct BaseCtx
delete idstring_str_to_idx; delete idstring_str_to_idx;
delete idstring_idx_to_str; delete idstring_idx_to_str;
} }
Context *getCtx() { return reinterpret_cast<Context *>(this); }
const Context *getCtx() const { return reinterpret_cast<const Context *>(this); }
// --------------------------------------------------------------
bool allUiReload = false;
bool frameUiReload = false;
std::unordered_set<BelId> belUiReload;
std::unordered_set<WireId> wireUiReload;
std::unordered_set<PipId> pipUiReload;
std::unordered_set<GroupId> groupUiReload;
void refreshUi() { allUiReload = true; }
void refreshUiFrame() { frameUiReload = true; }
void refreshUiBel(BelId bel) { belUiReload.insert(bel); }
void refreshUiWire(WireId wire) { wireUiReload.insert(wire); }
void refreshUiPip(PipId pip) { pipUiReload.insert(pip); }
void refreshUiGroup(GroupId group) { groupUiReload.insert(group); }
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
@ -273,6 +312,11 @@ struct Context : Arch
// -------------------------------------------------------------- // --------------------------------------------------------------
// provided by router1.cc
bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t &delay);
// --------------------------------------------------------------
uint64_t rngstate = 0x3141592653589793; uint64_t rngstate = 0x3141592653589793;
uint64_t rng64() uint64_t rng64()

View File

@ -21,7 +21,7 @@
* *
*/ */
#include "place_sa.h" #include "placer1.h"
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
@ -429,7 +429,7 @@ class SAPlacer
const float post_legalise_dia_scale = 2; const float post_legalise_dia_scale = 2;
}; };
bool place_design_sa(Context *ctx) bool placer1(Context *ctx)
{ {
try { try {
SAPlacer placer(ctx); SAPlacer placer(ctx);

View File

@ -23,7 +23,7 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
extern bool place_design_sa(Context *ctx); extern bool placer1(Context *ctx);
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -110,13 +110,13 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
readwrite_wrapper<CellInfo &, decltype(&CellInfo::belStrength), &CellInfo::belStrength, pass_through<PlaceStrength>, readwrite_wrapper<CellInfo &, decltype(&CellInfo::belStrength), &CellInfo::belStrength, pass_through<PlaceStrength>,
pass_through<PlaceStrength>>::def_wrap(ci_cls, "belStrength"); pass_through<PlaceStrength>>::def_wrap(ci_cls, "belStrength");
readonly_wrapper<CellInfo &, decltype(&CellInfo::pins), &CellInfo::pins, wrap_context<PinMap &>>::def_wrap(ci_cls, readonly_wrapper<CellInfo &, decltype(&CellInfo::pins), &CellInfo::pins, wrap_context<PinMap &>>::def_wrap(ci_cls,
"pins"); "pins");
auto pi_cls = class_<ContextualWrapper<PortInfo &>>("PortInfo", no_init); auto pi_cls = class_<ContextualWrapper<PortInfo &>>("PortInfo", no_init);
readwrite_wrapper<PortInfo &, decltype(&PortInfo::name), &PortInfo::name, conv_to_str<IdString>, readwrite_wrapper<PortInfo &, decltype(&PortInfo::name), &PortInfo::name, conv_to_str<IdString>,
conv_from_str<IdString>>::def_wrap(pi_cls, "name"); conv_from_str<IdString>>::def_wrap(pi_cls, "name");
readonly_wrapper<PortInfo &, decltype(&PortInfo::net), &PortInfo::net, deref_and_wrap<NetInfo>>::def_wrap(pi_cls, readonly_wrapper<PortInfo &, decltype(&PortInfo::net), &PortInfo::net, deref_and_wrap<NetInfo>>::def_wrap(pi_cls,
"net"); "net");
readwrite_wrapper<PortInfo &, decltype(&PortInfo::type), &PortInfo::type, pass_through<PortType>, readwrite_wrapper<PortInfo &, decltype(&PortInfo::type), &PortInfo::type, pass_through<PortType>,
pass_through<PortType>>::def_wrap(pi_cls, "type"); pass_through<PortType>>::def_wrap(pi_cls, "type");
@ -131,11 +131,11 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
readonly_wrapper<NetInfo &, decltype(&NetInfo::users), &NetInfo::users, wrap_context<PortVector &>>::def_wrap( readonly_wrapper<NetInfo &, decltype(&NetInfo::users), &NetInfo::users, wrap_context<PortVector &>>::def_wrap(
ni_cls, "users"); ni_cls, "users");
readonly_wrapper<NetInfo &, decltype(&NetInfo::wires), &NetInfo::wires, wrap_context<WireMap &>>::def_wrap(ni_cls, readonly_wrapper<NetInfo &, decltype(&NetInfo::wires), &NetInfo::wires, wrap_context<WireMap &>>::def_wrap(ni_cls,
"wires"); "wires");
auto pr_cls = class_<ContextualWrapper<PortRef &>>("PortRef", no_init); auto pr_cls = class_<ContextualWrapper<PortRef &>>("PortRef", no_init);
readonly_wrapper<PortRef &, decltype(&PortRef::cell), &PortRef::cell, deref_and_wrap<CellInfo>>::def_wrap(pr_cls, readonly_wrapper<PortRef &, decltype(&PortRef::cell), &PortRef::cell, deref_and_wrap<CellInfo>>::def_wrap(pr_cls,
"cell"); "cell");
readwrite_wrapper<PortRef &, decltype(&PortRef::port), &PortRef::port, conv_to_str<IdString>, readwrite_wrapper<PortRef &, decltype(&PortRef::port), &PortRef::port, conv_to_str<IdString>,
conv_from_str<IdString>>::def_wrap(pr_cls, "port"); conv_from_str<IdString>>::def_wrap(pr_cls, "port");
readwrite_wrapper<PortRef &, decltype(&PortRef::budget), &PortRef::budget, pass_through<delay_t>, readwrite_wrapper<PortRef &, decltype(&PortRef::budget), &PortRef::budget, pass_through<delay_t>,
@ -151,7 +151,9 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
arch_wrap_python(); arch_wrap_python();
} }
#ifdef MAIN_EXECUTABLE
static wchar_t *program; static wchar_t *program;
#endif
void init_python(const char *executable, bool first) void init_python(const char *executable, bool first)
{ {

View File

@ -21,7 +21,7 @@
#include <queue> #include <queue>
#include "log.h" #include "log.h"
#include "route.h" #include "router1.h"
namespace { namespace {
@ -402,7 +402,7 @@ struct Router
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
bool route_design(Context *ctx) bool router1(Context *ctx)
{ {
try { try {
int totalVisitCnt = 0, totalRevisitCnt = 0, totalOvertimeRevisitCnt = 0; int totalVisitCnt = 0, totalRevisitCnt = 0, totalOvertimeRevisitCnt = 0;
@ -643,10 +643,10 @@ bool route_design(Context *ctx)
} }
} }
bool get_actual_route_delay(Context *ctx, WireId src_wire, WireId dst_wire, delay_t &delay) bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t &delay)
{ {
RipupScoreboard scores; RipupScoreboard scores;
Router router(ctx, scores, src_wire, dst_wire); Router router(this, scores, src_wire, dst_wire);
if (router.routedOkay) if (router.routedOkay)
delay = router.visited.at(dst_wire).delay; delay = router.visited.at(dst_wire).delay;
return router.routedOkay; return router.routedOkay;

View File

@ -2,7 +2,6 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -18,15 +17,15 @@
* *
*/ */
#ifndef PACK_H #ifndef ROUTER1_H
#define PACK_H #define ROUTER1_H
#include "nextpnr.h" #include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
bool pack_design(Context *ctx); extern bool router1(Context *ctx);
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif // ROUTE_H #endif // ROUTER1_H

View File

@ -39,6 +39,18 @@ ValueType get_or_default(const Container &ct, const KeyType &key, ValueType def
return found->second; return found->second;
}; };
// Get a value from a map-style container, returning default if value is not
// found (forces string)
template <typename Container, typename KeyType>
std::string str_or_default(const Container &ct, const KeyType &key, std::string def = "")
{
auto found = ct.find(key);
if (found == ct.end())
return def;
else
return found->second;
};
// Get a value from a map-style container, converting to int, and returning // Get a value from a map-style container, converting to int, and returning
// default if value is not found // default if value is not found
template <typename Container, typename KeyType> int int_or_default(const Container &ct, const KeyType &key, int def = 0) template <typename Container, typename KeyType> int int_or_default(const Container &ct, const KeyType &key, int def = 0)
@ -84,6 +96,7 @@ inline const NetInfo *get_net_or_empty(const CellInfo *cell, const IdString port
else else
return nullptr; return nullptr;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif #endif

2
ecp5/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__
chipdbs/

333
ecp5/arch.cc Normal file
View File

@ -0,0 +1,333 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <algorithm>
#include <cmath>
#include <cstring>
#include "log.h"
#include "nextpnr.h"
#include "placer1.h"
#include "router1.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
static std::tuple<int, int, std::string> split_identifier_name(const std::string &name)
{
size_t first_slash = name.find('/');
NPNR_ASSERT(first_slash != std::string::npos);
size_t second_slash = name.find('/', first_slash + 1);
NPNR_ASSERT(second_slash != std::string::npos);
return std::make_tuple(std::stoi(name.substr(1, first_slash)),
std::stoi(name.substr(first_slash + 2, second_slash - first_slash)),
name.substr(second_slash + 1));
};
// -----------------------------------------------------------------------
IdString Arch::belTypeToId(BelType type) const
{
if (type == TYPE_TRELLIS_SLICE)
return id("TRELLIS_SLICE");
if (type == TYPE_TRELLIS_IO)
return id("TRELLIS_IO");
return IdString();
}
BelType Arch::belTypeFromId(IdString type) const
{
if (type == id("TRELLIS_SLICE"))
return TYPE_TRELLIS_SLICE;
if (type == id("TRELLIS_IO"))
return TYPE_TRELLIS_IO;
return TYPE_NONE;
}
// -----------------------------------------------------------------------
void IdString::initialize_arch(const BaseCtx *ctx)
{
#define X(t) initialize_add(ctx, #t, PIN_##t);
#include "portpins.inc"
#undef X
}
IdString Arch::portPinToId(PortPin type) const
{
IdString ret;
if (type > 0 && type < PIN_MAXIDX)
ret.index = type;
return ret;
}
PortPin Arch::portPinFromId(IdString type) const
{
if (type.index > 0 && type.index < PIN_MAXIDX)
return PortPin(type.index);
return PIN_NONE;
}
// -----------------------------------------------------------------------
static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); }
#if defined(_MSC_VER)
void load_chipdb();
#endif
#define LFE5U_45F_ONLY
Arch::Arch(ArchArgs args) : args(args)
{
#if defined(_MSC_VER)
load_chipdb();
#endif
#ifdef LFE5U_45F_ONLY
if (args.type == ArchArgs::LFE5U_45F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_45k));
} else {
log_error("Unsupported ECP5 chip type.\n");
}
#else
if (args.type == ArchArgs::LFE5U_25F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_25k));
} else if (args.type == ArchArgs::LFE5U_45F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_45k));
} else if (args.type == ArchArgs::LFE5U_85F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_85k));
} else {
log_error("Unsupported ECP5 chip type.\n");
}
#endif
}
// -----------------------------------------------------------------------
std::string Arch::getChipName()
{
if (args.type == ArchArgs::LFE5U_25F) {
return "LFE5U-25F";
} else if (args.type == ArchArgs::LFE5U_45F) {
return "LFE5U-45F";
} else if (args.type == ArchArgs::LFE5U_85F) {
return "LFE5U-85F";
} else {
log_error("Unknown chip\n");
}
}
// -----------------------------------------------------------------------
IdString Arch::archArgsToId(ArchArgs args) const
{
if (args.type == ArchArgs::LFE5U_25F)
return id("lfe5u_25f");
if (args.type == ArchArgs::LFE5U_45F)
return id("lfe5u_45f");
if (args.type == ArchArgs::LFE5U_85F)
return id("lfe5u_85f");
return IdString();
}
// -----------------------------------------------------------------------
BelId Arch::getBelByName(IdString name) const
{
BelId ret;
auto it = bel_by_name.find(name);
if (it != bel_by_name.end())
return it->second;
Location loc;
std::string basename;
std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this));
ret.location = loc;
const LocationTypePOD *loci = locInfo(ret);
for (int i = 0; i < loci->num_bels; i++) {
if (std::strcmp(loci->bel_data[i].name.get(), basename.c_str()) == 0) {
ret.index = i;
break;
}
}
if (ret.index >= 0)
bel_by_name[name] = ret;
return ret;
}
BelRange Arch::getBelsAtSameTile(BelId bel) const
{
BelRange br;
NPNR_ASSERT(bel != BelId());
br.b.cursor_tile = bel.location.y * chip_info->width + bel.location.x;
br.e.cursor_tile = bel.location.y * chip_info->width + bel.location.x;
br.b.cursor_index = 0;
br.e.cursor_index = locInfo(bel)->num_bels;
return br;
}
WireId Arch::getWireBelPin(BelId bel, PortPin pin) const
{
WireId ret;
NPNR_ASSERT(bel != BelId());
int num_bel_wires = locInfo(bel)->bel_data[bel.index].num_bel_wires;
const BelWirePOD *bel_wires = locInfo(bel)->bel_data[bel.index].bel_wires.get();
for (int i = 0; i < num_bel_wires; i++)
if (bel_wires[i].port == pin) {
ret.location = bel.location + bel_wires[i].rel_wire_loc;
ret.index = bel_wires[i].wire_index;
break;
}
return ret;
}
// -----------------------------------------------------------------------
WireId Arch::getWireByName(IdString name) const
{
WireId ret;
auto it = wire_by_name.find(name);
if (it != wire_by_name.end())
return it->second;
Location loc;
std::string basename;
std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this));
ret.location = loc;
const LocationTypePOD *loci = locInfo(ret);
for (int i = 0; i < loci->num_wires; i++) {
if (std::strcmp(loci->wire_data[i].name.get(), basename.c_str()) == 0) {
ret.index = i;
ret.location = loc;
break;
}
}
if (ret.index >= 0)
wire_by_name[name] = ret;
else
ret.location = Location();
return ret;
}
// -----------------------------------------------------------------------
PipId Arch::getPipByName(IdString name) const
{
auto it = pip_by_name.find(name);
if (it != pip_by_name.end())
return it->second;
PipId ret;
Location loc;
std::string basename;
std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this));
const LocationTypePOD *loci = locInfo(ret);
for (int i = 0; i < loci->num_pips; i++) {
PipId curr;
curr.location = loc;
curr.index = i;
pip_by_name[getPipName(curr)] = curr;
}
return pip_by_name[name];
}
IdString Arch::getPipName(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
int x = pip.location.x;
int y = pip.location.y;
std::string src_name = getWireName(getPipSrcWire(pip)).str(this);
std::replace(src_name.begin(), src_name.end(), '/', '.');
std::string dst_name = getWireName(getPipDstWire(pip)).str(this);
std::replace(dst_name.begin(), dst_name.end(), '/', '.');
return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name);
}
// -----------------------------------------------------------------------
BelId Arch::getPackagePinBel(const std::string &pin) const { return BelId(); }
std::string Arch::getBelPackagePin(BelId bel) const { return ""; }
// -----------------------------------------------------------------------
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
{
x = bel.location.x;
y = bel.location.y;
gb = false;
}
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
return abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y);
}
// -----------------------------------------------------------------------
bool Arch::place() { return placer1(getCtx()); }
bool Arch::route() { return router1(getCtx()); }
// -----------------------------------------------------------------------
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decalId) const
{
std::vector<GraphicElement> ret;
// FIXME
return ret;
}
DecalXY Arch::getFrameDecal() const { return {}; }
DecalXY Arch::getBelDecal(BelId bel) const { return {}; }
DecalXY Arch::getWireDecal(WireId wire) const { return {}; }
DecalXY Arch::getPipDecal(PipId pip) const { return {}; };
DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
// -----------------------------------------------------------------------
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; }
bool Arch::isBelLocationValid(BelId bel) const { return true; }
// -----------------------------------------------------------------------
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const
{
return false;
}
IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return IdString(); }
bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; }
NEXTPNR_NAMESPACE_END

765
ecp5/arch.h Normal file
View File

@ -0,0 +1,765 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef NEXTPNR_H
#error Include "arch.h" via "nextpnr.h" only.
#endif
#include <sstream>
NEXTPNR_NAMESPACE_BEGIN
/**** Everything in this section must be kept in sync with chipdb.py ****/
template <typename T> struct RelPtr
{
int32_t offset;
// void set(const T *ptr) {
// offset = reinterpret_cast<const char*>(ptr) -
// reinterpret_cast<const char*>(this);
// }
const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }
const T &operator[](size_t index) const { return get()[index]; }
const T &operator*() const { return *(get()); }
const T *operator->() const { return get(); }
};
NPNR_PACKED_STRUCT(struct BelWirePOD {
LocationPOD rel_wire_loc;
int32_t wire_index;
PortPin port;
});
NPNR_PACKED_STRUCT(struct BelInfoPOD {
RelPtr<char> name;
BelType type;
int32_t num_bel_wires;
RelPtr<BelWirePOD> bel_wires;
});
NPNR_PACKED_STRUCT(struct BelPortPOD {
LocationPOD rel_bel_loc;
int32_t bel_index;
PortPin port;
});
NPNR_PACKED_STRUCT(struct PipInfoPOD {
LocationPOD rel_src_loc, rel_dst_loc;
int32_t src_idx, dst_idx;
int32_t delay;
int16_t tile_type;
int8_t pip_type;
int8_t padding_0;
});
NPNR_PACKED_STRUCT(struct PipLocatorPOD {
LocationPOD rel_loc;
int32_t index;
});
NPNR_PACKED_STRUCT(struct WireInfoPOD {
RelPtr<char> name;
int32_t num_uphill, num_downhill;
RelPtr<PipLocatorPOD> pips_uphill, pips_downhill;
int32_t num_bels_downhill;
BelPortPOD bel_uphill;
RelPtr<BelPortPOD> bels_downhill;
});
NPNR_PACKED_STRUCT(struct LocationTypePOD {
int32_t num_bels, num_wires, num_pips;
RelPtr<BelInfoPOD> bel_data;
RelPtr<WireInfoPOD> wire_data;
RelPtr<PipInfoPOD> pip_data;
});
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_tiles;
int32_t num_location_types;
RelPtr<LocationTypePOD> locations;
RelPtr<int32_t> location_type;
RelPtr<RelPtr<char>> tiletype_names;
});
#if defined(_MSC_VER)
extern const char *chipdb_blob_25k;
extern const char *chipdb_blob_45k;
extern const char *chipdb_blob_85k;
#else
extern const char chipdb_blob_25k[];
extern const char chipdb_blob_45k[];
extern const char chipdb_blob_85k[];
#endif
/************************ End of chipdb section. ************************/
struct BelIterator
{
const ChipInfoPOD *chip;
int cursor_index;
int cursor_tile;
BelIterator operator++()
{
cursor_index++;
while (cursor_tile < chip->num_tiles &&
cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_bels) {
cursor_index = 0;
cursor_tile++;
}
return *this;
}
BelIterator operator++(int)
{
BelIterator prior(*this);
++(*this);
return prior;
}
bool operator!=(const BelIterator &other) const
{
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
}
bool operator==(const BelIterator &other) const
{
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
}
BelId operator*() const
{
BelId ret;
ret.location.x = cursor_tile % chip->width;
ret.location.y = cursor_tile / chip->width;
ret.index = cursor_index;
return ret;
}
};
struct BelRange
{
BelIterator b, e;
BelIterator begin() const { return b; }
BelIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct BelPinIterator
{
const BelPortPOD *ptr = nullptr;
Location wire_loc;
void operator++() { ptr++; }
bool operator!=(const BelPinIterator &other) const { return ptr != other.ptr; }
BelPin operator*() const
{
BelPin ret;
ret.bel.index = ptr->bel_index;
ret.bel.location = wire_loc + ptr->rel_bel_loc;
ret.pin = ptr->port;
return ret;
}
};
struct BelPinRange
{
BelPinIterator b, e;
BelPinIterator begin() const { return b; }
BelPinIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct WireIterator
{
const ChipInfoPOD *chip;
int cursor_index;
int cursor_tile;
WireIterator operator++()
{
cursor_index++;
while (cursor_tile < chip->num_tiles &&
cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_wires) {
cursor_index = 0;
cursor_tile++;
}
return *this;
}
WireIterator operator++(int)
{
WireIterator prior(*this);
++(*this);
return prior;
}
bool operator!=(const WireIterator &other) const
{
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
}
bool operator==(const WireIterator &other) const
{
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
}
WireId operator*() const
{
WireId ret;
ret.location.x = cursor_tile % chip->width;
ret.location.y = cursor_tile / chip->width;
ret.index = cursor_index;
return ret;
}
};
struct WireRange
{
WireIterator b, e;
WireIterator begin() const { return b; }
WireIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct AllPipIterator
{
const ChipInfoPOD *chip;
int cursor_index;
int cursor_tile;
AllPipIterator operator++()
{
cursor_index++;
while (cursor_tile < chip->num_tiles &&
cursor_index >= chip->locations[chip->location_type[cursor_tile]].num_pips) {
cursor_index = 0;
cursor_tile++;
}
return *this;
}
AllPipIterator operator++(int)
{
AllPipIterator prior(*this);
++(*this);
return prior;
}
bool operator!=(const AllPipIterator &other) const
{
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
}
bool operator==(const AllPipIterator &other) const
{
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
}
PipId operator*() const
{
PipId ret;
ret.location.x = cursor_tile % chip->width;
ret.location.y = cursor_tile / chip->width;
ret.index = cursor_index;
return ret;
}
};
struct AllPipRange
{
AllPipIterator b, e;
AllPipIterator begin() const { return b; }
AllPipIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct PipIterator
{
const PipLocatorPOD *cursor = nullptr;
Location wire_loc;
void operator++() { cursor++; }
bool operator!=(const PipIterator &other) const { return cursor != other.cursor; }
PipId operator*() const
{
PipId ret;
ret.index = cursor->index;
ret.location = wire_loc + cursor->rel_loc;
return ret;
}
};
struct PipRange
{
PipIterator b, e;
PipIterator begin() const { return b; }
PipIterator end() const { return e; }
};
struct ArchArgs
{
enum
{
NONE,
LFE5U_25F,
LFE5U_45F,
LFE5U_85F,
} type = NONE;
std::string package;
int speed = 6;
};
struct Arch : BaseCtx
{
const ChipInfoPOD *chip_info;
mutable std::unordered_map<IdString, BelId> bel_by_name;
mutable std::unordered_map<IdString, WireId> wire_by_name;
mutable std::unordered_map<IdString, PipId> pip_by_name;
std::unordered_map<BelId, IdString> bel_to_cell;
std::unordered_map<WireId, IdString> wire_to_net;
std::unordered_map<PipId, IdString> pip_to_net;
std::unordered_map<PipId, IdString> switches_locked;
ArchArgs args;
Arch(ArchArgs args);
std::string getChipName();
IdString archId() const { return id("ecp5"); }
IdString archArgsToId(ArchArgs args) const;
IdString belTypeToId(BelType type) const;
BelType belTypeFromId(IdString id) const;
IdString portPinToId(PortPin type) const;
PortPin portPinFromId(IdString id) const;
// -------------------------------------------------
BelId getBelByName(IdString name) const;
template <typename Id> const LocationTypePOD *locInfo(Id &id) const
{
return &(chip_info->locations[chip_info->location_type[id.location.y * chip_info->width + id.location.x]]);
}
IdString getBelName(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
std::stringstream name;
name << "X" << bel.location.x << "/Y" << bel.location.y << "/" << locInfo(bel)->bel_data[bel.index].name.get();
return id(name.str());
}
uint32_t getBelChecksum(BelId bel) const { return bel.index; }
void bindBel(BelId bel, IdString cell, PlaceStrength strength)
{
NPNR_ASSERT(bel != BelId());
NPNR_ASSERT(bel_to_cell[bel] == IdString());
bel_to_cell[bel] = cell;
cells[cell]->bel = bel;
cells[cell]->belStrength = strength;
}
void unbindBel(BelId bel)
{
NPNR_ASSERT(bel != BelId());
NPNR_ASSERT(bel_to_cell[bel] != IdString());
cells[bel_to_cell[bel]]->bel = BelId();
cells[bel_to_cell[bel]]->belStrength = STRENGTH_NONE;
bel_to_cell[bel] = IdString();
}
bool checkBelAvail(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell.find(bel) == bel_to_cell.end() || bel_to_cell.at(bel) == IdString();
}
IdString getBoundBelCell(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
if (bel_to_cell.find(bel) == bel_to_cell.end())
return IdString();
else
return bel_to_cell.at(bel);
}
IdString getConflictingBelCell(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
if (bel_to_cell.find(bel) == bel_to_cell.end())
return IdString();
else
return bel_to_cell.at(bel);
}
BelRange getBels() const
{
BelRange range;
range.b.cursor_tile = 0;
range.b.cursor_index = -1;
range.b.chip = chip_info;
++range.b; //-1 and then ++ deals with the case of no Bels in the first tile
range.e.cursor_tile = chip_info->width * chip_info->height;
range.e.cursor_index = 0;
range.e.chip = chip_info;
return range;
}
BelRange getBelsByType(BelType type) const
{
BelRange range;
// FIXME
#if 0
if (type == "TYPE_A") {
range.b.cursor = bels_type_a_begin;
range.e.cursor = bels_type_a_end;
}
...
#endif
return range;
}
BelRange getBelsAtSameTile(BelId bel) const;
BelType getBelType(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return locInfo(bel)->bel_data[bel.index].type;
}
WireId getWireBelPin(BelId bel, PortPin pin) const;
BelPin getBelPinUphill(WireId wire) const
{
BelPin ret;
NPNR_ASSERT(wire != WireId());
if (locInfo(wire)->wire_data[wire.index].bel_uphill.bel_index >= 0) {
ret.bel.index = locInfo(wire)->wire_data[wire.index].bel_uphill.bel_index;
ret.bel.location = wire.location + locInfo(wire)->wire_data[wire.index].bel_uphill.rel_bel_loc;
ret.pin = locInfo(wire)->wire_data[wire.index].bel_uphill.port;
}
return ret;
}
BelPinRange getBelPinsDownhill(WireId wire) const
{
BelPinRange range;
NPNR_ASSERT(wire != WireId());
range.b.ptr = locInfo(wire)->wire_data[wire.index].bels_downhill.get();
range.b.wire_loc = wire.location;
range.e.ptr = range.b.ptr + locInfo(wire)->wire_data[wire.index].num_bels_downhill;
range.e.wire_loc = wire.location;
return range;
}
// -------------------------------------------------
WireId getWireByName(IdString name) const;
IdString getWireName(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
std::stringstream name;
name << "X" << wire.location.x << "/Y" << wire.location.y << "/"
<< locInfo(wire)->wire_data[wire.index].name.get();
return id(name.str());
}
uint32_t getWireChecksum(WireId wire) const { return wire.index; }
void bindWire(WireId wire, IdString net, PlaceStrength strength)
{
NPNR_ASSERT(wire != WireId());
NPNR_ASSERT(wire_to_net[wire] == IdString());
wire_to_net[wire] = net;
nets[net]->wires[wire].pip = PipId();
nets[net]->wires[wire].strength = strength;
}
void unbindWire(WireId wire)
{
NPNR_ASSERT(wire != WireId());
NPNR_ASSERT(wire_to_net[wire] != IdString());
auto &net_wires = nets[wire_to_net[wire]]->wires;
auto it = net_wires.find(wire);
NPNR_ASSERT(it != net_wires.end());
auto pip = it->second.pip;
if (pip != PipId()) {
pip_to_net[pip] = IdString();
}
net_wires.erase(it);
wire_to_net[wire] = IdString();
}
bool checkWireAvail(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return wire_to_net.find(wire) == wire_to_net.end() || wire_to_net.at(wire) == IdString();
}
IdString getBoundWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
if (wire_to_net.find(wire) == wire_to_net.end())
return IdString();
else
return wire_to_net.at(wire);
}
IdString getConflictingWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
if (wire_to_net.find(wire) == wire_to_net.end())
return IdString();
else
return wire_to_net.at(wire);
}
WireRange getWires() const
{
WireRange range;
range.b.cursor_tile = 0;
range.b.cursor_index = -1;
range.b.chip = chip_info;
++range.b; //-1 and then ++ deals with the case of no wries in the first tile
range.e.cursor_tile = chip_info->width * chip_info->height;
range.e.cursor_index = 0;
range.e.chip = chip_info;
return range;
}
// -------------------------------------------------
PipId getPipByName(IdString name) const;
IdString getPipName(PipId pip) const;
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
void bindPip(PipId pip, IdString net, PlaceStrength strength)
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip] == IdString());
pip_to_net[pip] = net;
WireId dst;
dst.index = locInfo(pip)->pip_data[pip.index].dst_idx;
dst.location = pip.location + locInfo(pip)->pip_data[pip.index].rel_dst_loc;
NPNR_ASSERT(wire_to_net[dst] == IdString());
wire_to_net[dst] = net;
nets[net]->wires[dst].pip = pip;
nets[net]->wires[dst].strength = strength;
}
void unbindPip(PipId pip)
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip] != IdString());
WireId dst;
dst.index = locInfo(pip)->pip_data[pip.index].dst_idx;
dst.location = pip.location + locInfo(pip)->pip_data[pip.index].rel_dst_loc;
NPNR_ASSERT(wire_to_net[dst] != IdString());
wire_to_net[dst] = IdString();
nets[pip_to_net[pip]]->wires.erase(dst);
pip_to_net[pip] = IdString();
}
bool checkPipAvail(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
return pip_to_net.find(pip) == pip_to_net.end() || pip_to_net.at(pip) == IdString();
}
IdString getBoundPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
if (pip_to_net.find(pip) == pip_to_net.end())
return IdString();
else
return pip_to_net.at(pip);
}
IdString getConflictingPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
if (pip_to_net.find(pip) == pip_to_net.end())
return IdString();
else
return pip_to_net.at(pip);
}
AllPipRange getPips() const
{
AllPipRange range;
range.b.cursor_tile = 0;
range.b.cursor_index = -1;
range.b.chip = chip_info;
++range.b; //-1 and then ++ deals with the case of no wries in the first tile
range.e.cursor_tile = chip_info->width * chip_info->height;
range.e.cursor_index = 0;
range.e.chip = chip_info;
return range;
}
WireId getPipSrcWire(PipId pip) const
{
WireId wire;
NPNR_ASSERT(pip != PipId());
wire.index = locInfo(pip)->pip_data[pip.index].src_idx;
wire.location = pip.location + locInfo(pip)->pip_data[pip.index].rel_src_loc;
return wire;
}
WireId getPipDstWire(PipId pip) const
{
WireId wire;
NPNR_ASSERT(pip != PipId());
wire.index = locInfo(pip)->pip_data[pip.index].dst_idx;
wire.location = pip.location + locInfo(pip)->pip_data[pip.index].rel_dst_loc;
return wire;
}
DelayInfo getPipDelay(PipId pip) const
{
DelayInfo delay;
NPNR_ASSERT(pip != PipId());
delay.delay = locInfo(pip)->pip_data[pip.index].delay;
return delay;
}
PipRange getPipsDownhill(WireId wire) const
{
PipRange range;
NPNR_ASSERT(wire != WireId());
range.b.cursor = locInfo(wire)->wire_data[wire.index].pips_downhill.get();
range.b.wire_loc = wire.location;
range.e.cursor = range.b.cursor + locInfo(wire)->wire_data[wire.index].num_downhill;
range.e.wire_loc = wire.location;
return range;
}
PipRange getPipsUphill(WireId wire) const
{
PipRange range;
NPNR_ASSERT(wire != WireId());
range.b.cursor = locInfo(wire)->wire_data[wire.index].pips_uphill.get();
range.b.wire_loc = wire.location;
range.e.cursor = range.b.cursor + locInfo(wire)->wire_data[wire.index].num_uphill;
range.e.wire_loc = wire.location;
return range;
}
PipRange getWireAliases(WireId wire) const
{
PipRange range;
NPNR_ASSERT(wire != WireId());
range.b.cursor = nullptr;
range.e.cursor = nullptr;
return range;
}
std::string getPipTiletype(PipId pip) const
{
return chip_info->tiletype_names[locInfo(pip)->pip_data[pip.index].tile_type].get();
}
int8_t getPipType(PipId pip) const { return locInfo(pip)->pip_data[pip.index].pip_type; }
BelId getPackagePinBel(const std::string &pin) const;
std::string getBelPackagePin(BelId bel) const;
// -------------------------------------------------
GroupId getGroupByName(IdString name) const { return GroupId(); }
IdString getGroupName(GroupId group) const { return IdString(); }
std::vector<GroupId> getGroups() const { return std::vector<GroupId>(); }
std::vector<BelId> getGroupBels(GroupId group) const { return std::vector<BelId>(); }
std::vector<WireId> getGroupWires(GroupId group) const { return std::vector<WireId>(); }
std::vector<PipId> getGroupPips(GroupId group) const { return std::vector<PipId>(); }
std::vector<GroupId> getGroupGroups(GroupId group) const { return std::vector<GroupId>(); }
// -------------------------------------------------
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
delay_t estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; }
uint32_t getDelayChecksum(delay_t v) const { return v; }
// -------------------------------------------------
bool pack();
bool place();
bool route();
// -------------------------------------------------
std::vector<GraphicElement> getDecalGraphics(DecalId decal) const;
DecalXY getFrameDecal() const;
DecalXY getBelDecal(BelId bel) const;
DecalXY getWireDecal(WireId wire) const;
DecalXY getPipDecal(PipId pip) const;
DecalXY getGroupDecal(GroupId group) const;
// -------------------------------------------------
// Get the delay through a cell from one port to another, returning false
// if no path exists
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const;
// Get the associated clock to a port, or empty if the port is combinational
IdString getPortClock(const CellInfo *cell, IdString port) const;
// Return true if a port is a clock
bool isClockPort(const CellInfo *cell, IdString port) const;
// Return true if a port is a net
bool isGlobalNet(const NetInfo *net) const;
// -------------------------------------------------
// Placement validity checks
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
bool isBelLocationValid(BelId bel) const;
};
NEXTPNR_NAMESPACE_END

View File

@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <dave@ds0.me>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -17,16 +18,15 @@
* *
*/ */
#ifndef ROUTE_H #ifndef NO_PYTHON
#define ROUTE_H
#include "nextpnr.h" #include "nextpnr.h"
#include "pybindings.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
extern bool route_design(Context *ctx); void arch_wrap_python() {}
extern bool get_actual_route_delay(Context *ctx, WireId src_wire, WireId dst_wire, delay_t &delay);
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif // ROUTE_H #endif

75
ecp5/arch_pybindings.h Normal file
View File

@ -0,0 +1,75 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef ARCH_PYBINDINGS_H
#define ARCH_PYBINDINGS_H
#ifndef NO_PYTHON
#include "nextpnr.h"
#include "pybindings.h"
NEXTPNR_NAMESPACE_BEGIN
namespace PythonConversion {
template <> struct string_converter<BelId>
{
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
std::string to_str(Context *ctx, BelId id)
{
if (id == BelId())
throw bad_wrap();
return ctx->getBelName(id).str(ctx);
}
};
template <> struct string_converter<BelType>
{
BelType from_str(Context *ctx, std::string name) { return ctx->belTypeFromId(ctx->id(name)); }
std::string to_str(Context *ctx, BelType typ) { return ctx->belTypeToId(typ).str(ctx); }
};
template <> struct string_converter<WireId>
{
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
};
template <> struct string_converter<PipId>
{
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); }
};
template <> struct string_converter<PortPin>
{
PortPin from_str(Context *ctx, std::string name) { return ctx->portPinFromId(ctx->id(name)); }
std::string to_str(Context *ctx, PortPin id) { return ctx->portPinToId(id).str(ctx); }
};
} // namespace PythonConversion
NEXTPNR_NAMESPACE_END
#endif
#endif

202
ecp5/archdefs.h Normal file
View File

@ -0,0 +1,202 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef NEXTPNR_H
#error Include "archdefs.h" via "nextpnr.h" only.
#endif
#include <boost/functional/hash.hpp>
NEXTPNR_NAMESPACE_BEGIN
typedef int delay_t;
struct DelayInfo
{
delay_t delay = 0;
delay_t raiseDelay() const { return delay; }
delay_t fallDelay() const { return delay; }
delay_t avgDelay() const { return delay; }
DelayInfo operator+(const DelayInfo &other) const
{
DelayInfo ret;
ret.delay = this->delay + other.delay;
return ret;
}
};
// -----------------------------------------------------------------------
enum BelType : int32_t
{
TYPE_NONE,
TYPE_TRELLIS_SLICE,
TYPE_TRELLIS_IO
};
enum PortPin : int32_t
{
PIN_NONE,
#define X(t) PIN_##t,
#include "portpins.inc"
#undef X
PIN_MAXIDX
};
NPNR_PACKED_STRUCT(struct LocationPOD { int16_t x, y; });
struct Location
{
int16_t x = -1, y = -1;
Location() : x(-1), y(-1){};
Location(int16_t x, int16_t y) : x(x), y(y){};
Location(const LocationPOD &pod) : x(pod.x), y(pod.y){};
Location(const Location &loc) : x(loc.x), y(loc.y){};
bool operator==(const Location &other) const { return x == other.x && y == other.y; }
bool operator!=(const Location &other) const { return x != other.x || y != other.y; }
};
inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); }
struct BelId
{
Location location;
int32_t index = -1;
bool operator==(const BelId &other) const { return index == other.index && location == other.location; }
bool operator!=(const BelId &other) const { return index != other.index || location != other.location; }
};
struct WireId
{
Location location;
int32_t index = -1;
bool operator==(const WireId &other) const { return index == other.index && location == other.location; }
bool operator!=(const WireId &other) const { return index != other.index || location != other.location; }
};
struct PipId
{
Location location;
int32_t index = -1;
bool operator==(const PipId &other) const { return index == other.index && location == other.location; }
bool operator!=(const PipId &other) const { return index != other.index || location != other.location; }
};
struct GroupId
{
int32_t index = -1;
bool operator==(const GroupId &other) const { return index == other.index; }
bool operator!=(const GroupId &other) const { return index != other.index; }
};
struct DecalId
{
char type = 0; // Bel/Wire/Pip/Frame (b/w/p/f)
Location location;
uint32_t z = 0;
bool operator==(const DecalId &other) const
{
return type == other.type && location == other.location && z == other.z;
}
bool operator!=(const DecalId &other) const
{
return type != other.type || location != other.location || z != other.z;
}
};
NEXTPNR_NAMESPACE_END
namespace std {
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX Location>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Location &loc) const noexcept
{
std::size_t seed = std::hash<int>()(loc.x);
seed ^= std::hash<int>()(loc.y) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept
{
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(bel.location);
seed ^= std::hash<int>()(bel.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX WireId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept
{
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(wire.location);
seed ^= std::hash<int>()(wire.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept
{
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(pip.location);
seed ^= std::hash<int>()(pip.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX GroupId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept
{
return std::hash<int>()(group.index);
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<int>()(decal.type));
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX Location>()(decal.location));
boost::hash_combine(seed, hash<int>()(decal.z));
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelType> : hash<int>
{
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PortPin> : hash<int>
{
};
} // namespace std

257
ecp5/bitstream.cc Normal file
View File

@ -0,0 +1,257 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "bitstream.h"
// From Project Trellis
#include "BitDatabase.hpp"
#include "Bitstream.hpp"
#include "Chip.hpp"
#include "ChipConfig.hpp"
#include "Tile.hpp"
#include "TileConfig.hpp"
#include <fstream>
#include <streambuf>
#include "log.h"
#include "util.h"
#define fmt_str(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())
NEXTPNR_NAMESPACE_BEGIN
// Convert an absolute wire name to a relative Trellis one
static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire)
{
std::string basename = ctx->locInfo(wire)->wire_data[wire.index].name.get();
std::string prefix2 = basename.substr(0, 2);
if (prefix2 == "G_" || prefix2 == "L_" || prefix2 == "R_")
return basename;
if (loc == wire.location)
return basename;
std::string rel_prefix;
if (wire.location.y < loc.y)
rel_prefix += "N" + to_string(loc.y - wire.location.y);
if (wire.location.y > loc.y)
rel_prefix += "S" + to_string(wire.location.y - loc.y);
if (wire.location.x > loc.x)
rel_prefix += "E" + to_string(wire.location.x - loc.x);
if (wire.location.x < loc.x)
rel_prefix += "W" + to_string(loc.x - wire.location.x);
return rel_prefix + "_" + basename;
}
static std::vector<bool> int_to_bitvector(int val, int size)
{
std::vector<bool> bv;
for (int i = 0; i < size; i++) {
bv.push_back((val & (1 << i)) != 0);
}
return bv;
}
// Get the PIO tile corresponding to a PIO bel
static std::string get_pio_tile(Context *ctx, Trellis::Chip &chip, BelId bel)
{
static const std::set<std::string> pioabcd_l = {"PICL1", "PICL1_DQS0", "PICL1_DQS3"};
static const std::set<std::string> pioabcd_r = {"PICR1", "PICR1_DQS0", "PICR1_DQS3"};
static const std::set<std::string> pioa_b = {"PICB0", "EFB0_PICB0", "EFB2_PICB0"};
static const std::set<std::string> piob_b = {"PICB1", "EFB1_PICB1", "EFB3_PICB1"};
std::string pio_name = ctx->locInfo(bel)->bel_data[bel.index].name.get();
if (bel.location.y == 0) {
if (pio_name == "PIOA") {
return chip.get_tile_by_position_and_type(0, bel.location.x, "PIOT0");
} else if (pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(0, bel.location.x + 1, "PIOT1");
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.y == ctx->chip_info->height - 1) {
if (pio_name == "PIOA") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pioa_b);
} else if (pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, piob_b);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.x == 0) {
return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_l);
} else if (bel.location.x == ctx->chip_info->width - 1) {
return chip.get_tile_by_position_and_type(bel.location.y + 1, bel.location.x, pioabcd_r);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
}
// Get the PIC tile corresponding to a PIO bel
static std::string get_pic_tile(Context *ctx, Trellis::Chip &chip, BelId bel)
{
static const std::set<std::string> picab_l = {"PICL0", "PICL0_DQS2"};
static const std::set<std::string> piccd_l = {"PICL2", "PICL2_DQS1", "MIB_CIB_LR"};
static const std::set<std::string> picab_r = {"PICR0", "PICR0_DQS2"};
static const std::set<std::string> piccd_r = {"PICR2", "PICR2_DQS1", "MIB_CIB_LR_A"};
static const std::set<std::string> pica_b = {"PICB0", "EFB0_PICB0", "EFB2_PICB0"};
static const std::set<std::string> picb_b = {"PICB1", "EFB1_PICB1", "EFB3_PICB1"};
std::string pio_name = ctx->locInfo(bel)->bel_data[bel.index].name.get();
if (bel.location.y == 0) {
if (pio_name == "PIOA") {
return chip.get_tile_by_position_and_type(1, bel.location.x, "PICT0");
} else if (pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(1, bel.location.x + 1, "PICT1");
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.y == ctx->chip_info->height - 1) {
if (pio_name == "PIOA") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, pica_b);
} else if (pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x + 1, picb_b);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.x == 0) {
if (pio_name == "PIOA" || pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_l);
} else if (pio_name == "PIOC" || pio_name == "PIOD") {
return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_l);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else if (bel.location.x == ctx->chip_info->width - 1) {
if (pio_name == "PIOA" || pio_name == "PIOB") {
return chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, picab_r);
} else if (pio_name == "PIOC" || pio_name == "PIOD") {
return chip.get_tile_by_position_and_type(bel.location.y + 2, bel.location.x, piccd_r);
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
} else {
NPNR_ASSERT_FALSE("bad PIO location");
}
}
void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file,
std::string bitstream_file)
{
Trellis::Chip empty_chip(ctx->getChipName());
Trellis::ChipConfig cc;
std::set<std::string> cib_tiles = {"CIB", "CIB_LR", "CIB_LR_S", "CIB_EFB0", "CIB_EFB1"};
if (!base_config_file.empty()) {
std::ifstream config_file(base_config_file);
if (!config_file) {
log_error("failed to open base config file '%s'\n", base_config_file.c_str());
}
std::string str((std::istreambuf_iterator<char>(config_file)), std::istreambuf_iterator<char>());
cc = Trellis::ChipConfig::from_string(str);
} else {
cc.chip_name = ctx->getChipName();
// TODO: .bit metadata
}
// Add all set, configurable pips to the config
for (auto pip : ctx->getPips()) {
if (ctx->getBoundPipNet(pip) != IdString()) {
if (ctx->getPipType(pip) == 0) { // ignore fixed pips
std::string tile = empty_chip.get_tile_by_position_and_type(pip.location.y, pip.location.x,
ctx->getPipTiletype(pip));
std::string source = get_trellis_wirename(ctx, pip.location, ctx->getPipSrcWire(pip));
std::string sink = get_trellis_wirename(ctx, pip.location, ctx->getPipDstWire(pip));
cc.tiles[tile].add_arc(sink, source);
}
}
}
// Set all bankref tiles to 3.3V (TODO)
for (const auto &tile : empty_chip.tiles) {
std::string type = tile.second->info.type;
if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") {
cc.tiles[tile.first].add_enum("BANK.VCCIO", "3V3");
}
}
// Configure slices
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (ci->bel == BelId()) {
log_warning("found unplaced cell '%s' during bitstream gen\n", ci->name.c_str(ctx));
}
BelId bel = ci->bel;
if (ci->type == ctx->id("TRELLIS_SLICE")) {
std::string tname = empty_chip.get_tile_by_position_and_type(bel.location.y, bel.location.x, "PLC2");
std::string slice = ctx->locInfo(bel)->bel_data[bel.index].name.get();
int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL"));
int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL"));
cc.tiles[tname].add_word(slice + ".K0.INIT", int_to_bitvector(lut0_init, 16));
cc.tiles[tname].add_word(slice + ".K1.INIT", int_to_bitvector(lut1_init, 16));
cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, ctx->id("MODE"), "LOGIC"));
cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED"));
cc.tiles[tname].add_enum(slice + ".REG0.SD", str_or_default(ci->params, ctx->id("REG0_SD"), "0"));
cc.tiles[tname].add_enum(slice + ".REG1.SD", str_or_default(ci->params, ctx->id("REG1_SD"), "0"));
cc.tiles[tname].add_enum(slice + ".REG0.REGSET",
str_or_default(ci->params, ctx->id("REG0_REGSET"), "RESET"));
cc.tiles[tname].add_enum(slice + ".REG1.REGSET",
str_or_default(ci->params, ctx->id("REG1_REGSET"), "RESET"));
cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, ctx->id("CEMUX"), "1"));
// TODO: CLKMUX, CEMUX, carry
} else if (ci->type == ctx->id("TRELLIS_IO")) {
std::string pio = ctx->locInfo(bel)->bel_data[bel.index].name.get();
std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33");
std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT");
std::string pio_tile = get_pio_tile(ctx, empty_chip, bel);
std::string pic_tile = get_pic_tile(ctx, empty_chip, bel);
cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
if (dir != "INPUT" &&
(ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr)) {
// Tie tristate low if unconnected for outputs or bidir
std::string jpt = fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/JPADDT" << pio.back());
WireId jpt_wire = ctx->getWireByName(ctx->id(jpt));
PipId jpt_pip = *ctx->getPipsUphill(jpt_wire).begin();
WireId cib_wire = ctx->getPipSrcWire(jpt_pip);
std::string cib_tile =
empty_chip.get_tile_by_position_and_type(cib_wire.location.y, cib_wire.location.x, cib_tiles);
std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get();
cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0");
}
if (dir == "INPUT") {
cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON");
}
} else {
NPNR_ASSERT_FALSE("unsupported cell type");
}
}
// Configure chip
Trellis::Chip cfg_chip = cc.to_chip();
if (!bitstream_file.empty()) {
Trellis::Bitstream::serialise_chip(cfg_chip).write_bit_py(bitstream_file);
}
if (!text_config_file.empty()) {
std::ofstream out_config(text_config_file);
out_config << cc.to_string();
}
}
NEXTPNR_NAMESPACE_END

32
ecp5/bitstream.h Normal file
View File

@ -0,0 +1,32 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef BITSTREAM_H
#define BITSTREAM_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
void write_bitstream(Context *ctx, std::string base_config_file = "", std::string text_config_file = "",
std::string bitstream_file = "");
NEXTPNR_NAMESPACE_END
#endif // BITSTREAM_H

60
ecp5/family.cmake Normal file
View File

@ -0,0 +1,60 @@
set(devices 45k)
if (NOT DEFINED TRELLIS_ROOT)
message(FATAL_ERROR "you must define TRELLIS_ROOT using -DTRELLIS_ROOT=/path/to/prjtrellis for ECP5 support")
endif()
file( GLOB found_pytrellis ${TRELLIS_ROOT}/libtrellis/pytrellis.*)
if ("${found_pytrellis}" STREQUAL "")
message(FATAL_ERROR "failed to find pytrellis library in ${TRELLIS_ROOT}/libtrellis/")
endif()
set(DB_PY ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/trellis_import.py)
file(MAKE_DIRECTORY ecp5/chipdbs/)
add_library(ecp5_chipdb OBJECT ecp5/chipdbs/)
target_compile_definitions(ecp5_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family})
target_include_directories(ecp5_chipdb PRIVATE ${family}/)
set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=${TRELLIS_ROOT}/libtrellis:${TRELLIS_ROOT}/util/common")
if (MSVC)
target_sources(ecp5_chipdb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resource/embed.cc)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resources/chipdb.rc PROPERTIES LANGUAGE RC)
foreach (dev ${devices})
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bin)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND ${ENV_CMD} python3 ${DB_PY} -b -p ${DEV_PORTS_INC} ${dev} ${DEV_CC_DB}
DEPENDS ${DB_PY}
)
target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB})
set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE)
foreach (target ${family_targets})
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ecp5_chipdb> ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resource/chipdb.rc)
endforeach (target)
endforeach (dev)
else()
target_compile_options(ecp5_chipdb PRIVATE -g0 -O0 -w)
foreach (dev ${devices})
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.cc)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND ${ENV_CMD} python3 ${DB_PY} -c -p ${DEV_PORTS_INC} ${dev} ${DEV_CC_DB}
DEPENDS ${DB_PY}
)
target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB})
foreach (target ${family_targets})
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ecp5_chipdb>)
endforeach (target)
endforeach (dev)
endif()
find_library(TRELLIS_LIB trellis PATHS ${TRELLIS_ROOT}/libtrellis)
foreach (target ${family_targets})
target_compile_definitions(${target} PRIVATE TRELLIS_ROOT="${TRELLIS_ROOT}")
target_include_directories(${target} PRIVATE ${TRELLIS_ROOT}/libtrellis/include)
target_link_libraries(${target} PRIVATE ${TRELLIS_LIB})
endforeach (target)

199
ecp5/main.cc Normal file
View File

@ -0,0 +1,199 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifdef MAIN_EXECUTABLE
#ifndef NO_GUI
#include <QApplication>
#include "application.h"
#include "mainwindow.h"
#endif
#ifndef NO_PYTHON
#include "pybindings.h"
#endif
#include <boost/filesystem/convenience.hpp>
#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>
#include "Chip.hpp"
#include "Database.hpp"
#include "Tile.hpp"
#include "log.h"
#include "nextpnr.h"
#include "version.h"
#include "bitstream.h"
#include "design_utils.h"
#include "jsonparse.h"
#include "timing.h"
USING_NEXTPNR_NAMESPACE
int main(int argc, char *argv[])
{
try {
namespace po = boost::program_options;
int rc = 0;
log_files.push_back(stdout);
po::options_description options("Allowed options");
options.add_options()("help,h", "show help");
options.add_options()("verbose,v", "verbose output");
options.add_options()("force,f", "keep running after errors");
#ifndef NO_GUI
options.add_options()("gui", "start gui");
#endif
options.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
options.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format");
options.add_options()("bit", po::value<std::string>(), "bitstream file to write");
options.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
po::positional_options_description pos;
#ifndef NO_PYTHON
options.add_options()("run", po::value<std::vector<std::string>>(), "python file to execute");
pos.add("run", -1);
#endif
options.add_options()("version,V", "show version");
po::variables_map vm;
try {
po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run();
po::store(parsed, vm);
po::notify(vm);
}
catch (std::exception &e) {
std::cout << e.what() << "\n";
return 1;
}
if (vm.count("help") || argc == 1) {
std::cout << boost::filesystem::basename(argv[0])
<< " -- Next Generation Place and Route (git "
"sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << "\n";
std::cout << options << "\n";
return argc != 1;
}
if (vm.count("version")) {
std::cout << boost::filesystem::basename(argv[0])
<< " -- Next Generation Place and Route (git "
"sha1 " GIT_COMMIT_HASH_STR ")\n";
return 1;
}
Trellis::load_database(TRELLIS_ROOT "/database");
ArchArgs args;
args.type = ArchArgs::LFE5U_45F;
args.package = "CABGA381";
args.speed = 6;
std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(args));
if (vm.count("verbose")) {
ctx->verbose = true;
}
if (vm.count("force")) {
ctx->force = true;
}
if (vm.count("seed")) {
ctx->rngseed(vm["seed"].as<int>());
}
ctx->timing_driven = true;
if (vm.count("no-tmdriv"))
ctx->timing_driven = false;
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv);
MainWindow w(std::move(ctx));
w.show();
return a.exec();
}
#endif
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::ifstream f(filename);
if (!parse_json_file(f, filename, ctx.get()))
log_error("Loading design failed.\n");
if (!ctx->pack() && !ctx->force)
log_error("Packing design failed.\n");
if (vm.count("freq"))
ctx->target_freq = vm["freq"].as<double>() * 1e6;
assign_budget(ctx.get());
ctx->check();
print_utilisation(ctx.get());
if (!ctx->place() && !ctx->force)
log_error("Placing design failed.\n");
ctx->check();
if (!ctx->route() && !ctx->force)
log_error("Routing design failed.\n");
std::string basecfg;
if (vm.count("basecfg"))
basecfg = vm["basecfg"].as<std::string>();
std::string bitstream;
if (vm.count("bit"))
bitstream = vm["bit"].as<std::string>();
std::string textcfg;
if (vm.count("textcfg"))
textcfg = vm["textcfg"].as<std::string>();
write_bitstream(ctx.get(), basecfg, textcfg, bitstream);
}
#ifndef NO_PYTHON
if (vm.count("run")) {
init_python(argv[0], true);
python_export_global("ctx", ctx);
std::vector<std::string> files = vm["run"].as<std::vector<std::string>>();
for (auto filename : files)
execute_python_file(filename.c_str());
deinit_python();
}
#endif
return rc;
} catch (log_execution_error_exception) {
#if defined(_MSC_VER)
_exit(EXIT_FAILURE);
#else
_Exit(EXIT_FAILURE);
#endif
}
}
#endif

99
ecp5/pack.cc Normal file
View File

@ -0,0 +1,99 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <algorithm>
#include <iterator>
#include <unordered_set>
#include "design_utils.h"
#include "log.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
{
return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
cell->type == ctx->id("$nextpnr_iobuf");
}
static bool is_trellis_io(const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); }
// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated
void pack_io(Context *ctx)
{
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
log_info("Packing IOs..\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_nextpnr_iob(ctx, ci)) {
CellInfo *trio = nullptr;
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
trio = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_trellis_io, ctx->id("B"), true, ci);
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci);
}
if (trio != nullptr) {
// Trivial case, TRELLIS_IO used. Just destroy the net and the
// iobuf
log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx),
ci->type.c_str(ctx), ci->name.c_str(ctx));
NetInfo *net = trio->ports.at(ctx->id("B")).net;
if (net != nullptr) {
ctx->nets.erase(net->name);
trio->ports.at(ctx->id("B")).net = nullptr;
}
if (ci->type == ctx->id("$nextpnr_iobuf")) {
NetInfo *net2 = ci->ports.at(ctx->id("I")).net;
if (net2 != nullptr) {
ctx->nets.erase(net2->name);
}
}
} else {
log_error("TRELLIS_IO required on all top level IOs...\n");
}
packed_cells.insert(ci->name);
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin()));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Main pack function
bool Arch::pack()
{
Context *ctx = getCtx();
try {
log_break();
pack_io(ctx);
log_info("Checksum: 0x%08x\n", ctx->checksum());
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END

26
ecp5/place_legaliser.cc Normal file
View File

@ -0,0 +1,26 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "place_legaliser.h"
NEXTPNR_NAMESPACE_BEGIN
bool legalise_design(Context *ctx) { return true; }
NEXTPNR_NAMESPACE_END

31
ecp5/place_legaliser.h Normal file
View File

@ -0,0 +1,31 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef PLACE_LEGALISER_H
#define PLACE_LEGALISER_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
bool legalise_design(Context *ctx);
NEXTPNR_NAMESPACE_END
#endif

46
ecp5/portpins.inc Normal file
View File

@ -0,0 +1,46 @@
X(A0)
X(B0)
X(C0)
X(D0)
X(A1)
X(B1)
X(C1)
X(D1)
X(M0)
X(M1)
X(FCI)
X(FXA)
X(FXB)
X(CLK)
X(LSR)
X(CE)
X(DI0)
X(DI1)
X(WD0)
X(WD1)
X(WAD0)
X(WAD1)
X(WAD2)
X(WAD3)
X(WRE)
X(WCK)
X(F0)
X(Q0)
X(F1)
X(Q1)
X(FCO)
X(OFX0)
X(OFX1)
X(WDO0)
X(WDO1)
X(WDO2)
X(WDO3)
X(WADO0)
X(WADO1)
X(WADO2)
X(WADO3)
X(I)
X(O)
X(T)
X(B)

1
ecp5/synth/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.bit

77
ecp5/synth/blinky.v Normal file
View File

@ -0,0 +1,77 @@
module top(input clk_pin, input btn_pin, output [3:0] led_pin, output gpio0_pin);
wire clk;
wire [7:0] led;
wire btn;
wire gpio0;
(* BEL="X0/Y35/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk));
(* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn));
(* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0]));
(* BEL="X0/Y23/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1]));
(* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2]));
(* BEL="X0/Y26/PIOC" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3]));
(* BEL="X0/Y26/PIOB" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4]));
(* BEL="X0/Y32/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5]));
(* BEL="X0/Y26/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6]));
(* BEL="X0/Y29/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7]));
(* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
localparam ctr_width = 24;
localparam ctr_max = 2**ctr_width - 1;
reg [ctr_width-1:0] ctr = 0;
reg [9:0] pwm_ctr = 0;
reg dir = 0;
always@(posedge clk) begin
ctr <= btn ? ctr : (dir ? ctr - 1'b1 : ctr + 1'b1);
if (ctr[ctr_width-1 : ctr_width-3] == 0 && dir == 1)
dir <= 1'b0;
else if (ctr[ctr_width-1 : ctr_width-3] == 7 && dir == 0)
dir <= 1'b1;
pwm_ctr <= pwm_ctr + 1'b1;
end
reg [9:0] brightness [0:7];
localparam bright_max = 2**10 - 1;
reg [7:0] led_reg;
genvar i;
generate
for (i = 0; i < 8; i=i+1) begin
always @ (posedge clk) begin
if (ctr[ctr_width-1 : ctr_width-3] == i)
brightness[i] <= bright_max;
else if (ctr[ctr_width-1 : ctr_width-3] == (i - 1))
brightness[i] <= ctr[ctr_width-4:ctr_width-13];
else if (ctr[ctr_width-1 : ctr_width-3] == (i + 1))
brightness[i] <= bright_max - ctr[ctr_width-4:ctr_width-13];
else
brightness[i] <= 0;
led_reg[i] <= pwm_ctr < brightness[i];
end
end
endgenerate
assign led = led_reg;
// Tie GPIO0, keep board from rebooting
TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
endmodule

9
ecp5/synth/blinky.ys Normal file
View File

@ -0,0 +1,9 @@
read_verilog blinky.v
read_verilog -lib cells.v
synth -top top
abc -lut 4
techmap -map simple_map.v
splitnets
opt_clean
stat
write_json blinky.json

49
ecp5/synth/cells.v Normal file
View File

@ -0,0 +1,49 @@
(* blackbox *)
module TRELLIS_SLICE(
input A0, B0, C0, D0,
input A1, B1, C1, D1,
input M0, M1,
input FCI, FXA, FXB,
input CLK, LSR, CE,
input DI0, DI1,
input WD0, WD1,
input WAD0, WAD1, WAD2, WAD3,
input WRE, WCK,
output F0, Q0,
output F1, Q1,
output FCO, OFX0, OFX1,
output WDO0, WDO1, WDO2, WDO3,
output WADO0, WADO1, WADO2, WADO3
);
parameter MODE = "LOGIC";
parameter GSR = "ENABLED";
parameter SRMODE = "LSR_OVER_CE";
parameter CEMUX = "1";
parameter CLKMUX = "CLK";
parameter LSRMUX = "LSR";
parameter LUT0_INITVAL = 16'h0000;
parameter LUT1_INITVAL = 16'h0000;
parameter REG0_SD = "0";
parameter REG1_SD = "0";
parameter REG0_REGSET = "RESET";
parameter REG1_REGSET = "RESET";
parameter CCU2_INJECT1_0 = "NO";
parameter CCU2_INJECT1_1 = "NO";
endmodule
(* blackbox *) (* keep *)
module TRELLIS_IO(
inout B,
input I,
input T,
output O,
);
parameter DIR = "INPUT";
endmodule

68
ecp5/synth/simple_map.v Normal file
View File

@ -0,0 +1,68 @@
module \$_DFF_P_ (input D, C, output Q);
TRELLIS_SLICE #(
.MODE("LOGIC"),
.CLKMUX("CLK"),
.CEMUX("1"),
.REG0_SD("0"),
.REG0_REGSET("RESET"),
.SRMODE("LSR_OVER_CE"),
.GSR("DISABLED")
) _TECHMAP_REPLACE_ (
.CLK(C),
.M0(D),
.Q0(Q)
);
endmodule
module \$lut (A, Y);
parameter WIDTH = 0;
parameter LUT = 0;
input [WIDTH-1:0] A;
output Y;
generate
if (WIDTH == 1) begin
TRELLIS_SLICE #(
.MODE("LOGIC"),
.LUT0_INITVAL({8{LUT[1:0]}})
) _TECHMAP_REPLACE_ (
.A0(A[0]),
.F0(Y)
);
end
if (WIDTH == 2) begin
TRELLIS_SLICE #(
.MODE("LOGIC"),
.LUT0_INITVAL({4{LUT[3:0]}})
) _TECHMAP_REPLACE_ (
.A0(A[0]),
.B0(A[1]),
.F0(Y)
);
end
if (WIDTH == 3) begin
TRELLIS_SLICE #(
.MODE("LOGIC"),
.LUT0_INITVAL({2{LUT[7:0]}})
) _TECHMAP_REPLACE_ (
.A0(A[0]),
.B0(A[1]),
.C0(A[2]),
.F0(Y)
);
end
if (WIDTH == 4) begin
TRELLIS_SLICE #(
.MODE("LOGIC"),
.LUT0_INITVAL(LUT)
) _TECHMAP_REPLACE_ (
.A0(A[0]),
.B0(A[1]),
.C0(A[2]),
.D0(A[3]),
.F0(Y)
);
end
endgenerate
endmodule

18
ecp5/synth/ulx3s.v Normal file
View File

@ -0,0 +1,18 @@
module top(input a_pin, output led_pin, output led2_pin, output gpio0_pin);
wire a;
wire led, led2;
wire gpio0;
(* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a));
(* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led));
(* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led2_buf (.B(led2_pin), .I(led2));
(* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
assign led = a;
assign led2 = !a;
TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
endmodule

9
ecp5/synth/ulx3s.ys Normal file
View File

@ -0,0 +1,9 @@
read_verilog ulx3s.v
read_verilog -lib cells.v
synth -top top
abc -lut 4
techmap -map simple_map.v
splitnets
opt_clean
stat
write_json ulx3s.json

View File

@ -0,0 +1,439 @@
.device LFE5U-45F
.tile CIB_R10C3:PVT_COUNT2
unknown: F2B0
unknown: F3B0
unknown: F5B0
unknown: F11B0
unknown: F13B0
.tile CIB_R5C1:CIB_PLL1
enum: CIB.JA3MUX 0
enum: CIB.JB3MUX 0
.tile CIB_R5C89:CIB_PLL1
enum: CIB.JA3MUX 0
enum: CIB.JB3MUX 0
.tile CIB_R70C3:CIB_PLL3
enum: CIB.JA3MUX 0
enum: CIB.JB3MUX 0
.tile CIB_R70C42:VCIB_DCU0
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C43:VCIB_DCUA
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C44:VCIB_DCUB
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C45:VCIB_DCUC
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C46:VCIB_DCUD
enum: CIB.JA1MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C47:VCIB_DCUF
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C48:VCIB_DCU3
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C49:VCIB_DCU2
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C50:VCIB_DCUG
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C51:VCIB_DCUH
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C52:VCIB_DCUI
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C53:VCIB_DCU1
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
.tile CIB_R70C69:VCIB_DCU0
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C6:CIB_EFB0
enum: CIB.JB3MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C70:VCIB_DCUA
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C71:VCIB_DCUB
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C72:VCIB_DCUC
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C73:VCIB_DCUD
enum: CIB.JA1MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C74:VCIB_DCUF
enum: CIB.JA1MUX 0
enum: CIB.JA3MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC2MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C75:VCIB_DCU3
enum: CIB.JA5MUX 0
enum: CIB.JA7MUX 0
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JC0MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC6MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C76:VCIB_DCU2
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C77:VCIB_DCUG
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C78:VCIB_DCUH
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C79:VCIB_DCUI
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB7MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD6MUX 0
.tile CIB_R70C7:CIB_EFB1
enum: CIB.JA3MUX 0
enum: CIB.JA4MUX 0
enum: CIB.JA5MUX 0
enum: CIB.JA6MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB4MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JB6MUX 0
enum: CIB.JC3MUX 0
enum: CIB.JC4MUX 0
enum: CIB.JC5MUX 0
enum: CIB.JD3MUX 0
enum: CIB.JD4MUX 0
enum: CIB.JD5MUX 0
.tile CIB_R70C80:VCIB_DCU1
enum: CIB.JB1MUX 0
enum: CIB.JB3MUX 0
enum: CIB.JB5MUX 0
enum: CIB.JD0MUX 0
enum: CIB.JD2MUX 0
.tile CIB_R70C87:CIB_PLL3
enum: CIB.JA3MUX 0
enum: CIB.JB3MUX 0
.tile MIB_R10C40:CMUX_UL_0
arc: G_DCS0CLK0 G_VPFN0000
.tile MIB_R10C41:CMUX_UR_0
arc: G_DCS0CLK1 G_VPFN0000
.tile MIB_R58C40:CMUX_LL_0
arc: G_DCS1CLK0 G_VPFN0000
.tile MIB_R58C41:CMUX_LR_0
arc: G_DCS1CLK1 G_VPFN0000
.tile MIB_R71C4:EFB0_PICB0
unknown: F54B1
unknown: F56B1
unknown: F82B1
unknown: F94B1
.tile MIB_R71C3:BANKREF8
unknown: F18B0

11
ecp5/synth/wire.v Normal file
View File

@ -0,0 +1,11 @@
module top(input a_pin, output [3:0] led_pin);
wire a;
wire [3:0] led;
TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a));
TRELLIS_IO #(.DIR("OUTPUT")) led_buf [3:0] (.B(led_pin), .I(led));
//assign led[0] = !a;
always @(posedge a) led[0] <= !led[0];
endmodule

9
ecp5/synth/wire.ys Normal file
View File

@ -0,0 +1,9 @@
read_verilog wire.v
read_verilog -lib cells.v
synth -top top
abc -lut 4
techmap -map simple_map.v
splitnets
opt_clean
stat
write_json wire.json

701
ecp5/trellis_import.py Executable file
View File

@ -0,0 +1,701 @@
#!/usr/bin/env python3
import pytrellis
import database
import argparse
location_types = dict()
type_at_location = dict()
tiletype_names = dict()
parser = argparse.ArgumentParser(description="import ECP5 routing and bels from Project Trellis")
group = parser.add_mutually_exclusive_group()
group.add_argument("-b", "--binary", action="store_true")
group.add_argument("-c", "--c_file", action="store_true")
parser.add_argument("device", type=str, help="target device")
parser.add_argument("outfile", type=argparse.FileType('w'), help="output filename")
parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc")
args = parser.parse_args()
def is_global(loc):
return loc.x == -2 and loc.y == -2
# Get the index for a tiletype
def get_tiletype_index(name):
if name in tiletype_names:
return tiletype_names[name]
idx = len(tiletype_names)
tiletype_names[name] = idx
return idx
portpins = dict()
loc_wire_indices = dict()
loc_wires = dict()
loc_bels = dict()
wire_bel_pins_uphill = dict()
wire_bel_pins_downhill = dict()
# Import all wire names at all locations
def import_location_wires(rg, x, y):
loc_wire_indices[x, y] = dict()
loc_wires[x, y] = list()
wire_bel_pins_uphill[x, y] = list()
wire_bel_pins_downhill[x, y] = list()
rtile = rg.tiles[pytrellis.Location(x, y)]
for wire in rtile.wires:
name = rg.to_str(wire.key())
idx = len(loc_wires[x, y])
loc_wires[x, y].append(name)
loc_wire_indices[x, y][name] = idx
wire_bel_pins_uphill[x, y].append([])
wire_bel_pins_downhill[x, y].append([])
# Take a RoutingId from Trellis and make into a (relx, rely, name) tuple
def resolve_wirename(rg, rid, cur_x, cur_y):
if is_global(rid.loc):
return (cur_x, cur_y, rg.to_str(rid.id))
else:
x = rid.loc.x
y = rid.loc.y
widx = loc_wire_indices[x, y][rg.to_str(rid.id)]
return (x - cur_x, y - cur_y, widx)
loc_arc_indices = dict() # Map RoutingId index to nextpnr index
loc_arcs = dict()
# Import all arc indices at a location
def index_location_arcs(rg, x, y):
loc_arc_indices[x, y] = dict()
loc_arcs[x, y] = list()
rtile = rg.tiles[pytrellis.Location(x, y)]
for arc in rtile.arcs:
idx = len(loc_arcs[x, y])
trid = arc.key()
loc_arcs[x, y].append(trid)
loc_arc_indices[x, y][trid] = idx
def add_bel_input(bel_x, bel_y, bel_idx, bel_pin, wire_x, wire_y, wire_name):
bel_pin = portpins[bel_pin]
loc_bels[bel_x, bel_y][bel_idx][2].append((bel_pin, (wire_x, wire_y, loc_wire_indices[wire_x, wire_y][wire_name])))
wire_bel_pins_downhill[wire_x, wire_y][loc_wire_indices[wire_x, wire_y][wire_name]].append((
(bel_x, bel_y, bel_idx), bel_pin))
def add_bel_output(bel_x, bel_y, bel_idx, bel_pin, wire_x, wire_y, wire_name):
bel_pin = portpins[bel_pin]
loc_bels[bel_x, bel_y][bel_idx][2].append((bel_pin, (wire_x, wire_y, loc_wire_indices[wire_x, wire_y][wire_name])))
wire_bel_pins_uphill[wire_x, wire_y][loc_wire_indices[wire_x, wire_y][wire_name]].append((
(bel_x, bel_y, bel_idx), bel_pin))
def add_slice(x, y, z):
idx = len(loc_bels[x, y])
l = ("A", "B", "C", "D")[z]
name = "SLICE" + l
loc_bels[x, y].append((name, "SLICE", []))
lc0 = z * 2
lc1 = z * 2 + 1
add_bel_input(x, y, idx, "A0", x, y, "A{}_SLICE".format(lc0))
add_bel_input(x, y, idx, "B0", x, y, "B{}_SLICE".format(lc0))
add_bel_input(x, y, idx, "C0", x, y, "C{}_SLICE".format(lc0))
add_bel_input(x, y, idx, "D0", x, y, "D{}_SLICE".format(lc0))
add_bel_input(x, y, idx, "M0", x, y, "M{}_SLICE".format(lc0))
add_bel_input(x, y, idx, "A1", x, y, "A{}_SLICE".format(lc1))
add_bel_input(x, y, idx, "B1", x, y, "B{}_SLICE".format(lc1))
add_bel_input(x, y, idx, "C1", x, y, "C{}_SLICE".format(lc1))
add_bel_input(x, y, idx, "D1", x, y, "D{}_SLICE".format(lc1))
add_bel_input(x, y, idx, "M1", x, y, "M{}_SLICE".format(lc1))
add_bel_input(x, y, idx, "FCI", x, y, "FCI{}_SLICE".format(l if z > 0 else ""))
add_bel_input(x, y, idx, "FXA", x, y, "FXA{}_SLICE".format(l))
add_bel_input(x, y, idx, "FXB", x, y, "FXB{}_SLICE".format(l))
add_bel_input(x, y, idx, "CLK", x, y, "CLK{}_SLICE".format(z))
add_bel_input(x, y, idx, "LSR", x, y, "LSR{}_SLICE".format(z))
add_bel_input(x, y, idx, "CE", x, y, "CE{}_SLICE".format(z))
add_bel_input(x, y, idx, "DI0", x, y, "DI{}_SLICE".format(lc0))
add_bel_input(x, y, idx, "DI1", x, y, "DI{}_SLICE".format(lc1))
if z == 0 or z == 1:
add_bel_input(x, y, idx, "WD0", x, y, "WD0{}_SLICE".format(l))
add_bel_input(x, y, idx, "WD1", x, y, "WD1{}_SLICE".format(l))
add_bel_input(x, y, idx, "WAD0", x, y, "WAD0{}_SLICE".format(l))
add_bel_input(x, y, idx, "WAD1", x, y, "WAD1{}_SLICE".format(l))
add_bel_input(x, y, idx, "WAD2", x, y, "WAD2{}_SLICE".format(l))
add_bel_input(x, y, idx, "WAD3", x, y, "WAD3{}_SLICE".format(l))
add_bel_input(x, y, idx, "WRE", x, y, "WRE{}_SLICE".format(z))
add_bel_input(x, y, idx, "WCK", x, y, "WCK{}_SLICE".format(z))
add_bel_output(x, y, idx, "F0", x, y, "F{}_SLICE".format(lc0))
add_bel_output(x, y, idx, "Q0", x, y, "Q{}_SLICE".format(lc0))
add_bel_output(x, y, idx, "F1", x, y, "F{}_SLICE".format(lc1))
add_bel_output(x, y, idx, "Q1", x, y, "Q{}_SLICE".format(lc1))
add_bel_output(x, y, idx, "OFX0", x, y, "F5{}_SLICE".format(l))
add_bel_output(x, y, idx, "OFX1", x, y, "FX{}_SLICE".format(l))
add_bel_output(x, y, idx, "FCO", x, y, "FCO{}_SLICE".format(l if z < 3 else ""))
if z == 2:
add_bel_output(x, y, idx, "WDO0", x, y, "WDO0C_SLICE")
add_bel_output(x, y, idx, "WDO1", x, y, "WDO1C_SLICE")
add_bel_output(x, y, idx, "WDO2", x, y, "WDO2C_SLICE")
add_bel_output(x, y, idx, "WDO3", x, y, "WDO3C_SLICE")
add_bel_output(x, y, idx, "WADO0", x, y, "WADO0C_SLICE")
add_bel_output(x, y, idx, "WADO1", x, y, "WADO1C_SLICE")
add_bel_output(x, y, idx, "WADO2", x, y, "WADO2C_SLICE")
add_bel_output(x, y, idx, "WADO3", x, y, "WADO3C_SLICE")
def add_pio(x, y, z):
idx = len(loc_bels[x, y])
l = ("A", "B", "C", "D")[z]
name = "PIO" + l
loc_bels[x, y].append((name, "PIO", []))
add_bel_input(x, y, idx, "I", x, y, "PADDO{}_PIO".format(l))
add_bel_input(x, y, idx, "T", x, y, "PADDT{}_PIO".format(l))
add_bel_output(x, y, idx, "O", x, y, "JPADDI{}_PIO".format(l))
def add_bels(chip, x, y):
loc_bels[x, y] = []
tiles = chip.get_tiles_by_position(y, x)
num_slices = 0
num_pios = 0
for tile in tiles:
tt = tile.info.type
if tt == "PLC2":
num_slices = 4
elif "PICL0" in tt or "PICR0" in tt:
num_pios = 4
elif "PIOT0" in tt or ("PICB0" in tt and "SPICB" not in tt):
num_pios = 2
for i in range(num_slices):
add_slice(x, y, i)
for i in range(num_pios):
add_pio(x, y, i)
# Import a location, deduplicating if appropriate
def import_location(rg, x, y):
rtile = rg.tiles[pytrellis.Location(x, y)]
arcs = [] # (src, dst, configurable, tiletype)
wires = [] # (name, uphill, downhill, belpin_uphill, belpins_downhill)
bels = [] # (name, [(pin, wire)])
for name in loc_wires[x, y]:
w = rtile.wires[rg.ident(name)]
arcs_uphill = []
arcs_downhill = []
belpins_uphill = []
belpins_downhill = []
for uh in w.uphill:
arcidx = loc_arc_indices[uh.loc.x, uh.loc.y][uh.id]
arcs_uphill.append((uh.loc.x - x, uh.loc.y - y, arcidx))
for dh in w.downhill:
arcidx = loc_arc_indices[dh.loc.x, dh.loc.y][dh.id]
arcs_downhill.append((dh.loc.x - x, dh.loc.y - y, arcidx))
for bp in wire_bel_pins_uphill[x, y][loc_wire_indices[x, y][name]]:
bel, pin = bp
bel_x, bel_y, bel_idx = bel
belpins_uphill.append(((bel_x - x, bel_y - y, bel_idx), pin))
for bp in wire_bel_pins_downhill[x, y][loc_wire_indices[x, y][name]]:
bel, pin = bp
bel_x, bel_y, bel_idx = bel
belpins_downhill.append(((bel_x - x, bel_y - y, bel_idx), pin))
assert len(belpins_uphill) <= 1
wires.append((name, tuple(arcs_downhill), tuple(arcs_uphill), tuple(belpins_uphill), tuple(belpins_downhill)))
for bel in loc_bels[x, y]:
name, beltype, pins = bel
xformed_pins = tuple((p[0], (p[1][0] - x, p[1][1] - y, p[1][2])) for p in pins)
bels.append((name, beltype, xformed_pins))
for arcidx in loc_arcs[x, y]:
a = rtile.arcs[arcidx]
source_wire = resolve_wirename(rg, a.source, x, y)
dest_wire = resolve_wirename(rg, a.sink, x, y)
arcs.append((source_wire, dest_wire, a.configurable, get_tiletype_index(rg.to_str(a.tiletype))))
tile_data = (tuple(wires), tuple(arcs), tuple(bels))
if tile_data in location_types:
type_at_location[x, y] = location_types[tile_data]
else:
idx = len(location_types)
location_types[tile_data] = idx
type_at_location[x, y] = idx
class BinaryBlobAssembler:
def __init__(self, cname, endianness, nodebug=False):
assert endianness in ["le", "be"]
self.cname = cname
self.endianness = endianness
self.finalized = False
self.data = bytearray()
self.comments = dict()
self.labels = dict()
self.exports = set()
self.labels_byaddr = dict()
self.ltypes_byaddr = dict()
self.strings = dict()
self.refs = dict()
self.nodebug = nodebug
def l(self, name, ltype=None, export=False):
assert not self.finalized
assert name not in self.labels
assert len(self.data) not in self.labels_byaddr
self.labels[name] = len(self.data)
if ltype is not None:
self.ltypes_byaddr[len(self.data)] = ltype
self.labels_byaddr[len(self.data)] = name
if export:
assert ltype is not None
self.exports.add(len(self.data))
def r(self, name, comment):
assert not self.finalized
assert len(self.data) % 4 == 0
assert len(self.data) not in self.refs
if self.nodebug:
comment = None
if name is not None:
self.refs[len(self.data)] = (name, comment)
self.data.append(0)
self.data.append(0)
self.data.append(0)
self.data.append(0)
if (name is None) and (comment is not None):
self.comments[len(self.data)] = comment + " (null reference)"
def s(self, s, comment):
assert not self.finalized
if self.nodebug:
comment = None
if s not in self.strings:
index = len(self.strings)
self.strings[s] = index
else:
index = self.strings[s]
if comment is not None:
self.r("str%d" % index, '%s: "%s"' % (comment, s))
else:
self.r("str%d" % index, None)
def u8(self, v, comment):
assert not self.finalized
if self.nodebug:
comment = None
self.data.append(v)
if comment is not None:
self.comments[len(self.data)] = comment
def u16(self, v, comment):
assert not self.finalized
assert len(self.data) % 2 == 0
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
elif self.endianness == "be":
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
def s16(self, v, comment):
assert not self.finalized
assert len(self.data) % 2 == 0
if self.nodebug:
comment = None
c2val = (((-v) ^ 0xffff) + 1) if v < 0 else v
if self.endianness == "le":
self.data.append(c2val & 255)
self.data.append((c2val >> 8) & 255)
elif self.endianness == "be":
self.data.append((c2val >> 8) & 255)
self.data.append(c2val & 255)
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
def u32(self, v, comment):
assert not self.finalized
assert len(self.data) % 4 == 0
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 24) & 255)
elif self.endianness == "be":
self.data.append((v >> 24) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
def finalize(self):
assert not self.finalized
for s, index in sorted(self.strings.items()):
self.l("str%d" % index, "char")
for c in s:
self.data.append(ord(c))
self.data.append(0)
self.finalized = True
cursor = 0
while cursor < len(self.data):
if cursor in self.refs:
v = self.labels[self.refs[cursor][0]] - cursor
if self.endianness == "le":
self.data[cursor + 0] = (v & 255)
self.data[cursor + 1] = ((v >> 8) & 255)
self.data[cursor + 2] = ((v >> 16) & 255)
self.data[cursor + 3] = ((v >> 24) & 255)
elif self.endianness == "be":
self.data[cursor + 0] = ((v >> 24) & 255)
self.data[cursor + 1] = ((v >> 16) & 255)
self.data[cursor + 2] = ((v >> 8) & 255)
self.data[cursor + 3] = (v & 255)
else:
assert 0
cursor += 4
else:
cursor += 1
def write_verbose_c(self, f, ctype="const unsigned char"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
cursor = 0
bytecnt = 0
while cursor < len(self.data):
if cursor in self.comments:
if bytecnt == 0:
print(" ", end="", file=f)
print(" // %s" % self.comments[cursor], file=f)
bytecnt = 0
if cursor in self.labels_byaddr:
if bytecnt != 0:
print(file=f)
if cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (
self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
else:
print(" // [%d] %s" % (cursor, self.labels_byaddr[cursor]), file=f)
bytecnt = 0
if cursor in self.refs:
if bytecnt != 0:
print(file=f)
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 0]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 1]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 2]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 3]), end="", file=f)
print(" // [%d] %s (reference to %s)" % (cursor, self.refs[cursor][1], self.refs[cursor][0]), file=f)
bytecnt = 0
cursor += 4
else:
if bytecnt == 0:
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor]), end=("" if bytecnt < 15 else "\n"), file=f)
bytecnt = (bytecnt + 1) & 15
cursor += 1
if bytecnt != 0:
print(file=f)
print("};", file=f)
def write_compact_c(self, f, ctype="const unsigned char"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
column = 0
for v in self.data:
if column == 0:
print(" ", end="", file=f)
column += 2
s = "%d," % v
print(s, end="", file=f)
column += len(s)
if column > 75:
print(file=f)
column = 0
if column != 0:
print(file=f)
for cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (
self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
print("};", file=f)
def write_uint64_c(self, f, ctype="const uint64_t"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, (len(self.data) + 7) // 8), file=f)
column = 0
for i in range((len(self.data) + 7) // 8):
v0 = self.data[8 * i + 0] if 8 * i + 0 < len(self.data) else 0
v1 = self.data[8 * i + 1] if 8 * i + 1 < len(self.data) else 0
v2 = self.data[8 * i + 2] if 8 * i + 2 < len(self.data) else 0
v3 = self.data[8 * i + 3] if 8 * i + 3 < len(self.data) else 0
v4 = self.data[8 * i + 4] if 8 * i + 4 < len(self.data) else 0
v5 = self.data[8 * i + 5] if 8 * i + 5 < len(self.data) else 0
v6 = self.data[8 * i + 6] if 8 * i + 6 < len(self.data) else 0
v7 = self.data[8 * i + 7] if 8 * i + 7 < len(self.data) else 0
if self.endianness == "le":
v = v0 << 0
v |= v1 << 8
v |= v2 << 16
v |= v3 << 24
v |= v4 << 32
v |= v5 << 40
v |= v6 << 48
v |= v7 << 56
elif self.endianness == "be":
v = v7 << 0
v |= v6 << 8
v |= v5 << 16
v |= v4 << 24
v |= v3 << 32
v |= v2 << 40
v |= v1 << 48
v |= v0 << 56
else:
assert 0
if column == 3:
print(" 0x%016x," % v, file=f)
column = 0
else:
if column == 0:
print(" ", end="", file=f)
print(" 0x%016x," % v, end="", file=f)
column += 1
if column != 0:
print("", file=f)
print("};", file=f)
def write_string_c(self, f, ctype="const char"):
assert self.finalized
assert self.data[len(self.data) - 1] == 0
print("%s %s[%d] =" % (ctype, self.cname, len(self.data)), file=f)
print(" \"", end="", file=f)
column = 0
for i in range(len(self.data) - 1):
if (self.data[i] < 32) or (self.data[i] > 126):
print("\\%03o" % self.data[i], end="", file=f)
column += 4
elif self.data[i] == ord('"') or self.data[i] == ord('\\'):
print("\\" + chr(self.data[i]), end="", file=f)
column += 2
else:
print(chr(self.data[i]), end="", file=f)
column += 1
if column > 70 and (i != len(self.data) - 2):
print("\"\n \"", end="", file=f)
column = 0
print("\";", file=f)
def write_binary(self, f):
assert self.finalized
assert self.data[len(self.data) - 1] == 0
f.buffer.write(self.data)
bel_types = {
"NONE": 0,
"SLICE": 1,
"PIO": 2
}
def write_database(dev_name, endianness):
def write_loc(x, y, sym_name):
bba.s16(x, "%s.x" % sym_name)
bba.s16(y, "%s.y" % sym_name)
bba = BinaryBlobAssembler("chipdb_blob_%s" % dev_name, endianness)
bba.r("chip_info", "chip_info")
for loctype, idx in sorted(location_types.items(), key=lambda x: x[1]):
wires, arcs, bels = loctype
if len(arcs) > 0:
bba.l("loc%d_pips" % idx, "PipInfoPOD")
for arc in arcs:
src_wire, dst_wire, configurable, tile_type = arc
write_loc(src_wire[0], src_wire[1], "src")
write_loc(dst_wire[0], dst_wire[1], "dst")
bba.u32(src_wire[2], "src_idx")
bba.u32(dst_wire[2], "dst_idx")
bba.u32(1, "delay") # TODO:delay
bba.u16(tile_type, "tile_type")
bba.u8(1 if not configurable else 0, "pip_type")
bba.u8(0, "padding")
if len(wires) > 0:
for wire_idx in range(len(wires)):
wire = wires[wire_idx]
name, downpips, uppips, downbels, upbels = wire
if len(downpips) > 0:
bba.l("loc%d_wire%d_downpips" % (idx, wire_idx), "PipLocatorPOD")
for dp in downpips:
write_loc(dp[0], dp[1], "rel_loc")
bba.u32(dp[2], "index")
if len(uppips) > 0:
bba.l("loc%d_wire%d_uppips" % (idx, wire_idx), "PipLocatorPOD")
for up in uppips:
write_loc(up[0], up[1], "rel_loc")
bba.u32(up[2], "index")
if len(downbels) > 0:
bba.l("loc%d_wire%d_downbels" % (idx, wire_idx), "BelPortPOD")
for db in downbels:
bel, pin = db
write_loc(bel[0], bel[1], "rel_bel_loc")
bba.u32(bel[2], "bel_index")
bba.u32(pin, "port")
bba.l("loc%d_wires" % idx, "WireInfoPOD")
for wire_idx in range(len(wires)):
wire = wires[wire_idx]
name, downpips, uppips, downbels, upbels = wire
bba.s(name, "name")
bba.u32(len(uppips), "num_uphill")
bba.u32(len(downpips), "num_downhill")
bba.r("loc%d_wire%d_uppips" % (idx, wire_idx) if len(uppips) > 0 else None, "pips_uphill")
bba.r("loc%d_wire%d_downpips" % (idx, wire_idx) if len(downpips) > 0 else None, "pips_downhill")
bba.u32(len(downbels), "num_bels_downhill")
if len(upbels) == 1:
bel, pin = upbels[0]
write_loc(bel[0], bel[1], "uphill_bel_loc")
bba.u32(bel[2], "uphill_bel_idx")
bba.u32(pin, "uphill_bel_pin")
else:
write_loc(-1, -1, "bel_uphill.rel_bel_loc")
bba.u32(0xFFFFFFFF, "bel_uphill.bel_index")
bba.u32(0, "bel_uphill.port")
bba.r("loc%d_wire%d_downbels" % (idx, wire_idx) if len(downbels) > 0 else None, "bels_downhill")
if len(bels) > 0:
for bel_idx in range(len(bels)):
bel, beltype, pins = bels[bel_idx]
bba.l("loc%d_bel%d_wires" % (idx, bel_idx), "BelPortPOD")
for pin in pins:
port, wire = pin
write_loc(wire[0], wire[1], "rel_wire_loc")
bba.u32(wire[2], "wire_index")
bba.u32(port, "port")
bba.l("loc%d_bels" % idx, "BelInfoPOD")
for bel_idx in range(len(bels)):
bel, beltype, pins = bels[bel_idx]
bba.s(bel, "name")
bba.u32(bel_types[beltype], "type")
bba.u32(len(pins), "num_bel_wires")
bba.r("loc%d_bel%d_wires" % (idx, bel_idx), "bel_wires")
bba.l("locations", "LocationTypePOD")
for loctype, idx in sorted(location_types.items(), key=lambda x: x[1]):
wires, arcs, bels = loctype
bba.u32(len(bels), "num_bels")
bba.u32(len(wires), "num_wires")
bba.u32(len(arcs), "num_pips")
bba.r("loc%d_bels" % idx if len(bels) > 0 else None, "bel_data")
bba.r("loc%d_wires" % idx if len(wires) > 0 else None, "wire_data")
bba.r("loc%d_pips" % idx if len(arcs) > 0 else None, "pips_data")
bba.l("location_types", "int32_t")
for y in range(0, max_row+1):
for x in range(0, max_col+1):
bba.u32(type_at_location[x, y], "loctype")
bba.l("tiletype_names", "RelPtr<char>")
for tt in tiletype_names:
bba.s(tt, "name")
bba.l("chip_info")
bba.u32(max_col + 1, "width")
bba.u32(max_row + 1, "height")
bba.u32((max_col + 1) * (max_row + 1), "num_tiles")
bba.u32(len(location_types), "num_location_types")
bba.r("locations", "locations")
bba.r("location_types", "location_type")
bba.r("tiletype_names", "tiletype_names")
bba.finalize()
return bba
dev_names = {"25k": "LFE5U-25F", "45k": "LFE5U-45F", "85k": "LFE5U-85F"}
def main():
global max_row, max_col
pytrellis.load_database(database.get_db_root())
args = parser.parse_args()
# Read port pin file
with open(args.portspins) as f:
for line in f:
line = line.replace("(", " ")
line = line.replace(")", " ")
line = line.split()
if len(line) == 0:
continue
assert len(line) == 2
assert line[0] == "X"
idx = len(portpins) + 1
portpins[line[1]] = idx
print("Initialising chip...")
chip = pytrellis.Chip(dev_names[args.device])
print("Building routing graph...")
rg = chip.get_routing_graph()
max_row = chip.get_max_row()
max_col = chip.get_max_col()
print("Indexing wires...")
for y in range(0, max_row + 1):
for x in range(0, max_col + 1):
import_location_wires(rg, x, y)
print("Indexing arcs...")
for y in range(0, max_row + 1):
for x in range(0, max_col + 1):
index_location_arcs(rg, x, y)
print("Adding bels...")
for y in range(0, max_row + 1):
for x in range(0, max_col + 1):
add_bels(chip, x, y)
print("Importing tiles...")
for y in range(0, max_row + 1):
for x in range(0, max_col + 1):
print(" At R{}C{}".format(y, x))
import_location(rg, x, y)
print("{} unique location types".format(len(location_types)))
bba = write_database(args.device, "le")
if args.c_file:
print('#include "nextpnr.h"', file=args.outfile)
print('NEXTPNR_NAMESPACE_BEGIN', file=args.outfile)
if args.binary:
bba.write_binary(args.outfile)
if args.c_file:
bba.write_string_c(args.outfile)
if args.c_file:
print('NEXTPNR_NAMESPACE_END', file=args.outfile)
if __name__ == "__main__":
main()

View File

@ -19,6 +19,8 @@
#include <math.h> #include <math.h>
#include "nextpnr.h" #include "nextpnr.h"
#include "placer1.h"
#include "router1.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -107,28 +109,48 @@ void Arch::addBelInout(IdString bel, IdString name, IdString wire)
wires.at(wire).downhill_bel_pins.push_back(BelPin{bel, name}); wires.at(wire).downhill_bel_pins.push_back(BelPin{bel, name});
} }
void Arch::addFrameGraphic(const GraphicElement &graphic) void Arch::addGroupBel(IdString group, IdString bel) { groups[group].bels.push_back(bel); }
void Arch::addGroupWire(IdString group, IdString wire) { groups[group].wires.push_back(wire); }
void Arch::addGroupPip(IdString group, IdString pip) { groups[group].pips.push_back(pip); }
void Arch::addGroupGroup(IdString group, IdString grp) { groups[group].groups.push_back(grp); }
void Arch::addDecalGraphic(DecalId decal, const GraphicElement &graphic)
{ {
frame_graphics.push_back(graphic); decal_graphics[decal].push_back(graphic);
frameGraphicsReload = true; refreshUi();
} }
void Arch::addWireGraphic(WireId wire, const GraphicElement &graphic) void Arch::setFrameDecal(DecalXY decalxy)
{ {
wires.at(wire).graphics.push_back(graphic); frame_decalxy = decalxy;
wireGraphicsReload.insert(wire); refreshUiFrame();
} }
void Arch::addPipGraphic(PipId pip, const GraphicElement &graphic) void Arch::setWireDecal(WireId wire, DecalXY decalxy)
{ {
pips.at(pip).graphics.push_back(graphic); wires.at(wire).decalxy = decalxy;
pipGraphicsReload.insert(pip); refreshUiWire(wire);
} }
void Arch::addBelGraphic(BelId bel, const GraphicElement &graphic) void Arch::setPipDecal(PipId pip, DecalXY decalxy)
{ {
bels.at(bel).graphics.push_back(graphic); pips.at(pip).decalxy = decalxy;
belGraphicsReload.insert(bel); refreshUiPip(pip);
}
void Arch::setBelDecal(BelId bel, DecalXY decalxy)
{
bels.at(bel).decalxy = decalxy;
refreshUiBel(bel);
}
void Arch::setGroupDecal(GroupId group, DecalXY decalxy)
{
groups[group].decalxy = decalxy;
refreshUiGroup(group);
} }
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -159,6 +181,7 @@ void Arch::bindBel(BelId bel, IdString cell, PlaceStrength strength)
bels.at(bel).bound_cell = cell; bels.at(bel).bound_cell = cell;
cells.at(cell)->bel = bel; cells.at(cell)->bel = bel;
cells.at(cell)->belStrength = strength; cells.at(cell)->belStrength = strength;
refreshUiBel(bel);
} }
void Arch::unbindBel(BelId bel) void Arch::unbindBel(BelId bel)
@ -166,6 +189,7 @@ void Arch::unbindBel(BelId bel)
cells.at(bels.at(bel).bound_cell)->bel = BelId(); cells.at(bels.at(bel).bound_cell)->bel = BelId();
cells.at(bels.at(bel).bound_cell)->belStrength = STRENGTH_NONE; cells.at(bels.at(bel).bound_cell)->belStrength = STRENGTH_NONE;
bels.at(bel).bound_cell = IdString(); bels.at(bel).bound_cell = IdString();
refreshUiBel(bel);
} }
bool Arch::checkBelAvail(BelId bel) const { return bels.at(bel).bound_cell == IdString(); } bool Arch::checkBelAvail(BelId bel) const { return bels.at(bel).bound_cell == IdString(); }
@ -214,6 +238,7 @@ void Arch::bindWire(WireId wire, IdString net, PlaceStrength strength)
wires.at(wire).bound_net = net; wires.at(wire).bound_net = net;
nets.at(net)->wires[wire].pip = PipId(); nets.at(net)->wires[wire].pip = PipId();
nets.at(net)->wires[wire].strength = strength; nets.at(net)->wires[wire].strength = strength;
refreshUiWire(wire);
} }
void Arch::unbindWire(WireId wire) void Arch::unbindWire(WireId wire)
@ -221,11 +246,14 @@ void Arch::unbindWire(WireId wire)
auto &net_wires = nets[wires.at(wire).bound_net]->wires; auto &net_wires = nets[wires.at(wire).bound_net]->wires;
auto pip = net_wires.at(wire).pip; auto pip = net_wires.at(wire).pip;
if (pip != PipId()) if (pip != PipId()) {
pips.at(pip).bound_net = IdString(); pips.at(pip).bound_net = IdString();
refreshUiPip(pip);
}
net_wires.erase(wire); net_wires.erase(wire);
wires.at(wire).bound_net = IdString(); wires.at(wire).bound_net = IdString();
refreshUiWire(wire);
} }
bool Arch::checkWireAvail(WireId wire) const { return wires.at(wire).bound_net == IdString(); } bool Arch::checkWireAvail(WireId wire) const { return wires.at(wire).bound_net == IdString(); }
@ -260,6 +288,8 @@ void Arch::bindPip(PipId pip, IdString net, PlaceStrength strength)
wires.at(wire).bound_net = net; wires.at(wire).bound_net = net;
nets.at(net)->wires[wire].pip = pip; nets.at(net)->wires[wire].pip = pip;
nets.at(net)->wires[wire].strength = strength; nets.at(net)->wires[wire].strength = strength;
refreshUiPip(pip);
refreshUiWire(wire);
} }
void Arch::unbindPip(PipId pip) void Arch::unbindPip(PipId pip)
@ -268,6 +298,8 @@ void Arch::unbindPip(PipId pip)
nets.at(wires.at(wire).bound_net)->wires.erase(wire); nets.at(wires.at(wire).bound_net)->wires.erase(wire);
pips.at(pip).bound_net = IdString(); pips.at(pip).bound_net = IdString();
wires.at(wire).bound_net = IdString(); wires.at(wire).bound_net = IdString();
refreshUiPip(pip);
refreshUiWire(wire);
} }
bool Arch::checkPipAvail(PipId pip) const { return pips.at(pip).bound_net == IdString(); } bool Arch::checkPipAvail(PipId pip) const { return pips.at(pip).bound_net == IdString(); }
@ -292,6 +324,28 @@ const std::vector<PipId> &Arch::getWireAliases(WireId wire) const { return wires
// --------------------------------------------------------------- // ---------------------------------------------------------------
GroupId Arch::getGroupByName(IdString name) const { return name; }
IdString Arch::getGroupName(GroupId group) const { return group; }
std::vector<GroupId> Arch::getGroups() const
{
std::vector<GroupId> ret;
for (auto &it : groups)
ret.push_back(it.first);
return ret;
}
const std::vector<BelId> &Arch::getGroupBels(GroupId group) const { return groups.at(group).bels; }
const std::vector<WireId> &Arch::getGroupWires(GroupId group) const { return groups.at(group).wires; }
const std::vector<PipId> &Arch::getGroupPips(GroupId group) const { return groups.at(group).pips; }
const std::vector<GroupId> &Arch::getGroupGroups(GroupId group) const { return groups.at(group).groups; }
// ---------------------------------------------------------------
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
{ {
x = bels.at(bel).grid_x; x = bels.at(bel).grid_x;
@ -310,13 +364,23 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
// --------------------------------------------------------------- // ---------------------------------------------------------------
const std::vector<GraphicElement> &Arch::getFrameGraphics() const { return frame_graphics; } bool Arch::place() { return placer1(getCtx()); }
const std::vector<GraphicElement> &Arch::getBelGraphics(BelId bel) const { return bels.at(bel).graphics; } bool Arch::route() { return router1(getCtx()); }
const std::vector<GraphicElement> &Arch::getWireGraphics(WireId wire) const { return wires.at(wire).graphics; } // ---------------------------------------------------------------
const std::vector<GraphicElement> &Arch::getPipGraphics(PipId pip) const { return pips.at(pip).graphics; } const std::vector<GraphicElement> &Arch::getDecalGraphics(DecalId decal) const { return decal_graphics.at(decal); }
DecalXY Arch::getFrameDecal() const { return frame_decalxy; }
DecalXY Arch::getBelDecal(BelId bel) const { return bels.at(bel).decalxy; }
DecalXY Arch::getWireDecal(WireId wire) const { return wires.at(wire).decalxy; }
DecalXY Arch::getPipDecal(PipId pip) const { return pips.at(pip).decalxy; }
DecalXY Arch::getGroupDecal(GroupId group) const { return groups.at(group).decalxy; }
// --------------------------------------------------------------- // ---------------------------------------------------------------

View File

@ -34,16 +34,16 @@ struct PipInfo
IdString name, bound_net; IdString name, bound_net;
WireId srcWire, dstWire; WireId srcWire, dstWire;
DelayInfo delay; DelayInfo delay;
std::vector<GraphicElement> graphics; DecalXY decalxy;
}; };
struct WireInfo struct WireInfo
{ {
IdString name, bound_net; IdString name, bound_net;
std::vector<GraphicElement> graphics;
std::vector<PipId> downhill, uphill, aliases; std::vector<PipId> downhill, uphill, aliases;
BelPin uphill_bel_pin; BelPin uphill_bel_pin;
std::vector<BelPin> downhill_bel_pins; std::vector<BelPin> downhill_bel_pins;
DecalXY decalxy;
int grid_x, grid_y; int grid_x, grid_y;
}; };
@ -58,11 +58,21 @@ struct BelInfo
{ {
IdString name, type, bound_cell; IdString name, type, bound_cell;
std::unordered_map<IdString, PinInfo> pins; std::unordered_map<IdString, PinInfo> pins;
std::vector<GraphicElement> graphics; DecalXY decalxy;
int grid_x, grid_y; int grid_x, grid_y;
bool gb; bool gb;
}; };
struct GroupInfo
{
IdString name;
std::vector<BelId> bels;
std::vector<WireId> wires;
std::vector<PipId> pips;
std::vector<GroupId> groups;
DecalXY decalxy;
};
struct Arch : BaseCtx struct Arch : BaseCtx
{ {
std::string chipName; std::string chipName;
@ -70,11 +80,14 @@ struct Arch : BaseCtx
std::unordered_map<IdString, WireInfo> wires; std::unordered_map<IdString, WireInfo> wires;
std::unordered_map<IdString, PipInfo> pips; std::unordered_map<IdString, PipInfo> pips;
std::unordered_map<IdString, BelInfo> bels; std::unordered_map<IdString, BelInfo> bels;
std::unordered_map<GroupId, GroupInfo> groups;
std::vector<IdString> bel_ids, wire_ids, pip_ids; std::vector<IdString> bel_ids, wire_ids, pip_ids;
std::unordered_map<IdString, std::vector<IdString>> bel_ids_by_type; std::unordered_map<IdString, std::vector<IdString>> bel_ids_by_type;
std::vector<GraphicElement> frame_graphics; std::unordered_map<DecalId, std::vector<GraphicElement>> decal_graphics;
DecalXY frame_decalxy;
float grid_distance_to_delay; float grid_distance_to_delay;
void addWire(IdString name, int x, int y); void addWire(IdString name, int x, int y);
@ -86,10 +99,17 @@ struct Arch : BaseCtx
void addBelOutput(IdString bel, IdString name, IdString wire); void addBelOutput(IdString bel, IdString name, IdString wire);
void addBelInout(IdString bel, IdString name, IdString wire); void addBelInout(IdString bel, IdString name, IdString wire);
void addFrameGraphic(const GraphicElement &graphic); void addGroupBel(IdString group, IdString bel);
void addWireGraphic(WireId wire, const GraphicElement &graphic); void addGroupWire(IdString group, IdString wire);
void addPipGraphic(PipId pip, const GraphicElement &graphic); void addGroupPip(IdString group, IdString pip);
void addBelGraphic(BelId bel, const GraphicElement &graphic); void addGroupGroup(IdString group, IdString grp);
void addDecalGraphic(DecalId decal, const GraphicElement &graphic);
void setFrameDecal(DecalXY decalxy);
void setWireDecal(WireId wire, DecalXY decalxy);
void setPipDecal(PipId pip, DecalXY decalxy);
void setBelDecal(BelId bel, DecalXY decalxy);
void setGroupDecal(GroupId group, DecalXY decalxy);
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Common Arch API. Every arch must provide the following methods. // Common Arch API. Every arch must provide the following methods.
@ -148,6 +168,14 @@ struct Arch : BaseCtx
const std::vector<PipId> &getPipsUphill(WireId wire) const; const std::vector<PipId> &getPipsUphill(WireId wire) const;
const std::vector<PipId> &getWireAliases(WireId wire) const; const std::vector<PipId> &getWireAliases(WireId wire) const;
GroupId getGroupByName(IdString name) const;
IdString getGroupName(GroupId group) const;
std::vector<GroupId> getGroups() const;
const std::vector<BelId> &getGroupBels(GroupId group) const;
const std::vector<WireId> &getGroupWires(GroupId group) const;
const std::vector<PipId> &getGroupPips(GroupId group) const;
const std::vector<GroupId> &getGroupGroups(GroupId group) const;
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const; void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
delay_t estimateDelay(WireId src, WireId dst) const; delay_t estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 0.01; } delay_t getDelayEpsilon() const { return 0.01; }
@ -155,16 +183,16 @@ struct Arch : BaseCtx
float getDelayNS(delay_t v) const { return v; } float getDelayNS(delay_t v) const { return v; }
uint32_t getDelayChecksum(delay_t v) const { return 0; } uint32_t getDelayChecksum(delay_t v) const { return 0; }
const std::vector<GraphicElement> &getFrameGraphics() const; bool pack() { return true; }
const std::vector<GraphicElement> &getBelGraphics(BelId bel) const; bool place();
const std::vector<GraphicElement> &getWireGraphics(WireId wire) const; bool route();
const std::vector<GraphicElement> &getPipGraphics(PipId pip) const;
bool allGraphicsReload = false; const std::vector<GraphicElement> &getDecalGraphics(DecalId decal) const;
bool frameGraphicsReload = false; DecalXY getFrameDecal() const;
std::unordered_set<BelId> belGraphicsReload; DecalXY getBelDecal(BelId bel) const;
std::unordered_set<WireId> wireGraphicsReload; DecalXY getWireDecal(WireId wire) const;
std::unordered_set<PipId> pipGraphicsReload; DecalXY getPipDecal(PipId pip) const;
DecalXY getGroupDecal(GroupId group) const;
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const; bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const;
IdString getPortClock(const CellInfo *cell, IdString port) const; IdString getPortClock(const CellInfo *cell, IdString port) const;

View File

@ -49,5 +49,7 @@ typedef IdString PortPin;
typedef IdString BelId; typedef IdString BelId;
typedef IdString WireId; typedef IdString WireId;
typedef IdString PipId; typedef IdString PipId;
typedef IdString GroupId;
typedef IdString DecalId;
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -90,24 +90,34 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
Context ctx(ArchArgs{}); std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(ArchArgs{}));
if (vm.count("verbose")) { if (vm.count("verbose")) {
ctx.verbose = true; ctx->verbose = true;
} }
if (vm.count("force")) { if (vm.count("force")) {
ctx.force = true; ctx->force = true;
} }
if (vm.count("seed")) { if (vm.count("seed")) {
ctx.rngseed(vm["seed"].as<int>()); ctx->rngseed(vm["seed"].as<int>());
} }
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv);
MainWindow w(std::move(ctx));
w.show();
return a.exec();
}
#endif
#ifndef NO_PYTHON #ifndef NO_PYTHON
if (vm.count("run")) { if (vm.count("run")) {
init_python(argv[0], true); init_python(argv[0], true);
python_export_global("ctx", ctx); python_export_global("ctx", *ctx.get());
std::vector<std::string> files = vm["run"].as<std::vector<std::string>>(); std::vector<std::string> files = vm["run"].as<std::vector<std::string>>();
for (auto filename : files) for (auto filename : files)
@ -117,15 +127,6 @@ int main(int argc, char *argv[])
} }
#endif #endif
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv);
MainWindow w;
w.show();
rc = a.exec();
}
#endif
return rc; return rc;
} catch (log_execution_error_exception) { } catch (log_execution_error_exception) {
#if defined(_MSC_VER) #if defined(_MSC_VER)

View File

@ -12,7 +12,6 @@ if (BUILD_PYTHON)
../3rdparty/python-console/modified/pyredirector.cc ../3rdparty/python-console/modified/pyredirector.cc
../3rdparty/python-console/modified/pyinterpreter.cc ../3rdparty/python-console/modified/pyinterpreter.cc
../3rdparty/python-console/modified/pyconsole.cc
) )
endif() endif()
@ -26,6 +25,7 @@ qt5_add_resources(GUI_RESOURCE_FILES ${_RESOURCES})
set(GUI_LIBRARY_FILES_${ufamily} Qt5::Widgets Qt5::OpenGL ${OPENGL_LIBRARIES} QtPropertyBrowser PARENT_SCOPE) set(GUI_LIBRARY_FILES_${ufamily} Qt5::Widgets Qt5::OpenGL ${OPENGL_LIBRARIES} QtPropertyBrowser PARENT_SCOPE)
add_library(gui_${family} STATIC ${GUI_SOURCE_FILES} ${PYTHON_CONSOLE_SRC} ${GUI_RESOURCE_FILES}) add_library(gui_${family} STATIC ${GUI_SOURCE_FILES} ${PYTHON_CONSOLE_SRC} ${GUI_RESOURCE_FILES})
include(${family}/family.cmake)
target_include_directories(gui_${family} PRIVATE ../${family} ${family} ../3rdparty/QtPropertyBrowser/src) target_include_directories(gui_${family} PRIVATE ../${family} ${family} ../3rdparty/QtPropertyBrowser/src)
if (BUILD_PYTHON) if (BUILD_PYTHON)

View File

@ -4,5 +4,11 @@
<file>resources/open.png</file> <file>resources/open.png</file>
<file>resources/save.png</file> <file>resources/save.png</file>
<file>resources/exit.png</file> <file>resources/exit.png</file>
<file>resources/zoom.png</file>
<file>resources/resultset_first.png</file>
<file>resources/resultset_previous.png</file>
<file>resources/resultset_next.png</file>
<file>resources/resultset_last.png</file>
<file>resources/splash.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -18,6 +18,7 @@
*/ */
#include <QAction> #include <QAction>
#include <QCoreApplication>
#include <QFileDialog> #include <QFileDialog>
#include <QGridLayout> #include <QGridLayout>
#include <QIcon> #include <QIcon>
@ -27,16 +28,14 @@
#include "jsonparse.h" #include "jsonparse.h"
#include "log.h" #include "log.h"
#include "mainwindow.h" #include "mainwindow.h"
#ifndef NO_PYTHON
#include "pythontab.h" #include "pythontab.h"
#endif
static void initBasenameResource() { Q_INIT_RESOURCE(base); } static void initBasenameResource() { Q_INIT_RESOURCE(base); }
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent), ctx(nullptr) BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent)
: QMainWindow(parent), ctx(std::move(context))
{ {
initBasenameResource(); initBasenameResource();
qRegisterMetaType<std::string>(); qRegisterMetaType<std::string>();
@ -44,7 +43,7 @@ BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent), ctx(nullp
log_files.clear(); log_files.clear();
log_streams.clear(); log_streams.clear();
setObjectName(QStringLiteral("BaseMainWindow")); setObjectName("BaseMainWindow");
resize(1024, 768); resize(1024, 768);
createMenusAndBars(); createMenusAndBars();
@ -63,70 +62,80 @@ BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent), ctx(nullp
setCentralWidget(centralWidget); setCentralWidget(centralWidget);
DesignWidget *designview = new DesignWidget(); designview = new DesignWidget();
designview->setMinimumWidth(300); designview->setMinimumWidth(300);
designview->setMaximumWidth(300);
splitter_h->addWidget(designview); splitter_h->addWidget(designview);
connect(this, SIGNAL(contextChanged(Context *)), designview, SLOT(newContext(Context *)));
connect(this, SIGNAL(updateTreeView()), designview, SLOT(updateTree()));
connect(designview, SIGNAL(info(std::string)), this, SLOT(writeInfo(std::string)));
tabWidget = new QTabWidget(); tabWidget = new QTabWidget();
#ifndef NO_PYTHON
PythonTab *pythontab = new PythonTab(); console = new PythonTab();
tabWidget->addTab(pythontab, "Python"); tabWidget->addTab(console, "Console");
connect(this, SIGNAL(contextChanged(Context *)), pythontab, SLOT(newContext(Context *))); connect(this, SIGNAL(contextChanged(Context *)), console, SLOT(newContext(Context *)));
#endif
info = new InfoTab();
tabWidget->addTab(info, "Info");
centralTabWidget = new QTabWidget(); centralTabWidget = new QTabWidget();
FPGAViewWidget *fpgaView = new FPGAViewWidget(); FPGAViewWidget *fpgaView = new FPGAViewWidget();
centralTabWidget->addTab(fpgaView, "Graphics"); centralTabWidget->addTab(fpgaView, "Graphics");
connect(this, SIGNAL(contextChanged(Context *)), fpgaView, SLOT(newContext(Context *))); connect(this, SIGNAL(contextChanged(Context *)), fpgaView, SLOT(newContext(Context *)));
connect(designview, SIGNAL(selected(std::vector<DecalXY>)), fpgaView,
SLOT(onSelectedArchItem(std::vector<DecalXY>)));
connect(designview, SIGNAL(highlight(std::vector<DecalXY>, int)), fpgaView,
SLOT(onHighlightGroupChanged(std::vector<DecalXY>, int)));
connect(this, SIGNAL(contextChanged(Context *)), designview, SLOT(newContext(Context *)));
connect(this, SIGNAL(updateTreeView()), designview, SLOT(updateTree()));
connect(designview, SIGNAL(info(std::string)), this, SLOT(writeInfo(std::string)));
splitter_v->addWidget(centralTabWidget); splitter_v->addWidget(centralTabWidget);
splitter_v->addWidget(tabWidget); splitter_v->addWidget(tabWidget);
displaySplash();
} }
BaseMainWindow::~BaseMainWindow() {} BaseMainWindow::~BaseMainWindow() {}
void BaseMainWindow::writeInfo(std::string text) { info->info(text); } void BaseMainWindow::displaySplash()
{
splash = new QSplashScreen();
splash->setPixmap(QPixmap(":/icons/resources/splash.png"));
splash->show();
connect(designview, SIGNAL(finishContextLoad()), splash, SLOT(close()));
connect(designview, SIGNAL(contextLoadStatus(std::string)), this, SLOT(displaySplashMessage(std::string)));
QCoreApplication::instance()->processEvents();
}
void BaseMainWindow::displaySplashMessage(std::string msg)
{
splash->showMessage(msg.c_str(), Qt::AlignCenter | Qt::AlignBottom, Qt::white);
QCoreApplication::instance()->processEvents();
}
void BaseMainWindow::writeInfo(std::string text) { console->info(text); }
void BaseMainWindow::createMenusAndBars() void BaseMainWindow::createMenusAndBars()
{ {
actionNew = new QAction("New", this); actionNew = new QAction("New", this);
QIcon iconNew; actionNew->setIcon(QIcon(":/icons/resources/new.png"));
iconNew.addFile(QStringLiteral(":/icons/resources/new.png"));
actionNew->setIcon(iconNew);
actionNew->setShortcuts(QKeySequence::New); actionNew->setShortcuts(QKeySequence::New);
actionNew->setStatusTip("New project file"); actionNew->setStatusTip("New project file");
connect(actionNew, SIGNAL(triggered()), this, SLOT(new_proj())); connect(actionNew, SIGNAL(triggered()), this, SLOT(new_proj()));
actionOpen = new QAction("Open", this); actionOpen = new QAction("Open", this);
QIcon iconOpen; actionOpen->setIcon(QIcon(":/icons/resources/open.png"));
iconOpen.addFile(QStringLiteral(":/icons/resources/open.png"));
actionOpen->setIcon(iconOpen);
actionOpen->setShortcuts(QKeySequence::Open); actionOpen->setShortcuts(QKeySequence::Open);
actionOpen->setStatusTip("Open an existing project file"); actionOpen->setStatusTip("Open an existing project file");
connect(actionOpen, SIGNAL(triggered()), this, SLOT(open_proj())); connect(actionOpen, SIGNAL(triggered()), this, SLOT(open_proj()));
QAction *actionSave = new QAction("Save", this); QAction *actionSave = new QAction("Save", this);
QIcon iconSave; actionSave->setIcon(QIcon(":/icons/resources/save.png"));
iconSave.addFile(QStringLiteral(":/icons/resources/save.png"));
actionSave->setIcon(iconSave);
actionSave->setShortcuts(QKeySequence::Save); actionSave->setShortcuts(QKeySequence::Save);
actionSave->setStatusTip("Save existing project to disk"); actionSave->setStatusTip("Save existing project to disk");
connect(actionSave, SIGNAL(triggered()), this, SLOT(save_proj()));
actionSave->setEnabled(false); actionSave->setEnabled(false);
connect(actionSave, SIGNAL(triggered()), this, SLOT(save_proj()));
QAction *actionExit = new QAction("Exit", this); QAction *actionExit = new QAction("Exit", this);
QIcon iconExit; actionExit->setIcon(QIcon(":/icons/resources/exit.png"));
iconExit.addFile(QStringLiteral(":/icons/resources/exit.png"));
actionExit->setIcon(iconExit);
actionExit->setShortcuts(QKeySequence::Quit); actionExit->setShortcuts(QKeySequence::Quit);
actionExit->setStatusTip("Exit the application"); actionExit->setStatusTip("Exit the application");
connect(actionExit, SIGNAL(triggered()), this, SLOT(close())); connect(actionExit, SIGNAL(triggered()), this, SLOT(close()));
@ -145,6 +154,12 @@ void BaseMainWindow::createMenusAndBars()
addToolBar(Qt::TopToolBarArea, mainToolBar); addToolBar(Qt::TopToolBarArea, mainToolBar);
statusBar = new QStatusBar(); statusBar = new QStatusBar();
progressBar = new QProgressBar(statusBar);
progressBar->setAlignment(Qt::AlignRight);
progressBar->setMaximumSize(180, 19);
statusBar->addPermanentWidget(progressBar);
progressBar->setValue(0);
progressBar->setEnabled(false);
setStatusBar(statusBar); setStatusBar(statusBar);
menu_File->addAction(actionNew); menu_File->addAction(actionNew);

View File

@ -20,34 +20,41 @@
#ifndef BASEMAINWINDOW_H #ifndef BASEMAINWINDOW_H
#define BASEMAINWINDOW_H #define BASEMAINWINDOW_H
#include "infotab.h"
#include "nextpnr.h" #include "nextpnr.h"
#include <QMainWindow> #include <QMainWindow>
#include <QMenu> #include <QMenu>
#include <QMenuBar> #include <QMenuBar>
#include <QProgressBar>
#include <QSplashScreen>
#include <QStatusBar> #include <QStatusBar>
#include <QTabWidget> #include <QTabWidget>
#include <QToolBar> #include <QToolBar>
Q_DECLARE_METATYPE(std::string) Q_DECLARE_METATYPE(std::string)
Q_DECLARE_METATYPE(NEXTPNR_NAMESPACE_PREFIX DecalXY)
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
class PythonTab;
class DesignWidget;
class BaseMainWindow : public QMainWindow class BaseMainWindow : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit BaseMainWindow(QWidget *parent = 0); explicit BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent = 0);
virtual ~BaseMainWindow(); virtual ~BaseMainWindow();
Context *getContext() { return ctx; } Context *getContext() { return ctx.get(); }
protected: protected:
void createMenusAndBars(); void createMenusAndBars();
void displaySplash();
protected Q_SLOTS: protected Q_SLOTS:
void writeInfo(std::string text); void writeInfo(std::string text);
void displaySplashMessage(std::string msg);
virtual void new_proj() = 0; virtual void new_proj() = 0;
virtual void open_proj() = 0; virtual void open_proj() = 0;
@ -58,16 +65,19 @@ class BaseMainWindow : public QMainWindow
void updateTreeView(); void updateTreeView();
protected: protected:
Context *ctx; std::unique_ptr<Context> ctx;
QTabWidget *tabWidget; QTabWidget *tabWidget;
QTabWidget *centralTabWidget; QTabWidget *centralTabWidget;
InfoTab *info; PythonTab *console;
QMenuBar *menuBar; QMenuBar *menuBar;
QToolBar *mainToolBar; QToolBar *mainToolBar;
QStatusBar *statusBar; QStatusBar *statusBar;
QAction *actionNew; QAction *actionNew;
QAction *actionOpen; QAction *actionOpen;
QProgressBar *progressBar;
QSplashScreen *splash;
DesignWidget *designview;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -20,23 +20,15 @@
#include "designwidget.h" #include "designwidget.h"
#include <QAction> #include <QAction>
#include <QGridLayout> #include <QGridLayout>
#include <QLineEdit>
#include <QMenu> #include <QMenu>
#include <QSplitter> #include <QSplitter>
#include <QToolBar>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include "fpgaviewwidget.h" #include "fpgaviewwidget.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
enum class ElementType
{
NONE,
BEL,
WIRE,
PIP,
NET,
CELL
};
class ElementTreeItem : public QTreeWidgetItem class ElementTreeItem : public QTreeWidgetItem
{ {
public: public:
@ -85,12 +77,88 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
propertyEditor = new QtTreePropertyBrowser(this); propertyEditor = new QtTreePropertyBrowser(this);
propertyEditor->setFactoryForManager(variantManager, variantFactory); propertyEditor->setFactoryForManager(variantManager, variantFactory);
propertyEditor->setPropertiesWithoutValueMarked(true); propertyEditor->setPropertiesWithoutValueMarked(true);
propertyEditor->show(); propertyEditor->show();
propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu);
QLineEdit *lineEdit = new QLineEdit();
lineEdit->setClearButtonEnabled(true);
lineEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition);
lineEdit->setPlaceholderText("Search...");
actionFirst = new QAction("", this);
actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png"));
actionFirst->setEnabled(false);
connect(actionFirst, &QAction::triggered, this, [this] {
history_ignore = true;
history_index = 0;
treeWidget->setCurrentItem(history.at(history_index));
updateButtons();
});
actionPrev = new QAction("", this);
actionPrev->setIcon(QIcon(":/icons/resources/resultset_previous.png"));
actionPrev->setEnabled(false);
connect(actionPrev, &QAction::triggered, this, [this] {
history_ignore = true;
history_index--;
treeWidget->setCurrentItem(history.at(history_index));
updateButtons();
});
actionNext = new QAction("", this);
actionNext->setIcon(QIcon(":/icons/resources/resultset_next.png"));
actionNext->setEnabled(false);
connect(actionNext, &QAction::triggered, this, [this] {
history_ignore = true;
history_index++;
treeWidget->setCurrentItem(history.at(history_index));
updateButtons();
});
actionLast = new QAction("", this);
actionLast->setIcon(QIcon(":/icons/resources/resultset_last.png"));
actionLast->setEnabled(false);
connect(actionLast, &QAction::triggered, this, [this] {
history_ignore = true;
history_index = int(history.size() - 1);
treeWidget->setCurrentItem(history.at(history_index));
updateButtons();
});
QToolBar *toolbar = new QToolBar();
toolbar->addAction(actionFirst);
toolbar->addAction(actionPrev);
toolbar->addAction(actionNext);
toolbar->addAction(actionLast);
QWidget *topWidget = new QWidget();
QVBoxLayout *vbox1 = new QVBoxLayout();
topWidget->setLayout(vbox1);
vbox1->setSpacing(5);
vbox1->setContentsMargins(0, 0, 0, 0);
vbox1->addWidget(lineEdit);
vbox1->addWidget(treeWidget);
QWidget *toolbarWidget = new QWidget();
QHBoxLayout *hbox = new QHBoxLayout;
hbox->setAlignment(Qt::AlignCenter);
toolbarWidget->setLayout(hbox);
hbox->setSpacing(0);
hbox->setContentsMargins(0, 0, 0, 0);
hbox->addWidget(toolbar);
QWidget *btmWidget = new QWidget();
QVBoxLayout *vbox2 = new QVBoxLayout();
btmWidget->setLayout(vbox2);
vbox2->setSpacing(0);
vbox2->setContentsMargins(0, 0, 0, 0);
vbox2->addWidget(toolbarWidget);
vbox2->addWidget(propertyEditor);
QSplitter *splitter = new QSplitter(Qt::Vertical); QSplitter *splitter = new QSplitter(Qt::Vertical);
splitter->addWidget(treeWidget); splitter->addWidget(topWidget);
splitter->addWidget(propertyEditor); splitter->addWidget(btmWidget);
QGridLayout *mainLayout = new QGridLayout(); QGridLayout *mainLayout = new QGridLayout();
mainLayout->setSpacing(0); mainLayout->setSpacing(0);
@ -99,16 +167,61 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
setLayout(mainLayout); setLayout(mainLayout);
// Connection // Connection
connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenu); connect(propertyEditor->treeWidget(), &QTreeWidget::customContextMenuRequested, this,
&DesignWidget::prepareMenuProperty);
connect(propertyEditor->treeWidget(), &QTreeWidget::itemDoubleClicked, this, &DesignWidget::onItemDoubleClicked);
connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), SLOT(onItemClicked(QTreeWidgetItem *, int))); connect(treeWidget, SIGNAL(itemSelectionChanged()), SLOT(onItemSelectionChanged()));
connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenuTree);
history_index = -1;
history_ignore = false;
highlightColors[0] = QColor("#6495ed");
highlightColors[1] = QColor("#7fffd4");
highlightColors[2] = QColor("#98fb98");
highlightColors[3] = QColor("#ffd700");
highlightColors[4] = QColor("#cd5c5c");
highlightColors[5] = QColor("#fa8072");
highlightColors[6] = QColor("#ff69b4");
highlightColors[7] = QColor("#da70d6");
} }
DesignWidget::~DesignWidget() {} DesignWidget::~DesignWidget() {}
void DesignWidget::updateButtons()
{
int count = int(history.size());
actionFirst->setEnabled(history_index > 0);
actionPrev->setEnabled(history_index > 0);
actionNext->setEnabled(history_index < (count - 1));
actionLast->setEnabled(history_index < (count - 1));
}
void DesignWidget::addToHistory(QTreeWidgetItem *item)
{
if (!history_ignore) {
int count = int(history.size());
for (int i = count - 1; i > history_index; i--)
history.pop_back();
history.push_back(item);
history_index++;
}
history_ignore = false;
updateButtons();
}
void DesignWidget::newContext(Context *ctx) void DesignWidget::newContext(Context *ctx)
{ {
treeWidget->clear(); treeWidget->clear();
history_ignore = false;
history_index = -1;
history.clear();
updateButtons();
for (int i = 0; i < 6; i++)
nameToItem[i].clear();
this->ctx = ctx; this->ctx = ctx;
// Add bels to tree // Add bels to tree
@ -117,6 +230,7 @@ void DesignWidget::newContext(Context *ctx)
bel_root->setText(0, "Bels"); bel_root->setText(0, "Bels");
treeWidget->insertTopLevelItem(0, bel_root); treeWidget->insertTopLevelItem(0, bel_root);
if (ctx) { if (ctx) {
Q_EMIT contextLoadStatus("Configuring bels...");
for (auto bel : ctx->getBels()) { for (auto bel : ctx->getBels()) {
auto id = ctx->getBelName(bel); auto id = ctx->getBelName(bel);
QStringList items = QString(id.c_str(ctx)).split("/"); QStringList items = QString(id.c_str(ctx)).split("/");
@ -128,7 +242,7 @@ void DesignWidget::newContext(Context *ctx)
name += items.at(i); name += items.at(i);
if (!bel_items.contains(name)) { if (!bel_items.contains(name)) {
if (i == items.size() - 1) if (i == items.size() - 1)
bel_items.insert(name, new IdStringTreeItem(id, ElementType::BEL, items.at(i), parent)); nameToItem[0].insert(name, new IdStringTreeItem(id, ElementType::BEL, items.at(i), parent));
else else
bel_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent)); bel_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
} }
@ -139,6 +253,9 @@ void DesignWidget::newContext(Context *ctx)
for (auto bel : bel_items.toStdMap()) { for (auto bel : bel_items.toStdMap()) {
bel_root->addChild(bel.second); bel_root->addChild(bel.second);
} }
for (auto bel : nameToItem[0].toStdMap()) {
bel_root->addChild(bel.second);
}
// Add wires to tree // Add wires to tree
QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget); QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget);
@ -146,6 +263,7 @@ void DesignWidget::newContext(Context *ctx)
wire_root->setText(0, "Wires"); wire_root->setText(0, "Wires");
treeWidget->insertTopLevelItem(0, wire_root); treeWidget->insertTopLevelItem(0, wire_root);
if (ctx) { if (ctx) {
Q_EMIT contextLoadStatus("Configuring wires...");
for (auto wire : ctx->getWires()) { for (auto wire : ctx->getWires()) {
auto id = ctx->getWireName(wire); auto id = ctx->getWireName(wire);
QStringList items = QString(id.c_str(ctx)).split("/"); QStringList items = QString(id.c_str(ctx)).split("/");
@ -157,7 +275,7 @@ void DesignWidget::newContext(Context *ctx)
name += items.at(i); name += items.at(i);
if (!wire_items.contains(name)) { if (!wire_items.contains(name)) {
if (i == items.size() - 1) if (i == items.size() - 1)
wire_items.insert(name, new IdStringTreeItem(id, ElementType::WIRE, items.at(i), parent)); nameToItem[1].insert(name, new IdStringTreeItem(id, ElementType::WIRE, items.at(i), parent));
else else
wire_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent)); wire_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
} }
@ -168,13 +286,16 @@ void DesignWidget::newContext(Context *ctx)
for (auto wire : wire_items.toStdMap()) { for (auto wire : wire_items.toStdMap()) {
wire_root->addChild(wire.second); wire_root->addChild(wire.second);
} }
for (auto wire : nameToItem[1].toStdMap()) {
wire_root->addChild(wire.second);
}
// Add pips to tree // Add pips to tree
QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget); QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> pip_items; QMap<QString, QTreeWidgetItem *> pip_items;
pip_root->setText(0, "Pips"); pip_root->setText(0, "Pips");
treeWidget->insertTopLevelItem(0, pip_root); treeWidget->insertTopLevelItem(0, pip_root);
if (ctx) { if (ctx) {
Q_EMIT contextLoadStatus("Configuring pips...");
for (auto pip : ctx->getPips()) { for (auto pip : ctx->getPips()) {
auto id = ctx->getPipName(pip); auto id = ctx->getPipName(pip);
QStringList items = QString(id.c_str(ctx)).split("/"); QStringList items = QString(id.c_str(ctx)).split("/");
@ -186,7 +307,7 @@ void DesignWidget::newContext(Context *ctx)
name += items.at(i); name += items.at(i);
if (!pip_items.contains(name)) { if (!pip_items.contains(name)) {
if (i == items.size() - 1) if (i == items.size() - 1)
pip_items.insert(name, new IdStringTreeItem(id, ElementType::PIP, items.at(i), parent)); nameToItem[2].insert(name, new IdStringTreeItem(id, ElementType::PIP, items.at(i), parent));
else else
pip_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent)); pip_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
} }
@ -197,6 +318,9 @@ void DesignWidget::newContext(Context *ctx)
for (auto pip : pip_items.toStdMap()) { for (auto pip : pip_items.toStdMap()) {
pip_root->addChild(pip.second); pip_root->addChild(pip.second);
} }
for (auto pip : nameToItem[2].toStdMap()) {
pip_root->addChild(pip.second);
}
// Add nets to tree // Add nets to tree
nets_root = new QTreeWidgetItem(treeWidget); nets_root = new QTreeWidgetItem(treeWidget);
@ -207,6 +331,8 @@ void DesignWidget::newContext(Context *ctx)
cells_root = new QTreeWidgetItem(treeWidget); cells_root = new QTreeWidgetItem(treeWidget);
cells_root->setText(0, "Cells"); cells_root->setText(0, "Cells");
treeWidget->insertTopLevelItem(0, cells_root); treeWidget->insertTopLevelItem(0, cells_root);
Q_EMIT finishContextLoad();
} }
void DesignWidget::updateTree() void DesignWidget::updateTree()
@ -214,45 +340,48 @@ void DesignWidget::updateTree()
clearProperties(); clearProperties();
delete nets_root; delete nets_root;
delete cells_root; delete cells_root;
nameToItem[3].clear();
nameToItem[4].clear();
// Add nets to tree // Add nets to tree
nets_root = new QTreeWidgetItem(treeWidget); nets_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> nets_items;
nets_root->setText(0, "Nets"); nets_root->setText(0, "Nets");
treeWidget->insertTopLevelItem(0, nets_root); treeWidget->insertTopLevelItem(0, nets_root);
if (ctx) { if (ctx) {
for (auto &item : ctx->nets) { for (auto &item : ctx->nets) {
auto id = item.first; auto id = item.first;
QString name = QString(id.c_str(ctx)); QString name = QString(id.c_str(ctx));
nets_items.insert(name, new IdStringTreeItem(id, ElementType::NET, name, nullptr)); IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::NET, name, nullptr);
nameToItem[3].insert(name, newItem);
} }
} }
for (auto item : nets_items.toStdMap()) { for (auto item : nameToItem[3].toStdMap()) {
nets_root->addChild(item.second); nets_root->addChild(item.second);
} }
// Add cells to tree // Add cells to tree
cells_root = new QTreeWidgetItem(treeWidget); cells_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> cells_items;
cells_root->setText(0, "Cells"); cells_root->setText(0, "Cells");
treeWidget->insertTopLevelItem(0, cells_root); treeWidget->insertTopLevelItem(0, cells_root);
if (ctx) { if (ctx) {
for (auto &item : ctx->cells) { for (auto &item : ctx->cells) {
auto id = item.first; auto id = item.first;
QString name = QString(id.c_str(ctx)); QString name = QString(id.c_str(ctx));
cells_items.insert(name, new IdStringTreeItem(id, ElementType::CELL, name, nullptr)); IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::CELL, name, nullptr);
nameToItem[4].insert(name, newItem);
} }
} }
for (auto item : cells_items.toStdMap()) { for (auto item : nameToItem[4].toStdMap()) {
cells_root->addChild(item.second); cells_root->addChild(item.second);
} }
} }
QtProperty *DesignWidget::addTopLevelProperty(const QString &id)
void DesignWidget::addProperty(QtProperty *property, const QString &id)
{ {
propertyToId[property] = id; QtProperty *topItem = groupManager->addProperty(id);
idToProperty[id] = property; propertyToId[topItem] = id;
propertyEditor->addProperty(property); idToProperty[id] = topItem;
propertyEditor->addProperty(topItem);
return topItem;
} }
void DesignWidget::clearProperties() void DesignWidget::clearProperties()
@ -266,8 +395,75 @@ void DesignWidget::clearProperties()
idToProperty.clear(); idToProperty.clear();
} }
void DesignWidget::onItemClicked(QTreeWidgetItem *clickItem, int pos) QString DesignWidget::getElementTypeName(ElementType type)
{ {
if (type == ElementType::NONE)
return "";
if (type == ElementType::BEL)
return "BEL";
if (type == ElementType::WIRE)
return "WIRE";
if (type == ElementType::PIP)
return "PIP";
if (type == ElementType::NET)
return "NET";
if (type == ElementType::CELL)
return "CELL";
return "";
}
int DesignWidget::getElementIndex(ElementType type)
{
if (type == ElementType::BEL)
return 0;
if (type == ElementType::WIRE)
return 1;
if (type == ElementType::PIP)
return 2;
if (type == ElementType::NET)
return 3;
if (type == ElementType::CELL)
return 4;
return -1;
}
ElementType DesignWidget::getElementTypeByName(QString type)
{
if (type == "BEL")
return ElementType::BEL;
if (type == "WIRE")
return ElementType::WIRE;
if (type == "PIP")
return ElementType::PIP;
if (type == "NET")
return ElementType::NET;
if (type == "CELL")
return ElementType::CELL;
return ElementType::NONE;
}
void DesignWidget::addProperty(QtProperty *topItem, int propertyType, const QString &name, QVariant value,
const ElementType &type)
{
QtVariantProperty *item = readOnlyManager->addProperty(propertyType, name);
item->setValue(value);
item->setPropertyId(getElementTypeName(type));
topItem->addSubProperty(item);
}
QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name)
{
QtProperty *item = groupManager->addProperty(name);
topItem->addSubProperty(item);
return item;
}
void DesignWidget::onItemSelectionChanged()
{
if (treeWidget->selectedItems().size() == 0)
return;
QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0);
if (!clickItem->parent()) if (!clickItem->parent())
return; return;
@ -276,305 +472,193 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *clickItem, int pos)
return; return;
} }
std::vector<DecalXY> decals;
addToHistory(clickItem);
clearProperties(); clearProperties();
if (type == ElementType::BEL) { if (type == ElementType::BEL) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData(); IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
BelId bel = ctx->getBelByName(c); BelId bel = ctx->getBelByName(c);
QtProperty *topItem = groupManager->addProperty("Bel"); decals.push_back(ctx->getBelDecal(bel));
addProperty(topItem, "Bel"); Q_EMIT selected(decals);
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); QtProperty *topItem = addTopLevelProperty("Bel");
nameItem->setValue(c.c_str(ctx));
topItem->addSubProperty(nameItem);
QtVariantProperty *typeItem = readOnlyManager->addProperty(QVariant::String, "Type"); addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
typeItem->setValue(ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx)); addProperty(topItem, QVariant::String, "Type", ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx));
topItem->addSubProperty(typeItem); addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel));
addProperty(topItem, QVariant::String, "Bound Cell", ctx->getBoundBelCell(bel).c_str(ctx), ElementType::CELL);
QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available"); addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->getConflictingBelCell(bel).c_str(ctx),
availItem->setValue(ctx->checkBelAvail(bel)); ElementType::CELL);
topItem->addSubProperty(availItem);
QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Cell");
cellItem->setValue(ctx->getBoundBelCell(bel).c_str(ctx));
topItem->addSubProperty(cellItem);
QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Cell");
conflictItem->setValue(ctx->getConflictingBelCell(bel).c_str(ctx));
topItem->addSubProperty(conflictItem);
} else if (type == ElementType::WIRE) { } else if (type == ElementType::WIRE) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData(); IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
WireId wire = ctx->getWireByName(c); WireId wire = ctx->getWireByName(c);
QtProperty *topItem = groupManager->addProperty("Wire"); decals.push_back(ctx->getWireDecal(wire));
addProperty(topItem, "Wire"); Q_EMIT selected(decals);
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); QtProperty *topItem = addTopLevelProperty("Wire");
nameItem->setValue(c.c_str(ctx));
topItem->addSubProperty(nameItem);
QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available"); addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
availItem->setValue(ctx->checkWireAvail(wire)); addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire));
topItem->addSubProperty(availItem); addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundWireNet(wire).c_str(ctx), ElementType::NET);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx),
QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Net"); ElementType::NET);
cellItem->setValue(ctx->getBoundWireNet(wire).c_str(ctx));
topItem->addSubProperty(cellItem);
QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Net");
conflictItem->setValue(ctx->getConflictingWireNet(wire).c_str(ctx));
topItem->addSubProperty(conflictItem);
QtProperty *belpinItem = addSubGroup(topItem, "BelPin Uphill");
BelPin uphill = ctx->getBelPinUphill(wire); BelPin uphill = ctx->getBelPinUphill(wire);
QtProperty *belpinItem = groupManager->addProperty("BelPin Uphill");
topItem->addSubProperty(belpinItem);
QtVariantProperty *belUphillItem = readOnlyManager->addProperty(QVariant::String, "Bel");
if (uphill.bel != BelId()) if (uphill.bel != BelId())
belUphillItem->setValue(ctx->getBelName(uphill.bel).c_str(ctx)); addProperty(belpinItem, QVariant::String, "Bel", ctx->getBelName(uphill.bel).c_str(ctx), ElementType::BEL);
else else
belUphillItem->setValue(""); addProperty(belpinItem, QVariant::String, "Bel", "", ElementType::BEL);
belpinItem->addSubProperty(belUphillItem);
QtVariantProperty *portUphillItem = readOnlyManager->addProperty(QVariant::String, "PortPin"); addProperty(belpinItem, QVariant::String, "PortPin", ctx->portPinToId(uphill.pin).c_str(ctx), ElementType::BEL);
portUphillItem->setValue(ctx->portPinToId(uphill.pin).c_str(ctx));
belpinItem->addSubProperty(portUphillItem);
QtProperty *downhillItem = groupManager->addProperty("BelPins Downhill"); QtProperty *downhillItem = addSubGroup(topItem, "BelPin Downhill");
topItem->addSubProperty(downhillItem);
for (const auto &item : ctx->getBelPinsDownhill(wire)) { for (const auto &item : ctx->getBelPinsDownhill(wire)) {
QString belname = ""; QString belname = "";
if (item.bel != BelId()) if (item.bel != BelId())
belname = ctx->getBelName(item.bel).c_str(ctx); belname = ctx->getBelName(item.bel).c_str(ctx);
QString pinname = ctx->portPinToId(item.pin).c_str(ctx); QString pinname = ctx->portPinToId(item.pin).c_str(ctx);
QtProperty *dhItem = groupManager->addProperty(belname + "-" + pinname); QtProperty *dhItem = addSubGroup(downhillItem, belname + "-" + pinname);
downhillItem->addSubProperty(dhItem); addProperty(dhItem, QVariant::String, "Bel", belname, ElementType::BEL);
addProperty(dhItem, QVariant::String, "PortPin", pinname);
QtVariantProperty *belItem = readOnlyManager->addProperty(QVariant::String, "Bel");
belItem->setValue(belname);
dhItem->addSubProperty(belItem);
QtVariantProperty *portItem = readOnlyManager->addProperty(QVariant::String, "PortPin");
portItem->setValue(pinname);
dhItem->addSubProperty(portItem);
} }
QtProperty *pipsDownItem = groupManager->addProperty("Pips Downhill"); int counter = 0;
topItem->addSubProperty(pipsDownItem); QtProperty *pipsDownItem = addSubGroup(downhillItem, "Pips Downhill");
for (const auto &item : ctx->getPipsDownhill(wire)) { for (const auto &item : ctx->getPipsDownhill(wire)) {
QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, ""); addProperty(pipsDownItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
pipItem->setValue(ctx->getPipName(item).c_str(ctx)); counter++;
pipsDownItem->addSubProperty(pipItem); if (counter == 50) {
addProperty(pipsDownItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
break;
}
} }
QtProperty *pipsUpItem = groupManager->addProperty("Pips Uphill"); counter = 0;
topItem->addSubProperty(pipsUpItem); QtProperty *pipsUpItem = addSubGroup(downhillItem, "Pips Uphill");
for (const auto &item : ctx->getPipsUphill(wire)) { for (const auto &item : ctx->getPipsUphill(wire)) {
QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, ""); addProperty(pipsUpItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
pipItem->setValue(ctx->getPipName(item).c_str(ctx)); counter++;
pipsUpItem->addSubProperty(pipItem); if (counter == 50) {
addProperty(pipsUpItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
break;
}
} }
} else if (type == ElementType::PIP) { } else if (type == ElementType::PIP) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData(); IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
PipId pip = ctx->getPipByName(c); PipId pip = ctx->getPipByName(c);
QtProperty *topItem = groupManager->addProperty("Pip"); decals.push_back(ctx->getPipDecal(pip));
addProperty(topItem, "Pip"); Q_EMIT selected(decals);
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); QtProperty *topItem = addTopLevelProperty("Pip");
nameItem->setValue(c.c_str(ctx));
topItem->addSubProperty(nameItem);
QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available"); addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
availItem->setValue(ctx->checkPipAvail(pip)); addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
topItem->addSubProperty(availItem); addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundPipNet(pip).c_str(ctx), ElementType::NET);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingPipNet(pip).c_str(ctx),
QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Net"); ElementType::NET);
cellItem->setValue(ctx->getBoundPipNet(pip).c_str(ctx)); addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
topItem->addSubProperty(cellItem); ElementType::WIRE);
addProperty(topItem, QVariant::String, "Dest Wire", ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx),
QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Net"); ElementType::WIRE);
conflictItem->setValue(ctx->getConflictingPipNet(pip).c_str(ctx));
topItem->addSubProperty(conflictItem);
QtVariantProperty *srcWireItem = readOnlyManager->addProperty(QVariant::String, "Src Wire");
srcWireItem->setValue(ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx));
topItem->addSubProperty(srcWireItem);
QtVariantProperty *destWireItem = readOnlyManager->addProperty(QVariant::String, "Dest Wire");
destWireItem->setValue(ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx));
topItem->addSubProperty(destWireItem);
DelayInfo delay = ctx->getPipDelay(pip); DelayInfo delay = ctx->getPipDelay(pip);
QtProperty *delayItem = groupManager->addProperty("Delay");
topItem->addSubProperty(delayItem);
QtVariantProperty *raiseDelayItem = readOnlyManager->addProperty(QVariant::Double, "Raise");
raiseDelayItem->setValue(delay.raiseDelay());
delayItem->addSubProperty(raiseDelayItem);
QtVariantProperty *fallDelayItem = readOnlyManager->addProperty(QVariant::Double, "Fall");
fallDelayItem->setValue(delay.fallDelay());
delayItem->addSubProperty(fallDelayItem);
QtVariantProperty *avgDelayItem = readOnlyManager->addProperty(QVariant::Double, "Average");
avgDelayItem->setValue(delay.avgDelay());
delayItem->addSubProperty(avgDelayItem);
QtProperty *delayItem = addSubGroup(topItem, "Delay");
addProperty(delayItem, QVariant::Double, "Raise", delay.raiseDelay());
addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay());
addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay());
} else if (type == ElementType::NET) { } else if (type == ElementType::NET) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData(); IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
NetInfo *net = ctx->nets.at(c).get(); NetInfo *net = ctx->nets.at(c).get();
QtProperty *topItem = groupManager->addProperty("Net"); QtProperty *topItem = addTopLevelProperty("Net");
addProperty(topItem, "Net");
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); addProperty(topItem, QVariant::String, "Name", net->name.c_str(ctx));
nameItem->setValue(net->name.c_str(ctx));
topItem->addSubProperty(nameItem);
QtProperty *driverItem = groupManager->addProperty("Driver"); QtProperty *driverItem = addSubGroup(topItem, "Driver");
topItem->addSubProperty(driverItem); addProperty(driverItem, QVariant::String, "Port", net->driver.port.c_str(ctx));
addProperty(driverItem, QVariant::Double, "Budget", net->driver.budget);
QtVariantProperty *portItem = readOnlyManager->addProperty(QVariant::String, "Port");
portItem->setValue(net->driver.port.c_str(ctx));
driverItem->addSubProperty(portItem);
QtVariantProperty *budgetItem = readOnlyManager->addProperty(QVariant::Double, "Budget");
budgetItem->setValue(net->driver.budget);
driverItem->addSubProperty(budgetItem);
QtVariantProperty *cellNameItem = readOnlyManager->addProperty(QVariant::String, "Cell");
if (net->driver.cell) if (net->driver.cell)
cellNameItem->setValue(net->driver.cell->name.c_str(ctx)); addProperty(driverItem, QVariant::String, "Cell", net->driver.cell->name.c_str(ctx), ElementType::CELL);
else else
cellNameItem->setValue(""); addProperty(driverItem, QVariant::String, "Cell", "", ElementType::CELL);
driverItem->addSubProperty(cellNameItem);
QtProperty *usersItem = groupManager->addProperty("Users"); QtProperty *usersItem = addSubGroup(topItem, "Users");
topItem->addSubProperty(usersItem);
for (auto &item : net->users) { for (auto &item : net->users) {
QtProperty *portItem = groupManager->addProperty(item.port.c_str(ctx)); QtProperty *portItem = addSubGroup(usersItem, item.port.c_str(ctx));
usersItem->addSubProperty(portItem);
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Port"); addProperty(portItem, QVariant::String, "Port", item.port.c_str(ctx));
nameItem->setValue(item.port.c_str(ctx)); addProperty(portItem, QVariant::Double, "Budget", item.budget);
portItem->addSubProperty(nameItem);
QtVariantProperty *budgetItem = readOnlyManager->addProperty(QVariant::Double, "Budget");
budgetItem->setValue(item.budget);
portItem->addSubProperty(budgetItem);
QtVariantProperty *userItem = readOnlyManager->addProperty(QVariant::String, "Cell");
if (item.cell) if (item.cell)
userItem->setValue(item.cell->name.c_str(ctx)); addProperty(portItem, QVariant::String, "Cell", item.cell->name.c_str(ctx), ElementType::CELL);
else else
userItem->setValue(""); addProperty(portItem, QVariant::String, "Cell", "", ElementType::CELL);
portItem->addSubProperty(userItem);
} }
QtProperty *attrsItem = groupManager->addProperty("Attributes"); QtProperty *attrsItem = addSubGroup(topItem, "Attributes");
topItem->addSubProperty(attrsItem);
for (auto &item : net->attrs) { for (auto &item : net->attrs) {
QtVariantProperty *attrItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx)); addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
attrItem->setValue(item.second.c_str());
attrsItem->addSubProperty(attrItem);
} }
QtProperty *wiresItem = groupManager->addProperty("Wires"); QtProperty *wiresItem = addSubGroup(topItem, "Wires");
topItem->addSubProperty(wiresItem);
for (auto &item : net->wires) { for (auto &item : net->wires) {
auto name = ctx->getWireName(item.first).c_str(ctx); auto name = ctx->getWireName(item.first).c_str(ctx);
QtProperty *wireItem = groupManager->addProperty(name); QtProperty *wireItem = addSubGroup(wiresItem, name);
addProperty(wireItem, QVariant::String, "Name", name);
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
nameItem->setValue(name);
wireItem->addSubProperty(nameItem);
QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, "Pip");
if (item.second.pip != PipId()) if (item.second.pip != PipId())
pipItem->setValue(ctx->getPipName(item.second.pip).c_str(ctx)); addProperty(wireItem, QVariant::String, "Pip", ctx->getPipName(item.second.pip).c_str(ctx),
ElementType::PIP);
else else
pipItem->setValue(""); addProperty(wireItem, QVariant::String, "Pip", "", ElementType::PIP);
wireItem->addSubProperty(pipItem);
QtVariantProperty *strengthItem = readOnlyManager->addProperty(QVariant::Int, "Strength"); addProperty(wireItem, QVariant::Int, "Strength", (int)item.second.strength);
strengthItem->setValue((int)item.second.strength);
wireItem->addSubProperty(strengthItem);
wiresItem->addSubProperty(wireItem);
} }
} else if (type == ElementType::CELL) { } else if (type == ElementType::CELL) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData(); IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
CellInfo *cell = ctx->cells.at(c).get(); CellInfo *cell = ctx->cells.at(c).get();
QtProperty *topItem = groupManager->addProperty("Cell"); QtProperty *topItem = addTopLevelProperty("Cell");
addProperty(topItem, "Cell");
QtVariantProperty *cellNameItem = readOnlyManager->addProperty(QVariant::String, "Name"); addProperty(topItem, QVariant::String, "Name", cell->name.c_str(ctx));
cellNameItem->setValue(cell->name.c_str(ctx)); addProperty(topItem, QVariant::String, "Type", cell->type.c_str(ctx));
topItem->addSubProperty(cellNameItem);
QtVariantProperty *cellTypeItem = readOnlyManager->addProperty(QVariant::String, "Type");
cellTypeItem->setValue(cell->type.c_str(ctx));
topItem->addSubProperty(cellTypeItem);
QtVariantProperty *cellBelItem = readOnlyManager->addProperty(QVariant::String, "Bel");
if (cell->bel != BelId()) if (cell->bel != BelId())
cellBelItem->setValue(ctx->getBelName(cell->bel).c_str(ctx)); addProperty(topItem, QVariant::String, "Bel", ctx->getBelName(cell->bel).c_str(ctx), ElementType::BEL);
else else
cellBelItem->setValue(""); addProperty(topItem, QVariant::String, "Bel", "", ElementType::BEL);
topItem->addSubProperty(cellBelItem); addProperty(topItem, QVariant::Int, "Bel strength", int(cell->belStrength));
QtVariantProperty *cellBelStrItem = readOnlyManager->addProperty(QVariant::Int, "Bel strength"); QtProperty *cellPortsItem = addSubGroup(topItem, "Ports");
cellBelStrItem->setValue(int(cell->belStrength));
topItem->addSubProperty(cellBelStrItem);
QtProperty *cellPortsItem = groupManager->addProperty("Ports");
topItem->addSubProperty(cellPortsItem);
for (auto &item : cell->ports) { for (auto &item : cell->ports) {
PortInfo p = item.second; PortInfo p = item.second;
QtProperty *portInfoItem = groupManager->addProperty(p.name.c_str(ctx)); QtProperty *portInfoItem = addSubGroup(cellPortsItem, p.name.c_str(ctx));
addProperty(portInfoItem, QVariant::String, "Name", p.name.c_str(ctx));
QtVariantProperty *portInfoNameItem = readOnlyManager->addProperty(QVariant::String, "Name"); addProperty(portInfoItem, QVariant::Int, "Type", int(p.type));
portInfoNameItem->setValue(p.name.c_str(ctx));
portInfoItem->addSubProperty(portInfoNameItem);
QtVariantProperty *portInfoTypeItem = readOnlyManager->addProperty(QVariant::Int, "Type");
portInfoTypeItem->setValue(int(p.type));
portInfoItem->addSubProperty(portInfoTypeItem);
QtVariantProperty *portInfoNetItem = readOnlyManager->addProperty(QVariant::String, "Net");
if (p.net) if (p.net)
portInfoNetItem->setValue(p.net->name.c_str(ctx)); addProperty(portInfoItem, QVariant::String, "Net", p.net->name.c_str(ctx), ElementType::NET);
else else
portInfoNetItem->setValue(""); addProperty(portInfoItem, QVariant::String, "Net", "", ElementType::NET);
portInfoItem->addSubProperty(portInfoNetItem);
cellPortsItem->addSubProperty(portInfoItem);
} }
QtProperty *cellAttrItem = groupManager->addProperty("Attributes"); QtProperty *cellAttrItem = addSubGroup(topItem, "Attributes");
topItem->addSubProperty(cellAttrItem);
for (auto &item : cell->attrs) { for (auto &item : cell->attrs) {
QtVariantProperty *attrItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx)); addProperty(cellAttrItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
attrItem->setValue(item.second.c_str());
cellAttrItem->addSubProperty(attrItem);
} }
QtProperty *cellParamsItem = groupManager->addProperty("Parameters"); QtProperty *cellParamsItem = addSubGroup(topItem, "Parameters");
topItem->addSubProperty(cellParamsItem);
for (auto &item : cell->params) { for (auto &item : cell->params) {
QtVariantProperty *paramItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx)); addProperty(cellParamsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
paramItem->setValue(item.second.c_str());
cellParamsItem->addSubProperty(paramItem);
} }
QtProperty *cellPinsItem = groupManager->addProperty("Pins"); QtProperty *cellPinsItem = groupManager->addProperty("Pins");
@ -583,38 +667,159 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *clickItem, int pos)
std::string cell_port = item.first.c_str(ctx); std::string cell_port = item.first.c_str(ctx);
std::string bel_pin = item.second.c_str(ctx); std::string bel_pin = item.second.c_str(ctx);
QtProperty *pinGroupItem = groupManager->addProperty((cell_port + " -> " + bel_pin).c_str()); QtProperty *pinGroupItem = addSubGroup(cellPortsItem, (cell_port + " -> " + bel_pin).c_str());
QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Cell"); addProperty(pinGroupItem, QVariant::String, "Cell", cell_port.c_str(), ElementType::CELL);
cellItem->setValue(cell_port.c_str()); addProperty(pinGroupItem, QVariant::String, "Bel", bel_pin.c_str(), ElementType::BEL);
pinGroupItem->addSubProperty(cellItem);
QtVariantProperty *belItem = readOnlyManager->addProperty(QVariant::String, "Bel");
belItem->setValue(bel_pin.c_str());
pinGroupItem->addSubProperty(belItem);
cellPinsItem->addSubProperty(pinGroupItem);
} }
} }
} }
void DesignWidget::prepareMenu(const QPoint &pos) std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdString value)
{
std::vector<DecalXY> decals;
switch (type) {
case ElementType::BEL: {
BelId bel = ctx->getBelByName(value);
if (bel != BelId()) {
decals.push_back(ctx->getBelDecal(bel));
}
} break;
case ElementType::WIRE: {
WireId wire = ctx->getWireByName(value);
if (wire != WireId()) {
decals.push_back(ctx->getWireDecal(wire));
Q_EMIT selected(decals);
}
} break;
case ElementType::PIP: {
PipId pip = ctx->getPipByName(value);
if (pip != PipId()) {
decals.push_back(ctx->getPipDecal(pip));
Q_EMIT selected(decals);
}
} break;
case ElementType::NET: {
} break;
case ElementType::CELL: {
} break;
default:
break;
}
return decals;
}
void DesignWidget::updateHighlightGroup(QTreeWidgetItem *item, int group)
{
if (highlightSelected.contains(item)) {
if (highlightSelected[item] == group) {
highlightSelected.remove(item);
} else
highlightSelected[item] = group;
} else
highlightSelected.insert(item, group);
std::vector<DecalXY> decals;
for (auto it : highlightSelected.toStdMap()) {
if (it.second == group) {
ElementType type = static_cast<ElementTreeItem *>(it.first)->getType();
IdString value = static_cast<IdStringTreeItem *>(it.first)->getData();
std::vector<DecalXY> d = getDecals(type, value);
std::move(d.begin(), d.end(), std::back_inserter(decals));
}
}
Q_EMIT highlight(decals, group);
}
void DesignWidget::prepareMenuProperty(const QPoint &pos)
{
QTreeWidget *tree = propertyEditor->treeWidget();
itemContextMenu = tree->itemAt(pos);
if (itemContextMenu->parent() == nullptr)
return;
QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu);
if (!browserItem)
return;
QtProperty *selectedProperty = browserItem->property();
ElementType type = getElementTypeByName(selectedProperty->propertyId());
if (type == ElementType::NONE)
return;
IdString value = ctx->id(selectedProperty->valueText().toStdString());
QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx));
QMenu menu(this);
QAction *selectAction = new QAction("&Select", this);
connect(selectAction, &QAction::triggered, this, [this, type, value] { Q_EMIT selected(getDecals(type, value)); });
menu.addAction(selectAction);
QMenu *subMenu = menu.addMenu("Highlight");
QActionGroup *group = new QActionGroup(this);
group->setExclusive(true);
for (int i = 0; i < 8; i++) {
QPixmap pixmap(32, 32);
pixmap.fill(QColor(highlightColors[i]));
QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this);
action->setCheckable(true);
subMenu->addAction(action);
group->addAction(action);
if (highlightSelected.contains(item) && highlightSelected[item] == i)
action->setChecked(true);
connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); });
}
menu.exec(tree->mapToGlobal(pos));
}
void DesignWidget::prepareMenuTree(const QPoint &pos)
{ {
QTreeWidget *tree = treeWidget; QTreeWidget *tree = treeWidget;
itemContextMenu = tree->itemAt(pos); itemContextMenu = tree->itemAt(pos);
QAction *selectAction = new QAction("&Select", this); ElementType type = static_cast<ElementTreeItem *>(itemContextMenu)->getType();
selectAction->setStatusTip("Select item on view"); IdString value = static_cast<IdStringTreeItem *>(itemContextMenu)->getData();
connect(selectAction, SIGNAL(triggered()), this, SLOT(selectObject())); if (type == ElementType::NONE)
return;
QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx));
QMenu menu(this); QMenu menu(this);
menu.addAction(selectAction); QMenu *subMenu = menu.addMenu("Highlight");
QActionGroup *group = new QActionGroup(this);
group->setExclusive(true);
for (int i = 0; i < 8; i++) {
QPixmap pixmap(32, 32);
pixmap.fill(QColor(highlightColors[i]));
QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this);
action->setCheckable(true);
subMenu->addAction(action);
group->addAction(action);
if (highlightSelected.contains(item) && highlightSelected[item] == i)
action->setChecked(true);
connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); });
}
menu.exec(tree->mapToGlobal(pos)); menu.exec(tree->mapToGlobal(pos));
} }
void DesignWidget::selectObject() { Q_EMIT info("selected " + itemContextMenu->text(0).toStdString() + "\n"); } void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
{
QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property();
ElementType type = getElementTypeByName(selectedProperty->propertyId());
QString value = selectedProperty->valueText();
int index = getElementIndex(type);
switch (type) {
case ElementType::NONE:
return;
default: {
if (nameToItem[index].contains(value))
treeWidget->setCurrentItem(nameToItem[index].value(value));
} break;
}
}
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -21,6 +21,7 @@
#define DESIGNWIDGET_H #define DESIGNWIDGET_H
#include <QTreeWidget> #include <QTreeWidget>
#include <QVariant>
#include "nextpnr.h" #include "nextpnr.h"
#include "qtgroupboxpropertybrowser.h" #include "qtgroupboxpropertybrowser.h"
#include "qtpropertymanager.h" #include "qtpropertymanager.h"
@ -29,6 +30,16 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
enum class ElementType
{
NONE,
BEL,
WIRE,
PIP,
NET,
CELL
};
class DesignWidget : public QWidget class DesignWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -38,16 +49,30 @@ class DesignWidget : public QWidget
~DesignWidget(); ~DesignWidget();
private: private:
void addProperty(QtProperty *property, const QString &id);
void clearProperties(); void clearProperties();
QtProperty *addTopLevelProperty(const QString &id);
QtProperty *addSubGroup(QtProperty *topItem, const QString &name);
void addProperty(QtProperty *topItem, int propertyType, const QString &name, QVariant value,
const ElementType &type = ElementType::NONE);
QString getElementTypeName(ElementType type);
ElementType getElementTypeByName(QString type);
int getElementIndex(ElementType type);
void updateButtons();
void addToHistory(QTreeWidgetItem *item);
std::vector<DecalXY> getDecals(ElementType type, IdString value);
void updateHighlightGroup(QTreeWidgetItem *item, int group);
Q_SIGNALS: Q_SIGNALS:
void info(std::string text); void info(std::string text);
void selected(std::vector<DecalXY> decal);
void highlight(std::vector<DecalXY> decal, int group);
void finishContextLoad();
void contextLoadStatus(std::string text);
private Q_SLOTS: private Q_SLOTS:
void prepareMenu(const QPoint &pos); void prepareMenuProperty(const QPoint &pos);
void onItemClicked(QTreeWidgetItem *item, int); void prepareMenuTree(const QPoint &pos);
void selectObject(); void onItemSelectionChanged();
void onItemDoubleClicked(QTreeWidgetItem *item, int column);
public Q_SLOTS: public Q_SLOTS:
void newContext(Context *ctx); void newContext(Context *ctx);
void updateTree(); void updateTree();
@ -66,8 +91,22 @@ class DesignWidget : public QWidget
QMap<QtProperty *, QString> propertyToId; QMap<QtProperty *, QString> propertyToId;
QMap<QString, QtProperty *> idToProperty; QMap<QString, QtProperty *> idToProperty;
QMap<QString, QTreeWidgetItem *> nameToItem[6];
std::vector<QTreeWidgetItem *> history;
int history_index;
bool history_ignore;
QTreeWidgetItem *nets_root; QTreeWidgetItem *nets_root;
QTreeWidgetItem *cells_root; QTreeWidgetItem *cells_root;
QAction *actionFirst;
QAction *actionPrev;
QAction *actionNext;
QAction *actionLast;
QColor highlightColors[8];
QMap<QTreeWidgetItem *, int> highlightSelected;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

0
gui/ecp5/family.cmake Normal file
View File

51
gui/ecp5/mainwindow.cc Normal file
View File

@ -0,0 +1,51 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "mainwindow.h"
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
MainWindow::MainWindow(std::unique_ptr<Context> context, QWidget *parent) : BaseMainWindow(std::move(context), parent)
{
initMainResource();
std::string title = "nextpnr-ecp5 - [EMPTY]";
setWindowTitle(title.c_str());
createMenu();
Q_EMIT contextChanged(ctx.get());
}
MainWindow::~MainWindow() {}
void MainWindow::createMenu()
{
QMenu *menu_Custom = new QMenu("&Generic", menuBar);
menuBar->addAction(menu_Custom->menuAction());
}
void MainWindow::new_proj() {}
void MainWindow::open_proj() {}
bool MainWindow::save_proj() { return false; }
NEXTPNR_NAMESPACE_END

46
gui/ecp5/mainwindow.h Normal file
View File

@ -0,0 +1,46 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "../basewindow.h"
NEXTPNR_NAMESPACE_BEGIN
class MainWindow : public BaseMainWindow
{
Q_OBJECT
public:
explicit MainWindow(std::unique_ptr<Context> context, QWidget *parent = 0);
virtual ~MainWindow();
public:
void createMenu();
protected Q_SLOTS:
virtual void new_proj();
virtual void open_proj();
virtual bool save_proj();
};
NEXTPNR_NAMESPACE_END
#endif // MAINWINDOW_H

2
gui/ecp5/nextpnr.qrc Normal file
View File

@ -0,0 +1,2 @@
<RCC>
</RCC>

View File

@ -195,7 +195,7 @@ bool LineShader::compile(void)
return true; return true;
} }
void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection) void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection)
{ {
auto gl = QOpenGLContext::currentContext()->functions(); auto gl = QOpenGLContext::currentContext()->functions();
vao_.bind(); vao_.bind();
@ -214,8 +214,8 @@ void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection)
buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size()); buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
program_->setUniformValue(uniforms_.projection, projection); program_->setUniformValue(uniforms_.projection, projection);
program_->setUniformValue(uniforms_.thickness, line.thickness); program_->setUniformValue(uniforms_.thickness, thickness);
program_->setUniformValue(uniforms_.color, line.color.r, line.color.g, line.color.b, line.color.a); program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF());
buffers_.position.bind(); buffers_.position.bind();
program_->enableAttributeArray("position"); program_->enableAttributeArray("position");
@ -241,8 +241,27 @@ void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection)
} }
FPGAViewWidget::FPGAViewWidget(QWidget *parent) FPGAViewWidget::FPGAViewWidget(QWidget *parent)
: QOpenGLWidget(parent), moveX_(0), moveY_(0), zoom_(10.0f), lineShader_(this), ctx_(nullptr) : QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), selectedItemsChanged_(false)
{ {
backgroundColor_ = QColor("#000000");
gridColor_ = QColor("#333");
gFrameColor_ = QColor("#d0d0d0");
gHiddenColor_ = QColor("#606060");
gInactiveColor_ = QColor("#303030");
gActiveColor_ = QColor("#f0f0f0");
gSelectedColor_ = QColor("#ff6600");
frameColor_ = QColor("#0066ba");
highlightColors[0] = QColor("#6495ed");
highlightColors[1] = QColor("#7fffd4");
highlightColors[2] = QColor("#98fb98");
highlightColors[3] = QColor("#ffd700");
highlightColors[4] = QColor("#cd5c5c");
highlightColors[5] = QColor("#fa8072");
highlightColors[6] = QColor("#ff69b4");
highlightColors[7] = QColor("#da70d6");
for (int i = 0; i < 8; i++)
highlightItemsChanged_[i] = false;
auto fmt = format(); auto fmt = format();
fmt.setMajorVersion(3); fmt.setMajorVersion(3);
fmt.setMinorVersion(1); fmt.setMinorVersion(1);
@ -264,6 +283,7 @@ FPGAViewWidget::~FPGAViewWidget() {}
void FPGAViewWidget::newContext(Context *ctx) void FPGAViewWidget::newContext(Context *ctx)
{ {
ctx_ = ctx; ctx_ = ctx;
selectedItems_.clear();
update(); update();
} }
@ -271,66 +291,92 @@ QSize FPGAViewWidget::minimumSizeHint() const { return QSize(640, 480); }
QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); } QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); }
void FPGAViewWidget::setXTranslation(float t_x)
{
if (t_x == moveX_)
return;
moveX_ = t_x;
update();
}
void FPGAViewWidget::setYTranslation(float t_y)
{
if (t_y == moveY_)
return;
moveY_ = t_y;
update();
}
void FPGAViewWidget::setZoom(float t_z)
{
if (t_z == zoom_)
return;
zoom_ = t_z;
if (zoom_ < 1.0f)
zoom_ = 1.0f;
if (zoom_ > 100.f)
zoom_ = 100.0f;
update();
}
void FPGAViewWidget::initializeGL() void FPGAViewWidget::initializeGL()
{ {
if (!lineShader_.compile()) { if (!lineShader_.compile()) {
log_error("Could not compile shader.\n"); log_error("Could not compile shader.\n");
} }
initializeOpenGLFunctions(); initializeOpenGLFunctions();
glClearColor(1.0, 1.0, 1.0, 0.0); glClearColor(backgroundColor_.red() / 255, backgroundColor_.green() / 255, backgroundColor_.blue() / 255, 0.0);
} }
void FPGAViewWidget::drawElement(LineShaderData &out, const GraphicElement &el) void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal)
{ {
const float scale = 1.0, offset = 0.0; const float scale = 1.0;
float offsetX = 0.0, offsetY = 0.0;
if (el.type == GraphicElement::G_BOX) { for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
auto line = PolyLine(true); offsetX = decal.x;
line.point(offset + scale * el.x1, offset + scale * el.y1); offsetY = decal.y;
line.point(offset + scale * el.x2, offset + scale * el.y1);
line.point(offset + scale * el.x2, offset + scale * el.y2);
line.point(offset + scale * el.x1, offset + scale * el.y2);
line.build(out);
}
if (el.type == GraphicElement::G_LINE) { if (el.type == GraphicElement::G_BOX) {
PolyLine(offset + scale * el.x1, offset + scale * el.y1, offset + scale * el.x2, offset + scale * el.y2) auto line = PolyLine(true);
.build(out); line.point(offsetX + scale * el.x1, offsetY + scale * el.y1);
line.point(offsetX + scale * el.x2, offsetY + scale * el.y1);
line.point(offsetX + scale * el.x2, offsetY + scale * el.y2);
line.point(offsetX + scale * el.x1, offsetY + scale * el.y2);
line.build(out);
}
if (el.type == GraphicElement::G_LINE) {
PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2)
.build(out);
}
} }
} }
void FPGAViewWidget::drawDecal(LineShaderData out[], const DecalXY &decal)
{
const float scale = 1.0;
float offsetX = 0.0, offsetY = 0.0;
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
offsetX = decal.x;
offsetY = decal.y;
if (el.type == GraphicElement::G_BOX) {
auto line = PolyLine(true);
line.point(offsetX + scale * el.x1, offsetY + scale * el.y1);
line.point(offsetX + scale * el.x2, offsetY + scale * el.y1);
line.point(offsetX + scale * el.x2, offsetY + scale * el.y2);
line.point(offsetX + scale * el.x1, offsetY + scale * el.y2);
switch (el.style) {
case GraphicElement::G_FRAME:
case GraphicElement::G_INACTIVE:
case GraphicElement::G_ACTIVE:
line.build(out[el.style]);
break;
default:
break;
}
}
if (el.type == GraphicElement::G_LINE) {
auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2,
offsetY + scale * el.y2);
switch (el.style) {
case GraphicElement::G_FRAME:
case GraphicElement::G_INACTIVE:
case GraphicElement::G_ACTIVE:
line.build(out[el.style]);
break;
default:
break;
}
}
}
}
QMatrix4x4 FPGAViewWidget::getProjection(void)
{
QMatrix4x4 matrix;
const float aspect = float(width()) / float(height());
matrix.perspective(3.14 / 2, aspect, zoomNear_, zoomFar_);
matrix.translate(0.0f, 0.0f, -zoom_);
return matrix;
}
void FPGAViewWidget::paintGL() void FPGAViewWidget::paintGL()
{ {
auto gl = QOpenGLContext::currentContext()->functions(); auto gl = QOpenGLContext::currentContext()->functions();
@ -338,65 +384,112 @@ void FPGAViewWidget::paintGL()
gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale); gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
const float aspect = float(width()) / float(height()); QMatrix4x4 matrix = getProjection();
QMatrix4x4 matrix; matrix *= viewMove_;
matrix.ortho(QRectF(-aspect / 2.0, -0.5, aspect, 1.0f));
matrix.translate(moveX_, moveY_, -0.5); // Calculate world thickness to achieve a screen 1px/1.1px line.
matrix.scale(zoom_ * 0.01f, zoom_ * 0.01f, 0); float thick1Px = mouseToWorldCoordinates(1, 0).x();
float thick11Px = mouseToWorldCoordinates(1.1, 0).x();
// Draw grid. // Draw grid.
auto grid = LineShaderData(0.01f, QColor("#DDD")); auto grid = LineShaderData();
for (float i = -100.0f; i < 100.0f; i += 1.0f) { for (float i = -100.0f; i < 100.0f; i += 1.0f) {
PolyLine(-100.0f, i, 100.0f, i).build(grid); PolyLine(-100.0f, i, 100.0f, i).build(grid);
PolyLine(i, -100.0f, i, 100.0f).build(grid); PolyLine(i, -100.0f, i, 100.0f).build(grid);
} }
lineShader_.draw(grid, matrix); lineShader_.draw(grid, gridColor_, thick1Px, matrix);
LineShaderData shaders[4] = {[GraphicElement::G_FRAME] = LineShaderData(),
[GraphicElement::G_HIDDEN] = LineShaderData(),
[GraphicElement::G_INACTIVE] = LineShaderData(),
[GraphicElement::G_ACTIVE] = LineShaderData()};
// Draw Bels.
auto bels = LineShaderData(0.02f, QColor("#b000ba"));
if (ctx_) { if (ctx_) {
// Draw Bels.
for (auto bel : ctx_->getBels()) { for (auto bel : ctx_->getBels()) {
for (auto &el : ctx_->getBelGraphics(bel)) drawDecal(shaders, ctx_->getBelDecal(bel));
drawElement(bels, el); }
// Draw Wires.
for (auto wire : ctx_->getWires()) {
drawDecal(shaders, ctx_->getWireDecal(wire));
}
// Draw Pips.
for (auto pip : ctx_->getPips()) {
drawDecal(shaders, ctx_->getPipDecal(pip));
}
// Draw Groups.
for (auto group : ctx_->getGroups()) {
drawDecal(shaders, ctx_->getGroupDecal(group));
}
if (selectedItemsChanged_) {
selectedItemsChanged_ = false;
selectedShader_.clear();
for (auto decal : selectedItems_) {
drawDecal(selectedShader_, decal);
}
}
for (int i = 0; i < 8; i++) {
if (highlightItemsChanged_[i]) {
highlightItemsChanged_[i] = false;
highlightShader_[i].clear();
for (auto decal : highlightItems_[i]) {
drawDecal(highlightShader_[i], decal);
}
}
} }
lineShader_.draw(bels, matrix);
} }
// Draw Frame Graphics. lineShader_.draw(shaders[0], gFrameColor_, thick11Px, matrix);
auto frames = LineShaderData(0.02f, QColor("#0066ba")); lineShader_.draw(shaders[1], gHiddenColor_, thick11Px, matrix);
if (ctx_) { lineShader_.draw(shaders[2], gInactiveColor_, thick11Px, matrix);
for (auto &el : ctx_->getFrameGraphics()) { lineShader_.draw(shaders[3], gActiveColor_, thick11Px, matrix);
drawElement(frames, el); for (int i = 0; i < 8; i++)
} lineShader_.draw(highlightShader_[i], highlightColors[i], thick11Px, matrix);
lineShader_.draw(frames, matrix); lineShader_.draw(selectedShader_, gSelectedColor_, thick11Px, matrix);
} }
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
{
selectedItems_ = decals;
selectedItemsChanged_ = true;
update();
}
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
{
highlightItems_[group] = decals;
highlightItemsChanged_[group] = true;
update();
} }
void FPGAViewWidget::resizeGL(int width, int height) {} void FPGAViewWidget::resizeGL(int width, int height) {}
void FPGAViewWidget::mousePressEvent(QMouseEvent *event) void FPGAViewWidget::mousePressEvent(QMouseEvent *event) { lastPos_ = event->pos(); }
// Invert the projection matrix to calculate screen/mouse to world/grid
// coordinates.
QVector4D FPGAViewWidget::mouseToWorldCoordinates(int x, int y)
{ {
startDragX_ = moveX_; QMatrix4x4 p = getProjection();
startDragY_ = moveY_; QVector2D unit = p.map(QVector4D(1, 1, 0, 1)).toVector2DAffine();
lastPos_ = event->pos();
float sx = (((float)x) / (width() / 2));
float sy = (((float)y) / (height() / 2));
return QVector4D(sx / unit.x(), sy / unit.y(), 0, 1);
} }
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event) void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
{ {
const int dx = event->x() - lastPos_.x(); const int dx = event->x() - lastPos_.x();
const int dy = event->y() - lastPos_.y(); const int dy = event->y() - lastPos_.y();
lastPos_ = event->pos();
const qreal retinaScale = devicePixelRatio(); auto world = mouseToWorldCoordinates(dx, dy);
float aspect = float(width()) / float(height()); viewMove_.translate(world.x(), -world.y());
const float dx_scale = dx * (1 / (float)width() * retinaScale * aspect);
const float dy_scale = dy * (1 / (float)height() * retinaScale);
float xpos = dx_scale + startDragX_; update();
float ypos = dy_scale + startDragY_;
setXTranslation(xpos);
setYTranslation(ypos);
} }
void FPGAViewWidget::wheelEvent(QWheelEvent *event) void FPGAViewWidget::wheelEvent(QWheelEvent *event)
@ -404,8 +497,19 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event)
QPoint degree = event->angleDelta() / 8; QPoint degree = event->angleDelta() / 8;
if (!degree.isNull()) { if (!degree.isNull()) {
float steps = degree.y() / 15.0;
setZoom(zoom_ + steps); if (zoom_ < zoomNear_) {
zoom_ = zoomNear_;
} else if (zoom_ < zoomLvl1_) {
zoom_ -= degree.y() / 10.0;
} else if (zoom_ < zoomLvl2_) {
zoom_ -= degree.y() / 5.0;
} else if (zoom_ < zoomFar_) {
zoom_ -= degree.y();
} else {
zoom_ = zoomFar_;
}
update();
} }
} }

View File

@ -41,18 +41,6 @@ NPNR_PACKED_STRUCT(struct Vertex2DPOD {
Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {} Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {}
}); });
// Vertex2DPOD is a structure of R, G, B, A values that can be passed to OpenGL
// directly.
NPNR_PACKED_STRUCT(struct ColorPOD {
GLfloat r;
GLfloat g;
GLfloat b;
GLfloat a;
ColorPOD(GLfloat R, GLfloat G, GLfloat B, GLfloat A) : r(R), g(G), b(B), a(A) {}
ColorPOD(const QColor &color) : r(color.redF()), g(color.greenF()), b(color.blueF()), a(color.alphaF()) {}
});
// LineShaderData is a built set of vertices that can be rendered by the // LineShaderData is a built set of vertices that can be rendered by the
// LineShader. // LineShader.
// Each LineShaderData can have its' own color and thickness. // Each LineShaderData can have its' own color and thickness.
@ -63,10 +51,15 @@ struct LineShaderData
std::vector<GLfloat> miters; std::vector<GLfloat> miters;
std::vector<GLuint> indices; std::vector<GLuint> indices;
GLfloat thickness; LineShaderData(void) {}
ColorPOD color;
LineShaderData(GLfloat Thickness, QColor Color) : thickness(Thickness), color(Color) {} void clear(void)
{
vertices.clear();
normals.clear();
miters.clear();
indices.clear();
}
}; };
// PolyLine is a set of segments defined by points, that can be built to a // PolyLine is a set of segments defined by points, that can be built to a
@ -210,12 +203,20 @@ class LineShader
bool compile(void); bool compile(void);
// Render a LineShaderData with a given M/V/P transformation. // Render a LineShaderData with a given M/V/P transformation.
void draw(const LineShaderData &data, const QMatrix4x4 &projection); void draw(const LineShaderData &data, const QColor &color, float thickness, const QMatrix4x4 &projection);
}; };
class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QColor backgroundColor MEMBER backgroundColor_ DESIGNABLE true)
Q_PROPERTY(QColor gridColor MEMBER gridColor_ DESIGNABLE true)
Q_PROPERTY(QColor gFrameColor MEMBER gFrameColor_ DESIGNABLE true)
Q_PROPERTY(QColor gHiddenColor MEMBER gHiddenColor_ DESIGNABLE true)
Q_PROPERTY(QColor gInactiveColor MEMBER gInactiveColor_ DESIGNABLE true)
Q_PROPERTY(QColor gActiveColor MEMBER gActiveColor_ DESIGNABLE true)
Q_PROPERTY(QColor gSelectedColor MEMBER gSelectedColor_ DESIGNABLE true)
Q_PROPERTY(QColor frameColor MEMBER frameColor_ DESIGNABLE true)
public: public:
FPGAViewWidget(QWidget *parent = 0); FPGAViewWidget(QWidget *parent = 0);
@ -239,20 +240,46 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
void drawElement(LineShaderData &data, const GraphicElement &el); void drawDecal(LineShaderData &data, const DecalXY &decal);
void drawDecal(LineShaderData out[], const DecalXY &decal);
public Q_SLOTS: public Q_SLOTS:
void newContext(Context *ctx); void newContext(Context *ctx);
void onSelectedArchItem(std::vector<DecalXY> decals);
void onHighlightGroupChanged(std::vector<DecalXY> decals, int group);
private: private:
QPoint lastPos_; QPoint lastPos_;
float moveX_;
float moveY_;
float zoom_;
LineShader lineShader_; LineShader lineShader_;
QMatrix4x4 viewMove_;
float zoom_;
QMatrix4x4 getProjection(void);
QVector4D mouseToWorldCoordinates(int x, int y);
const float zoomNear_ = 1.0f; // do not zoom closer than this
const float zoomFar_ = 10000.0f; // do not zoom further than this
const float zoomLvl1_ = 100.0f;
const float zoomLvl2_ = 50.0f;
float startDragX_;
float startDragY_;
Context *ctx_; Context *ctx_;
QColor backgroundColor_;
QColor gridColor_;
QColor gFrameColor_;
QColor gHiddenColor_;
QColor gInactiveColor_;
QColor gActiveColor_;
QColor gSelectedColor_;
QColor frameColor_;
LineShaderData selectedShader_;
std::vector<DecalXY> selectedItems_;
bool selectedItemsChanged_;
LineShaderData highlightShader_[8];
std::vector<DecalXY> highlightItems_[8];
bool highlightItemsChanged_[8];
QColor highlightColors[8];
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

0
gui/generic/family.cmake Normal file
View File

View File

@ -23,7 +23,7 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent) MainWindow::MainWindow(std::unique_ptr<Context> context, QWidget *parent) : BaseMainWindow(std::move(context), parent)
{ {
initMainResource(); initMainResource();
@ -31,6 +31,7 @@ MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent)
setWindowTitle(title.c_str()); setWindowTitle(title.c_str());
createMenu(); createMenu();
Q_EMIT contextChanged(ctx.get());
} }
MainWindow::~MainWindow() {} MainWindow::~MainWindow() {}

View File

@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(QWidget *parent = 0); explicit MainWindow(std::unique_ptr<Context> context, QWidget *parent = 0);
virtual ~MainWindow(); virtual ~MainWindow();
public: public:

3
gui/ice40/family.cmake Normal file
View File

@ -0,0 +1,3 @@
if(ICE40_HX1K_ONLY)
target_compile_definitions(gui_${family} PRIVATE ICE40_HX1K_ONLY=1)
endif()

View File

@ -27,16 +27,14 @@
#include "design_utils.h" #include "design_utils.h"
#include "jsonparse.h" #include "jsonparse.h"
#include "log.h" #include "log.h"
#include "pack.h"
#include "pcf.h" #include "pcf.h"
#include "place_sa.h"
#include "route.h"
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent), timing_driven(false) MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
: BaseMainWindow(std::move(context), parent), timing_driven(false), chipArgs(args)
{ {
initMainResource(); initMainResource();
@ -62,6 +60,8 @@ MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent), timing_driven(
connect(this, SIGNAL(contextChanged(Context *)), task, SIGNAL(contextChanged(Context *))); connect(this, SIGNAL(contextChanged(Context *)), task, SIGNAL(contextChanged(Context *)));
createMenu(); createMenu();
Q_EMIT contextChanged(ctx.get());
} }
MainWindow::~MainWindow() { delete task; } MainWindow::~MainWindow() { delete task; }
@ -72,60 +72,46 @@ void MainWindow::createMenu()
menuBar->addAction(menu_Design->menuAction()); menuBar->addAction(menu_Design->menuAction());
actionLoadJSON = new QAction("Open JSON", this); actionLoadJSON = new QAction("Open JSON", this);
QIcon iconLoadJSON; actionLoadJSON->setIcon(QIcon(":/icons/resources/open_json.png"));
iconLoadJSON.addFile(QStringLiteral(":/icons/resources/open_json.png"));
actionLoadJSON->setIcon(iconLoadJSON);
actionLoadJSON->setStatusTip("Open an existing JSON file"); actionLoadJSON->setStatusTip("Open an existing JSON file");
actionLoadJSON->setEnabled(true);
connect(actionLoadJSON, SIGNAL(triggered()), this, SLOT(open_json())); connect(actionLoadJSON, SIGNAL(triggered()), this, SLOT(open_json()));
actionLoadJSON->setEnabled(false);
actionLoadPCF = new QAction("Open PCF", this); actionLoadPCF = new QAction("Open PCF", this);
QIcon iconLoadPCF; actionLoadPCF->setIcon(QIcon(":/icons/resources/open_pcf.png"));
iconLoadPCF.addFile(QStringLiteral(":/icons/resources/open_pcf.png"));
actionLoadPCF->setIcon(iconLoadPCF);
actionLoadPCF->setStatusTip("Open PCF file"); actionLoadPCF->setStatusTip("Open PCF file");
connect(actionLoadPCF, SIGNAL(triggered()), this, SLOT(open_pcf()));
actionLoadPCF->setEnabled(false); actionLoadPCF->setEnabled(false);
connect(actionLoadPCF, SIGNAL(triggered()), this, SLOT(open_pcf()));
actionPack = new QAction("Pack", this); actionPack = new QAction("Pack", this);
QIcon iconPack; actionPack->setIcon(QIcon(":/icons/resources/pack.png"));
iconPack.addFile(QStringLiteral(":/icons/resources/pack.png"));
actionPack->setIcon(iconPack);
actionPack->setStatusTip("Pack current design"); actionPack->setStatusTip("Pack current design");
connect(actionPack, SIGNAL(triggered()), task, SIGNAL(pack()));
actionPack->setEnabled(false); actionPack->setEnabled(false);
connect(actionPack, SIGNAL(triggered()), task, SIGNAL(pack()));
actionAssignBudget = new QAction("Assign Budget", this); actionAssignBudget = new QAction("Assign Budget", this);
QIcon iconAssignBudget; actionAssignBudget->setIcon(QIcon(":/icons/resources/time_add.png"));
iconAssignBudget.addFile(QStringLiteral(":/icons/resources/time_add.png"));
actionAssignBudget->setIcon(iconAssignBudget);
actionAssignBudget->setStatusTip("Assign time budget for current design"); actionAssignBudget->setStatusTip("Assign time budget for current design");
connect(actionAssignBudget, SIGNAL(triggered()), this, SLOT(budget()));
actionAssignBudget->setEnabled(false); actionAssignBudget->setEnabled(false);
connect(actionAssignBudget, SIGNAL(triggered()), this, SLOT(budget()));
actionPlace = new QAction("Place", this); actionPlace = new QAction("Place", this);
QIcon iconPlace; actionPlace->setIcon(QIcon(":/icons/resources/place.png"));
iconPlace.addFile(QStringLiteral(":/icons/resources/place.png"));
actionPlace->setIcon(iconPlace);
actionPlace->setStatusTip("Place current design"); actionPlace->setStatusTip("Place current design");
connect(actionPlace, SIGNAL(triggered()), this, SLOT(place()));
actionPlace->setEnabled(false); actionPlace->setEnabled(false);
connect(actionPlace, SIGNAL(triggered()), this, SLOT(place()));
actionRoute = new QAction("Route", this); actionRoute = new QAction("Route", this);
QIcon iconRoute; actionRoute->setIcon(QIcon(":/icons/resources/route.png"));
iconRoute.addFile(QStringLiteral(":/icons/resources/route.png"));
actionRoute->setIcon(iconRoute);
actionRoute->setStatusTip("Route current design"); actionRoute->setStatusTip("Route current design");
connect(actionRoute, SIGNAL(triggered()), task, SIGNAL(route()));
actionRoute->setEnabled(false); actionRoute->setEnabled(false);
connect(actionRoute, SIGNAL(triggered()), task, SIGNAL(route()));
actionSaveAsc = new QAction("Save ASC", this); actionSaveAsc = new QAction("Save ASC", this);
QIcon iconSaveAsc; actionSaveAsc->setIcon(QIcon(":/icons/resources/save_asc.png"));
iconSaveAsc.addFile(QStringLiteral(":/icons/resources/save_asc.png"));
actionSaveAsc->setIcon(iconSaveAsc);
actionSaveAsc->setStatusTip("Save ASC file"); actionSaveAsc->setStatusTip("Save ASC file");
connect(actionSaveAsc, SIGNAL(triggered()), this, SLOT(save_asc()));
actionSaveAsc->setEnabled(false); actionSaveAsc->setEnabled(false);
connect(actionSaveAsc, SIGNAL(triggered()), this, SLOT(save_asc()));
QToolBar *taskFPGABar = new QToolBar(); QToolBar *taskFPGABar = new QToolBar();
addToolBar(Qt::TopToolBarArea, taskFPGABar); addToolBar(Qt::TopToolBarArea, taskFPGABar);
@ -147,28 +133,22 @@ void MainWindow::createMenu()
menu_Design->addAction(actionSaveAsc); menu_Design->addAction(actionSaveAsc);
actionPlay = new QAction("Play", this); actionPlay = new QAction("Play", this);
QIcon iconPlay; actionPlay->setIcon(QIcon(":/icons/resources/control_play.png"));
iconPlay.addFile(QStringLiteral(":/icons/resources/control_play.png"));
actionPlay->setIcon(iconPlay);
actionPlay->setStatusTip("Continue running task"); actionPlay->setStatusTip("Continue running task");
connect(actionPlay, SIGNAL(triggered()), task, SLOT(continue_thread()));
actionPlay->setEnabled(false); actionPlay->setEnabled(false);
connect(actionPlay, SIGNAL(triggered()), task, SLOT(continue_thread()));
actionPause = new QAction("Pause", this); actionPause = new QAction("Pause", this);
QIcon iconPause; actionPause->setIcon(QIcon(":/icons/resources/control_pause.png"));
iconPause.addFile(QStringLiteral(":/icons/resources/control_pause.png"));
actionPause->setIcon(iconPause);
actionPause->setStatusTip("Pause running task"); actionPause->setStatusTip("Pause running task");
connect(actionPause, SIGNAL(triggered()), task, SLOT(pause_thread()));
actionPause->setEnabled(false); actionPause->setEnabled(false);
connect(actionPause, SIGNAL(triggered()), task, SLOT(pause_thread()));
actionStop = new QAction("Stop", this); actionStop = new QAction("Stop", this);
QIcon iconStop; actionStop->setIcon(QIcon(":/icons/resources/control_stop.png"));
iconStop.addFile(QStringLiteral(":/icons/resources/control_stop.png"));
actionStop->setIcon(iconStop);
actionStop->setStatusTip("Stop running task"); actionStop->setStatusTip("Stop running task");
connect(actionStop, SIGNAL(triggered()), task, SLOT(terminate_thread()));
actionStop->setEnabled(false); actionStop->setEnabled(false);
connect(actionStop, SIGNAL(triggered()), task, SLOT(terminate_thread()));
QToolBar *taskToolBar = new QToolBar(); QToolBar *taskToolBar = new QToolBar();
addToolBar(Qt::TopToolBarArea, taskToolBar); addToolBar(Qt::TopToolBarArea, taskToolBar);
@ -220,12 +200,16 @@ QStringList getSupportedPackages(ArchArgs::ArchArgsTypes chip)
void MainWindow::new_proj() void MainWindow::new_proj()
{ {
QMap<QString, int> arch; QMap<QString, int> arch;
#ifdef ICE40_HX1K_ONLY
arch.insert("Lattice HX1K", ArchArgs::HX1K);
#else
arch.insert("Lattice LP384", ArchArgs::LP384); arch.insert("Lattice LP384", ArchArgs::LP384);
arch.insert("Lattice LP1K", ArchArgs::LP1K); arch.insert("Lattice LP1K", ArchArgs::LP1K);
arch.insert("Lattice HX1K", ArchArgs::HX1K); arch.insert("Lattice HX1K", ArchArgs::HX1K);
arch.insert("Lattice UP5K", ArchArgs::UP5K); arch.insert("Lattice UP5K", ArchArgs::UP5K);
arch.insert("Lattice LP8K", ArchArgs::LP8K); arch.insert("Lattice LP8K", ArchArgs::LP8K);
arch.insert("Lattice HX8K", ArchArgs::HX8K); arch.insert("Lattice HX8K", ArchArgs::HX8K);
#endif
bool ok; bool ok;
QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok); QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
if (ok && !item.isEmpty()) { if (ok && !item.isEmpty()) {
@ -237,18 +221,30 @@ void MainWindow::new_proj()
if (ok && !item.isEmpty()) { if (ok && !item.isEmpty()) {
disableActions(); disableActions();
preload_pcf = "";
chipArgs.package = package.toStdString().c_str(); chipArgs.package = package.toStdString().c_str();
if (ctx) ctx = std::unique_ptr<Context>(new Context(chipArgs));
delete ctx;
ctx = new Context(chipArgs);
Q_EMIT contextChanged(ctx);
actionLoadJSON->setEnabled(true); actionLoadJSON->setEnabled(true);
Q_EMIT displaySplash();
Q_EMIT contextChanged(ctx.get());
} }
} }
} }
void MainWindow::load_json(std::string filename, std::string pcf)
{
preload_pcf = pcf;
disableActions();
Q_EMIT task->loadfile(filename);
}
void MainWindow::load_pcf(std::string filename)
{
disableActions();
Q_EMIT task->loadpcf(filename);
}
void MainWindow::newContext(Context *ctx) void MainWindow::newContext(Context *ctx)
{ {
std::string title = "nextpnr-ice40 - " + ctx->getChipName() + " ( " + chipArgs.package + " )"; std::string title = "nextpnr-ice40 - " + ctx->getChipName() + " ( " + chipArgs.package + " )";
@ -259,8 +255,6 @@ void MainWindow::open_proj()
{ {
QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj")); QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj"));
if (!fileName.isEmpty()) { if (!fileName.isEmpty()) {
tabWidget->setCurrentWidget(info);
std::string fn = fileName.toStdString(); std::string fn = fileName.toStdString();
disableActions(); disableActions();
} }
@ -270,12 +264,7 @@ void MainWindow::open_json()
{ {
QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json")); QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
if (!fileName.isEmpty()) { if (!fileName.isEmpty()) {
tabWidget->setCurrentWidget(info); load_json(fileName.toStdString(), "");
std::string fn = fileName.toStdString();
disableActions();
timing_driven = false;
Q_EMIT task->loadfile(fn);
} }
} }
@ -283,11 +272,7 @@ void MainWindow::open_pcf()
{ {
QString fileName = QFileDialog::getOpenFileName(this, QString("Open PCF"), QString(), QString("*.pcf")); QString fileName = QFileDialog::getOpenFileName(this, QString("Open PCF"), QString(), QString("*.pcf"));
if (!fileName.isEmpty()) { if (!fileName.isEmpty()) {
tabWidget->setCurrentWidget(info); load_pcf(fileName.toStdString());
std::string fn = fileName.toStdString();
disableActions();
Q_EMIT task->loadpcf(fn);
} }
} }
@ -328,9 +313,12 @@ void MainWindow::loadfile_finished(bool status)
log("Loading design successful.\n"); log("Loading design successful.\n");
actionLoadPCF->setEnabled(true); actionLoadPCF->setEnabled(true);
actionPack->setEnabled(true); actionPack->setEnabled(true);
if (!preload_pcf.empty())
load_pcf(preload_pcf);
Q_EMIT updateTreeView(); Q_EMIT updateTreeView();
} else { } else {
log("Loading design failed.\n"); log("Loading design failed.\n");
preload_pcf = "";
} }
} }
@ -440,4 +428,4 @@ void MainWindow::budget()
void MainWindow::place() { Q_EMIT task->place(timing_driven); } void MainWindow::place() { Q_EMIT task->place(timing_driven); }
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -30,12 +30,13 @@ class MainWindow : public BaseMainWindow
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(QWidget *parent = 0); explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
virtual ~MainWindow(); virtual ~MainWindow();
public: public:
void createMenu(); void createMenu();
void load_json(std::string filename, std::string pcf);
void load_pcf(std::string filename);
protected Q_SLOTS: protected Q_SLOTS:
virtual void new_proj(); virtual void new_proj();
virtual void open_proj(); virtual void open_proj();
@ -78,6 +79,7 @@ class MainWindow : public BaseMainWindow
bool timing_driven; bool timing_driven;
ArchArgs chipArgs; ArchArgs chipArgs;
std::string preload_pcf;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -23,10 +23,7 @@
#include "design_utils.h" #include "design_utils.h"
#include "jsonparse.h" #include "jsonparse.h"
#include "log.h" #include "log.h"
#include "pack.h"
#include "pcf.h" #include "pcf.h"
#include "place_sa.h"
#include "route.h"
#include "timing.h" #include "timing.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -99,7 +96,7 @@ void Worker::pack()
{ {
Q_EMIT taskStarted(); Q_EMIT taskStarted();
try { try {
bool res = pack_design(ctx); bool res = ctx->pack();
print_utilisation(ctx); print_utilisation(ctx);
Q_EMIT pack_finished(res); Q_EMIT pack_finished(res);
} catch (WorkerInterruptionRequested) { } catch (WorkerInterruptionRequested) {
@ -124,7 +121,7 @@ void Worker::place(bool timing_driven)
Q_EMIT taskStarted(); Q_EMIT taskStarted();
try { try {
ctx->timing_driven = timing_driven; ctx->timing_driven = timing_driven;
Q_EMIT place_finished(place_design_sa(ctx)); Q_EMIT place_finished(ctx->place());
} catch (WorkerInterruptionRequested) { } catch (WorkerInterruptionRequested) {
Q_EMIT taskCanceled(); Q_EMIT taskCanceled();
} }
@ -134,7 +131,7 @@ void Worker::route()
{ {
Q_EMIT taskStarted(); Q_EMIT taskStarted();
try { try {
Q_EMIT route_finished(route_design(ctx)); Q_EMIT route_finished(ctx->route());
} catch (WorkerInterruptionRequested) { } catch (WorkerInterruptionRequested) {
Q_EMIT taskCanceled(); Q_EMIT taskCanceled();
} }

View File

@ -33,9 +33,10 @@ class InfoTab : public QWidget
public: public:
explicit InfoTab(QWidget *parent = 0); explicit InfoTab(QWidget *parent = 0);
void info(std::string str); void info(std::string str);
public Q_SLOTS:
void clearBuffer();
private Q_SLOTS: private Q_SLOTS:
void showContextMenu(const QPoint &pt); void showContextMenu(const QPoint &pt);
void clearBuffer();
private: private:
QPlainTextEdit *plainTextEdit; QPlainTextEdit *plainTextEdit;

View File

@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -19,10 +20,14 @@
#include "line_editor.h" #include "line_editor.h"
#include <QKeyEvent> #include <QKeyEvent>
#include <QToolTip>
#include "ColumnFormatter.h"
#include "Utils.h"
#include "pyinterpreter.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0) LineEditor::LineEditor(ParseHelper *helper, QWidget *parent) : QLineEdit(parent), index(0), parseHelper(helper)
{ {
setContextMenuPolicy(Qt::CustomContextMenu); setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &history", this); QAction *clearAction = new QAction("Clear &history", this);
@ -38,10 +43,12 @@ LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0)
void LineEditor::keyPressEvent(QKeyEvent *ev) void LineEditor::keyPressEvent(QKeyEvent *ev)
{ {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
QToolTip::hideText();
if (lines.empty()) if (lines.empty())
return; return;
printf("Key_Up\n");
if (ev->key() == Qt::Key_Up) if (ev->key() == Qt::Key_Up)
index--; index--;
if (ev->key() == Qt::Key_Down) if (ev->key() == Qt::Key_Down)
@ -56,12 +63,21 @@ void LineEditor::keyPressEvent(QKeyEvent *ev)
} }
setText(lines[index]); setText(lines[index]);
} else if (ev->key() == Qt::Key_Escape) { } else if (ev->key() == Qt::Key_Escape) {
QToolTip::hideText();
clear(); clear();
return; return;
} else if (ev->key() == Qt::Key_Tab) {
autocomplete();
return;
} }
QToolTip::hideText();
QLineEdit::keyPressEvent(ev); QLineEdit::keyPressEvent(ev);
} }
// This makes TAB work
bool LineEditor::focusNextPrevChild(bool next) { return false; }
void LineEditor::textInserted() void LineEditor::textInserted()
{ {
if (lines.empty() || lines.back() != text()) if (lines.empty() || lines.back() != text())
@ -82,4 +98,34 @@ void LineEditor::clearHistory()
clear(); clear();
} }
NEXTPNR_NAMESPACE_END void LineEditor::autocomplete()
{
QString line = text();
const std::list<std::string> &suggestions = pyinterpreter_suggest(line.toStdString());
if (suggestions.size() == 1) {
line = suggestions.back().c_str();
} else {
// try to complete to longest common prefix
std::string prefix = LongestCommonPrefix(suggestions.begin(), suggestions.end());
if (prefix.size() > (size_t)line.size()) {
line = prefix.c_str();
} else {
ColumnFormatter fmt;
fmt.setItems(suggestions.begin(), suggestions.end());
fmt.format(width() / 5);
QString out = "";
for (auto &it : fmt.formattedOutput()) {
if (!out.isEmpty())
out += "\n";
out += it.c_str();
}
QToolTip::setFont(font());
if (!out.trimmed().isEmpty())
QToolTip::showText(mapToGlobal(QPoint(0, 0)), out);
}
}
// set up the next line on the console
setText(line);
}
NEXTPNR_NAMESPACE_END

View File

@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -22,6 +23,7 @@
#include <QLineEdit> #include <QLineEdit>
#include <QMenu> #include <QMenu>
#include "ParseHelper.h"
#include "nextpnr.h" #include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -31,7 +33,7 @@ class LineEditor : public QLineEdit
Q_OBJECT Q_OBJECT
public: public:
explicit LineEditor(QWidget *parent = 0); explicit LineEditor(ParseHelper *helper, QWidget *parent = 0);
private Q_SLOTS: private Q_SLOTS:
void textInserted(); void textInserted();
@ -43,11 +45,14 @@ class LineEditor : public QLineEdit
protected: protected:
void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE;
bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE;
void autocomplete();
private: private:
int index; int index;
QStringList lines; QStringList lines;
QMenu *contextMenu; QMenu *contextMenu;
ParseHelper *parseHelper;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

79
gui/pyconsole.cc Normal file
View File

@ -0,0 +1,79 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "pyconsole.h"
#include "pyinterpreter.h"
NEXTPNR_NAMESPACE_BEGIN
const QColor PythonConsole::NORMAL_COLOR = QColor::fromRgbF(0, 0, 0);
const QColor PythonConsole::ERROR_COLOR = QColor::fromRgbF(1.0, 0, 0);
const QColor PythonConsole::OUTPUT_COLOR = QColor::fromRgbF(0, 0, 1.0);
PythonConsole::PythonConsole(QWidget *parent) : QTextEdit(parent) {}
void PythonConsole::parseEvent(const ParseMessage &message)
{
// handle invalid user input
if (message.errorCode) {
setTextColor(ERROR_COLOR);
append(message.message.c_str());
setTextColor(NORMAL_COLOR);
append("");
return;
}
// interpret valid user input
int errorCode = 0;
std::string res;
if (message.message.size())
res = pyinterpreter_execute(message.message, &errorCode);
if (errorCode) {
setTextColor(ERROR_COLOR);
} else {
setTextColor(OUTPUT_COLOR);
}
if (res.size()) {
append(res.c_str());
}
setTextColor(NORMAL_COLOR);
append("");
moveCursorToEnd();
}
void PythonConsole::displayString(QString text)
{
QTextCursor cursor = textCursor();
cursor.movePosition(QTextCursor::End);
setTextColor(NORMAL_COLOR);
cursor.insertText(text);
cursor.movePosition(QTextCursor::EndOfLine);
moveCursorToEnd();
}
void PythonConsole::moveCursorToEnd()
{
QTextCursor cursor = textCursor();
cursor.movePosition(QTextCursor::End);
setTextCursor(cursor);
}
NEXTPNR_NAMESPACE_END

55
gui/pyconsole.h Normal file
View File

@ -0,0 +1,55 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
*
* 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 PYCONSOLE_H
#define PYCONSOLE_H
#include <QColor>
#include <QMimeData>
#include <QTextEdit>
#include "ParseHelper.h"
#include "ParseListener.h"
#include "nextpnr.h"
class QWidget;
class QKeyEvent;
NEXTPNR_NAMESPACE_BEGIN
class PythonConsole : public QTextEdit, public ParseListener
{
Q_OBJECT
public:
PythonConsole(QWidget *parent = 0);
void displayString(QString text);
void moveCursorToEnd();
virtual void parseEvent(const ParseMessage &message);
protected:
static const QColor NORMAL_COLOR;
static const QColor ERROR_COLOR;
static const QColor OUTPUT_COLOR;
};
NEXTPNR_NAMESPACE_END
#endif // PYCONSOLE_H

View File

@ -16,7 +16,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* *
*/ */
#ifndef NO_PYTHON
#include "pythontab.h" #include "pythontab.h"
#include <QGridLayout> #include <QGridLayout>
@ -25,12 +24,20 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
const QString PythonTab::PROMPT = ">>> ";
const QString PythonTab::MULTILINE_PROMPT = "... ";
PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false) PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
{ {
QFont f("unexistent");
f.setStyleHint(QFont::Monospace);
// Add text area for Python output and input line // Add text area for Python output and input line
console = new PythonConsole(); console = new PythonConsole();
console->setMinimumHeight(100); console->setMinimumHeight(100);
console->setEnabled(false); console->setReadOnly(true);
console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
console->setFont(f);
console->setContextMenuPolicy(Qt::CustomContextMenu); console->setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &buffer", this); QAction *clearAction = new QAction("Clear &buffer", this);
@ -41,9 +48,21 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
contextMenu->addAction(clearAction); contextMenu->addAction(clearAction);
connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint))); connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
lineEdit = new LineEditor(&parseHelper);
lineEdit->setMinimumHeight(30);
lineEdit->setMaximumHeight(30);
lineEdit->setFont(f);
lineEdit->setPlaceholderText(PythonTab::PROMPT);
connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));
QGridLayout *mainLayout = new QGridLayout(); QGridLayout *mainLayout = new QGridLayout();
mainLayout->addWidget(console, 0, 0); mainLayout->addWidget(console, 0, 0);
mainLayout->addWidget(lineEdit, 1, 0);
setLayout(mainLayout); setLayout(mainLayout);
parseHelper.subscribe(console);
prompt = PythonTab::PROMPT;
} }
PythonTab::~PythonTab() PythonTab::~PythonTab()
@ -54,13 +73,26 @@ PythonTab::~PythonTab()
} }
} }
void PythonTab::editLineReturnPressed(QString text)
{
console->displayString(prompt + text + "\n");
parseHelper.process(text.toStdString());
if (parseHelper.buffered())
prompt = PythonTab::MULTILINE_PROMPT;
else
prompt = PythonTab::PROMPT;
lineEdit->setPlaceholderText(prompt);
}
void PythonTab::newContext(Context *ctx) void PythonTab::newContext(Context *ctx)
{ {
if (initialized) { if (initialized) {
pyinterpreter_finalize(); pyinterpreter_finalize();
deinit_python(); deinit_python();
} }
console->setEnabled(true);
console->clear(); console->clear();
pyinterpreter_preinit(); pyinterpreter_preinit();
@ -74,13 +106,12 @@ void PythonTab::newContext(Context *ctx)
QString version = QString("Python %1 on %2\n").arg(Py_GetVersion(), Py_GetPlatform()); QString version = QString("Python %1 on %2\n").arg(Py_GetVersion(), Py_GetPlatform());
console->displayString(version); console->displayString(version);
console->displayPrompt();
} }
void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); } void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
void PythonTab::clearBuffer() { console->clear(); } void PythonTab::clearBuffer() { console->clear(); }
NEXTPNR_NAMESPACE_END void PythonTab::info(std::string str) { console->displayString(str.c_str()); }
#endif NEXTPNR_NAMESPACE_END

View File

@ -20,11 +20,10 @@
#ifndef PYTHONTAB_H #ifndef PYTHONTAB_H
#define PYTHONTAB_H #define PYTHONTAB_H
#ifndef NO_PYTHON
#include <QLineEdit> #include <QLineEdit>
#include <QMenu> #include <QMenu>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include "ParseHelper.h"
#include "line_editor.h" #include "line_editor.h"
#include "nextpnr.h" #include "nextpnr.h"
#include "pyconsole.h" #include "pyconsole.h"
@ -41,17 +40,24 @@ class PythonTab : public QWidget
private Q_SLOTS: private Q_SLOTS:
void showContextMenu(const QPoint &pt); void showContextMenu(const QPoint &pt);
void clearBuffer(); void editLineReturnPressed(QString text);
public Q_SLOTS: public Q_SLOTS:
void newContext(Context *ctx); void newContext(Context *ctx);
void info(std::string str);
void clearBuffer();
private: private:
PythonConsole *console; PythonConsole *console;
LineEditor *lineEdit;
QMenu *contextMenu; QMenu *contextMenu;
bool initialized; bool initialized;
ParseHelper parseHelper;
QString prompt;
static const QString PROMPT;
static const QString MULTILINE_PROMPT;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif // NO_PYTHON
#endif // PYTHONTAB_H #endif // PYTHONTAB_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

BIN
gui/resources/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
gui/resources/zoom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

View File

@ -19,9 +19,13 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include "gfx.h"
#include "log.h" #include "log.h"
#include "nextpnr.h" #include "nextpnr.h"
#include "placer1.h"
#include "router1.h"
#include "util.h" #include "util.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -365,6 +369,49 @@ std::string Arch::getBelPackagePin(BelId bel) const
} }
return ""; return "";
} }
// -----------------------------------------------------------------------
GroupId Arch::getGroupByName(IdString name) const
{
for (auto g : getGroups())
if (getGroupName(g) == name)
return g;
return GroupId();
}
IdString Arch::getGroupName(GroupId group) const { return IdString(); }
std::vector<GroupId> Arch::getGroups() const
{
std::vector<GroupId> ret;
return ret;
}
std::vector<BelId> Arch::getGroupBels(GroupId group) const
{
std::vector<BelId> ret;
return ret;
}
std::vector<WireId> Arch::getGroupWires(GroupId group) const
{
std::vector<WireId> ret;
return ret;
}
std::vector<PipId> Arch::getGroupPips(GroupId group) const
{
std::vector<PipId> ret;
return ret;
}
std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
{
std::vector<GroupId> ret;
return ret;
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
@ -398,100 +445,206 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
std::vector<GraphicElement> Arch::getFrameGraphics() const bool Arch::place() { return placer1(getCtx()); }
bool Arch::route() { return router1(getCtx()); }
// -----------------------------------------------------------------------
DecalXY Arch::getFrameDecal() const
{ {
std::vector<GraphicElement> ret; DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_FRAME;
for (int x = 0; x <= chip_info->width; x++) decalxy.decal.active = true;
for (int y = 0; y <= chip_info->height; y++) { return decalxy;
GraphicElement el;
el.type = GraphicElement::G_LINE;
el.x1 = x - 0.05, el.x2 = x + 0.05, el.y1 = y, el.y2 = y, el.z = 0;
ret.push_back(el);
el.x1 = x, el.x2 = x, el.y1 = y - 0.05, el.y2 = y + 0.05, el.z = 0;
ret.push_back(el);
}
return ret;
} }
std::vector<GraphicElement> Arch::getBelGraphics(BelId bel) const DecalXY Arch::getBelDecal(BelId bel) const
{ {
std::vector<GraphicElement> ret; DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_BEL;
auto bel_type = getBelType(bel); decalxy.decal.index = bel.index;
decalxy.decal.active = bel_to_cell.at(bel.index) != IdString();
if (bel_type == TYPE_ICESTORM_LC) { return decalxy;
GraphicElement el;
el.type = GraphicElement::G_BOX;
el.x1 = chip_info->bel_data[bel.index].x + 0.1;
el.x2 = chip_info->bel_data[bel.index].x + 0.9;
el.y1 = chip_info->bel_data[bel.index].y + 0.10 + (chip_info->bel_data[bel.index].z) * (0.8 / 8);
el.y2 = chip_info->bel_data[bel.index].y + 0.18 + (chip_info->bel_data[bel.index].z) * (0.8 / 8);
el.z = 0;
ret.push_back(el);
}
if (bel_type == TYPE_SB_IO) {
if (chip_info->bel_data[bel.index].x == 0 || chip_info->bel_data[bel.index].x == chip_info->width - 1) {
GraphicElement el;
el.type = GraphicElement::G_BOX;
el.x1 = chip_info->bel_data[bel.index].x + 0.1;
el.x2 = chip_info->bel_data[bel.index].x + 0.9;
if (chip_info->bel_data[bel.index].z == 0) {
el.y1 = chip_info->bel_data[bel.index].y + 0.10;
el.y2 = chip_info->bel_data[bel.index].y + 0.45;
} else {
el.y1 = chip_info->bel_data[bel.index].y + 0.55;
el.y2 = chip_info->bel_data[bel.index].y + 0.90;
}
el.z = 0;
ret.push_back(el);
} else {
GraphicElement el;
el.type = GraphicElement::G_BOX;
if (chip_info->bel_data[bel.index].z == 0) {
el.x1 = chip_info->bel_data[bel.index].x + 0.10;
el.x2 = chip_info->bel_data[bel.index].x + 0.45;
} else {
el.x1 = chip_info->bel_data[bel.index].x + 0.55;
el.x2 = chip_info->bel_data[bel.index].x + 0.90;
}
el.y1 = chip_info->bel_data[bel.index].y + 0.1;
el.y2 = chip_info->bel_data[bel.index].y + 0.9;
el.z = 0;
ret.push_back(el);
}
}
if (bel_type == TYPE_ICESTORM_RAM) {
GraphicElement el;
el.type = GraphicElement::G_BOX;
el.x1 = chip_info->bel_data[bel.index].x + 0.1;
el.x2 = chip_info->bel_data[bel.index].x + 0.9;
el.y1 = chip_info->bel_data[bel.index].y + 0.1;
el.y2 = chip_info->bel_data[bel.index].y + 1.9;
el.z = 0;
ret.push_back(el);
}
return ret;
} }
std::vector<GraphicElement> Arch::getWireGraphics(WireId wire) const DecalXY Arch::getWireDecal(WireId wire) const
{ {
std::vector<GraphicElement> ret; DecalXY decalxy;
// FIXME decalxy.decal.type = DecalId::TYPE_WIRE;
return ret; decalxy.decal.index = wire.index;
decalxy.decal.active = wire_to_net.at(wire.index) != IdString();
return decalxy;
} }
std::vector<GraphicElement> Arch::getPipGraphics(PipId pip) const DecalXY Arch::getPipDecal(PipId pip) const
{ {
std::vector<GraphicElement> ret; DecalXY decalxy;
// FIXME decalxy.decal.type = DecalId::TYPE_PIP;
return ret; decalxy.decal.index = pip.index;
decalxy.decal.active = pip_to_net.at(pip.index) != IdString();
return decalxy;
}; };
DecalXY Arch::getGroupDecal(GroupId group) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_GROUP;
decalxy.decal.index = (group.type << 16) | (group.x << 8) | (group.y);
decalxy.decal.active = true;
return decalxy;
};
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
{
std::vector<GraphicElement> ret;
if (decal.type == DecalId::TYPE_FRAME) {
for (int x = 0; x <= chip_info->width; x++)
for (int y = 0; y <= chip_info->height; y++) {
GraphicElement el;
el.type = GraphicElement::G_LINE;
el.x1 = x - 0.05, el.x2 = x + 0.05, el.y1 = y, el.y2 = y, el.z = 0;
ret.push_back(el);
el.x1 = x, el.x2 = x, el.y1 = y - 0.05, el.y2 = y + 0.05, el.z = 0;
ret.push_back(el);
}
}
if (decal.type == DecalId::TYPE_WIRE) {
WireId wire;
wire.index = decal.index;
int n = chip_info->wire_data[wire.index].num_segments;
const WireSegmentPOD *p = chip_info->wire_data[wire.index].segments.get();
GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
for (int i = 0; i < n; i++)
gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style);
}
if (decal.type == DecalId::TYPE_BEL) {
BelId bel;
bel.index = decal.index;
auto bel_type = getBelType(bel);
if (bel_type == TYPE_ICESTORM_LC) {
GraphicElement el;
el.type = GraphicElement::G_BOX;
el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_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 +
(chip_info->bel_data[bel.index].z) * logic_cell_pitch;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
(chip_info->bel_data[bel.index].z) * logic_cell_pitch;
el.z = 0;
ret.push_back(el);
if (chip_info->bel_data[bel.index].z == 0) {
int tx = chip_info->bel_data[bel.index].x;
int ty = chip_info->bel_data[bel.index].y;
// Main switchbox
GraphicElement main_sw;
main_sw.type = GraphicElement::G_BOX;
main_sw.style = GraphicElement::G_FRAME;
main_sw.x1 = tx + main_swbox_x1;
main_sw.x2 = tx + main_swbox_x2;
main_sw.y1 = ty + main_swbox_y1;
main_sw.y2 = ty + main_swbox_y2;
ret.push_back(main_sw);
// Local tracks to LUT input switchbox
GraphicElement local_sw;
local_sw.type = GraphicElement::G_BOX;
local_sw.style = GraphicElement::G_FRAME;
local_sw.x1 = tx + local_swbox_x1;
local_sw.x2 = tx + local_swbox_x2;
local_sw.y1 = ty + local_swbox_y1;
local_sw.y2 = ty + local_swbox_y2;
local_sw.z = 0;
ret.push_back(local_sw);
}
}
if (bel_type == TYPE_SB_IO) {
if (chip_info->bel_data[bel.index].x == 0 || chip_info->bel_data[bel.index].x == chip_info->width - 1) {
GraphicElement el;
el.type = GraphicElement::G_BOX;
el.x1 = chip_info->bel_data[bel.index].x + 0.1;
el.x2 = chip_info->bel_data[bel.index].x + 0.9;
if (chip_info->bel_data[bel.index].z == 0) {
el.y1 = chip_info->bel_data[bel.index].y + 0.10;
el.y2 = chip_info->bel_data[bel.index].y + 0.45;
} else {
el.y1 = chip_info->bel_data[bel.index].y + 0.55;
el.y2 = chip_info->bel_data[bel.index].y + 0.90;
}
el.z = 0;
ret.push_back(el);
} else {
GraphicElement el;
el.type = GraphicElement::G_BOX;
if (chip_info->bel_data[bel.index].z == 0) {
el.x1 = chip_info->bel_data[bel.index].x + 0.10;
el.x2 = chip_info->bel_data[bel.index].x + 0.45;
} else {
el.x1 = chip_info->bel_data[bel.index].x + 0.55;
el.x2 = chip_info->bel_data[bel.index].x + 0.90;
}
el.y1 = chip_info->bel_data[bel.index].y + 0.1;
el.y2 = chip_info->bel_data[bel.index].y + 0.9;
el.z = 0;
ret.push_back(el);
}
}
if (bel_type == TYPE_ICESTORM_RAM) {
for (int i = 0; i < 2; i++)
{
int tx = chip_info->bel_data[bel.index].x;
int ty = chip_info->bel_data[bel.index].y + i;
GraphicElement el;
el.type = GraphicElement::G_BOX;
el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_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;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + 7*logic_cell_pitch;
el.z = 0;
ret.push_back(el);
// Main switchbox
GraphicElement main_sw;
main_sw.type = GraphicElement::G_BOX;
main_sw.style = GraphicElement::G_FRAME;
main_sw.x1 = tx + main_swbox_x1;
main_sw.x2 = tx + main_swbox_x2;
main_sw.y1 = ty + main_swbox_y1;
main_sw.y2 = ty + main_swbox_y2;
ret.push_back(main_sw);
// Local tracks to LUT input switchbox
GraphicElement local_sw;
local_sw.type = GraphicElement::G_BOX;
local_sw.style = GraphicElement::G_FRAME;
local_sw.x1 = tx + local_swbox_x1;
local_sw.x2 = tx + local_swbox_x2;
local_sw.y1 = ty + local_swbox_y1;
local_sw.y2 = ty + local_swbox_y2;
local_sw.z = 0;
ret.push_back(local_sw);
}
}
}
return ret;
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const

View File

@ -70,6 +70,11 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD {
int32_t switch_index; int32_t switch_index;
}); });
NPNR_PACKED_STRUCT(struct WireSegmentPOD {
int8_t x, y;
int16_t index;
});
NPNR_PACKED_STRUCT(struct WireInfoPOD { NPNR_PACKED_STRUCT(struct WireInfoPOD {
RelPtr<char> name; RelPtr<char> name;
int32_t num_uphill, num_downhill; int32_t num_uphill, num_downhill;
@ -79,6 +84,9 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD {
BelPortPOD bel_uphill; BelPortPOD bel_uphill;
RelPtr<BelPortPOD> bels_downhill; RelPtr<BelPortPOD> bels_downhill;
int32_t num_segments;
RelPtr<WireSegmentPOD> segments;
int8_t x, y; int8_t x, y;
WireType type; WireType type;
int8_t padding_0; int8_t padding_0;
@ -515,9 +523,6 @@ struct Arch : BaseCtx
// ------------------------------------------------- // -------------------------------------------------
PipId getPipByName(IdString name) const; PipId getPipByName(IdString name) const;
IdString getPipName(PipId pip) const;
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
void bindPip(PipId pip, IdString net, PlaceStrength strength) void bindPip(PipId pip, IdString net, PlaceStrength strength)
{ {
@ -577,6 +582,10 @@ struct Arch : BaseCtx
range.e.cursor = chip_info->num_pips; range.e.cursor = chip_info->num_pips;
return range; return range;
} }
IdString getPipName(PipId pip) const;
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
WireId getPipSrcWire(PipId pip) const WireId getPipSrcWire(PipId pip) const
{ {
@ -634,6 +643,16 @@ struct Arch : BaseCtx
// ------------------------------------------------- // -------------------------------------------------
GroupId getGroupByName(IdString name) const;
IdString getGroupName(GroupId group) const;
std::vector<GroupId> getGroups() const;
std::vector<BelId> getGroupBels(GroupId group) const;
std::vector<WireId> getGroupWires(GroupId group) const;
std::vector<PipId> getGroupPips(GroupId group) const;
std::vector<GroupId> getGroupGroups(GroupId group) const;
// -------------------------------------------------
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const; void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
delay_t estimateDelay(WireId src, WireId dst) const; delay_t estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 20; } delay_t getDelayEpsilon() const { return 20; }
@ -643,16 +662,19 @@ struct Arch : BaseCtx
// ------------------------------------------------- // -------------------------------------------------
std::vector<GraphicElement> getFrameGraphics() const; bool pack();
std::vector<GraphicElement> getBelGraphics(BelId bel) const; bool place();
std::vector<GraphicElement> getWireGraphics(WireId wire) const; bool route();
std::vector<GraphicElement> getPipGraphics(PipId pip) const;
bool allGraphicsReload = false; // -------------------------------------------------
bool frameGraphicsReload = false;
std::unordered_set<BelId> belGraphicsReload; std::vector<GraphicElement> getDecalGraphics(DecalId decal) const;
std::unordered_set<WireId> wireGraphicsReload;
std::unordered_set<PipId> pipGraphicsReload; DecalXY getFrameDecal() const;
DecalXY getBelDecal(BelId bel) const;
DecalXY getWireDecal(WireId wire) const;
DecalXY getPipDecal(PipId pip) const;
DecalXY getGroupDecal(GroupId group) const;
// ------------------------------------------------- // -------------------------------------------------

View File

@ -58,7 +58,10 @@ void arch_wrap_python()
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>()); auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init) auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
.def("checksum", &Context::checksum); .def("checksum", &Context::checksum)
.def("pack", &Context::pack)
.def("place", &Context::place)
.def("route", &Context::route);
fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<BelType>, fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<BelType>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType"); conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
@ -75,7 +78,7 @@ void arch_wrap_python()
fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell, fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell"); conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls, fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
"getBels"); "getBels");
fn_wrapper_1a<Context, decltype(&Context::getBelsAtSameTile), &Context::getBelsAtSameTile, wrap_context<BelRange>, fn_wrapper_1a<Context, decltype(&Context::getBelsAtSameTile), &Context::getBelsAtSameTile, wrap_context<BelRange>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelsAtSameTile"); conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelsAtSameTile");
@ -139,15 +142,15 @@ void arch_wrap_python()
fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap( fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
ctx_cls, "getChipName"); ctx_cls, "getChipName");
fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls, fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
"archId"); "archId");
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap; typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap; typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls, readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
"cells"); "cells");
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls, readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
"nets"); "nets");
WRAP_RANGE(Bel, conv_to_str<BelId>); WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>); WRAP_RANGE(Wire, conv_to_str<WireId>);
WRAP_RANGE(AllPip, conv_to_str<PipId>); WRAP_RANGE(AllPip, conv_to_str<PipId>);

View File

@ -21,6 +21,8 @@
#error Include "archdefs.h" via "nextpnr.h" only. #error Include "archdefs.h" via "nextpnr.h" only.
#endif #endif
#include <boost/functional/hash.hpp>
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
typedef int delay_t; typedef int delay_t;
@ -107,6 +109,47 @@ struct PipId
bool operator!=(const PipId &other) const { return index != other.index; } bool operator!=(const PipId &other) const { return index != other.index; }
}; };
struct GroupId
{
enum : int8_t
{
TYPE_NONE,
TYPE_FRAME,
TYPE_MAIN_SW,
TYPE_LOCAL_SW,
TYPE_LC0_SW,
TYPE_LC1_SW,
TYPE_LC2_SW,
TYPE_LC3_SW,
TYPE_LC4_SW,
TYPE_LC5_SW,
TYPE_LC6_SW,
TYPE_LC7_SW
} type = TYPE_NONE;
int8_t x = 0, y = 0;
bool operator==(const GroupId &other) const { return (type == other.type) && (x == other.x) && (y == other.y); }
bool operator!=(const GroupId &other) const { return (type != other.type) || (x != other.x) || (y == other.y); }
};
struct DecalId
{
enum : int8_t
{
TYPE_NONE,
TYPE_FRAME,
TYPE_BEL,
TYPE_WIRE,
TYPE_PIP,
TYPE_GROUP
} type = TYPE_NONE;
int32_t index = -1;
bool active = false;
bool operator==(const DecalId &other) const { return (type == other.type) && (index == other.index); }
bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); }
};
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
namespace std { namespace std {
@ -135,4 +178,28 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelType> : hash<int>
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PortPin> : hash<int> template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PortPin> : hash<int>
{ {
}; };
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX GroupId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<int>()(group.type));
boost::hash_combine(seed, hash<int>()(group.x));
boost::hash_combine(seed, hash<int>()(group.y));
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<int>()(decal.type));
boost::hash_combine(seed, hash<int>()(decal.index));
return seed;
}
};
} // namespace std } // namespace std

View File

@ -341,9 +341,8 @@ void write_asc(const Context *ctx, std::ostream &out)
set_config(ti, config.at(y).at(x), set_config(ti, config.at(y).at(x),
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true); "Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
else else
set_config(ti, config.at(y).at(x), set_config(ti, config.at(y).at(x), "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) +
"Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" + "_LC0" + std::to_string(lc_idx) + "_inmux02_5",
std::to_string(lc_idx) + "_inmux02_5",
true); true);
} }
} }

View File

@ -11,6 +11,7 @@ group.add_argument("-b", "--binary", action="store_true")
group.add_argument("-c", "--c_file", action="store_true") group.add_argument("-c", "--c_file", action="store_true")
parser.add_argument("filename", type=str, help="chipdb input filename") parser.add_argument("filename", type=str, help="chipdb input filename")
parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc") parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc")
parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h")
args = parser.parse_args() args = parser.parse_args()
endianness = "le" endianness = "le"
@ -54,6 +55,9 @@ beltypes = dict()
tiletypes = dict() tiletypes = dict()
wiretypes = dict() wiretypes = dict()
gfx_wire_ids = dict()
wire_segments = dict()
with open(args.portspins) as f: with open(args.portspins) as f:
for line in f: for line in f:
line = line.replace("(", " ") line = line.replace("(", " ")
@ -66,6 +70,20 @@ with open(args.portspins) as f:
idx = len(portpins) + 1 idx = len(portpins) + 1
portpins[line[1]] = idx portpins[line[1]] = idx
with open(args.gfxh) as f:
state = 0
for line in f:
if state == 0 and line.startswith("enum GfxTileWireId"):
state = 1
elif state == 1 and line.startswith("};"):
state = 0
elif state == 1 and (line.startswith("{") or line.strip() == ""):
pass
elif state == 1:
idx = len(gfx_wire_ids)
name = line.strip().rstrip(",")
gfx_wire_ids[name] = idx
beltypes["ICESTORM_LC"] = 1 beltypes["ICESTORM_LC"] = 1
beltypes["ICESTORM_RAM"] = 2 beltypes["ICESTORM_RAM"] = 2
beltypes["SB_IO"] = 3 beltypes["SB_IO"] = 3
@ -371,6 +389,10 @@ with open(args.filename, "r") as f:
if mode[1] not in wire_xy: if mode[1] not in wire_xy:
wire_xy[mode[1]] = list() wire_xy[mode[1]] = list()
wire_xy[mode[1]].append((int(line[0]), int(line[1]))) wire_xy[mode[1]].append((int(line[0]), int(line[1])))
if mode[1] not in wire_segments:
wire_segments[mode[1]] = set()
if ("TILE_WIRE_" + wname[2].upper().replace("/", "_")) in gfx_wire_ids:
wire_segments[mode[1]].add((wname[0], wname[1], gfx_wire_ids["TILE_WIRE_" + wname[2].upper().replace("/", "_")]))
continue continue
if mode[0] in ("buffer", "routing"): if mode[0] in ("buffer", "routing"):
@ -712,7 +734,7 @@ class BinaryBlobAssembler:
def finalize(self): def finalize(self):
assert not self.finalized assert not self.finalized
for s, index in self.strings.items(): for s, index in sorted(self.strings.items()):
self.l("str%d" % index, "char") self.l("str%d" % index, "char")
for c in s: for c in s:
self.data.append(ord(c)) self.data.append(ord(c))
@ -947,7 +969,7 @@ for wire in range(num_wires):
if wire in wire_downhill_belports: if wire in wire_downhill_belports:
num_bels_downhill = len(wire_downhill_belports[wire]) num_bels_downhill = len(wire_downhill_belports[wire])
bba.l("wire%d_downbels" % wire, "BelPortPOD") bba.l("wire%d_downbels" % wire, "BelPortPOD")
for belport in wire_downhill_belports[wire]: for belport in sorted(wire_downhill_belports[wire]):
bba.u32(belport[0], "bel_index") bba.u32(belport[0], "bel_index")
bba.u32(portpins[belport[1]], "port") bba.u32(portpins[belport[1]], "port")
else: else:
@ -1040,7 +1062,7 @@ for t in range(num_tile_types):
tileinfo.append(ti) tileinfo.append(ti)
bba.l("wire_data_%s" % dev_name, "WireInfoPOD") bba.l("wire_data_%s" % dev_name, "WireInfoPOD")
for info in wireinfo: for wire, info in enumerate(wireinfo):
bba.s(info["name"], "name") bba.s(info["name"], "name")
bba.u32(info["num_uphill"], "num_uphill") bba.u32(info["num_uphill"], "num_uphill")
bba.u32(info["num_downhill"], "num_downhill") bba.u32(info["num_downhill"], "num_downhill")
@ -1050,11 +1072,24 @@ for info in wireinfo:
bba.u32(info["uphill_bel"], "bel_uphill.bel_index") bba.u32(info["uphill_bel"], "bel_uphill.bel_index")
bba.u32(info["uphill_pin"], "bel_uphill.port") bba.u32(info["uphill_pin"], "bel_uphill.port")
bba.r(info["list_bels_downhill"], "bels_downhill") bba.r(info["list_bels_downhill"], "bels_downhill")
bba.u32(len(wire_segments[wire]), "num_segments")
if len(wire_segments[wire]):
bba.r("wire_segments_%d" % wire, "segments")
else:
bba.u32(0, "segments")
bba.u8(info["x"], "x") bba.u8(info["x"], "x")
bba.u8(info["y"], "y") bba.u8(info["y"], "y")
bba.u8(wiretypes[wire_type(info["name"])], "type") bba.u8(wiretypes[wire_type(info["name"])], "type")
bba.u8(0, "padding") bba.u8(0, "padding")
for wire in range(num_wires):
if len(wire_segments[wire]):
bba.l("wire_segments_%d" % wire, "WireSegmentPOD")
for seg in sorted(wire_segments[wire]):
bba.u8(seg[0], "x")
bba.u8(seg[1], "y")
bba.u16(seg[2], "index")
bba.l("pip_data_%s" % dev_name, "PipInfoPOD") bba.l("pip_data_%s" % dev_name, "PipInfoPOD")
for info in pipinfo: for info in pipinfo:
bba.u32(info["src"], "src") bba.u32(info["src"], "src")

View File

@ -21,8 +21,9 @@ if (MSVC)
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt) set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_DB} add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} ${DEV_TXT_DB} > ${DEV_CC_DB} COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY} DEPENDS ${DEV_TXT_DB} ${DB_PY}
) )
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB}) target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
@ -37,8 +38,9 @@ else()
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt) set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_DB} add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} ${DEV_TXT_DB} > ${DEV_CC_DB}.new COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB}.new
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB} COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY} DEPENDS ${DEV_TXT_DB} ${DB_PY}
) )

488
ice40/gfx.cc Normal file
View File

@ -0,0 +1,488 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "gfx.h"
NEXTPNR_NAMESPACE_BEGIN
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
{
GraphicElement el;
el.type = GraphicElement::G_LINE;
el.style = style;
// Horizontal Span-4 Wires
if (id >= TILE_WIRE_SP4_H_L_36 && id <= TILE_WIRE_SP4_H_L_47) {
int idx = (id - TILE_WIRE_SP4_H_L_36) + 48;
float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1)));
float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
el.x1 = x;
el.x2 = x + 0.01;
el.y1 = y1;
el.y2 = y1;
g.push_back(el);
el.x1 = x + 0.01;
el.x2 = x + 0.02;
el.y1 = y1;
el.y2 = y2;
g.push_back(el);
el.x1 = x + 0.02;
el.x2 = x + 0.9;
el.y1 = y2;
el.y2 = y2;
g.push_back(el);
el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 35);
el.x2 = el.x1;
el.y1 = y2;
el.y2 = y + main_swbox_y2;
g.push_back(el);
}
if (id >= TILE_WIRE_SP4_H_R_0 && id <= TILE_WIRE_SP4_H_R_47) {
int idx = id - TILE_WIRE_SP4_H_R_0;
float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1)));
float y3 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1) - 12));
if (idx >= 12) {
el.x1 = x;
el.x2 = x + 0.01;
el.y1 = y1;
el.y2 = y1;
g.push_back(el);
el.x1 = x + 0.01;
el.x2 = x + 0.02;
el.y1 = y1;
el.y2 = y2;
g.push_back(el);
}
el.x1 = x + 0.02;
el.x2 = x + 0.9;
el.y1 = y2;
el.y2 = y2;
g.push_back(el);
el.x1 = x + 0.9;
el.x2 = x + 1.0;
el.y1 = y2;
el.y2 = y3;
g.push_back(el);
el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
el.x2 = el.x1;
el.y1 = y2;
el.y2 = y + main_swbox_y2;
g.push_back(el);
}
// Vertical Span-4 Wires
if (id >= TILE_WIRE_SP4_V_T_36 && id <= TILE_WIRE_SP4_V_T_47) {
int idx = (id - TILE_WIRE_SP4_V_T_36) + 48;
float x1 = x + 0.03 + 0.0025 * (60 - (idx ^ 1));
float x2 = x + 0.03 + 0.0025 * (60 - idx);
el.y1 = y + 1.00;
el.y2 = y + 0.99;
el.x1 = x1;
el.x2 = x1;
g.push_back(el);
el.y1 = y + 0.99;
el.y2 = y + 0.98;
el.x1 = x1;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 0.98;
el.y2 = y + 0.10;
el.x1 = x2;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - idx));
el.y2 = el.y1;
el.x1 = x2;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
if (id >= TILE_WIRE_SP4_V_B_0 && id <= TILE_WIRE_SP4_V_B_47) {
int idx = id - TILE_WIRE_SP4_V_B_0;
float x1 = x + 0.03 + 0.0025 * (60 - idx);
float x2 = x + 0.03 + 0.0025 * (60 - (idx ^ 1));
float x3 = x + 0.03 + 0.0025 * (60 - (idx ^ 1) - 12);
if (idx >= 12) {
el.y1 = y + 1.00;
el.y2 = y + 0.99;
el.x1 = x1;
el.x2 = x1;
g.push_back(el);
el.y1 = y + 0.99;
el.y2 = y + 0.98;
el.x1 = x1;
el.x2 = x2;
g.push_back(el);
}
el.y1 = y + 0.98;
el.y2 = y + 0.10;
el.x1 = x2;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 0.10;
el.y2 = y;
el.x1 = x2;
el.x2 = x3;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
el.y2 = el.y1;
el.x1 = x;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
el.y2 = el.y1;
el.x1 = x2;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
// Horizontal Span-12 Wires
if (id >= TILE_WIRE_SP12_H_L_22 && id <= TILE_WIRE_SP12_H_L_23) {
int idx = (id - TILE_WIRE_SP12_H_L_22) + 24;
float y1 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1)));
float y2 = y + 1.0 - (0.03 + 0.0025 * (90 - idx));
el.x1 = x;
el.x2 = x + 0.01;
el.y1 = y1;
el.y2 = y1;
g.push_back(el);
el.x1 = x + 0.01;
el.x2 = x + 0.02;
el.y1 = y1;
el.y2 = y2;
g.push_back(el);
el.x1 = x + 0.02;
el.x2 = x + 0.98333;
el.y1 = y2;
el.y2 = y2;
g.push_back(el);
el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 5);
el.x2 = el.x1;
el.y1 = y2;
el.y2 = y + main_swbox_y2;
g.push_back(el);
}
if (id >= TILE_WIRE_SP12_H_R_0 && id <= TILE_WIRE_SP12_H_R_23) {
int idx = id - TILE_WIRE_SP12_H_R_0;
float y1 = y + 1.0 - (0.03 + 0.0025 * (90 - idx));
float y2 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1)));
float y3 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1) - 2));
if (idx >= 2) {
el.x1 = x;
el.x2 = x + 0.01;
el.y1 = y1;
el.y2 = y1;
g.push_back(el);
el.x1 = x + 0.01;
el.x2 = x + 0.02;
el.y1 = y1;
el.y2 = y2;
g.push_back(el);
}
el.x1 = x + 0.02;
el.x2 = x + 0.98333;
el.y1 = y2;
el.y2 = y2;
g.push_back(el);
el.x1 = x + 0.98333;
el.x2 = x + 1.0;
el.y1 = y2;
el.y2 = y3;
g.push_back(el);
el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
el.x2 = el.x1;
el.y1 = y2;
el.y2 = y + main_swbox_y2;
g.push_back(el);
}
// Vertical Right Span-4
if (id >= TILE_WIRE_SP4_R_V_B_0 && id <= TILE_WIRE_SP4_R_V_B_47) {
int idx = id - TILE_WIRE_SP4_R_V_B_0;
float y1 = y + 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
el.y1 = y1;
el.y2 = y1;
el.x1 = x + main_swbox_x2;
el.x2 = x + 1.0;
g.push_back(el);
}
// Vertical Span-12 Wires
if (id >= TILE_WIRE_SP12_V_T_22 && id <= TILE_WIRE_SP12_V_T_23) {
int idx = (id - TILE_WIRE_SP12_V_T_22) + 24;
float x1 = x + 0.03 + 0.0025 * (90 - (idx ^ 1));
float x2 = x + 0.03 + 0.0025 * (90 - idx);
el.y1 = y + 1.00;
el.y2 = y + 0.99;
el.x1 = x1;
el.x2 = x1;
g.push_back(el);
el.y1 = y + 0.99;
el.y2 = y + 0.98;
el.x1 = x1;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 0.98;
el.y2 = y + 0.01667;
el.x1 = x2;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - idx));
el.y2 = el.y1;
el.x1 = x2;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
if (id >= TILE_WIRE_SP12_V_B_0 && id <= TILE_WIRE_SP12_V_B_23) {
int idx = id - TILE_WIRE_SP12_V_B_0;
float x1 = x + 0.03 + 0.0025 * (90 - idx);
float x2 = x + 0.03 + 0.0025 * (90 - (idx ^ 1));
float x3 = x + 0.03 + 0.0025 * (90 - (idx ^ 1) - 2);
if (idx >= 2) {
el.y1 = y + 1.00;
el.y2 = y + 0.99;
el.x1 = x1;
el.x2 = x1;
g.push_back(el);
el.y1 = y + 0.99;
el.y2 = y + 0.98;
el.x1 = x1;
el.x2 = x2;
g.push_back(el);
}
el.y1 = y + 0.98;
el.y2 = y + 0.01667;
el.x1 = x2;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 0.01667;
el.y2 = y;
el.x1 = x2;
el.x2 = x3;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
el.y2 = el.y1;
el.x1 = x2;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
// Global2Local
if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
int idx = id - TILE_WIRE_GLB2LOCAL_0;
el.x1 = x + main_swbox_x1 + 0.005 * (idx + 5);
el.x2 = el.x1;
el.y1 = y + main_swbox_y1;
el.y2 = el.y1 - 0.02;
g.push_back(el);
}
// GlobalNets
if (id >= TILE_WIRE_GLB_NETWK_0 && id <= TILE_WIRE_GLB_NETWK_7) {
int idx = id - TILE_WIRE_GLB_NETWK_0;
el.x1 = x + main_swbox_x1 - 0.05;
el.x2 = x + main_swbox_x1;
el.y1 = y + main_swbox_y1 + 0.005 * (13 - idx);
el.y2 = el.y1;
g.push_back(el);
}
// Neighbours
if (id >= TILE_WIRE_NEIGH_OP_BNL_0 && id <= TILE_WIRE_NEIGH_OP_TOP_7) {
int idx = id - TILE_WIRE_NEIGH_OP_BNL_0;
el.y1 = y + main_swbox_y2 - (0.0025 * (idx + 10) + 0.01 * (idx / 8));
el.y2 = el.y1;
el.x1 = x + main_swbox_x1 - 0.05;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
// Local Tracks
if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
int idx = id - TILE_WIRE_LOCAL_G0_0;
el.x1 = x + main_swbox_x2;
el.x2 = x + local_swbox_x1;
float yoff = y + (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
el.y1 = yoff + 0.005 * idx + 0.05 * (idx / 8);
el.y2 = el.y1;
g.push_back(el);
}
// LC Inputs
if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) {
int idx = id - TILE_WIRE_LUTFF_0_IN_0;
int z = idx / 4;
int input = idx % 4;
el.x1 = x + local_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.y2 = el.y1;
g.push_back(el);
}
// LC Outputs
if (id >= TILE_WIRE_LUTFF_0_OUT && id <= TILE_WIRE_LUTFF_7_OUT) {
int idx = id - TILE_WIRE_LUTFF_0_OUT;
float y1 = y + 1.0 - (0.03 + 0.0025 * (152 + idx));
el.y1 = y1;
el.y2 = y1;
el.x1 = x + main_swbox_x2;
el.x2 = x + 0.97 + 0.0025 * (7 - idx);
g.push_back(el);
el.y1 = y1;
el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + idx * logic_cell_pitch;
el.x1 = el.x2;
g.push_back(el);
el.y1 = el.y2;
el.x1 = x + logic_cell_x2;
g.push_back(el);
}
// LC Control
if (id >= TILE_WIRE_LUTFF_GLOBAL_CEN && id <= TILE_WIRE_LUTFF_GLOBAL_S_R) {
int idx = id - TILE_WIRE_LUTFF_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);
for (int i = 0; i < 7; i++) {
el.y1 = y + logic_cell_y2 + i * logic_cell_pitch;
el.y2 = y + logic_cell_y1 + (i + 1) * logic_cell_pitch;
g.push_back(el);
}
}
// LC Cascade
if (id >= TILE_WIRE_LUTFF_0_LOUT && id <= TILE_WIRE_LUTFF_6_LOUT) {
int idx = id - TILE_WIRE_LUTFF_0_LOUT;
el.x1 = x + logic_cell_x1 + 0.005 * 5;
el.x2 = el.x1;
el.y1 = y + logic_cell_y2 + idx * logic_cell_pitch;
el.y2 = y + logic_cell_y1 + (idx + 1) * logic_cell_pitch;
g.push_back(el);
}
// Carry Chain
if (id >= TILE_WIRE_LUTFF_0_COUT && id <= TILE_WIRE_LUTFF_7_COUT) {
int idx = id - TILE_WIRE_LUTFF_0_COUT;
el.x1 = x + logic_cell_x1 + 0.005 * 3;
el.x2 = el.x1;
el.y1 = y + logic_cell_y2 + idx * logic_cell_pitch;
el.y2 = y + (idx < 7 ? logic_cell_y1 + (idx + 1) * logic_cell_pitch : 1.0);
g.push_back(el);
}
if (id == TILE_WIRE_CARRY_IN) {
el.x1 = x + logic_cell_x1 + 0.005 * 3;
el.x2 = el.x1;
el.y1 = y;
el.y2 = y + 0.01;
g.push_back(el);
}
if (id == TILE_WIRE_CARRY_IN_MUX) {
el.x1 = x + logic_cell_x1 + 0.005 * 3;
el.x2 = el.x1;
el.y1 = y + 0.02;
el.y2 = y + logic_cell_y1;
g.push_back(el);
}
}
NEXTPNR_NAMESPACE_END

474
ice40/gfx.h Normal file
View File

@ -0,0 +1,474 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef GFX_H
#define GFX_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
const float main_swbox_x1 = 0.35;
const float main_swbox_x2 = 0.60;
const float main_swbox_y1 = 0.05;
const float main_swbox_y2 = 0.73;
const float local_swbox_x1 = 0.63;
const float local_swbox_x2 = 0.73;
const float local_swbox_y1 = 0.05;
const float local_swbox_y2 = 0.55;
const float logic_cell_x1 = 0.76;
const float logic_cell_x2 = 0.95;
const float logic_cell_y1 = 0.05;
const float logic_cell_y2 = 0.10;
const float logic_cell_pitch = 0.0625;
enum GfxTileWireId
{
TILE_WIRE_GLB2LOCAL_0,
TILE_WIRE_GLB2LOCAL_1,
TILE_WIRE_GLB2LOCAL_2,
TILE_WIRE_GLB2LOCAL_3,
TILE_WIRE_GLB_NETWK_0,
TILE_WIRE_GLB_NETWK_1,
TILE_WIRE_GLB_NETWK_2,
TILE_WIRE_GLB_NETWK_3,
TILE_WIRE_GLB_NETWK_4,
TILE_WIRE_GLB_NETWK_5,
TILE_WIRE_GLB_NETWK_6,
TILE_WIRE_GLB_NETWK_7,
TILE_WIRE_LOCAL_G0_0,
TILE_WIRE_LOCAL_G0_1,
TILE_WIRE_LOCAL_G0_2,
TILE_WIRE_LOCAL_G0_3,
TILE_WIRE_LOCAL_G0_4,
TILE_WIRE_LOCAL_G0_5,
TILE_WIRE_LOCAL_G0_6,
TILE_WIRE_LOCAL_G0_7,
TILE_WIRE_LOCAL_G1_0,
TILE_WIRE_LOCAL_G1_1,
TILE_WIRE_LOCAL_G1_2,
TILE_WIRE_LOCAL_G1_3,
TILE_WIRE_LOCAL_G1_4,
TILE_WIRE_LOCAL_G1_5,
TILE_WIRE_LOCAL_G1_6,
TILE_WIRE_LOCAL_G1_7,
TILE_WIRE_LOCAL_G2_0,
TILE_WIRE_LOCAL_G2_1,
TILE_WIRE_LOCAL_G2_2,
TILE_WIRE_LOCAL_G2_3,
TILE_WIRE_LOCAL_G2_4,
TILE_WIRE_LOCAL_G2_5,
TILE_WIRE_LOCAL_G2_6,
TILE_WIRE_LOCAL_G2_7,
TILE_WIRE_LOCAL_G3_0,
TILE_WIRE_LOCAL_G3_1,
TILE_WIRE_LOCAL_G3_2,
TILE_WIRE_LOCAL_G3_3,
TILE_WIRE_LOCAL_G3_4,
TILE_WIRE_LOCAL_G3_5,
TILE_WIRE_LOCAL_G3_6,
TILE_WIRE_LOCAL_G3_7,
TILE_WIRE_CARRY_IN,
TILE_WIRE_CARRY_IN_MUX,
TILE_WIRE_LUTFF_0_IN_0,
TILE_WIRE_LUTFF_0_IN_1,
TILE_WIRE_LUTFF_0_IN_2,
TILE_WIRE_LUTFF_0_IN_3,
TILE_WIRE_LUTFF_1_IN_0,
TILE_WIRE_LUTFF_1_IN_1,
TILE_WIRE_LUTFF_1_IN_2,
TILE_WIRE_LUTFF_1_IN_3,
TILE_WIRE_LUTFF_2_IN_0,
TILE_WIRE_LUTFF_2_IN_1,
TILE_WIRE_LUTFF_2_IN_2,
TILE_WIRE_LUTFF_2_IN_3,
TILE_WIRE_LUTFF_3_IN_0,
TILE_WIRE_LUTFF_3_IN_1,
TILE_WIRE_LUTFF_3_IN_2,
TILE_WIRE_LUTFF_3_IN_3,
TILE_WIRE_LUTFF_4_IN_0,
TILE_WIRE_LUTFF_4_IN_1,
TILE_WIRE_LUTFF_4_IN_2,
TILE_WIRE_LUTFF_4_IN_3,
TILE_WIRE_LUTFF_5_IN_0,
TILE_WIRE_LUTFF_5_IN_1,
TILE_WIRE_LUTFF_5_IN_2,
TILE_WIRE_LUTFF_5_IN_3,
TILE_WIRE_LUTFF_6_IN_0,
TILE_WIRE_LUTFF_6_IN_1,
TILE_WIRE_LUTFF_6_IN_2,
TILE_WIRE_LUTFF_6_IN_3,
TILE_WIRE_LUTFF_7_IN_0,
TILE_WIRE_LUTFF_7_IN_1,
TILE_WIRE_LUTFF_7_IN_2,
TILE_WIRE_LUTFF_7_IN_3,
TILE_WIRE_LUTFF_0_LOUT,
TILE_WIRE_LUTFF_1_LOUT,
TILE_WIRE_LUTFF_2_LOUT,
TILE_WIRE_LUTFF_3_LOUT,
TILE_WIRE_LUTFF_4_LOUT,
TILE_WIRE_LUTFF_5_LOUT,
TILE_WIRE_LUTFF_6_LOUT,
TILE_WIRE_LUTFF_0_OUT,
TILE_WIRE_LUTFF_1_OUT,
TILE_WIRE_LUTFF_2_OUT,
TILE_WIRE_LUTFF_3_OUT,
TILE_WIRE_LUTFF_4_OUT,
TILE_WIRE_LUTFF_5_OUT,
TILE_WIRE_LUTFF_6_OUT,
TILE_WIRE_LUTFF_7_OUT,
TILE_WIRE_LUTFF_0_COUT,
TILE_WIRE_LUTFF_1_COUT,
TILE_WIRE_LUTFF_2_COUT,
TILE_WIRE_LUTFF_3_COUT,
TILE_WIRE_LUTFF_4_COUT,
TILE_WIRE_LUTFF_5_COUT,
TILE_WIRE_LUTFF_6_COUT,
TILE_WIRE_LUTFF_7_COUT,
TILE_WIRE_LUTFF_GLOBAL_CEN,
TILE_WIRE_LUTFF_GLOBAL_CLK,
TILE_WIRE_LUTFF_GLOBAL_S_R,
TILE_WIRE_NEIGH_OP_BNL_0,
TILE_WIRE_NEIGH_OP_BNL_1,
TILE_WIRE_NEIGH_OP_BNL_2,
TILE_WIRE_NEIGH_OP_BNL_3,
TILE_WIRE_NEIGH_OP_BNL_4,
TILE_WIRE_NEIGH_OP_BNL_5,
TILE_WIRE_NEIGH_OP_BNL_6,
TILE_WIRE_NEIGH_OP_BNL_7,
TILE_WIRE_NEIGH_OP_BNR_0,
TILE_WIRE_NEIGH_OP_BNR_1,
TILE_WIRE_NEIGH_OP_BNR_2,
TILE_WIRE_NEIGH_OP_BNR_3,
TILE_WIRE_NEIGH_OP_BNR_4,
TILE_WIRE_NEIGH_OP_BNR_5,
TILE_WIRE_NEIGH_OP_BNR_6,
TILE_WIRE_NEIGH_OP_BNR_7,
TILE_WIRE_NEIGH_OP_BOT_0,
TILE_WIRE_NEIGH_OP_BOT_1,
TILE_WIRE_NEIGH_OP_BOT_2,
TILE_WIRE_NEIGH_OP_BOT_3,
TILE_WIRE_NEIGH_OP_BOT_4,
TILE_WIRE_NEIGH_OP_BOT_5,
TILE_WIRE_NEIGH_OP_BOT_6,
TILE_WIRE_NEIGH_OP_BOT_7,
TILE_WIRE_NEIGH_OP_LFT_0,
TILE_WIRE_NEIGH_OP_LFT_1,
TILE_WIRE_NEIGH_OP_LFT_2,
TILE_WIRE_NEIGH_OP_LFT_3,
TILE_WIRE_NEIGH_OP_LFT_4,
TILE_WIRE_NEIGH_OP_LFT_5,
TILE_WIRE_NEIGH_OP_LFT_6,
TILE_WIRE_NEIGH_OP_LFT_7,
TILE_WIRE_NEIGH_OP_RGT_0,
TILE_WIRE_NEIGH_OP_RGT_1,
TILE_WIRE_NEIGH_OP_RGT_2,
TILE_WIRE_NEIGH_OP_RGT_3,
TILE_WIRE_NEIGH_OP_RGT_4,
TILE_WIRE_NEIGH_OP_RGT_5,
TILE_WIRE_NEIGH_OP_RGT_6,
TILE_WIRE_NEIGH_OP_RGT_7,
TILE_WIRE_NEIGH_OP_TNL_0,
TILE_WIRE_NEIGH_OP_TNL_1,
TILE_WIRE_NEIGH_OP_TNL_2,
TILE_WIRE_NEIGH_OP_TNL_3,
TILE_WIRE_NEIGH_OP_TNL_4,
TILE_WIRE_NEIGH_OP_TNL_5,
TILE_WIRE_NEIGH_OP_TNL_6,
TILE_WIRE_NEIGH_OP_TNL_7,
TILE_WIRE_NEIGH_OP_TNR_0,
TILE_WIRE_NEIGH_OP_TNR_1,
TILE_WIRE_NEIGH_OP_TNR_2,
TILE_WIRE_NEIGH_OP_TNR_3,
TILE_WIRE_NEIGH_OP_TNR_4,
TILE_WIRE_NEIGH_OP_TNR_5,
TILE_WIRE_NEIGH_OP_TNR_6,
TILE_WIRE_NEIGH_OP_TNR_7,
TILE_WIRE_NEIGH_OP_TOP_0,
TILE_WIRE_NEIGH_OP_TOP_1,
TILE_WIRE_NEIGH_OP_TOP_2,
TILE_WIRE_NEIGH_OP_TOP_3,
TILE_WIRE_NEIGH_OP_TOP_4,
TILE_WIRE_NEIGH_OP_TOP_5,
TILE_WIRE_NEIGH_OP_TOP_6,
TILE_WIRE_NEIGH_OP_TOP_7,
TILE_WIRE_SP4_V_B_0,
TILE_WIRE_SP4_V_B_1,
TILE_WIRE_SP4_V_B_2,
TILE_WIRE_SP4_V_B_3,
TILE_WIRE_SP4_V_B_4,
TILE_WIRE_SP4_V_B_5,
TILE_WIRE_SP4_V_B_6,
TILE_WIRE_SP4_V_B_7,
TILE_WIRE_SP4_V_B_8,
TILE_WIRE_SP4_V_B_9,
TILE_WIRE_SP4_V_B_10,
TILE_WIRE_SP4_V_B_11,
TILE_WIRE_SP4_V_B_12,
TILE_WIRE_SP4_V_B_13,
TILE_WIRE_SP4_V_B_14,
TILE_WIRE_SP4_V_B_15,
TILE_WIRE_SP4_V_B_16,
TILE_WIRE_SP4_V_B_17,
TILE_WIRE_SP4_V_B_18,
TILE_WIRE_SP4_V_B_19,
TILE_WIRE_SP4_V_B_20,
TILE_WIRE_SP4_V_B_21,
TILE_WIRE_SP4_V_B_22,
TILE_WIRE_SP4_V_B_23,
TILE_WIRE_SP4_V_B_24,
TILE_WIRE_SP4_V_B_25,
TILE_WIRE_SP4_V_B_26,
TILE_WIRE_SP4_V_B_27,
TILE_WIRE_SP4_V_B_28,
TILE_WIRE_SP4_V_B_29,
TILE_WIRE_SP4_V_B_30,
TILE_WIRE_SP4_V_B_31,
TILE_WIRE_SP4_V_B_32,
TILE_WIRE_SP4_V_B_33,
TILE_WIRE_SP4_V_B_34,
TILE_WIRE_SP4_V_B_35,
TILE_WIRE_SP4_V_B_36,
TILE_WIRE_SP4_V_B_37,
TILE_WIRE_SP4_V_B_38,
TILE_WIRE_SP4_V_B_39,
TILE_WIRE_SP4_V_B_40,
TILE_WIRE_SP4_V_B_41,
TILE_WIRE_SP4_V_B_42,
TILE_WIRE_SP4_V_B_43,
TILE_WIRE_SP4_V_B_44,
TILE_WIRE_SP4_V_B_45,
TILE_WIRE_SP4_V_B_46,
TILE_WIRE_SP4_V_B_47,
TILE_WIRE_SP4_V_T_36,
TILE_WIRE_SP4_V_T_37,
TILE_WIRE_SP4_V_T_38,
TILE_WIRE_SP4_V_T_39,
TILE_WIRE_SP4_V_T_40,
TILE_WIRE_SP4_V_T_41,
TILE_WIRE_SP4_V_T_42,
TILE_WIRE_SP4_V_T_43,
TILE_WIRE_SP4_V_T_44,
TILE_WIRE_SP4_V_T_45,
TILE_WIRE_SP4_V_T_46,
TILE_WIRE_SP4_V_T_47,
TILE_WIRE_SP4_R_V_B_0,
TILE_WIRE_SP4_R_V_B_1,
TILE_WIRE_SP4_R_V_B_2,
TILE_WIRE_SP4_R_V_B_3,
TILE_WIRE_SP4_R_V_B_4,
TILE_WIRE_SP4_R_V_B_5,
TILE_WIRE_SP4_R_V_B_6,
TILE_WIRE_SP4_R_V_B_7,
TILE_WIRE_SP4_R_V_B_8,
TILE_WIRE_SP4_R_V_B_9,
TILE_WIRE_SP4_R_V_B_10,
TILE_WIRE_SP4_R_V_B_11,
TILE_WIRE_SP4_R_V_B_12,
TILE_WIRE_SP4_R_V_B_13,
TILE_WIRE_SP4_R_V_B_14,
TILE_WIRE_SP4_R_V_B_15,
TILE_WIRE_SP4_R_V_B_16,
TILE_WIRE_SP4_R_V_B_17,
TILE_WIRE_SP4_R_V_B_18,
TILE_WIRE_SP4_R_V_B_19,
TILE_WIRE_SP4_R_V_B_20,
TILE_WIRE_SP4_R_V_B_21,
TILE_WIRE_SP4_R_V_B_22,
TILE_WIRE_SP4_R_V_B_23,
TILE_WIRE_SP4_R_V_B_24,
TILE_WIRE_SP4_R_V_B_25,
TILE_WIRE_SP4_R_V_B_26,
TILE_WIRE_SP4_R_V_B_27,
TILE_WIRE_SP4_R_V_B_28,
TILE_WIRE_SP4_R_V_B_29,
TILE_WIRE_SP4_R_V_B_30,
TILE_WIRE_SP4_R_V_B_31,
TILE_WIRE_SP4_R_V_B_32,
TILE_WIRE_SP4_R_V_B_33,
TILE_WIRE_SP4_R_V_B_34,
TILE_WIRE_SP4_R_V_B_35,
TILE_WIRE_SP4_R_V_B_36,
TILE_WIRE_SP4_R_V_B_37,
TILE_WIRE_SP4_R_V_B_38,
TILE_WIRE_SP4_R_V_B_39,
TILE_WIRE_SP4_R_V_B_40,
TILE_WIRE_SP4_R_V_B_41,
TILE_WIRE_SP4_R_V_B_42,
TILE_WIRE_SP4_R_V_B_43,
TILE_WIRE_SP4_R_V_B_44,
TILE_WIRE_SP4_R_V_B_45,
TILE_WIRE_SP4_R_V_B_46,
TILE_WIRE_SP4_R_V_B_47,
TILE_WIRE_SP4_H_L_36,
TILE_WIRE_SP4_H_L_37,
TILE_WIRE_SP4_H_L_38,
TILE_WIRE_SP4_H_L_39,
TILE_WIRE_SP4_H_L_40,
TILE_WIRE_SP4_H_L_41,
TILE_WIRE_SP4_H_L_42,
TILE_WIRE_SP4_H_L_43,
TILE_WIRE_SP4_H_L_44,
TILE_WIRE_SP4_H_L_45,
TILE_WIRE_SP4_H_L_46,
TILE_WIRE_SP4_H_L_47,
TILE_WIRE_SP4_H_R_0,
TILE_WIRE_SP4_H_R_1,
TILE_WIRE_SP4_H_R_2,
TILE_WIRE_SP4_H_R_3,
TILE_WIRE_SP4_H_R_4,
TILE_WIRE_SP4_H_R_5,
TILE_WIRE_SP4_H_R_6,
TILE_WIRE_SP4_H_R_7,
TILE_WIRE_SP4_H_R_8,
TILE_WIRE_SP4_H_R_9,
TILE_WIRE_SP4_H_R_10,
TILE_WIRE_SP4_H_R_11,
TILE_WIRE_SP4_H_R_12,
TILE_WIRE_SP4_H_R_13,
TILE_WIRE_SP4_H_R_14,
TILE_WIRE_SP4_H_R_15,
TILE_WIRE_SP4_H_R_16,
TILE_WIRE_SP4_H_R_17,
TILE_WIRE_SP4_H_R_18,
TILE_WIRE_SP4_H_R_19,
TILE_WIRE_SP4_H_R_20,
TILE_WIRE_SP4_H_R_21,
TILE_WIRE_SP4_H_R_22,
TILE_WIRE_SP4_H_R_23,
TILE_WIRE_SP4_H_R_24,
TILE_WIRE_SP4_H_R_25,
TILE_WIRE_SP4_H_R_26,
TILE_WIRE_SP4_H_R_27,
TILE_WIRE_SP4_H_R_28,
TILE_WIRE_SP4_H_R_29,
TILE_WIRE_SP4_H_R_30,
TILE_WIRE_SP4_H_R_31,
TILE_WIRE_SP4_H_R_32,
TILE_WIRE_SP4_H_R_33,
TILE_WIRE_SP4_H_R_34,
TILE_WIRE_SP4_H_R_35,
TILE_WIRE_SP4_H_R_36,
TILE_WIRE_SP4_H_R_37,
TILE_WIRE_SP4_H_R_38,
TILE_WIRE_SP4_H_R_39,
TILE_WIRE_SP4_H_R_40,
TILE_WIRE_SP4_H_R_41,
TILE_WIRE_SP4_H_R_42,
TILE_WIRE_SP4_H_R_43,
TILE_WIRE_SP4_H_R_44,
TILE_WIRE_SP4_H_R_45,
TILE_WIRE_SP4_H_R_46,
TILE_WIRE_SP4_H_R_47,
TILE_WIRE_SP12_V_B_0,
TILE_WIRE_SP12_V_B_1,
TILE_WIRE_SP12_V_B_2,
TILE_WIRE_SP12_V_B_3,
TILE_WIRE_SP12_V_B_4,
TILE_WIRE_SP12_V_B_5,
TILE_WIRE_SP12_V_B_6,
TILE_WIRE_SP12_V_B_7,
TILE_WIRE_SP12_V_B_8,
TILE_WIRE_SP12_V_B_9,
TILE_WIRE_SP12_V_B_10,
TILE_WIRE_SP12_V_B_11,
TILE_WIRE_SP12_V_B_12,
TILE_WIRE_SP12_V_B_13,
TILE_WIRE_SP12_V_B_14,
TILE_WIRE_SP12_V_B_15,
TILE_WIRE_SP12_V_B_16,
TILE_WIRE_SP12_V_B_17,
TILE_WIRE_SP12_V_B_18,
TILE_WIRE_SP12_V_B_19,
TILE_WIRE_SP12_V_B_20,
TILE_WIRE_SP12_V_B_21,
TILE_WIRE_SP12_V_B_22,
TILE_WIRE_SP12_V_B_23,
TILE_WIRE_SP12_V_T_22,
TILE_WIRE_SP12_V_T_23,
TILE_WIRE_SP12_H_R_0,
TILE_WIRE_SP12_H_R_1,
TILE_WIRE_SP12_H_R_2,
TILE_WIRE_SP12_H_R_3,
TILE_WIRE_SP12_H_R_4,
TILE_WIRE_SP12_H_R_5,
TILE_WIRE_SP12_H_R_6,
TILE_WIRE_SP12_H_R_7,
TILE_WIRE_SP12_H_R_8,
TILE_WIRE_SP12_H_R_9,
TILE_WIRE_SP12_H_R_10,
TILE_WIRE_SP12_H_R_11,
TILE_WIRE_SP12_H_R_12,
TILE_WIRE_SP12_H_R_13,
TILE_WIRE_SP12_H_R_14,
TILE_WIRE_SP12_H_R_15,
TILE_WIRE_SP12_H_R_16,
TILE_WIRE_SP12_H_R_17,
TILE_WIRE_SP12_H_R_18,
TILE_WIRE_SP12_H_R_19,
TILE_WIRE_SP12_H_R_20,
TILE_WIRE_SP12_H_R_21,
TILE_WIRE_SP12_H_R_22,
TILE_WIRE_SP12_H_R_23,
TILE_WIRE_SP12_H_L_22,
TILE_WIRE_SP12_H_L_23
};
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
NEXTPNR_NAMESPACE_END
#endif // GFX_H

View File

@ -39,30 +39,30 @@
#include "jsonparse.h" #include "jsonparse.h"
#include "log.h" #include "log.h"
#include "nextpnr.h" #include "nextpnr.h"
#include "pack.h"
#include "pcf.h" #include "pcf.h"
#include "place_legaliser.h" #include "place_legaliser.h"
#include "place_sa.h"
#include "route.h"
#include "timing.h" #include "timing.h"
#include "version.h" #include "version.h"
USING_NEXTPNR_NAMESPACE USING_NEXTPNR_NAMESPACE
void svg_dump_el(const GraphicElement &el) void svg_dump_decal(const Context *ctx, const DecalXY &decal)
{ {
float scale = 10.0, offset = 10.0; const float scale = 10.0, offset = 10.0;
std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\""; const std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\"";
if (el.type == GraphicElement::G_BOX) { for (auto &el : ctx->getDecalGraphics(decal.decal)) {
std::cout << "<rect x=\"" << (offset + scale * el.x1) << "\" y=\"" << (offset + scale * el.y1) << "\" height=\"" if (el.type == GraphicElement::G_BOX) {
<< (scale * (el.y2 - el.y1)) << "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style std::cout << "<rect x=\"" << (offset + scale * (decal.x + el.x1)) << "\" y=\""
<< "/>\n"; << (offset + scale * (decal.y + el.y1)) << "\" height=\"" << (scale * (el.y2 - el.y1))
} << "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style << "/>\n";
}
if (el.type == GraphicElement::G_LINE) { if (el.type == GraphicElement::G_LINE) {
std::cout << "<line x1=\"" << (offset + scale * el.x1) << "\" y1=\"" << (offset + scale * el.y1) << "\" x2=\"" std::cout << "<line x1=\"" << (offset + scale * (decal.x + el.x1)) << "\" y1=\""
<< (offset + scale * el.x2) << "\" y2=\"" << (offset + scale * el.y2) << "\" " << style << "/>\n"; << (offset + scale * (decal.y + el.y1)) << "\" x2=\"" << (offset + scale * (decal.x + el.x2))
<< "\" y2=\"" << (offset + scale * (decal.y + el.y2)) << "\" " << style << "/>\n";
}
} }
} }
@ -98,12 +98,16 @@ int main(int argc, char *argv[])
options.add_options()("seed", po::value<int>(), "seed value for random number generator"); options.add_options()("seed", po::value<int>(), "seed value for random number generator");
options.add_options()("version,V", "show version"); options.add_options()("version,V", "show version");
options.add_options()("tmfuzz", "run path delay estimate fuzzer"); options.add_options()("tmfuzz", "run path delay estimate fuzzer");
#ifdef ICE40_HX1K_ONLY
options.add_options()("hx1k", "set device type to iCE40HX1K");
#else
options.add_options()("lp384", "set device type to iCE40LP384"); options.add_options()("lp384", "set device type to iCE40LP384");
options.add_options()("lp1k", "set device type to iCE40LP1K"); options.add_options()("lp1k", "set device type to iCE40LP1K");
options.add_options()("lp8k", "set device type to iCE40LP8K"); options.add_options()("lp8k", "set device type to iCE40LP8K");
options.add_options()("hx1k", "set device type to iCE40HX1K"); options.add_options()("hx1k", "set device type to iCE40HX1K");
options.add_options()("hx8k", "set device type to iCE40HX8K"); options.add_options()("hx8k", "set device type to iCE40HX8K");
options.add_options()("up5k", "set device type to iCE40UP5K"); options.add_options()("up5k", "set device type to iCE40UP5K");
#endif
options.add_options()("freq", po::value<double>(), "set target frequency for design in MHz"); options.add_options()("freq", po::value<double>(), "set target frequency for design in MHz");
options.add_options()("no-tmdriv", "disable timing-driven placement"); options.add_options()("no-tmdriv", "disable timing-driven placement");
options.add_options()("package", po::value<std::string>(), "set device package"); options.add_options()("package", po::value<std::string>(), "set device package");
@ -267,112 +271,126 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
Context ctx(chipArgs); std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(chipArgs));
if (vm.count("verbose")) { if (vm.count("verbose")) {
ctx.verbose = true; ctx->verbose = true;
} }
if (vm.count("debug")) { if (vm.count("debug")) {
ctx.verbose = true; ctx->verbose = true;
ctx.debug = true; ctx->debug = true;
} }
if (vm.count("force")) { if (vm.count("force")) {
ctx.force = true; ctx->force = true;
} }
if (vm.count("seed")) { if (vm.count("seed")) {
ctx.rngseed(vm["seed"].as<int>()); ctx->rngseed(vm["seed"].as<int>());
} }
if (vm.count("svg")) { if (vm.count("svg")) {
std::cout << "<svg xmlns=\"http://www.w3.org/2000/svg\" " std::cout << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
"xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"; "xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
for (auto bel : ctx.getBels()) { for (auto bel : ctx->getBels()) {
std::cout << "<!-- " << ctx.getBelName(bel).str(&ctx) << " -->\n"; std::cout << "<!-- " << ctx->getBelName(bel).str(ctx.get()) << " -->\n";
for (auto &el : ctx.getBelGraphics(bel)) svg_dump_decal(ctx.get(), ctx->getBelDecal(bel));
svg_dump_el(el);
} }
std::cout << "<!-- Frame -->\n"; std::cout << "<!-- Frame -->\n";
for (auto &el : ctx.getFrameGraphics()) svg_dump_decal(ctx.get(), ctx->getFrameDecal());
svg_dump_el(el);
std::cout << "</svg>\n"; std::cout << "</svg>\n";
} }
if (vm.count("tmfuzz")) { if (vm.count("tmfuzz")) {
std::vector<WireId> src_wires, dst_wires; std::vector<WireId> src_wires, dst_wires;
/*for (auto w : ctx.getWires()) /*for (auto w : ctx->getWires())
src_wires.push_back(w);*/ src_wires.push_back(w);*/
for (auto b : ctx.getBels()) { for (auto b : ctx->getBels()) {
if (ctx.getBelType(b) == TYPE_ICESTORM_LC) { if (ctx->getBelType(b) == TYPE_ICESTORM_LC) {
src_wires.push_back(ctx.getWireBelPin(b, PIN_O)); src_wires.push_back(ctx->getWireBelPin(b, PIN_O));
} }
if (ctx.getBelType(b) == TYPE_SB_IO) { if (ctx->getBelType(b) == TYPE_SB_IO) {
src_wires.push_back(ctx.getWireBelPin(b, PIN_D_IN_0)); src_wires.push_back(ctx->getWireBelPin(b, PIN_D_IN_0));
} }
} }
for (auto b : ctx.getBels()) { for (auto b : ctx->getBels()) {
if (ctx.getBelType(b) == TYPE_ICESTORM_LC) { if (ctx->getBelType(b) == TYPE_ICESTORM_LC) {
dst_wires.push_back(ctx.getWireBelPin(b, PIN_I0)); dst_wires.push_back(ctx->getWireBelPin(b, PIN_I0));
dst_wires.push_back(ctx.getWireBelPin(b, PIN_I1)); dst_wires.push_back(ctx->getWireBelPin(b, PIN_I1));
dst_wires.push_back(ctx.getWireBelPin(b, PIN_I2)); dst_wires.push_back(ctx->getWireBelPin(b, PIN_I2));
dst_wires.push_back(ctx.getWireBelPin(b, PIN_I3)); dst_wires.push_back(ctx->getWireBelPin(b, PIN_I3));
dst_wires.push_back(ctx.getWireBelPin(b, PIN_CEN)); dst_wires.push_back(ctx->getWireBelPin(b, PIN_CEN));
dst_wires.push_back(ctx.getWireBelPin(b, PIN_CIN)); dst_wires.push_back(ctx->getWireBelPin(b, PIN_CIN));
} }
if (ctx.getBelType(b) == TYPE_SB_IO) { if (ctx->getBelType(b) == TYPE_SB_IO) {
dst_wires.push_back(ctx.getWireBelPin(b, PIN_D_OUT_0)); dst_wires.push_back(ctx->getWireBelPin(b, PIN_D_OUT_0));
dst_wires.push_back(ctx.getWireBelPin(b, PIN_OUTPUT_ENABLE)); dst_wires.push_back(ctx->getWireBelPin(b, PIN_OUTPUT_ENABLE));
} }
} }
ctx.shuffle(src_wires); ctx->shuffle(src_wires);
ctx.shuffle(dst_wires); ctx->shuffle(dst_wires);
for (int i = 0; i < int(src_wires.size()) && i < int(dst_wires.size()); i++) { for (int i = 0; i < int(src_wires.size()) && i < int(dst_wires.size()); i++) {
delay_t actual_delay; delay_t actual_delay;
WireId src = src_wires[i], dst = dst_wires[i]; WireId src = src_wires[i], dst = dst_wires[i];
if (!get_actual_route_delay(&ctx, src, dst, actual_delay)) if (!ctx->getActualRouteDelay(src, dst, actual_delay))
continue; continue;
printf("%s %s %.3f %.3f %d %d %d %d %d %d\n", ctx.getWireName(src).c_str(&ctx), printf("%s %s %.3f %.3f %d %d %d %d %d %d\n", ctx->getWireName(src).c_str(ctx.get()),
ctx.getWireName(dst).c_str(&ctx), ctx.getDelayNS(actual_delay), ctx->getWireName(dst).c_str(ctx.get()), ctx->getDelayNS(actual_delay),
ctx.getDelayNS(ctx.estimateDelay(src, dst)), ctx.chip_info->wire_data[src.index].x, ctx->getDelayNS(ctx->estimateDelay(src, dst)), ctx->chip_info->wire_data[src.index].x,
ctx.chip_info->wire_data[src.index].y, ctx.chip_info->wire_data[src.index].type, ctx->chip_info->wire_data[src.index].y, ctx->chip_info->wire_data[src.index].type,
ctx.chip_info->wire_data[dst.index].x, ctx.chip_info->wire_data[dst.index].y, ctx->chip_info->wire_data[dst.index].x, ctx->chip_info->wire_data[dst.index].y,
ctx.chip_info->wire_data[dst.index].type); ctx->chip_info->wire_data[dst.index].type);
} }
} }
if (vm.count("freq"))
ctx->target_freq = vm["freq"].as<double>() * 1e6;
ctx->timing_driven = true;
if (vm.count("no-tmdriv"))
ctx->timing_driven = false;
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv);
MainWindow w(std::move(ctx), chipArgs);
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::string pcf = "";
if (vm.count("pcf"))
pcf = vm["pcf"].as<std::string>();
w.load_json(filename, pcf);
}
w.show();
return a.exec();
}
#endif
if (vm.count("json")) { if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>(); std::string filename = vm["json"].as<std::string>();
std::ifstream f(filename); std::ifstream f(filename);
if (!parse_json_file(f, filename, &ctx)) if (!parse_json_file(f, filename, ctx.get()))
log_error("Loading design failed.\n"); log_error("Loading design failed.\n");
if (vm.count("pcf")) { if (vm.count("pcf")) {
std::ifstream pcf(vm["pcf"].as<std::string>()); std::ifstream pcf(vm["pcf"].as<std::string>());
if (!apply_pcf(&ctx, pcf)) if (!apply_pcf(ctx.get(), pcf))
log_error("Loading PCF failed.\n"); log_error("Loading PCF failed.\n");
} }
if (!pack_design(&ctx) && !ctx.force) if (!ctx->pack() && !ctx->force)
log_error("Packing design failed.\n"); log_error("Packing design failed.\n");
if (vm.count("freq")) assign_budget(ctx.get());
ctx.target_freq = vm["freq"].as<double>() * 1e6; ctx->check();
assign_budget(&ctx); print_utilisation(ctx.get());
ctx.check();
print_utilisation(&ctx);
ctx.timing_driven = true;
if (vm.count("no-tmdriv"))
ctx.timing_driven = false;
if (!vm.count("pack-only")) { if (!vm.count("pack-only")) {
if (!place_design_sa(&ctx) && !ctx.force) if (!ctx->place() && !ctx->force)
log_error("Placing design failed.\n"); log_error("Placing design failed.\n");
ctx.check(); ctx->check();
if (!route_design(&ctx) && !ctx.force) if (!ctx->route() && !ctx->force)
log_error("Routing design failed.\n"); log_error("Routing design failed.\n");
} }
} }
@ -380,13 +398,13 @@ int main(int argc, char *argv[])
if (vm.count("asc")) { if (vm.count("asc")) {
std::string filename = vm["asc"].as<std::string>(); std::string filename = vm["asc"].as<std::string>();
std::ofstream f(filename); std::ofstream f(filename);
write_asc(&ctx, f); write_asc(ctx.get(), f);
} }
#ifndef NO_PYTHON #ifndef NO_PYTHON
if (vm.count("run")) { if (vm.count("run")) {
init_python(argv[0], true); init_python(argv[0], true);
python_export_global("ctx", ctx); python_export_global("ctx", *ctx.get());
std::vector<std::string> files = vm["run"].as<std::vector<std::string>>(); std::vector<std::string> files = vm["run"].as<std::vector<std::string>>();
for (auto filename : files) for (auto filename : files)
@ -395,16 +413,6 @@ int main(int argc, char *argv[])
deinit_python(); deinit_python();
} }
#endif #endif
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv);
MainWindow w;
w.show();
rc = a.exec();
}
#endif
return rc; return rc;
} catch (log_execution_error_exception) { } catch (log_execution_error_exception) {
#if defined(_MSC_VER) #if defined(_MSC_VER)

View File

@ -18,7 +18,6 @@
* *
*/ */
#include "pack.h"
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <unordered_set> #include <unordered_set>
@ -577,8 +576,9 @@ static void pack_special(Context *ctx)
} }
// Main pack function // Main pack function
bool pack_design(Context *ctx) bool Arch::pack()
{ {
Context *ctx = getCtx();
try { try {
log_break(); log_break();
pack_constants(ctx); pack_constants(ctx);