Merge branch 'master' into 'master'
Master See merge request eddiehung/nextpnr!1
This commit is contained in:
commit
0bae7f5606
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
/objs/
|
||||
/nextpnr-generic*
|
||||
/nextpnr-ice40*
|
||||
/nextpnr-ecp5*
|
||||
cmake-build-*/
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
|
@ -74,6 +74,7 @@ public:
|
||||
QtProperty *indexToProperty(const QModelIndex &index) const;
|
||||
QTreeWidgetItem *indexToItem(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;
|
||||
void disableItem(QTreeWidgetItem *item) const;
|
||||
void enableItem(QTreeWidgetItem *item) const;
|
||||
@ -1068,6 +1069,16 @@ void QtTreePropertyBrowser::editItem(QtBrowserItem *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
|
||||
QT_END_NAMESPACE
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@
|
||||
QT_BEGIN_NAMESPACE
|
||||
#endif
|
||||
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QtTreePropertyBrowserPrivate;
|
||||
|
||||
@ -107,6 +108,9 @@ public:
|
||||
|
||||
void editItem(QtBrowserItem *item);
|
||||
|
||||
//ADDED:miodrag
|
||||
QTreeWidget *treeWidget() const;
|
||||
QtBrowserItem *itemToBrowserItem(QTreeWidgetItem *item);
|
||||
Q_SIGNALS:
|
||||
|
||||
void collapsed(QtBrowserItem *item);
|
||||
|
322
3rdparty/python-console/modified/pyconsole.cc
vendored
322
3rdparty/python-console/modified/pyconsole.cc
vendored
@ -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( );
|
||||
}
|
||||
}
|
||||
}
|
80
3rdparty/python-console/modified/pyconsole.h
vendored
80
3rdparty/python-console/modified/pyconsole.h
vendored
@ -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
|
@ -7,7 +7,31 @@ option(BUILD_PYTHON "Build Python Integration" ON)
|
||||
option(BUILD_TESTS "Build GUI" OFF)
|
||||
|
||||
# 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)
|
||||
if (MSVC)
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||
@ -30,6 +54,10 @@ find_package(Sanitizers)
|
||||
# List of Boost libraries to include
|
||||
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)
|
||||
# TODO: sensible minimum Python version
|
||||
find_package(PythonInterp 3.5 REQUIRED)
|
||||
@ -62,7 +90,7 @@ if (BUILD_TESTS)
|
||||
endif()
|
||||
|
||||
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()
|
||||
|
||||
add_definitions("-DGIT_COMMIT_HASH=${GIT_COMMIT_HASH}")
|
||||
@ -109,6 +137,13 @@ if (BUILD_PYTHON)
|
||||
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)
|
||||
STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING})
|
||||
find_package(Boost COMPONENTS python-${gentoo_version} ${boost_libs})
|
||||
@ -132,22 +167,23 @@ if(MINGW)
|
||||
add_definitions("-Wa,-mbig-obj")
|
||||
endif(MINGW)
|
||||
|
||||
foreach (family ${FAMILIES})
|
||||
foreach (family ${ARCH})
|
||||
message(STATUS "Configuring architecture : ${family}")
|
||||
string(TOUPPER ${family} ufamily)
|
||||
aux_source_directory(${family}/ ${ufamily}_FILES)
|
||||
|
||||
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()
|
||||
|
||||
# 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)
|
||||
target_compile_definitions(nextpnr-${family} PRIVATE MAIN_EXECUTABLE)
|
||||
|
||||
if (BUILD_PYTHON)
|
||||
# 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()
|
||||
|
||||
# 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
|
||||
target_include_directories(${target} PRIVATE ${family}/ ${CMAKE_CURRENT_BINARY_DIR}/generated/)
|
||||
target_compile_definitions(${target} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family})
|
||||
target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES})
|
||||
target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES} pthread)
|
||||
add_sanitizers(${target})
|
||||
if (BUILD_GUI)
|
||||
target_include_directories(${target} PRIVATE gui/${family}/ gui/)
|
||||
@ -193,6 +229,7 @@ endforeach (family)
|
||||
|
||||
file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h)
|
||||
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 "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||
|
||||
|
42
README.md
42
README.md
@ -5,6 +5,7 @@ Supported Architectures
|
||||
-----------------------
|
||||
|
||||
- iCE40
|
||||
- ECP5
|
||||
|
||||
Prequisites
|
||||
-----------
|
||||
@ -23,23 +24,30 @@ Prequisites
|
||||
- For building on macOS, brew utility is needed.
|
||||
- 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`
|
||||
|
||||
- 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
|
||||
--------
|
||||
|
||||
- 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)
|
||||
- For a debug build, run `cmake -DCMAKE_BUILD_TYPE=Debug .`
|
||||
- For a debug build with HX1K support only, run ` cmake -DCMAKE_BUILD_TYPE=Debug -DICE40_HX1K_ONLY=1 .`
|
||||
- For a release build, run `cmake .`
|
||||
- For an iCE40 debug build, run `cmake -DARCH=ice40 -DCMAKE_BUILD_TYPE=Debug .`
|
||||
- For an iCE40 debug build with HX1K support only, run `cmake -DARCH=ice40 -DCMAKE_BUILD_TYPE=Debug -DICE40_HX1K_ONLY=1 .`
|
||||
- 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`
|
||||
- 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 ECP5 support, you must also specify the path to Project Trellis using `-DTRELLIS_ROOT=/path/trellis`
|
||||
- Use Make to run the build itself
|
||||
- For all binary targets, just run `make`
|
||||
- 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 GUI, run `cmake -DBUILD_GUI=OFF .`
|
||||
- For minimal binary without Python and GUI, run `cmake -DBUILD_PYTHON=OFF -DBUILD_GUI=OFF .`
|
||||
- To build binary without Python support, use `-DBUILD_PYTHON=OFF`
|
||||
- To build binary without GUI, use `-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`
|
||||
- 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`
|
||||
@ -47,12 +55,12 @@ Building
|
||||
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 use code sanitizers use:
|
||||
- `cmake . -DSANITIZE_ADDRESS=ON`
|
||||
- `cmake . -DSANITIZE_MEMORY=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++`
|
||||
- `cmake . -DSANITIZE_THREAD=ON`
|
||||
- `cmake . -DSANITIZE_UNDEFINED=ON`
|
||||
- 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 the `cmake` options:
|
||||
- `-DSANITIZE_ADDRESS=ON`
|
||||
- `-DSANITIZE_MEMORY=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++`
|
||||
- `-DSANITIZE_THREAD=ON`
|
||||
- `-DSANITIZE_UNDEFINED=ON`
|
||||
- Running valgrind example `valgrind --leak-check=yes --tool=memcheck ./nextpnr-ice40 --json ice40/blinky.json`
|
||||
|
||||
Running
|
||||
@ -65,6 +73,12 @@ Running
|
||||
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`
|
||||
|
||||
- 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
|
||||
-------
|
||||
|
||||
|
@ -136,7 +136,7 @@ NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct GraphicElement
|
||||
{
|
||||
enum
|
||||
enum type_t
|
||||
{
|
||||
G_NONE,
|
||||
G_LINE,
|
||||
@ -145,6 +145,14 @@ struct GraphicElement
|
||||
G_LABEL
|
||||
} 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;
|
||||
std::string text;
|
||||
};
|
||||
@ -155,6 +163,12 @@ NEXTPNR_NAMESPACE_END
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct DecalXY
|
||||
{
|
||||
DecalId decal;
|
||||
float x = 0, y = 0;
|
||||
};
|
||||
|
||||
struct BelPin
|
||||
{
|
||||
BelId bel;
|
||||
@ -253,6 +267,31 @@ struct BaseCtx
|
||||
delete idstring_str_to_idx;
|
||||
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
|
||||
@ -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 rng64()
|
||||
|
@ -21,7 +21,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "place_sa.h"
|
||||
#include "placer1.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
@ -429,7 +429,7 @@ class SAPlacer
|
||||
const float post_legalise_dia_scale = 2;
|
||||
};
|
||||
|
||||
bool place_design_sa(Context *ctx)
|
||||
bool placer1(Context *ctx)
|
||||
{
|
||||
try {
|
||||
SAPlacer placer(ctx);
|
@ -23,7 +23,7 @@
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
extern bool place_design_sa(Context *ctx);
|
||||
extern bool placer1(Context *ctx);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -110,13 +110,13 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
|
||||
readwrite_wrapper<CellInfo &, decltype(&CellInfo::belStrength), &CellInfo::belStrength, pass_through<PlaceStrength>,
|
||||
pass_through<PlaceStrength>>::def_wrap(ci_cls, "belStrength");
|
||||
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);
|
||||
readwrite_wrapper<PortInfo &, decltype(&PortInfo::name), &PortInfo::name, conv_to_str<IdString>,
|
||||
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,
|
||||
"net");
|
||||
"net");
|
||||
readwrite_wrapper<PortInfo &, decltype(&PortInfo::type), &PortInfo::type, pass_through<PortType>,
|
||||
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(
|
||||
ni_cls, "users");
|
||||
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);
|
||||
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>,
|
||||
conv_from_str<IdString>>::def_wrap(pr_cls, "port");
|
||||
readwrite_wrapper<PortRef &, decltype(&PortRef::budget), &PortRef::budget, pass_through<delay_t>,
|
||||
@ -151,7 +151,9 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
|
||||
arch_wrap_python();
|
||||
}
|
||||
|
||||
#ifdef MAIN_EXECUTABLE
|
||||
static wchar_t *program;
|
||||
#endif
|
||||
|
||||
void init_python(const char *executable, bool first)
|
||||
{
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <queue>
|
||||
|
||||
#include "log.h"
|
||||
#include "route.h"
|
||||
#include "router1.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -402,7 +402,7 @@ struct Router
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
bool route_design(Context *ctx)
|
||||
bool router1(Context *ctx)
|
||||
{
|
||||
try {
|
||||
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;
|
||||
Router router(ctx, scores, src_wire, dst_wire);
|
||||
Router router(this, scores, src_wire, dst_wire);
|
||||
if (router.routedOkay)
|
||||
delay = router.visited.at(dst_wire).delay;
|
||||
return router.routedOkay;
|
@ -2,7 +2,6 @@
|
||||
* 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
|
||||
@ -18,15 +17,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PACK_H
|
||||
#define PACK_H
|
||||
#ifndef ROUTER1_H
|
||||
#define ROUTER1_H
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
bool pack_design(Context *ctx);
|
||||
extern bool router1(Context *ctx);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // ROUTE_H
|
||||
#endif // ROUTER1_H
|
@ -39,6 +39,18 @@ ValueType get_or_default(const Container &ct, const KeyType &key, ValueType def
|
||||
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
|
||||
// default if value is not found
|
||||
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
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
2
ecp5/.gitignore
vendored
Normal file
2
ecp5/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
__pycache__
|
||||
chipdbs/
|
333
ecp5/arch.cc
Normal file
333
ecp5/arch.cc
Normal 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
765
ecp5/arch.h
Normal 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
|
@ -2,6 +2,7 @@
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||
* Copyright (C) 2018 David Shah <dave@ds0.me>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@ -17,16 +18,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ROUTE_H
|
||||
#define ROUTE_H
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "nextpnr.h"
|
||||
#include "pybindings.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
extern bool route_design(Context *ctx);
|
||||
extern bool get_actual_route_delay(Context *ctx, WireId src_wire, WireId dst_wire, delay_t &delay);
|
||||
void arch_wrap_python() {}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // ROUTE_H
|
||||
#endif
|
75
ecp5/arch_pybindings.h
Normal file
75
ecp5/arch_pybindings.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef ARCH_PYBINDINGS_H
|
||||
#define ARCH_PYBINDINGS_H
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "nextpnr.h"
|
||||
#include "pybindings.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace PythonConversion {
|
||||
|
||||
template <> struct string_converter<BelId>
|
||||
{
|
||||
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, BelId id)
|
||||
{
|
||||
if (id == BelId())
|
||||
throw bad_wrap();
|
||||
return ctx->getBelName(id).str(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct string_converter<BelType>
|
||||
{
|
||||
BelType from_str(Context *ctx, std::string name) { return ctx->belTypeFromId(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, BelType typ) { return ctx->belTypeToId(typ).str(ctx); }
|
||||
};
|
||||
|
||||
template <> struct string_converter<WireId>
|
||||
{
|
||||
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
|
||||
};
|
||||
|
||||
template <> struct string_converter<PipId>
|
||||
{
|
||||
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); }
|
||||
};
|
||||
|
||||
template <> struct string_converter<PortPin>
|
||||
{
|
||||
PortPin from_str(Context *ctx, std::string name) { return ctx->portPinFromId(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, PortPin id) { return ctx->portPinToId(id).str(ctx); }
|
||||
};
|
||||
|
||||
} // namespace PythonConversion
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
||||
#endif
|
202
ecp5/archdefs.h
Normal file
202
ecp5/archdefs.h
Normal 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
257
ecp5/bitstream.cc
Normal 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
32
ecp5/bitstream.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BITSTREAM_H
|
||||
#define BITSTREAM_H
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void write_bitstream(Context *ctx, std::string base_config_file = "", std::string text_config_file = "",
|
||||
std::string bitstream_file = "");
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // BITSTREAM_H
|
60
ecp5/family.cmake
Normal file
60
ecp5/family.cmake
Normal 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
199
ecp5/main.cc
Normal 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
99
ecp5/pack.cc
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <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
26
ecp5/place_legaliser.cc
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "place_legaliser.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
bool legalise_design(Context *ctx) { return true; }
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
31
ecp5/place_legaliser.h
Normal file
31
ecp5/place_legaliser.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PLACE_LEGALISER_H
|
||||
#define PLACE_LEGALISER_H
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
bool legalise_design(Context *ctx);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
46
ecp5/portpins.inc
Normal file
46
ecp5/portpins.inc
Normal 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
1
ecp5/synth/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.bit
|
77
ecp5/synth/blinky.v
Normal file
77
ecp5/synth/blinky.v
Normal file
@ -0,0 +1,77 @@
|
||||
module top(input clk_pin, input btn_pin, output [3:0] led_pin, output gpio0_pin);
|
||||
|
||||
wire clk;
|
||||
wire [7:0] led;
|
||||
wire btn;
|
||||
wire gpio0;
|
||||
|
||||
(* BEL="X0/Y35/PIOA" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk));
|
||||
|
||||
(* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn));
|
||||
|
||||
(* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0]));
|
||||
(* BEL="X0/Y23/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1]));
|
||||
(* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2]));
|
||||
(* BEL="X0/Y26/PIOC" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3]));
|
||||
|
||||
(* BEL="X0/Y26/PIOB" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4]));
|
||||
(* BEL="X0/Y32/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5]));
|
||||
(* BEL="X0/Y26/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6]));
|
||||
(* BEL="X0/Y29/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7]));
|
||||
|
||||
|
||||
(* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
|
||||
|
||||
localparam ctr_width = 24;
|
||||
localparam ctr_max = 2**ctr_width - 1;
|
||||
reg [ctr_width-1:0] ctr = 0;
|
||||
reg [9:0] pwm_ctr = 0;
|
||||
reg dir = 0;
|
||||
|
||||
always@(posedge clk) begin
|
||||
ctr <= btn ? ctr : (dir ? ctr - 1'b1 : ctr + 1'b1);
|
||||
if (ctr[ctr_width-1 : ctr_width-3] == 0 && dir == 1)
|
||||
dir <= 1'b0;
|
||||
else if (ctr[ctr_width-1 : ctr_width-3] == 7 && dir == 0)
|
||||
dir <= 1'b1;
|
||||
pwm_ctr <= pwm_ctr + 1'b1;
|
||||
end
|
||||
|
||||
reg [9:0] brightness [0:7];
|
||||
localparam bright_max = 2**10 - 1;
|
||||
reg [7:0] led_reg;
|
||||
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < 8; i=i+1) begin
|
||||
always @ (posedge clk) begin
|
||||
if (ctr[ctr_width-1 : ctr_width-3] == i)
|
||||
brightness[i] <= bright_max;
|
||||
else if (ctr[ctr_width-1 : ctr_width-3] == (i - 1))
|
||||
brightness[i] <= ctr[ctr_width-4:ctr_width-13];
|
||||
else if (ctr[ctr_width-1 : ctr_width-3] == (i + 1))
|
||||
brightness[i] <= bright_max - ctr[ctr_width-4:ctr_width-13];
|
||||
else
|
||||
brightness[i] <= 0;
|
||||
led_reg[i] <= pwm_ctr < brightness[i];
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign led = led_reg;
|
||||
|
||||
// Tie GPIO0, keep board from rebooting
|
||||
TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
|
||||
|
||||
endmodule
|
9
ecp5/synth/blinky.ys
Normal file
9
ecp5/synth/blinky.ys
Normal file
@ -0,0 +1,9 @@
|
||||
read_verilog blinky.v
|
||||
read_verilog -lib cells.v
|
||||
synth -top top
|
||||
abc -lut 4
|
||||
techmap -map simple_map.v
|
||||
splitnets
|
||||
opt_clean
|
||||
stat
|
||||
write_json blinky.json
|
49
ecp5/synth/cells.v
Normal file
49
ecp5/synth/cells.v
Normal 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
68
ecp5/synth/simple_map.v
Normal file
@ -0,0 +1,68 @@
|
||||
module \$_DFF_P_ (input D, C, output Q);
|
||||
TRELLIS_SLICE #(
|
||||
.MODE("LOGIC"),
|
||||
.CLKMUX("CLK"),
|
||||
.CEMUX("1"),
|
||||
.REG0_SD("0"),
|
||||
.REG0_REGSET("RESET"),
|
||||
.SRMODE("LSR_OVER_CE"),
|
||||
.GSR("DISABLED")
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.CLK(C),
|
||||
.M0(D),
|
||||
.Q0(Q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module \$lut (A, Y);
|
||||
parameter WIDTH = 0;
|
||||
parameter LUT = 0;
|
||||
|
||||
input [WIDTH-1:0] A;
|
||||
output Y;
|
||||
|
||||
generate
|
||||
if (WIDTH == 1) begin
|
||||
TRELLIS_SLICE #(
|
||||
.MODE("LOGIC"),
|
||||
.LUT0_INITVAL({8{LUT[1:0]}})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.A0(A[0]),
|
||||
.F0(Y)
|
||||
);
|
||||
end
|
||||
if (WIDTH == 2) begin
|
||||
TRELLIS_SLICE #(
|
||||
.MODE("LOGIC"),
|
||||
.LUT0_INITVAL({4{LUT[3:0]}})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.A0(A[0]),
|
||||
.B0(A[1]),
|
||||
.F0(Y)
|
||||
);
|
||||
end
|
||||
if (WIDTH == 3) begin
|
||||
TRELLIS_SLICE #(
|
||||
.MODE("LOGIC"),
|
||||
.LUT0_INITVAL({2{LUT[7:0]}})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.A0(A[0]),
|
||||
.B0(A[1]),
|
||||
.C0(A[2]),
|
||||
.F0(Y)
|
||||
);
|
||||
end
|
||||
if (WIDTH == 4) begin
|
||||
TRELLIS_SLICE #(
|
||||
.MODE("LOGIC"),
|
||||
.LUT0_INITVAL(LUT)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.A0(A[0]),
|
||||
.B0(A[1]),
|
||||
.C0(A[2]),
|
||||
.D0(A[3]),
|
||||
.F0(Y)
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
18
ecp5/synth/ulx3s.v
Normal file
18
ecp5/synth/ulx3s.v
Normal file
@ -0,0 +1,18 @@
|
||||
module top(input a_pin, output led_pin, output led2_pin, output gpio0_pin);
|
||||
|
||||
wire a;
|
||||
wire led, led2;
|
||||
wire gpio0;
|
||||
(* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a));
|
||||
(* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led));
|
||||
(* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led2_buf (.B(led2_pin), .I(led2));
|
||||
(* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
|
||||
assign led = a;
|
||||
assign led2 = !a;
|
||||
|
||||
TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
|
||||
endmodule
|
9
ecp5/synth/ulx3s.ys
Normal file
9
ecp5/synth/ulx3s.ys
Normal file
@ -0,0 +1,9 @@
|
||||
read_verilog ulx3s.v
|
||||
read_verilog -lib cells.v
|
||||
synth -top top
|
||||
abc -lut 4
|
||||
techmap -map simple_map.v
|
||||
splitnets
|
||||
opt_clean
|
||||
stat
|
||||
write_json ulx3s.json
|
439
ecp5/synth/ulx3s_empty.config
Normal file
439
ecp5/synth/ulx3s_empty.config
Normal file
@ -0,0 +1,439 @@
|
||||
.device LFE5U-45F
|
||||
|
||||
.tile CIB_R10C3:PVT_COUNT2
|
||||
unknown: F2B0
|
||||
unknown: F3B0
|
||||
unknown: F5B0
|
||||
unknown: F11B0
|
||||
unknown: F13B0
|
||||
|
||||
.tile CIB_R5C1:CIB_PLL1
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
|
||||
|
||||
.tile CIB_R5C89:CIB_PLL1
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C3:CIB_PLL3
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C42:VCIB_DCU0
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C43:VCIB_DCUA
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C44:VCIB_DCUB
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C45:VCIB_DCUC
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C46:VCIB_DCUD
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C47:VCIB_DCUF
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C48:VCIB_DCU3
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C49:VCIB_DCU2
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C50:VCIB_DCUG
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C51:VCIB_DCUH
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C52:VCIB_DCUI
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C53:VCIB_DCU1
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C69:VCIB_DCU0
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C6:CIB_EFB0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C70:VCIB_DCUA
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C71:VCIB_DCUB
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C72:VCIB_DCUC
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C73:VCIB_DCUD
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C74:VCIB_DCUF
|
||||
enum: CIB.JA1MUX 0
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC2MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C75:VCIB_DCU3
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA7MUX 0
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JC0MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC6MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C76:VCIB_DCU2
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C77:VCIB_DCUG
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C78:VCIB_DCUH
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C79:VCIB_DCUI
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB7MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD6MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C7:CIB_EFB1
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JA4MUX 0
|
||||
enum: CIB.JA5MUX 0
|
||||
enum: CIB.JA6MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB4MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JB6MUX 0
|
||||
enum: CIB.JC3MUX 0
|
||||
enum: CIB.JC4MUX 0
|
||||
enum: CIB.JC5MUX 0
|
||||
enum: CIB.JD3MUX 0
|
||||
enum: CIB.JD4MUX 0
|
||||
enum: CIB.JD5MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C80:VCIB_DCU1
|
||||
enum: CIB.JB1MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
enum: CIB.JB5MUX 0
|
||||
enum: CIB.JD0MUX 0
|
||||
enum: CIB.JD2MUX 0
|
||||
|
||||
|
||||
.tile CIB_R70C87:CIB_PLL3
|
||||
enum: CIB.JA3MUX 0
|
||||
enum: CIB.JB3MUX 0
|
||||
|
||||
|
||||
.tile MIB_R10C40:CMUX_UL_0
|
||||
arc: G_DCS0CLK0 G_VPFN0000
|
||||
|
||||
|
||||
.tile MIB_R10C41:CMUX_UR_0
|
||||
arc: G_DCS0CLK1 G_VPFN0000
|
||||
|
||||
|
||||
.tile MIB_R58C40:CMUX_LL_0
|
||||
arc: G_DCS1CLK0 G_VPFN0000
|
||||
|
||||
|
||||
.tile MIB_R58C41:CMUX_LR_0
|
||||
arc: G_DCS1CLK1 G_VPFN0000
|
||||
|
||||
|
||||
.tile MIB_R71C4:EFB0_PICB0
|
||||
unknown: F54B1
|
||||
unknown: F56B1
|
||||
unknown: F82B1
|
||||
unknown: F94B1
|
||||
|
||||
.tile MIB_R71C3:BANKREF8
|
||||
unknown: F18B0
|
||||
|
11
ecp5/synth/wire.v
Normal file
11
ecp5/synth/wire.v
Normal file
@ -0,0 +1,11 @@
|
||||
module top(input a_pin, output [3:0] led_pin);
|
||||
|
||||
wire a;
|
||||
wire [3:0] led;
|
||||
|
||||
TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a));
|
||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf [3:0] (.B(led_pin), .I(led));
|
||||
|
||||
//assign led[0] = !a;
|
||||
always @(posedge a) led[0] <= !led[0];
|
||||
endmodule
|
9
ecp5/synth/wire.ys
Normal file
9
ecp5/synth/wire.ys
Normal file
@ -0,0 +1,9 @@
|
||||
read_verilog wire.v
|
||||
read_verilog -lib cells.v
|
||||
synth -top top
|
||||
abc -lut 4
|
||||
techmap -map simple_map.v
|
||||
splitnets
|
||||
opt_clean
|
||||
stat
|
||||
write_json wire.json
|
701
ecp5/trellis_import.py
Executable file
701
ecp5/trellis_import.py
Executable 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()
|
@ -19,6 +19,8 @@
|
||||
|
||||
#include <math.h>
|
||||
#include "nextpnr.h"
|
||||
#include "placer1.h"
|
||||
#include "router1.h"
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
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);
|
||||
frameGraphicsReload = true;
|
||||
decal_graphics[decal].push_back(graphic);
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
void Arch::addWireGraphic(WireId wire, const GraphicElement &graphic)
|
||||
void Arch::setFrameDecal(DecalXY decalxy)
|
||||
{
|
||||
wires.at(wire).graphics.push_back(graphic);
|
||||
wireGraphicsReload.insert(wire);
|
||||
frame_decalxy = decalxy;
|
||||
refreshUiFrame();
|
||||
}
|
||||
|
||||
void Arch::addPipGraphic(PipId pip, const GraphicElement &graphic)
|
||||
void Arch::setWireDecal(WireId wire, DecalXY decalxy)
|
||||
{
|
||||
pips.at(pip).graphics.push_back(graphic);
|
||||
pipGraphicsReload.insert(pip);
|
||||
wires.at(wire).decalxy = decalxy;
|
||||
refreshUiWire(wire);
|
||||
}
|
||||
|
||||
void Arch::addBelGraphic(BelId bel, const GraphicElement &graphic)
|
||||
void Arch::setPipDecal(PipId pip, DecalXY decalxy)
|
||||
{
|
||||
bels.at(bel).graphics.push_back(graphic);
|
||||
belGraphicsReload.insert(bel);
|
||||
pips.at(pip).decalxy = decalxy;
|
||||
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;
|
||||
cells.at(cell)->bel = bel;
|
||||
cells.at(cell)->belStrength = strength;
|
||||
refreshUiBel(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)->belStrength = STRENGTH_NONE;
|
||||
bels.at(bel).bound_cell = IdString();
|
||||
refreshUiBel(bel);
|
||||
}
|
||||
|
||||
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;
|
||||
nets.at(net)->wires[wire].pip = PipId();
|
||||
nets.at(net)->wires[wire].strength = strength;
|
||||
refreshUiWire(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 pip = net_wires.at(wire).pip;
|
||||
if (pip != PipId())
|
||||
if (pip != PipId()) {
|
||||
pips.at(pip).bound_net = IdString();
|
||||
refreshUiPip(pip);
|
||||
}
|
||||
|
||||
net_wires.erase(wire);
|
||||
wires.at(wire).bound_net = IdString();
|
||||
refreshUiWire(wire);
|
||||
}
|
||||
|
||||
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;
|
||||
nets.at(net)->wires[wire].pip = pip;
|
||||
nets.at(net)->wires[wire].strength = strength;
|
||||
refreshUiPip(pip);
|
||||
refreshUiWire(wire);
|
||||
}
|
||||
|
||||
void Arch::unbindPip(PipId pip)
|
||||
@ -268,6 +298,8 @@ void Arch::unbindPip(PipId pip)
|
||||
nets.at(wires.at(wire).bound_net)->wires.erase(wire);
|
||||
pips.at(pip).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(); }
|
||||
@ -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
|
||||
{
|
||||
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; }
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
|
@ -34,16 +34,16 @@ struct PipInfo
|
||||
IdString name, bound_net;
|
||||
WireId srcWire, dstWire;
|
||||
DelayInfo delay;
|
||||
std::vector<GraphicElement> graphics;
|
||||
DecalXY decalxy;
|
||||
};
|
||||
|
||||
struct WireInfo
|
||||
{
|
||||
IdString name, bound_net;
|
||||
std::vector<GraphicElement> graphics;
|
||||
std::vector<PipId> downhill, uphill, aliases;
|
||||
BelPin uphill_bel_pin;
|
||||
std::vector<BelPin> downhill_bel_pins;
|
||||
DecalXY decalxy;
|
||||
int grid_x, grid_y;
|
||||
};
|
||||
|
||||
@ -58,11 +58,21 @@ struct BelInfo
|
||||
{
|
||||
IdString name, type, bound_cell;
|
||||
std::unordered_map<IdString, PinInfo> pins;
|
||||
std::vector<GraphicElement> graphics;
|
||||
DecalXY decalxy;
|
||||
int grid_x, grid_y;
|
||||
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
|
||||
{
|
||||
std::string chipName;
|
||||
@ -70,11 +80,14 @@ struct Arch : BaseCtx
|
||||
std::unordered_map<IdString, WireInfo> wires;
|
||||
std::unordered_map<IdString, PipInfo> pips;
|
||||
std::unordered_map<IdString, BelInfo> bels;
|
||||
std::unordered_map<GroupId, GroupInfo> groups;
|
||||
|
||||
std::vector<IdString> bel_ids, wire_ids, pip_ids;
|
||||
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;
|
||||
|
||||
void addWire(IdString name, int x, int y);
|
||||
@ -86,10 +99,17 @@ struct Arch : BaseCtx
|
||||
void addBelOutput(IdString bel, IdString name, IdString wire);
|
||||
void addBelInout(IdString bel, IdString name, IdString wire);
|
||||
|
||||
void addFrameGraphic(const GraphicElement &graphic);
|
||||
void addWireGraphic(WireId wire, const GraphicElement &graphic);
|
||||
void addPipGraphic(PipId pip, const GraphicElement &graphic);
|
||||
void addBelGraphic(BelId bel, const GraphicElement &graphic);
|
||||
void addGroupBel(IdString group, IdString bel);
|
||||
void addGroupWire(IdString group, IdString wire);
|
||||
void addGroupPip(IdString group, IdString pip);
|
||||
void addGroupGroup(IdString group, IdString grp);
|
||||
|
||||
void addDecalGraphic(DecalId decal, const GraphicElement &graphic);
|
||||
void 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.
|
||||
@ -148,6 +168,14 @@ struct Arch : BaseCtx
|
||||
const std::vector<PipId> &getPipsUphill(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;
|
||||
delay_t estimateDelay(WireId src, WireId dst) const;
|
||||
delay_t getDelayEpsilon() const { return 0.01; }
|
||||
@ -155,16 +183,16 @@ struct Arch : BaseCtx
|
||||
float getDelayNS(delay_t v) const { return v; }
|
||||
uint32_t getDelayChecksum(delay_t v) const { return 0; }
|
||||
|
||||
const std::vector<GraphicElement> &getFrameGraphics() const;
|
||||
const std::vector<GraphicElement> &getBelGraphics(BelId bel) const;
|
||||
const std::vector<GraphicElement> &getWireGraphics(WireId wire) const;
|
||||
const std::vector<GraphicElement> &getPipGraphics(PipId pip) const;
|
||||
bool pack() { return true; }
|
||||
bool place();
|
||||
bool route();
|
||||
|
||||
bool allGraphicsReload = false;
|
||||
bool frameGraphicsReload = false;
|
||||
std::unordered_set<BelId> belGraphicsReload;
|
||||
std::unordered_set<WireId> wireGraphicsReload;
|
||||
std::unordered_set<PipId> pipGraphicsReload;
|
||||
const 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;
|
||||
|
||||
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const;
|
||||
IdString getPortClock(const CellInfo *cell, IdString port) const;
|
||||
|
@ -49,5 +49,7 @@ typedef IdString PortPin;
|
||||
typedef IdString BelId;
|
||||
typedef IdString WireId;
|
||||
typedef IdString PipId;
|
||||
typedef IdString GroupId;
|
||||
typedef IdString DecalId;
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -90,24 +90,34 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
Context ctx(ArchArgs{});
|
||||
std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(ArchArgs{}));
|
||||
|
||||
if (vm.count("verbose")) {
|
||||
ctx.verbose = true;
|
||||
ctx->verbose = true;
|
||||
}
|
||||
|
||||
if (vm.count("force")) {
|
||||
ctx.force = true;
|
||||
ctx->force = true;
|
||||
}
|
||||
|
||||
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
|
||||
if (vm.count("run")) {
|
||||
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>>();
|
||||
for (auto filename : files)
|
||||
@ -117,15 +127,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_GUI
|
||||
if (vm.count("gui")) {
|
||||
Application a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
rc = a.exec();
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
} catch (log_execution_error_exception) {
|
||||
#if defined(_MSC_VER)
|
||||
|
@ -12,7 +12,6 @@ if (BUILD_PYTHON)
|
||||
|
||||
../3rdparty/python-console/modified/pyredirector.cc
|
||||
../3rdparty/python-console/modified/pyinterpreter.cc
|
||||
../3rdparty/python-console/modified/pyconsole.cc
|
||||
)
|
||||
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)
|
||||
|
||||
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)
|
||||
if (BUILD_PYTHON)
|
||||
|
@ -4,5 +4,11 @@
|
||||
<file>resources/open.png</file>
|
||||
<file>resources/save.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>
|
||||
</RCC>
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <QAction>
|
||||
#include <QCoreApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QIcon>
|
||||
@ -27,16 +28,14 @@
|
||||
#include "jsonparse.h"
|
||||
#include "log.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
#include "pythontab.h"
|
||||
#endif
|
||||
|
||||
static void initBasenameResource() { Q_INIT_RESOURCE(base); }
|
||||
|
||||
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();
|
||||
qRegisterMetaType<std::string>();
|
||||
@ -44,7 +43,7 @@ BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent), ctx(nullp
|
||||
log_files.clear();
|
||||
log_streams.clear();
|
||||
|
||||
setObjectName(QStringLiteral("BaseMainWindow"));
|
||||
setObjectName("BaseMainWindow");
|
||||
resize(1024, 768);
|
||||
|
||||
createMenusAndBars();
|
||||
@ -63,70 +62,80 @@ BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent), ctx(nullp
|
||||
|
||||
setCentralWidget(centralWidget);
|
||||
|
||||
DesignWidget *designview = new DesignWidget();
|
||||
designview = new DesignWidget();
|
||||
designview->setMinimumWidth(300);
|
||||
designview->setMaximumWidth(300);
|
||||
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();
|
||||
#ifndef NO_PYTHON
|
||||
PythonTab *pythontab = new PythonTab();
|
||||
tabWidget->addTab(pythontab, "Python");
|
||||
connect(this, SIGNAL(contextChanged(Context *)), pythontab, SLOT(newContext(Context *)));
|
||||
#endif
|
||||
info = new InfoTab();
|
||||
tabWidget->addTab(info, "Info");
|
||||
|
||||
console = new PythonTab();
|
||||
tabWidget->addTab(console, "Console");
|
||||
connect(this, SIGNAL(contextChanged(Context *)), console, SLOT(newContext(Context *)));
|
||||
|
||||
centralTabWidget = new QTabWidget();
|
||||
FPGAViewWidget *fpgaView = new FPGAViewWidget();
|
||||
centralTabWidget->addTab(fpgaView, "Graphics");
|
||||
|
||||
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(tabWidget);
|
||||
displaySplash();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
actionNew = new QAction("New", this);
|
||||
QIcon iconNew;
|
||||
iconNew.addFile(QStringLiteral(":/icons/resources/new.png"));
|
||||
actionNew->setIcon(iconNew);
|
||||
actionNew->setIcon(QIcon(":/icons/resources/new.png"));
|
||||
actionNew->setShortcuts(QKeySequence::New);
|
||||
actionNew->setStatusTip("New project file");
|
||||
connect(actionNew, SIGNAL(triggered()), this, SLOT(new_proj()));
|
||||
|
||||
actionOpen = new QAction("Open", this);
|
||||
QIcon iconOpen;
|
||||
iconOpen.addFile(QStringLiteral(":/icons/resources/open.png"));
|
||||
actionOpen->setIcon(iconOpen);
|
||||
actionOpen->setIcon(QIcon(":/icons/resources/open.png"));
|
||||
actionOpen->setShortcuts(QKeySequence::Open);
|
||||
actionOpen->setStatusTip("Open an existing project file");
|
||||
connect(actionOpen, SIGNAL(triggered()), this, SLOT(open_proj()));
|
||||
|
||||
QAction *actionSave = new QAction("Save", this);
|
||||
QIcon iconSave;
|
||||
iconSave.addFile(QStringLiteral(":/icons/resources/save.png"));
|
||||
actionSave->setIcon(iconSave);
|
||||
actionSave->setIcon(QIcon(":/icons/resources/save.png"));
|
||||
actionSave->setShortcuts(QKeySequence::Save);
|
||||
actionSave->setStatusTip("Save existing project to disk");
|
||||
connect(actionSave, SIGNAL(triggered()), this, SLOT(save_proj()));
|
||||
actionSave->setEnabled(false);
|
||||
connect(actionSave, SIGNAL(triggered()), this, SLOT(save_proj()));
|
||||
|
||||
QAction *actionExit = new QAction("Exit", this);
|
||||
QIcon iconExit;
|
||||
iconExit.addFile(QStringLiteral(":/icons/resources/exit.png"));
|
||||
actionExit->setIcon(iconExit);
|
||||
actionExit->setIcon(QIcon(":/icons/resources/exit.png"));
|
||||
actionExit->setShortcuts(QKeySequence::Quit);
|
||||
actionExit->setStatusTip("Exit the application");
|
||||
connect(actionExit, SIGNAL(triggered()), this, SLOT(close()));
|
||||
@ -145,6 +154,12 @@ void BaseMainWindow::createMenusAndBars()
|
||||
addToolBar(Qt::TopToolBarArea, mainToolBar);
|
||||
|
||||
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);
|
||||
|
||||
menu_File->addAction(actionNew);
|
||||
|
@ -20,34 +20,41 @@
|
||||
#ifndef BASEMAINWINDOW_H
|
||||
#define BASEMAINWINDOW_H
|
||||
|
||||
#include "infotab.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QProgressBar>
|
||||
#include <QSplashScreen>
|
||||
#include <QStatusBar>
|
||||
#include <QTabWidget>
|
||||
#include <QToolBar>
|
||||
|
||||
Q_DECLARE_METATYPE(std::string)
|
||||
Q_DECLARE_METATYPE(NEXTPNR_NAMESPACE_PREFIX DecalXY)
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
class PythonTab;
|
||||
class DesignWidget;
|
||||
|
||||
class BaseMainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BaseMainWindow(QWidget *parent = 0);
|
||||
explicit BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent = 0);
|
||||
virtual ~BaseMainWindow();
|
||||
Context *getContext() { return ctx; }
|
||||
Context *getContext() { return ctx.get(); }
|
||||
|
||||
protected:
|
||||
void createMenusAndBars();
|
||||
void displaySplash();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void writeInfo(std::string text);
|
||||
void displaySplashMessage(std::string msg);
|
||||
|
||||
virtual void new_proj() = 0;
|
||||
virtual void open_proj() = 0;
|
||||
@ -58,16 +65,19 @@ class BaseMainWindow : public QMainWindow
|
||||
void updateTreeView();
|
||||
|
||||
protected:
|
||||
Context *ctx;
|
||||
std::unique_ptr<Context> ctx;
|
||||
QTabWidget *tabWidget;
|
||||
QTabWidget *centralTabWidget;
|
||||
InfoTab *info;
|
||||
PythonTab *console;
|
||||
|
||||
QMenuBar *menuBar;
|
||||
QToolBar *mainToolBar;
|
||||
QStatusBar *statusBar;
|
||||
QAction *actionNew;
|
||||
QAction *actionOpen;
|
||||
QProgressBar *progressBar;
|
||||
QSplashScreen *splash;
|
||||
DesignWidget *designview;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -20,23 +20,15 @@
|
||||
#include "designwidget.h"
|
||||
#include <QAction>
|
||||
#include <QGridLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QSplitter>
|
||||
#include <QToolBar>
|
||||
#include <QTreeWidgetItem>
|
||||
#include "fpgaviewwidget.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
enum class ElementType
|
||||
{
|
||||
NONE,
|
||||
BEL,
|
||||
WIRE,
|
||||
PIP,
|
||||
NET,
|
||||
CELL
|
||||
};
|
||||
|
||||
class ElementTreeItem : public QTreeWidgetItem
|
||||
{
|
||||
public:
|
||||
@ -85,12 +77,88 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
|
||||
propertyEditor = new QtTreePropertyBrowser(this);
|
||||
propertyEditor->setFactoryForManager(variantManager, variantFactory);
|
||||
propertyEditor->setPropertiesWithoutValueMarked(true);
|
||||
|
||||
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);
|
||||
splitter->addWidget(treeWidget);
|
||||
splitter->addWidget(propertyEditor);
|
||||
splitter->addWidget(topWidget);
|
||||
splitter->addWidget(btmWidget);
|
||||
|
||||
QGridLayout *mainLayout = new QGridLayout();
|
||||
mainLayout->setSpacing(0);
|
||||
@ -99,16 +167,61 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
|
||||
setLayout(mainLayout);
|
||||
|
||||
// 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() {}
|
||||
|
||||
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)
|
||||
{
|
||||
treeWidget->clear();
|
||||
history_ignore = false;
|
||||
history_index = -1;
|
||||
history.clear();
|
||||
updateButtons();
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
nameToItem[i].clear();
|
||||
|
||||
this->ctx = ctx;
|
||||
|
||||
// Add bels to tree
|
||||
@ -117,6 +230,7 @@ void DesignWidget::newContext(Context *ctx)
|
||||
bel_root->setText(0, "Bels");
|
||||
treeWidget->insertTopLevelItem(0, bel_root);
|
||||
if (ctx) {
|
||||
Q_EMIT contextLoadStatus("Configuring bels...");
|
||||
for (auto bel : ctx->getBels()) {
|
||||
auto id = ctx->getBelName(bel);
|
||||
QStringList items = QString(id.c_str(ctx)).split("/");
|
||||
@ -128,7 +242,7 @@ void DesignWidget::newContext(Context *ctx)
|
||||
name += items.at(i);
|
||||
if (!bel_items.contains(name)) {
|
||||
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
|
||||
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()) {
|
||||
bel_root->addChild(bel.second);
|
||||
}
|
||||
for (auto bel : nameToItem[0].toStdMap()) {
|
||||
bel_root->addChild(bel.second);
|
||||
}
|
||||
|
||||
// Add wires to tree
|
||||
QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget);
|
||||
@ -146,6 +263,7 @@ void DesignWidget::newContext(Context *ctx)
|
||||
wire_root->setText(0, "Wires");
|
||||
treeWidget->insertTopLevelItem(0, wire_root);
|
||||
if (ctx) {
|
||||
Q_EMIT contextLoadStatus("Configuring wires...");
|
||||
for (auto wire : ctx->getWires()) {
|
||||
auto id = ctx->getWireName(wire);
|
||||
QStringList items = QString(id.c_str(ctx)).split("/");
|
||||
@ -157,7 +275,7 @@ void DesignWidget::newContext(Context *ctx)
|
||||
name += items.at(i);
|
||||
if (!wire_items.contains(name)) {
|
||||
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
|
||||
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()) {
|
||||
wire_root->addChild(wire.second);
|
||||
}
|
||||
|
||||
for (auto wire : nameToItem[1].toStdMap()) {
|
||||
wire_root->addChild(wire.second);
|
||||
}
|
||||
// Add pips to tree
|
||||
QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget);
|
||||
QMap<QString, QTreeWidgetItem *> pip_items;
|
||||
pip_root->setText(0, "Pips");
|
||||
treeWidget->insertTopLevelItem(0, pip_root);
|
||||
if (ctx) {
|
||||
Q_EMIT contextLoadStatus("Configuring pips...");
|
||||
for (auto pip : ctx->getPips()) {
|
||||
auto id = ctx->getPipName(pip);
|
||||
QStringList items = QString(id.c_str(ctx)).split("/");
|
||||
@ -186,7 +307,7 @@ void DesignWidget::newContext(Context *ctx)
|
||||
name += items.at(i);
|
||||
if (!pip_items.contains(name)) {
|
||||
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
|
||||
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()) {
|
||||
pip_root->addChild(pip.second);
|
||||
}
|
||||
for (auto pip : nameToItem[2].toStdMap()) {
|
||||
pip_root->addChild(pip.second);
|
||||
}
|
||||
|
||||
// Add nets to tree
|
||||
nets_root = new QTreeWidgetItem(treeWidget);
|
||||
@ -207,6 +331,8 @@ void DesignWidget::newContext(Context *ctx)
|
||||
cells_root = new QTreeWidgetItem(treeWidget);
|
||||
cells_root->setText(0, "Cells");
|
||||
treeWidget->insertTopLevelItem(0, cells_root);
|
||||
|
||||
Q_EMIT finishContextLoad();
|
||||
}
|
||||
|
||||
void DesignWidget::updateTree()
|
||||
@ -214,45 +340,48 @@ void DesignWidget::updateTree()
|
||||
clearProperties();
|
||||
delete nets_root;
|
||||
delete cells_root;
|
||||
nameToItem[3].clear();
|
||||
nameToItem[4].clear();
|
||||
|
||||
// Add nets to tree
|
||||
nets_root = new QTreeWidgetItem(treeWidget);
|
||||
QMap<QString, QTreeWidgetItem *> nets_items;
|
||||
nets_root->setText(0, "Nets");
|
||||
treeWidget->insertTopLevelItem(0, nets_root);
|
||||
if (ctx) {
|
||||
for (auto &item : ctx->nets) {
|
||||
auto id = item.first;
|
||||
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);
|
||||
}
|
||||
|
||||
// Add cells to tree
|
||||
cells_root = new QTreeWidgetItem(treeWidget);
|
||||
QMap<QString, QTreeWidgetItem *> cells_items;
|
||||
cells_root->setText(0, "Cells");
|
||||
treeWidget->insertTopLevelItem(0, cells_root);
|
||||
if (ctx) {
|
||||
for (auto &item : ctx->cells) {
|
||||
auto id = item.first;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void DesignWidget::addProperty(QtProperty *property, const QString &id)
|
||||
QtProperty *DesignWidget::addTopLevelProperty(const QString &id)
|
||||
{
|
||||
propertyToId[property] = id;
|
||||
idToProperty[id] = property;
|
||||
propertyEditor->addProperty(property);
|
||||
QtProperty *topItem = groupManager->addProperty(id);
|
||||
propertyToId[topItem] = id;
|
||||
idToProperty[id] = topItem;
|
||||
propertyEditor->addProperty(topItem);
|
||||
return topItem;
|
||||
}
|
||||
|
||||
void DesignWidget::clearProperties()
|
||||
@ -266,8 +395,75 @@ void DesignWidget::clearProperties()
|
||||
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())
|
||||
return;
|
||||
|
||||
@ -276,305 +472,193 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *clickItem, int pos)
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<DecalXY> decals;
|
||||
|
||||
addToHistory(clickItem);
|
||||
|
||||
clearProperties();
|
||||
if (type == ElementType::BEL) {
|
||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
||||
BelId bel = ctx->getBelByName(c);
|
||||
|
||||
QtProperty *topItem = groupManager->addProperty("Bel");
|
||||
addProperty(topItem, "Bel");
|
||||
decals.push_back(ctx->getBelDecal(bel));
|
||||
Q_EMIT selected(decals);
|
||||
|
||||
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
|
||||
nameItem->setValue(c.c_str(ctx));
|
||||
topItem->addSubProperty(nameItem);
|
||||
QtProperty *topItem = addTopLevelProperty("Bel");
|
||||
|
||||
QtVariantProperty *typeItem = readOnlyManager->addProperty(QVariant::String, "Type");
|
||||
typeItem->setValue(ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx));
|
||||
topItem->addSubProperty(typeItem);
|
||||
|
||||
QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available");
|
||||
availItem->setValue(ctx->checkBelAvail(bel));
|
||||
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);
|
||||
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
||||
addProperty(topItem, QVariant::String, "Type", ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx));
|
||||
addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel));
|
||||
addProperty(topItem, QVariant::String, "Bound Cell", ctx->getBoundBelCell(bel).c_str(ctx), ElementType::CELL);
|
||||
addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->getConflictingBelCell(bel).c_str(ctx),
|
||||
ElementType::CELL);
|
||||
|
||||
} else if (type == ElementType::WIRE) {
|
||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
||||
WireId wire = ctx->getWireByName(c);
|
||||
|
||||
QtProperty *topItem = groupManager->addProperty("Wire");
|
||||
addProperty(topItem, "Wire");
|
||||
decals.push_back(ctx->getWireDecal(wire));
|
||||
Q_EMIT selected(decals);
|
||||
|
||||
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
|
||||
nameItem->setValue(c.c_str(ctx));
|
||||
topItem->addSubProperty(nameItem);
|
||||
QtProperty *topItem = addTopLevelProperty("Wire");
|
||||
|
||||
QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available");
|
||||
availItem->setValue(ctx->checkWireAvail(wire));
|
||||
topItem->addSubProperty(availItem);
|
||||
|
||||
QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound 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);
|
||||
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
||||
addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire));
|
||||
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),
|
||||
ElementType::NET);
|
||||
|
||||
QtProperty *belpinItem = addSubGroup(topItem, "BelPin Uphill");
|
||||
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())
|
||||
belUphillItem->setValue(ctx->getBelName(uphill.bel).c_str(ctx));
|
||||
addProperty(belpinItem, QVariant::String, "Bel", ctx->getBelName(uphill.bel).c_str(ctx), ElementType::BEL);
|
||||
else
|
||||
belUphillItem->setValue("");
|
||||
belpinItem->addSubProperty(belUphillItem);
|
||||
addProperty(belpinItem, QVariant::String, "Bel", "", ElementType::BEL);
|
||||
|
||||
QtVariantProperty *portUphillItem = readOnlyManager->addProperty(QVariant::String, "PortPin");
|
||||
portUphillItem->setValue(ctx->portPinToId(uphill.pin).c_str(ctx));
|
||||
belpinItem->addSubProperty(portUphillItem);
|
||||
addProperty(belpinItem, QVariant::String, "PortPin", ctx->portPinToId(uphill.pin).c_str(ctx), ElementType::BEL);
|
||||
|
||||
QtProperty *downhillItem = groupManager->addProperty("BelPins Downhill");
|
||||
topItem->addSubProperty(downhillItem);
|
||||
QtProperty *downhillItem = addSubGroup(topItem, "BelPin Downhill");
|
||||
for (const auto &item : ctx->getBelPinsDownhill(wire)) {
|
||||
QString belname = "";
|
||||
if (item.bel != BelId())
|
||||
belname = ctx->getBelName(item.bel).c_str(ctx);
|
||||
QString pinname = ctx->portPinToId(item.pin).c_str(ctx);
|
||||
|
||||
QtProperty *dhItem = groupManager->addProperty(belname + "-" + pinname);
|
||||
downhillItem->addSubProperty(dhItem);
|
||||
|
||||
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 *dhItem = addSubGroup(downhillItem, belname + "-" + pinname);
|
||||
addProperty(dhItem, QVariant::String, "Bel", belname, ElementType::BEL);
|
||||
addProperty(dhItem, QVariant::String, "PortPin", pinname);
|
||||
}
|
||||
|
||||
QtProperty *pipsDownItem = groupManager->addProperty("Pips Downhill");
|
||||
topItem->addSubProperty(pipsDownItem);
|
||||
int counter = 0;
|
||||
QtProperty *pipsDownItem = addSubGroup(downhillItem, "Pips Downhill");
|
||||
for (const auto &item : ctx->getPipsDownhill(wire)) {
|
||||
QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, "");
|
||||
pipItem->setValue(ctx->getPipName(item).c_str(ctx));
|
||||
pipsDownItem->addSubProperty(pipItem);
|
||||
addProperty(pipsDownItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
|
||||
counter++;
|
||||
if (counter == 50) {
|
||||
addProperty(pipsDownItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QtProperty *pipsUpItem = groupManager->addProperty("Pips Uphill");
|
||||
topItem->addSubProperty(pipsUpItem);
|
||||
counter = 0;
|
||||
QtProperty *pipsUpItem = addSubGroup(downhillItem, "Pips Uphill");
|
||||
for (const auto &item : ctx->getPipsUphill(wire)) {
|
||||
QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, "");
|
||||
pipItem->setValue(ctx->getPipName(item).c_str(ctx));
|
||||
pipsUpItem->addSubProperty(pipItem);
|
||||
addProperty(pipsUpItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
|
||||
counter++;
|
||||
if (counter == 50) {
|
||||
addProperty(pipsUpItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (type == ElementType::PIP) {
|
||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
||||
PipId pip = ctx->getPipByName(c);
|
||||
|
||||
QtProperty *topItem = groupManager->addProperty("Pip");
|
||||
addProperty(topItem, "Pip");
|
||||
decals.push_back(ctx->getPipDecal(pip));
|
||||
Q_EMIT selected(decals);
|
||||
|
||||
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
|
||||
nameItem->setValue(c.c_str(ctx));
|
||||
topItem->addSubProperty(nameItem);
|
||||
QtProperty *topItem = addTopLevelProperty("Pip");
|
||||
|
||||
QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available");
|
||||
availItem->setValue(ctx->checkPipAvail(pip));
|
||||
topItem->addSubProperty(availItem);
|
||||
|
||||
QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Net");
|
||||
cellItem->setValue(ctx->getBoundPipNet(pip).c_str(ctx));
|
||||
topItem->addSubProperty(cellItem);
|
||||
|
||||
QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Net");
|
||||
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);
|
||||
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
||||
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
|
||||
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),
|
||||
ElementType::NET);
|
||||
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
|
||||
ElementType::WIRE);
|
||||
addProperty(topItem, QVariant::String, "Dest Wire", ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx),
|
||||
ElementType::WIRE);
|
||||
|
||||
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) {
|
||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
||||
NetInfo *net = ctx->nets.at(c).get();
|
||||
|
||||
QtProperty *topItem = groupManager->addProperty("Net");
|
||||
addProperty(topItem, "Net");
|
||||
QtProperty *topItem = addTopLevelProperty("Net");
|
||||
|
||||
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
|
||||
nameItem->setValue(net->name.c_str(ctx));
|
||||
topItem->addSubProperty(nameItem);
|
||||
addProperty(topItem, QVariant::String, "Name", net->name.c_str(ctx));
|
||||
|
||||
QtProperty *driverItem = groupManager->addProperty("Driver");
|
||||
topItem->addSubProperty(driverItem);
|
||||
|
||||
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");
|
||||
QtProperty *driverItem = addSubGroup(topItem, "Driver");
|
||||
addProperty(driverItem, QVariant::String, "Port", net->driver.port.c_str(ctx));
|
||||
addProperty(driverItem, QVariant::Double, "Budget", net->driver.budget);
|
||||
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
|
||||
cellNameItem->setValue("");
|
||||
driverItem->addSubProperty(cellNameItem);
|
||||
addProperty(driverItem, QVariant::String, "Cell", "", ElementType::CELL);
|
||||
|
||||
QtProperty *usersItem = groupManager->addProperty("Users");
|
||||
topItem->addSubProperty(usersItem);
|
||||
QtProperty *usersItem = addSubGroup(topItem, "Users");
|
||||
for (auto &item : net->users) {
|
||||
QtProperty *portItem = groupManager->addProperty(item.port.c_str(ctx));
|
||||
usersItem->addSubProperty(portItem);
|
||||
QtProperty *portItem = addSubGroup(usersItem, item.port.c_str(ctx));
|
||||
|
||||
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Port");
|
||||
nameItem->setValue(item.port.c_str(ctx));
|
||||
portItem->addSubProperty(nameItem);
|
||||
|
||||
QtVariantProperty *budgetItem = readOnlyManager->addProperty(QVariant::Double, "Budget");
|
||||
budgetItem->setValue(item.budget);
|
||||
portItem->addSubProperty(budgetItem);
|
||||
|
||||
QtVariantProperty *userItem = readOnlyManager->addProperty(QVariant::String, "Cell");
|
||||
addProperty(portItem, QVariant::String, "Port", item.port.c_str(ctx));
|
||||
addProperty(portItem, QVariant::Double, "Budget", item.budget);
|
||||
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
|
||||
userItem->setValue("");
|
||||
portItem->addSubProperty(userItem);
|
||||
addProperty(portItem, QVariant::String, "Cell", "", ElementType::CELL);
|
||||
}
|
||||
|
||||
QtProperty *attrsItem = groupManager->addProperty("Attributes");
|
||||
topItem->addSubProperty(attrsItem);
|
||||
QtProperty *attrsItem = addSubGroup(topItem, "Attributes");
|
||||
for (auto &item : net->attrs) {
|
||||
QtVariantProperty *attrItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx));
|
||||
attrItem->setValue(item.second.c_str());
|
||||
attrsItem->addSubProperty(attrItem);
|
||||
addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
||||
}
|
||||
|
||||
QtProperty *wiresItem = groupManager->addProperty("Wires");
|
||||
topItem->addSubProperty(wiresItem);
|
||||
QtProperty *wiresItem = addSubGroup(topItem, "Wires");
|
||||
for (auto &item : net->wires) {
|
||||
auto name = ctx->getWireName(item.first).c_str(ctx);
|
||||
|
||||
QtProperty *wireItem = groupManager->addProperty(name);
|
||||
|
||||
QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
|
||||
nameItem->setValue(name);
|
||||
wireItem->addSubProperty(nameItem);
|
||||
|
||||
QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, "Pip");
|
||||
QtProperty *wireItem = addSubGroup(wiresItem, name);
|
||||
addProperty(wireItem, QVariant::String, "Name", name);
|
||||
|
||||
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
|
||||
pipItem->setValue("");
|
||||
wireItem->addSubProperty(pipItem);
|
||||
addProperty(wireItem, QVariant::String, "Pip", "", ElementType::PIP);
|
||||
|
||||
QtVariantProperty *strengthItem = readOnlyManager->addProperty(QVariant::Int, "Strength");
|
||||
strengthItem->setValue((int)item.second.strength);
|
||||
wireItem->addSubProperty(strengthItem);
|
||||
|
||||
wiresItem->addSubProperty(wireItem);
|
||||
addProperty(wireItem, QVariant::Int, "Strength", (int)item.second.strength);
|
||||
}
|
||||
|
||||
} else if (type == ElementType::CELL) {
|
||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
||||
CellInfo *cell = ctx->cells.at(c).get();
|
||||
|
||||
QtProperty *topItem = groupManager->addProperty("Cell");
|
||||
addProperty(topItem, "Cell");
|
||||
QtProperty *topItem = addTopLevelProperty("Cell");
|
||||
|
||||
QtVariantProperty *cellNameItem = readOnlyManager->addProperty(QVariant::String, "Name");
|
||||
cellNameItem->setValue(cell->name.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");
|
||||
addProperty(topItem, QVariant::String, "Name", cell->name.c_str(ctx));
|
||||
addProperty(topItem, QVariant::String, "Type", cell->type.c_str(ctx));
|
||||
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
|
||||
cellBelItem->setValue("");
|
||||
topItem->addSubProperty(cellBelItem);
|
||||
addProperty(topItem, QVariant::String, "Bel", "", ElementType::BEL);
|
||||
addProperty(topItem, QVariant::Int, "Bel strength", int(cell->belStrength));
|
||||
|
||||
QtVariantProperty *cellBelStrItem = readOnlyManager->addProperty(QVariant::Int, "Bel strength");
|
||||
cellBelStrItem->setValue(int(cell->belStrength));
|
||||
topItem->addSubProperty(cellBelStrItem);
|
||||
|
||||
QtProperty *cellPortsItem = groupManager->addProperty("Ports");
|
||||
topItem->addSubProperty(cellPortsItem);
|
||||
QtProperty *cellPortsItem = addSubGroup(topItem, "Ports");
|
||||
for (auto &item : cell->ports) {
|
||||
PortInfo p = item.second;
|
||||
|
||||
QtProperty *portInfoItem = groupManager->addProperty(p.name.c_str(ctx));
|
||||
|
||||
QtVariantProperty *portInfoNameItem = readOnlyManager->addProperty(QVariant::String, "Name");
|
||||
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");
|
||||
QtProperty *portInfoItem = addSubGroup(cellPortsItem, p.name.c_str(ctx));
|
||||
addProperty(portInfoItem, QVariant::String, "Name", p.name.c_str(ctx));
|
||||
addProperty(portInfoItem, QVariant::Int, "Type", int(p.type));
|
||||
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
|
||||
portInfoNetItem->setValue("");
|
||||
portInfoItem->addSubProperty(portInfoNetItem);
|
||||
|
||||
cellPortsItem->addSubProperty(portInfoItem);
|
||||
addProperty(portInfoItem, QVariant::String, "Net", "", ElementType::NET);
|
||||
}
|
||||
|
||||
QtProperty *cellAttrItem = groupManager->addProperty("Attributes");
|
||||
topItem->addSubProperty(cellAttrItem);
|
||||
QtProperty *cellAttrItem = addSubGroup(topItem, "Attributes");
|
||||
for (auto &item : cell->attrs) {
|
||||
QtVariantProperty *attrItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx));
|
||||
attrItem->setValue(item.second.c_str());
|
||||
cellAttrItem->addSubProperty(attrItem);
|
||||
addProperty(cellAttrItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
||||
}
|
||||
|
||||
QtProperty *cellParamsItem = groupManager->addProperty("Parameters");
|
||||
topItem->addSubProperty(cellParamsItem);
|
||||
QtProperty *cellParamsItem = addSubGroup(topItem, "Parameters");
|
||||
for (auto &item : cell->params) {
|
||||
QtVariantProperty *paramItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx));
|
||||
paramItem->setValue(item.second.c_str());
|
||||
cellParamsItem->addSubProperty(paramItem);
|
||||
addProperty(cellParamsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
||||
}
|
||||
|
||||
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 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");
|
||||
cellItem->setValue(cell_port.c_str());
|
||||
pinGroupItem->addSubProperty(cellItem);
|
||||
|
||||
QtVariantProperty *belItem = readOnlyManager->addProperty(QVariant::String, "Bel");
|
||||
belItem->setValue(bel_pin.c_str());
|
||||
pinGroupItem->addSubProperty(belItem);
|
||||
|
||||
cellPinsItem->addSubProperty(pinGroupItem);
|
||||
addProperty(pinGroupItem, QVariant::String, "Cell", cell_port.c_str(), ElementType::CELL);
|
||||
addProperty(pinGroupItem, QVariant::String, "Bel", bel_pin.c_str(), ElementType::BEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
itemContextMenu = tree->itemAt(pos);
|
||||
|
||||
QAction *selectAction = new QAction("&Select", this);
|
||||
selectAction->setStatusTip("Select item on view");
|
||||
ElementType type = static_cast<ElementTreeItem *>(itemContextMenu)->getType();
|
||||
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);
|
||||
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::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
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define DESIGNWIDGET_H
|
||||
|
||||
#include <QTreeWidget>
|
||||
#include <QVariant>
|
||||
#include "nextpnr.h"
|
||||
#include "qtgroupboxpropertybrowser.h"
|
||||
#include "qtpropertymanager.h"
|
||||
@ -29,6 +30,16 @@
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
enum class ElementType
|
||||
{
|
||||
NONE,
|
||||
BEL,
|
||||
WIRE,
|
||||
PIP,
|
||||
NET,
|
||||
CELL
|
||||
};
|
||||
|
||||
class DesignWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -38,16 +49,30 @@ class DesignWidget : public QWidget
|
||||
~DesignWidget();
|
||||
|
||||
private:
|
||||
void addProperty(QtProperty *property, const QString &id);
|
||||
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:
|
||||
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:
|
||||
void prepareMenu(const QPoint &pos);
|
||||
void onItemClicked(QTreeWidgetItem *item, int);
|
||||
void selectObject();
|
||||
void prepareMenuProperty(const QPoint &pos);
|
||||
void prepareMenuTree(const QPoint &pos);
|
||||
void onItemSelectionChanged();
|
||||
void onItemDoubleClicked(QTreeWidgetItem *item, int column);
|
||||
public Q_SLOTS:
|
||||
void newContext(Context *ctx);
|
||||
void updateTree();
|
||||
@ -66,8 +91,22 @@ class DesignWidget : public QWidget
|
||||
|
||||
QMap<QtProperty *, QString> propertyToId;
|
||||
QMap<QString, QtProperty *> idToProperty;
|
||||
|
||||
QMap<QString, QTreeWidgetItem *> nameToItem[6];
|
||||
std::vector<QTreeWidgetItem *> history;
|
||||
int history_index;
|
||||
bool history_ignore;
|
||||
|
||||
QTreeWidgetItem *nets_root;
|
||||
QTreeWidgetItem *cells_root;
|
||||
|
||||
QAction *actionFirst;
|
||||
QAction *actionPrev;
|
||||
QAction *actionNext;
|
||||
QAction *actionLast;
|
||||
|
||||
QColor highlightColors[8];
|
||||
QMap<QTreeWidgetItem *, int> highlightSelected;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
0
gui/ecp5/family.cmake
Normal file
0
gui/ecp5/family.cmake
Normal file
51
gui/ecp5/mainwindow.cc
Normal file
51
gui/ecp5/mainwindow.cc
Normal 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
46
gui/ecp5/mainwindow.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include "../basewindow.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
class MainWindow : public BaseMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(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
2
gui/ecp5/nextpnr.qrc
Normal file
@ -0,0 +1,2 @@
|
||||
<RCC>
|
||||
</RCC>
|
@ -195,7 +195,7 @@ bool LineShader::compile(void)
|
||||
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();
|
||||
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());
|
||||
|
||||
program_->setUniformValue(uniforms_.projection, projection);
|
||||
program_->setUniformValue(uniforms_.thickness, line.thickness);
|
||||
program_->setUniformValue(uniforms_.color, line.color.r, line.color.g, line.color.b, line.color.a);
|
||||
program_->setUniformValue(uniforms_.thickness, thickness);
|
||||
program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF());
|
||||
|
||||
buffers_.position.bind();
|
||||
program_->enableAttributeArray("position");
|
||||
@ -241,8 +241,27 @@ void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection)
|
||||
}
|
||||
|
||||
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();
|
||||
fmt.setMajorVersion(3);
|
||||
fmt.setMinorVersion(1);
|
||||
@ -264,6 +283,7 @@ FPGAViewWidget::~FPGAViewWidget() {}
|
||||
void FPGAViewWidget::newContext(Context *ctx)
|
||||
{
|
||||
ctx_ = ctx;
|
||||
selectedItems_.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
@ -271,66 +291,92 @@ QSize FPGAViewWidget::minimumSizeHint() 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()
|
||||
{
|
||||
if (!lineShader_.compile()) {
|
||||
log_error("Could not compile shader.\n");
|
||||
}
|
||||
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) {
|
||||
auto line = PolyLine(true);
|
||||
line.point(offset + scale * el.x1, offset + scale * el.y1);
|
||||
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);
|
||||
}
|
||||
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
|
||||
offsetX = decal.x;
|
||||
offsetY = decal.y;
|
||||
|
||||
if (el.type == GraphicElement::G_LINE) {
|
||||
PolyLine(offset + scale * el.x1, offset + scale * el.y1, offset + scale * el.x2, offset + scale * el.y2)
|
||||
.build(out);
|
||||
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);
|
||||
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()
|
||||
{
|
||||
auto gl = QOpenGLContext::currentContext()->functions();
|
||||
@ -338,65 +384,112 @@ void FPGAViewWidget::paintGL()
|
||||
gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
|
||||
gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
const float aspect = float(width()) / float(height());
|
||||
QMatrix4x4 matrix = getProjection();
|
||||
|
||||
QMatrix4x4 matrix;
|
||||
matrix.ortho(QRectF(-aspect / 2.0, -0.5, aspect, 1.0f));
|
||||
matrix.translate(moveX_, moveY_, -0.5);
|
||||
matrix.scale(zoom_ * 0.01f, zoom_ * 0.01f, 0);
|
||||
matrix *= viewMove_;
|
||||
|
||||
// Calculate world thickness to achieve a screen 1px/1.1px line.
|
||||
float thick1Px = mouseToWorldCoordinates(1, 0).x();
|
||||
float thick11Px = mouseToWorldCoordinates(1.1, 0).x();
|
||||
|
||||
// Draw grid.
|
||||
auto grid = LineShaderData(0.01f, QColor("#DDD"));
|
||||
auto grid = LineShaderData();
|
||||
for (float i = -100.0f; i < 100.0f; i += 1.0f) {
|
||||
PolyLine(-100.0f, i, 100.0f, i).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_) {
|
||||
// Draw Bels.
|
||||
for (auto bel : ctx_->getBels()) {
|
||||
for (auto &el : ctx_->getBelGraphics(bel))
|
||||
drawElement(bels, el);
|
||||
drawDecal(shaders, ctx_->getBelDecal(bel));
|
||||
}
|
||||
// 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.
|
||||
auto frames = LineShaderData(0.02f, QColor("#0066ba"));
|
||||
if (ctx_) {
|
||||
for (auto &el : ctx_->getFrameGraphics()) {
|
||||
drawElement(frames, el);
|
||||
}
|
||||
lineShader_.draw(frames, matrix);
|
||||
}
|
||||
lineShader_.draw(shaders[0], gFrameColor_, thick11Px, matrix);
|
||||
lineShader_.draw(shaders[1], gHiddenColor_, thick11Px, matrix);
|
||||
lineShader_.draw(shaders[2], gInactiveColor_, thick11Px, matrix);
|
||||
lineShader_.draw(shaders[3], gActiveColor_, thick11Px, matrix);
|
||||
for (int i = 0; i < 8; i++)
|
||||
lineShader_.draw(highlightShader_[i], highlightColors[i], thick11Px, 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::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_;
|
||||
startDragY_ = moveY_;
|
||||
lastPos_ = event->pos();
|
||||
QMatrix4x4 p = getProjection();
|
||||
QVector2D unit = p.map(QVector4D(1, 1, 0, 1)).toVector2DAffine();
|
||||
|
||||
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)
|
||||
{
|
||||
const int dx = event->x() - lastPos_.x();
|
||||
const int dy = event->y() - lastPos_.y();
|
||||
lastPos_ = event->pos();
|
||||
|
||||
const qreal retinaScale = devicePixelRatio();
|
||||
float aspect = float(width()) / float(height());
|
||||
const float dx_scale = dx * (1 / (float)width() * retinaScale * aspect);
|
||||
const float dy_scale = dy * (1 / (float)height() * retinaScale);
|
||||
auto world = mouseToWorldCoordinates(dx, dy);
|
||||
viewMove_.translate(world.x(), -world.y());
|
||||
|
||||
float xpos = dx_scale + startDragX_;
|
||||
float ypos = dy_scale + startDragY_;
|
||||
|
||||
setXTranslation(xpos);
|
||||
setYTranslation(ypos);
|
||||
update();
|
||||
}
|
||||
|
||||
void FPGAViewWidget::wheelEvent(QWheelEvent *event)
|
||||
@ -404,8 +497,19 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event)
|
||||
QPoint degree = event->angleDelta() / 8;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,18 +41,6 @@ NPNR_PACKED_STRUCT(struct Vertex2DPOD {
|
||||
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
|
||||
// LineShader.
|
||||
// Each LineShaderData can have its' own color and thickness.
|
||||
@ -63,10 +51,15 @@ struct LineShaderData
|
||||
std::vector<GLfloat> miters;
|
||||
std::vector<GLuint> indices;
|
||||
|
||||
GLfloat thickness;
|
||||
ColorPOD color;
|
||||
LineShaderData(void) {}
|
||||
|
||||
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
|
||||
@ -210,12 +203,20 @@ class LineShader
|
||||
bool compile(void);
|
||||
|
||||
// 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
|
||||
{
|
||||
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:
|
||||
FPGAViewWidget(QWidget *parent = 0);
|
||||
@ -239,20 +240,46 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||
void mouseMoveEvent(QMouseEvent *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:
|
||||
void newContext(Context *ctx);
|
||||
void onSelectedArchItem(std::vector<DecalXY> decals);
|
||||
void onHighlightGroupChanged(std::vector<DecalXY> decals, int group);
|
||||
|
||||
private:
|
||||
QPoint lastPos_;
|
||||
float moveX_;
|
||||
float moveY_;
|
||||
float zoom_;
|
||||
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_;
|
||||
|
||||
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
|
||||
|
0
gui/generic/family.cmake
Normal file
0
gui/generic/family.cmake
Normal file
@ -23,7 +23,7 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent)
|
||||
MainWindow::MainWindow(std::unique_ptr<Context> context, QWidget *parent) : BaseMainWindow(std::move(context), parent)
|
||||
{
|
||||
initMainResource();
|
||||
|
||||
@ -31,6 +31,7 @@ MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent)
|
||||
setWindowTitle(title.c_str());
|
||||
|
||||
createMenu();
|
||||
Q_EMIT contextChanged(ctx.get());
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {}
|
||||
|
@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
explicit MainWindow(std::unique_ptr<Context> context, QWidget *parent = 0);
|
||||
virtual ~MainWindow();
|
||||
|
||||
public:
|
||||
|
3
gui/ice40/family.cmake
Normal file
3
gui/ice40/family.cmake
Normal file
@ -0,0 +1,3 @@
|
||||
if(ICE40_HX1K_ONLY)
|
||||
target_compile_definitions(gui_${family} PRIVATE ICE40_HX1K_ONLY=1)
|
||||
endif()
|
@ -27,16 +27,14 @@
|
||||
#include "design_utils.h"
|
||||
#include "jsonparse.h"
|
||||
#include "log.h"
|
||||
#include "pack.h"
|
||||
#include "pcf.h"
|
||||
#include "place_sa.h"
|
||||
#include "route.h"
|
||||
|
||||
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
|
||||
|
||||
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();
|
||||
|
||||
@ -62,6 +60,8 @@ MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent), timing_driven(
|
||||
connect(this, SIGNAL(contextChanged(Context *)), task, SIGNAL(contextChanged(Context *)));
|
||||
|
||||
createMenu();
|
||||
|
||||
Q_EMIT contextChanged(ctx.get());
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() { delete task; }
|
||||
@ -72,60 +72,46 @@ void MainWindow::createMenu()
|
||||
menuBar->addAction(menu_Design->menuAction());
|
||||
|
||||
actionLoadJSON = new QAction("Open JSON", this);
|
||||
QIcon iconLoadJSON;
|
||||
iconLoadJSON.addFile(QStringLiteral(":/icons/resources/open_json.png"));
|
||||
actionLoadJSON->setIcon(iconLoadJSON);
|
||||
actionLoadJSON->setIcon(QIcon(":/icons/resources/open_json.png"));
|
||||
actionLoadJSON->setStatusTip("Open an existing JSON file");
|
||||
actionLoadJSON->setEnabled(true);
|
||||
connect(actionLoadJSON, SIGNAL(triggered()), this, SLOT(open_json()));
|
||||
actionLoadJSON->setEnabled(false);
|
||||
|
||||
actionLoadPCF = new QAction("Open PCF", this);
|
||||
QIcon iconLoadPCF;
|
||||
iconLoadPCF.addFile(QStringLiteral(":/icons/resources/open_pcf.png"));
|
||||
actionLoadPCF->setIcon(iconLoadPCF);
|
||||
actionLoadPCF->setIcon(QIcon(":/icons/resources/open_pcf.png"));
|
||||
actionLoadPCF->setStatusTip("Open PCF file");
|
||||
connect(actionLoadPCF, SIGNAL(triggered()), this, SLOT(open_pcf()));
|
||||
actionLoadPCF->setEnabled(false);
|
||||
connect(actionLoadPCF, SIGNAL(triggered()), this, SLOT(open_pcf()));
|
||||
|
||||
actionPack = new QAction("Pack", this);
|
||||
QIcon iconPack;
|
||||
iconPack.addFile(QStringLiteral(":/icons/resources/pack.png"));
|
||||
actionPack->setIcon(iconPack);
|
||||
actionPack->setIcon(QIcon(":/icons/resources/pack.png"));
|
||||
actionPack->setStatusTip("Pack current design");
|
||||
connect(actionPack, SIGNAL(triggered()), task, SIGNAL(pack()));
|
||||
actionPack->setEnabled(false);
|
||||
connect(actionPack, SIGNAL(triggered()), task, SIGNAL(pack()));
|
||||
|
||||
actionAssignBudget = new QAction("Assign Budget", this);
|
||||
QIcon iconAssignBudget;
|
||||
iconAssignBudget.addFile(QStringLiteral(":/icons/resources/time_add.png"));
|
||||
actionAssignBudget->setIcon(iconAssignBudget);
|
||||
actionAssignBudget->setIcon(QIcon(":/icons/resources/time_add.png"));
|
||||
actionAssignBudget->setStatusTip("Assign time budget for current design");
|
||||
connect(actionAssignBudget, SIGNAL(triggered()), this, SLOT(budget()));
|
||||
actionAssignBudget->setEnabled(false);
|
||||
connect(actionAssignBudget, SIGNAL(triggered()), this, SLOT(budget()));
|
||||
|
||||
actionPlace = new QAction("Place", this);
|
||||
QIcon iconPlace;
|
||||
iconPlace.addFile(QStringLiteral(":/icons/resources/place.png"));
|
||||
actionPlace->setIcon(iconPlace);
|
||||
actionPlace->setIcon(QIcon(":/icons/resources/place.png"));
|
||||
actionPlace->setStatusTip("Place current design");
|
||||
connect(actionPlace, SIGNAL(triggered()), this, SLOT(place()));
|
||||
actionPlace->setEnabled(false);
|
||||
connect(actionPlace, SIGNAL(triggered()), this, SLOT(place()));
|
||||
|
||||
actionRoute = new QAction("Route", this);
|
||||
QIcon iconRoute;
|
||||
iconRoute.addFile(QStringLiteral(":/icons/resources/route.png"));
|
||||
actionRoute->setIcon(iconRoute);
|
||||
actionRoute->setIcon(QIcon(":/icons/resources/route.png"));
|
||||
actionRoute->setStatusTip("Route current design");
|
||||
connect(actionRoute, SIGNAL(triggered()), task, SIGNAL(route()));
|
||||
actionRoute->setEnabled(false);
|
||||
connect(actionRoute, SIGNAL(triggered()), task, SIGNAL(route()));
|
||||
|
||||
actionSaveAsc = new QAction("Save ASC", this);
|
||||
QIcon iconSaveAsc;
|
||||
iconSaveAsc.addFile(QStringLiteral(":/icons/resources/save_asc.png"));
|
||||
actionSaveAsc->setIcon(iconSaveAsc);
|
||||
actionSaveAsc->setIcon(QIcon(":/icons/resources/save_asc.png"));
|
||||
actionSaveAsc->setStatusTip("Save ASC file");
|
||||
connect(actionSaveAsc, SIGNAL(triggered()), this, SLOT(save_asc()));
|
||||
actionSaveAsc->setEnabled(false);
|
||||
connect(actionSaveAsc, SIGNAL(triggered()), this, SLOT(save_asc()));
|
||||
|
||||
QToolBar *taskFPGABar = new QToolBar();
|
||||
addToolBar(Qt::TopToolBarArea, taskFPGABar);
|
||||
@ -147,28 +133,22 @@ void MainWindow::createMenu()
|
||||
menu_Design->addAction(actionSaveAsc);
|
||||
|
||||
actionPlay = new QAction("Play", this);
|
||||
QIcon iconPlay;
|
||||
iconPlay.addFile(QStringLiteral(":/icons/resources/control_play.png"));
|
||||
actionPlay->setIcon(iconPlay);
|
||||
actionPlay->setIcon(QIcon(":/icons/resources/control_play.png"));
|
||||
actionPlay->setStatusTip("Continue running task");
|
||||
connect(actionPlay, SIGNAL(triggered()), task, SLOT(continue_thread()));
|
||||
actionPlay->setEnabled(false);
|
||||
connect(actionPlay, SIGNAL(triggered()), task, SLOT(continue_thread()));
|
||||
|
||||
actionPause = new QAction("Pause", this);
|
||||
QIcon iconPause;
|
||||
iconPause.addFile(QStringLiteral(":/icons/resources/control_pause.png"));
|
||||
actionPause->setIcon(iconPause);
|
||||
actionPause->setIcon(QIcon(":/icons/resources/control_pause.png"));
|
||||
actionPause->setStatusTip("Pause running task");
|
||||
connect(actionPause, SIGNAL(triggered()), task, SLOT(pause_thread()));
|
||||
actionPause->setEnabled(false);
|
||||
connect(actionPause, SIGNAL(triggered()), task, SLOT(pause_thread()));
|
||||
|
||||
actionStop = new QAction("Stop", this);
|
||||
QIcon iconStop;
|
||||
iconStop.addFile(QStringLiteral(":/icons/resources/control_stop.png"));
|
||||
actionStop->setIcon(iconStop);
|
||||
actionStop->setIcon(QIcon(":/icons/resources/control_stop.png"));
|
||||
actionStop->setStatusTip("Stop running task");
|
||||
connect(actionStop, SIGNAL(triggered()), task, SLOT(terminate_thread()));
|
||||
actionStop->setEnabled(false);
|
||||
connect(actionStop, SIGNAL(triggered()), task, SLOT(terminate_thread()));
|
||||
|
||||
QToolBar *taskToolBar = new QToolBar();
|
||||
addToolBar(Qt::TopToolBarArea, taskToolBar);
|
||||
@ -220,12 +200,16 @@ QStringList getSupportedPackages(ArchArgs::ArchArgsTypes chip)
|
||||
void MainWindow::new_proj()
|
||||
{
|
||||
QMap<QString, int> arch;
|
||||
#ifdef ICE40_HX1K_ONLY
|
||||
arch.insert("Lattice HX1K", ArchArgs::HX1K);
|
||||
#else
|
||||
arch.insert("Lattice LP384", ArchArgs::LP384);
|
||||
arch.insert("Lattice LP1K", ArchArgs::LP1K);
|
||||
arch.insert("Lattice HX1K", ArchArgs::HX1K);
|
||||
arch.insert("Lattice UP5K", ArchArgs::UP5K);
|
||||
arch.insert("Lattice LP8K", ArchArgs::LP8K);
|
||||
arch.insert("Lattice HX8K", ArchArgs::HX8K);
|
||||
#endif
|
||||
bool ok;
|
||||
QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
|
||||
if (ok && !item.isEmpty()) {
|
||||
@ -237,18 +221,30 @@ void MainWindow::new_proj()
|
||||
|
||||
if (ok && !item.isEmpty()) {
|
||||
disableActions();
|
||||
preload_pcf = "";
|
||||
chipArgs.package = package.toStdString().c_str();
|
||||
if (ctx)
|
||||
delete ctx;
|
||||
ctx = new Context(chipArgs);
|
||||
|
||||
Q_EMIT contextChanged(ctx);
|
||||
|
||||
ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
||||
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)
|
||||
{
|
||||
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"));
|
||||
if (!fileName.isEmpty()) {
|
||||
tabWidget->setCurrentWidget(info);
|
||||
|
||||
std::string fn = fileName.toStdString();
|
||||
disableActions();
|
||||
}
|
||||
@ -270,12 +264,7 @@ void MainWindow::open_json()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
|
||||
if (!fileName.isEmpty()) {
|
||||
tabWidget->setCurrentWidget(info);
|
||||
|
||||
std::string fn = fileName.toStdString();
|
||||
disableActions();
|
||||
timing_driven = false;
|
||||
Q_EMIT task->loadfile(fn);
|
||||
load_json(fileName.toStdString(), "");
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,11 +272,7 @@ void MainWindow::open_pcf()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this, QString("Open PCF"), QString(), QString("*.pcf"));
|
||||
if (!fileName.isEmpty()) {
|
||||
tabWidget->setCurrentWidget(info);
|
||||
|
||||
std::string fn = fileName.toStdString();
|
||||
disableActions();
|
||||
Q_EMIT task->loadpcf(fn);
|
||||
load_pcf(fileName.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,9 +313,12 @@ void MainWindow::loadfile_finished(bool status)
|
||||
log("Loading design successful.\n");
|
||||
actionLoadPCF->setEnabled(true);
|
||||
actionPack->setEnabled(true);
|
||||
if (!preload_pcf.empty())
|
||||
load_pcf(preload_pcf);
|
||||
Q_EMIT updateTreeView();
|
||||
} else {
|
||||
log("Loading design failed.\n");
|
||||
preload_pcf = "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,4 +428,4 @@ void MainWindow::budget()
|
||||
|
||||
void MainWindow::place() { Q_EMIT task->place(timing_driven); }
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -30,12 +30,13 @@ class MainWindow : public BaseMainWindow
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
|
||||
virtual ~MainWindow();
|
||||
|
||||
public:
|
||||
void createMenu();
|
||||
|
||||
void load_json(std::string filename, std::string pcf);
|
||||
void load_pcf(std::string filename);
|
||||
protected Q_SLOTS:
|
||||
virtual void new_proj();
|
||||
virtual void open_proj();
|
||||
@ -78,6 +79,7 @@ class MainWindow : public BaseMainWindow
|
||||
|
||||
bool timing_driven;
|
||||
ArchArgs chipArgs;
|
||||
std::string preload_pcf;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -23,10 +23,7 @@
|
||||
#include "design_utils.h"
|
||||
#include "jsonparse.h"
|
||||
#include "log.h"
|
||||
#include "pack.h"
|
||||
#include "pcf.h"
|
||||
#include "place_sa.h"
|
||||
#include "route.h"
|
||||
#include "timing.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
@ -99,7 +96,7 @@ void Worker::pack()
|
||||
{
|
||||
Q_EMIT taskStarted();
|
||||
try {
|
||||
bool res = pack_design(ctx);
|
||||
bool res = ctx->pack();
|
||||
print_utilisation(ctx);
|
||||
Q_EMIT pack_finished(res);
|
||||
} catch (WorkerInterruptionRequested) {
|
||||
@ -124,7 +121,7 @@ void Worker::place(bool timing_driven)
|
||||
Q_EMIT taskStarted();
|
||||
try {
|
||||
ctx->timing_driven = timing_driven;
|
||||
Q_EMIT place_finished(place_design_sa(ctx));
|
||||
Q_EMIT place_finished(ctx->place());
|
||||
} catch (WorkerInterruptionRequested) {
|
||||
Q_EMIT taskCanceled();
|
||||
}
|
||||
@ -134,7 +131,7 @@ void Worker::route()
|
||||
{
|
||||
Q_EMIT taskStarted();
|
||||
try {
|
||||
Q_EMIT route_finished(route_design(ctx));
|
||||
Q_EMIT route_finished(ctx->route());
|
||||
} catch (WorkerInterruptionRequested) {
|
||||
Q_EMIT taskCanceled();
|
||||
}
|
||||
|
@ -33,9 +33,10 @@ class InfoTab : public QWidget
|
||||
public:
|
||||
explicit InfoTab(QWidget *parent = 0);
|
||||
void info(std::string str);
|
||||
public Q_SLOTS:
|
||||
void clearBuffer();
|
||||
private Q_SLOTS:
|
||||
void showContextMenu(const QPoint &pt);
|
||||
void clearBuffer();
|
||||
|
||||
private:
|
||||
QPlainTextEdit *plainTextEdit;
|
||||
|
@ -2,6 +2,7 @@
|
||||
* 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
|
||||
@ -19,10 +20,14 @@
|
||||
|
||||
#include "line_editor.h"
|
||||
#include <QKeyEvent>
|
||||
#include <QToolTip>
|
||||
#include "ColumnFormatter.h"
|
||||
#include "Utils.h"
|
||||
#include "pyinterpreter.h"
|
||||
|
||||
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);
|
||||
QAction *clearAction = new QAction("Clear &history", this);
|
||||
@ -38,10 +43,12 @@ LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0)
|
||||
|
||||
void LineEditor::keyPressEvent(QKeyEvent *ev)
|
||||
{
|
||||
|
||||
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
|
||||
QToolTip::hideText();
|
||||
if (lines.empty())
|
||||
return;
|
||||
|
||||
printf("Key_Up\n");
|
||||
if (ev->key() == Qt::Key_Up)
|
||||
index--;
|
||||
if (ev->key() == Qt::Key_Down)
|
||||
@ -56,12 +63,21 @@ void LineEditor::keyPressEvent(QKeyEvent *ev)
|
||||
}
|
||||
setText(lines[index]);
|
||||
} else if (ev->key() == Qt::Key_Escape) {
|
||||
QToolTip::hideText();
|
||||
clear();
|
||||
return;
|
||||
} else if (ev->key() == Qt::Key_Tab) {
|
||||
autocomplete();
|
||||
return;
|
||||
}
|
||||
QToolTip::hideText();
|
||||
|
||||
QLineEdit::keyPressEvent(ev);
|
||||
}
|
||||
|
||||
// This makes TAB work
|
||||
bool LineEditor::focusNextPrevChild(bool next) { return false; }
|
||||
|
||||
void LineEditor::textInserted()
|
||||
{
|
||||
if (lines.empty() || lines.back() != text())
|
||||
@ -82,4 +98,34 @@ void LineEditor::clearHistory()
|
||||
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
|
||||
|
@ -2,6 +2,7 @@
|
||||
* 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
|
||||
@ -22,6 +23,7 @@
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include "ParseHelper.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
@ -31,7 +33,7 @@ class LineEditor : public QLineEdit
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LineEditor(QWidget *parent = 0);
|
||||
explicit LineEditor(ParseHelper *helper, QWidget *parent = 0);
|
||||
|
||||
private Q_SLOTS:
|
||||
void textInserted();
|
||||
@ -43,11 +45,14 @@ class LineEditor : public QLineEdit
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE;
|
||||
bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE;
|
||||
void autocomplete();
|
||||
|
||||
private:
|
||||
int index;
|
||||
QStringList lines;
|
||||
QMenu *contextMenu;
|
||||
ParseHelper *parseHelper;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
79
gui/pyconsole.cc
Normal file
79
gui/pyconsole.cc
Normal 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
55
gui/pyconsole.h
Normal 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
|
@ -16,7 +16,6 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "pythontab.h"
|
||||
#include <QGridLayout>
|
||||
@ -25,12 +24,20 @@
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
const QString PythonTab::PROMPT = ">>> ";
|
||||
const QString PythonTab::MULTILINE_PROMPT = "... ";
|
||||
|
||||
PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
|
||||
{
|
||||
QFont f("unexistent");
|
||||
f.setStyleHint(QFont::Monospace);
|
||||
|
||||
// Add text area for Python output and input line
|
||||
console = new PythonConsole();
|
||||
console->setMinimumHeight(100);
|
||||
console->setEnabled(false);
|
||||
console->setReadOnly(true);
|
||||
console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
|
||||
console->setFont(f);
|
||||
|
||||
console->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
QAction *clearAction = new QAction("Clear &buffer", this);
|
||||
@ -41,9 +48,21 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
|
||||
contextMenu->addAction(clearAction);
|
||||
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();
|
||||
mainLayout->addWidget(console, 0, 0);
|
||||
mainLayout->addWidget(lineEdit, 1, 0);
|
||||
setLayout(mainLayout);
|
||||
|
||||
parseHelper.subscribe(console);
|
||||
|
||||
prompt = PythonTab::PROMPT;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (initialized) {
|
||||
pyinterpreter_finalize();
|
||||
deinit_python();
|
||||
}
|
||||
console->setEnabled(true);
|
||||
console->clear();
|
||||
|
||||
pyinterpreter_preinit();
|
||||
@ -74,13 +106,12 @@ void PythonTab::newContext(Context *ctx)
|
||||
|
||||
QString version = QString("Python %1 on %2\n").arg(Py_GetVersion(), Py_GetPlatform());
|
||||
console->displayString(version);
|
||||
console->displayPrompt();
|
||||
}
|
||||
|
||||
void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
|
||||
|
||||
void PythonTab::clearBuffer() { console->clear(); }
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
void PythonTab::info(std::string str) { console->displayString(str.c_str()); }
|
||||
|
||||
#endif
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -20,11 +20,10 @@
|
||||
#ifndef PYTHONTAB_H
|
||||
#define PYTHONTAB_H
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QPlainTextEdit>
|
||||
#include "ParseHelper.h"
|
||||
#include "line_editor.h"
|
||||
#include "nextpnr.h"
|
||||
#include "pyconsole.h"
|
||||
@ -41,17 +40,24 @@ class PythonTab : public QWidget
|
||||
|
||||
private Q_SLOTS:
|
||||
void showContextMenu(const QPoint &pt);
|
||||
void clearBuffer();
|
||||
void editLineReturnPressed(QString text);
|
||||
public Q_SLOTS:
|
||||
void newContext(Context *ctx);
|
||||
void info(std::string str);
|
||||
void clearBuffer();
|
||||
|
||||
private:
|
||||
PythonConsole *console;
|
||||
LineEditor *lineEdit;
|
||||
QMenu *contextMenu;
|
||||
bool initialized;
|
||||
ParseHelper parseHelper;
|
||||
QString prompt;
|
||||
|
||||
static const QString PROMPT;
|
||||
static const QString MULTILINE_PROMPT;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif // NO_PYTHON
|
||||
|
||||
#endif // PYTHONTAB_H
|
||||
|
BIN
gui/resources/resultset_first.png
Normal file
BIN
gui/resources/resultset_first.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 522 B |
BIN
gui/resources/resultset_last.png
Normal file
BIN
gui/resources/resultset_last.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 524 B |
BIN
gui/resources/resultset_next.png
Normal file
BIN
gui/resources/resultset_next.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 395 B |
BIN
gui/resources/resultset_previous.png
Normal file
BIN
gui/resources/resultset_previous.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 389 B |
BIN
gui/resources/splash.png
Normal file
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
BIN
gui/resources/zoom.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 692 B |
317
ice40/arch.cc
317
ice40/arch.cc
@ -19,9 +19,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "gfx.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "placer1.h"
|
||||
#include "router1.h"
|
||||
#include "util.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@ -365,6 +369,49 @@ std::string Arch::getBelPackagePin(BelId bel) const
|
||||
}
|
||||
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
|
||||
@ -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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return ret;
|
||||
DecalXY decalxy;
|
||||
decalxy.decal.type = DecalId::TYPE_FRAME;
|
||||
decalxy.decal.active = true;
|
||||
return decalxy;
|
||||
}
|
||||
|
||||
std::vector<GraphicElement> Arch::getBelGraphics(BelId bel) const
|
||||
DecalXY Arch::getBelDecal(BelId bel) const
|
||||
{
|
||||
std::vector<GraphicElement> ret;
|
||||
|
||||
auto bel_type = getBelType(bel);
|
||||
|
||||
if (bel_type == TYPE_ICESTORM_LC) {
|
||||
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;
|
||||
DecalXY decalxy;
|
||||
decalxy.decal.type = DecalId::TYPE_BEL;
|
||||
decalxy.decal.index = bel.index;
|
||||
decalxy.decal.active = bel_to_cell.at(bel.index) != IdString();
|
||||
return decalxy;
|
||||
}
|
||||
|
||||
std::vector<GraphicElement> Arch::getWireGraphics(WireId wire) const
|
||||
DecalXY Arch::getWireDecal(WireId wire) const
|
||||
{
|
||||
std::vector<GraphicElement> ret;
|
||||
// FIXME
|
||||
return ret;
|
||||
DecalXY decalxy;
|
||||
decalxy.decal.type = DecalId::TYPE_WIRE;
|
||||
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;
|
||||
// FIXME
|
||||
return ret;
|
||||
DecalXY decalxy;
|
||||
decalxy.decal.type = DecalId::TYPE_PIP;
|
||||
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
|
||||
|
46
ice40/arch.h
46
ice40/arch.h
@ -70,6 +70,11 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD {
|
||||
int32_t switch_index;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct WireSegmentPOD {
|
||||
int8_t x, y;
|
||||
int16_t index;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct WireInfoPOD {
|
||||
RelPtr<char> name;
|
||||
int32_t num_uphill, num_downhill;
|
||||
@ -79,6 +84,9 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD {
|
||||
BelPortPOD bel_uphill;
|
||||
RelPtr<BelPortPOD> bels_downhill;
|
||||
|
||||
int32_t num_segments;
|
||||
RelPtr<WireSegmentPOD> segments;
|
||||
|
||||
int8_t x, y;
|
||||
WireType type;
|
||||
int8_t padding_0;
|
||||
@ -515,9 +523,6 @@ struct Arch : BaseCtx
|
||||
// -------------------------------------------------
|
||||
|
||||
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)
|
||||
{
|
||||
@ -577,6 +582,10 @@ struct Arch : BaseCtx
|
||||
range.e.cursor = chip_info->num_pips;
|
||||
return range;
|
||||
}
|
||||
|
||||
IdString getPipName(PipId pip) const;
|
||||
|
||||
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
|
||||
|
||||
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;
|
||||
delay_t estimateDelay(WireId src, WireId dst) const;
|
||||
delay_t getDelayEpsilon() const { return 20; }
|
||||
@ -643,16 +662,19 @@ struct Arch : BaseCtx
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
std::vector<GraphicElement> getFrameGraphics() const;
|
||||
std::vector<GraphicElement> getBelGraphics(BelId bel) const;
|
||||
std::vector<GraphicElement> getWireGraphics(WireId wire) const;
|
||||
std::vector<GraphicElement> getPipGraphics(PipId pip) const;
|
||||
bool pack();
|
||||
bool place();
|
||||
bool route();
|
||||
|
||||
bool allGraphicsReload = false;
|
||||
bool frameGraphicsReload = false;
|
||||
std::unordered_set<BelId> belGraphicsReload;
|
||||
std::unordered_set<WireId> wireGraphicsReload;
|
||||
std::unordered_set<PipId> pipGraphicsReload;
|
||||
// -------------------------------------------------
|
||||
|
||||
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;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
|
@ -58,7 +58,10 @@ void arch_wrap_python()
|
||||
|
||||
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)
|
||||
.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>,
|
||||
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,
|
||||
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,
|
||||
"getBels");
|
||||
"getBels");
|
||||
fn_wrapper_1a<Context, decltype(&Context::getBelsAtSameTile), &Context::getBelsAtSameTile, wrap_context<BelRange>,
|
||||
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(
|
||||
ctx_cls, "getChipName");
|
||||
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<NetInfo>> NetMap;
|
||||
|
||||
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,
|
||||
"nets");
|
||||
"nets");
|
||||
WRAP_RANGE(Bel, conv_to_str<BelId>);
|
||||
WRAP_RANGE(Wire, conv_to_str<WireId>);
|
||||
WRAP_RANGE(AllPip, conv_to_str<PipId>);
|
||||
|
@ -21,6 +21,8 @@
|
||||
#error Include "archdefs.h" via "nextpnr.h" only.
|
||||
#endif
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
typedef int delay_t;
|
||||
@ -107,6 +109,47 @@ struct PipId
|
||||
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
|
||||
|
||||
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 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
|
||||
|
@ -341,9 +341,8 @@ void write_asc(const Context *ctx, std::ostream &out)
|
||||
set_config(ti, config.at(y).at(x),
|
||||
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
|
||||
else
|
||||
set_config(ti, config.at(y).at(x),
|
||||
"Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
|
||||
std::to_string(lc_idx) + "_inmux02_5",
|
||||
set_config(ti, config.at(y).at(x), "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) +
|
||||
"_LC0" + std::to_string(lc_idx) + "_inmux02_5",
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ group.add_argument("-b", "--binary", 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("-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()
|
||||
|
||||
endianness = "le"
|
||||
@ -54,6 +55,9 @@ beltypes = dict()
|
||||
tiletypes = dict()
|
||||
wiretypes = dict()
|
||||
|
||||
gfx_wire_ids = dict()
|
||||
wire_segments = dict()
|
||||
|
||||
with open(args.portspins) as f:
|
||||
for line in f:
|
||||
line = line.replace("(", " ")
|
||||
@ -66,6 +70,20 @@ with open(args.portspins) as f:
|
||||
idx = len(portpins) + 1
|
||||
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_RAM"] = 2
|
||||
beltypes["SB_IO"] = 3
|
||||
@ -371,6 +389,10 @@ with open(args.filename, "r") as f:
|
||||
if mode[1] not in wire_xy:
|
||||
wire_xy[mode[1]] = list()
|
||||
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
|
||||
|
||||
if mode[0] in ("buffer", "routing"):
|
||||
@ -712,7 +734,7 @@ class BinaryBlobAssembler:
|
||||
|
||||
def finalize(self):
|
||||
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")
|
||||
for c in s:
|
||||
self.data.append(ord(c))
|
||||
@ -947,7 +969,7 @@ for wire in range(num_wires):
|
||||
if wire in wire_downhill_belports:
|
||||
num_bels_downhill = len(wire_downhill_belports[wire])
|
||||
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(portpins[belport[1]], "port")
|
||||
else:
|
||||
@ -1040,7 +1062,7 @@ for t in range(num_tile_types):
|
||||
tileinfo.append(ti)
|
||||
|
||||
bba.l("wire_data_%s" % dev_name, "WireInfoPOD")
|
||||
for info in wireinfo:
|
||||
for wire, info in enumerate(wireinfo):
|
||||
bba.s(info["name"], "name")
|
||||
bba.u32(info["num_uphill"], "num_uphill")
|
||||
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_pin"], "bel_uphill.port")
|
||||
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["y"], "y")
|
||||
bba.u8(wiretypes[wire_type(info["name"])], "type")
|
||||
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")
|
||||
for info in pipinfo:
|
||||
bba.u32(info["src"], "src")
|
||||
|
@ -21,8 +21,9 @@ if (MSVC)
|
||||
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
|
||||
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_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
|
||||
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}
|
||||
)
|
||||
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
|
||||
@ -37,8 +38,9 @@ else()
|
||||
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
|
||||
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_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
|
||||
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}
|
||||
DEPENDS ${DEV_TXT_DB} ${DB_PY}
|
||||
)
|
||||
|
488
ice40/gfx.cc
Normal file
488
ice40/gfx.cc
Normal 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
474
ice40/gfx.h
Normal 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
|
164
ice40/main.cc
164
ice40/main.cc
@ -39,30 +39,30 @@
|
||||
#include "jsonparse.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "pack.h"
|
||||
#include "pcf.h"
|
||||
#include "place_legaliser.h"
|
||||
#include "place_sa.h"
|
||||
#include "route.h"
|
||||
#include "timing.h"
|
||||
#include "version.h"
|
||||
|
||||
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;
|
||||
std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\"";
|
||||
const float scale = 10.0, offset = 10.0;
|
||||
const std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\"";
|
||||
|
||||
if (el.type == GraphicElement::G_BOX) {
|
||||
std::cout << "<rect x=\"" << (offset + scale * el.x1) << "\" y=\"" << (offset + scale * el.y1) << "\" height=\""
|
||||
<< (scale * (el.y2 - el.y1)) << "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style
|
||||
<< "/>\n";
|
||||
}
|
||||
for (auto &el : ctx->getDecalGraphics(decal.decal)) {
|
||||
if (el.type == GraphicElement::G_BOX) {
|
||||
std::cout << "<rect x=\"" << (offset + scale * (decal.x + el.x1)) << "\" y=\""
|
||||
<< (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) {
|
||||
std::cout << "<line x1=\"" << (offset + scale * el.x1) << "\" y1=\"" << (offset + scale * el.y1) << "\" x2=\""
|
||||
<< (offset + scale * el.x2) << "\" y2=\"" << (offset + scale * el.y2) << "\" " << style << "/>\n";
|
||||
if (el.type == GraphicElement::G_LINE) {
|
||||
std::cout << "<line x1=\"" << (offset + scale * (decal.x + el.x1)) << "\" y1=\""
|
||||
<< (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()("version,V", "show version");
|
||||
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()("lp1k", "set device type to iCE40LP1K");
|
||||
options.add_options()("lp8k", "set device type to iCE40LP8K");
|
||||
options.add_options()("hx1k", "set device type to iCE40HX1K");
|
||||
options.add_options()("hx8k", "set device type to iCE40HX8K");
|
||||
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()("no-tmdriv", "disable timing-driven placement");
|
||||
options.add_options()("package", po::value<std::string>(), "set device package");
|
||||
@ -267,112 +271,126 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
Context ctx(chipArgs);
|
||||
std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
||||
|
||||
if (vm.count("verbose")) {
|
||||
ctx.verbose = true;
|
||||
ctx->verbose = true;
|
||||
}
|
||||
|
||||
if (vm.count("debug")) {
|
||||
ctx.verbose = true;
|
||||
ctx.debug = true;
|
||||
ctx->verbose = true;
|
||||
ctx->debug = true;
|
||||
}
|
||||
|
||||
if (vm.count("force")) {
|
||||
ctx.force = true;
|
||||
ctx->force = true;
|
||||
}
|
||||
|
||||
if (vm.count("seed")) {
|
||||
ctx.rngseed(vm["seed"].as<int>());
|
||||
ctx->rngseed(vm["seed"].as<int>());
|
||||
}
|
||||
|
||||
if (vm.count("svg")) {
|
||||
std::cout << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
||||
"xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
|
||||
for (auto bel : ctx.getBels()) {
|
||||
std::cout << "<!-- " << ctx.getBelName(bel).str(&ctx) << " -->\n";
|
||||
for (auto &el : ctx.getBelGraphics(bel))
|
||||
svg_dump_el(el);
|
||||
for (auto bel : ctx->getBels()) {
|
||||
std::cout << "<!-- " << ctx->getBelName(bel).str(ctx.get()) << " -->\n";
|
||||
svg_dump_decal(ctx.get(), ctx->getBelDecal(bel));
|
||||
}
|
||||
std::cout << "<!-- Frame -->\n";
|
||||
for (auto &el : ctx.getFrameGraphics())
|
||||
svg_dump_el(el);
|
||||
svg_dump_decal(ctx.get(), ctx->getFrameDecal());
|
||||
std::cout << "</svg>\n";
|
||||
}
|
||||
|
||||
if (vm.count("tmfuzz")) {
|
||||
std::vector<WireId> src_wires, dst_wires;
|
||||
|
||||
/*for (auto w : ctx.getWires())
|
||||
/*for (auto w : ctx->getWires())
|
||||
src_wires.push_back(w);*/
|
||||
for (auto b : ctx.getBels()) {
|
||||
if (ctx.getBelType(b) == TYPE_ICESTORM_LC) {
|
||||
src_wires.push_back(ctx.getWireBelPin(b, PIN_O));
|
||||
for (auto b : ctx->getBels()) {
|
||||
if (ctx->getBelType(b) == TYPE_ICESTORM_LC) {
|
||||
src_wires.push_back(ctx->getWireBelPin(b, PIN_O));
|
||||
}
|
||||
if (ctx.getBelType(b) == TYPE_SB_IO) {
|
||||
src_wires.push_back(ctx.getWireBelPin(b, PIN_D_IN_0));
|
||||
if (ctx->getBelType(b) == TYPE_SB_IO) {
|
||||
src_wires.push_back(ctx->getWireBelPin(b, PIN_D_IN_0));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto b : ctx.getBels()) {
|
||||
if (ctx.getBelType(b) == TYPE_ICESTORM_LC) {
|
||||
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_I2));
|
||||
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_CIN));
|
||||
for (auto b : ctx->getBels()) {
|
||||
if (ctx->getBelType(b) == TYPE_ICESTORM_LC) {
|
||||
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_I2));
|
||||
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_CIN));
|
||||
}
|
||||
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_OUTPUT_ENABLE));
|
||||
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_OUTPUT_ENABLE));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.shuffle(src_wires);
|
||||
ctx.shuffle(dst_wires);
|
||||
ctx->shuffle(src_wires);
|
||||
ctx->shuffle(dst_wires);
|
||||
|
||||
for (int i = 0; i < int(src_wires.size()) && i < int(dst_wires.size()); i++) {
|
||||
delay_t actual_delay;
|
||||
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;
|
||||
printf("%s %s %.3f %.3f %d %d %d %d %d %d\n", ctx.getWireName(src).c_str(&ctx),
|
||||
ctx.getWireName(dst).c_str(&ctx), ctx.getDelayNS(actual_delay),
|
||||
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[dst.index].x, ctx.chip_info->wire_data[dst.index].y,
|
||||
ctx.chip_info->wire_data[dst.index].type);
|
||||
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.get()), ctx->getDelayNS(actual_delay),
|
||||
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[dst.index].x, ctx->chip_info->wire_data[dst.index].y,
|
||||
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")) {
|
||||
std::string filename = vm["json"].as<std::string>();
|
||||
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");
|
||||
|
||||
if (vm.count("pcf")) {
|
||||
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");
|
||||
}
|
||||
|
||||
if (!pack_design(&ctx) && !ctx.force)
|
||||
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);
|
||||
ctx.check();
|
||||
print_utilisation(&ctx);
|
||||
ctx.timing_driven = true;
|
||||
if (vm.count("no-tmdriv"))
|
||||
ctx.timing_driven = false;
|
||||
assign_budget(ctx.get());
|
||||
ctx->check();
|
||||
print_utilisation(ctx.get());
|
||||
if (!vm.count("pack-only")) {
|
||||
if (!place_design_sa(&ctx) && !ctx.force)
|
||||
if (!ctx->place() && !ctx->force)
|
||||
log_error("Placing design failed.\n");
|
||||
ctx.check();
|
||||
if (!route_design(&ctx) && !ctx.force)
|
||||
ctx->check();
|
||||
if (!ctx->route() && !ctx->force)
|
||||
log_error("Routing design failed.\n");
|
||||
}
|
||||
}
|
||||
@ -380,13 +398,13 @@ int main(int argc, char *argv[])
|
||||
if (vm.count("asc")) {
|
||||
std::string filename = vm["asc"].as<std::string>();
|
||||
std::ofstream f(filename);
|
||||
write_asc(&ctx, f);
|
||||
write_asc(ctx.get(), f);
|
||||
}
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
if (vm.count("run")) {
|
||||
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>>();
|
||||
for (auto filename : files)
|
||||
@ -395,16 +413,6 @@ int main(int argc, char *argv[])
|
||||
deinit_python();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_GUI
|
||||
if (vm.count("gui")) {
|
||||
Application a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
rc = a.exec();
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
} catch (log_execution_error_exception) {
|
||||
#if defined(_MSC_VER)
|
||||
|
@ -18,7 +18,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "pack.h"
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <unordered_set>
|
||||
@ -577,8 +576,9 @@ static void pack_special(Context *ctx)
|
||||
}
|
||||
|
||||
// Main pack function
|
||||
bool pack_design(Context *ctx)
|
||||
bool Arch::pack()
|
||||
{
|
||||
Context *ctx = getCtx();
|
||||
try {
|
||||
log_break();
|
||||
pack_constants(ctx);
|
||||
|
Loading…
Reference in New Issue
Block a user