Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr
This commit is contained in:
commit
145c849596
@ -79,36 +79,42 @@ aux_source_directory(frontend/json/ JSON_PARSER_FILES)
|
||||
set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES})
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
if(MINGW)
|
||||
add_definitions("-Wa,-mbig-obj")
|
||||
endif(MINGW)
|
||||
|
||||
foreach (family ${FAMILIES})
|
||||
string(TOUPPER ${family} ufamily)
|
||||
aux_source_directory(${family}/ ${ufamily}_FILES)
|
||||
aux_source_directory(tests/${family}/ ${ufamily}_TEST_FILES)
|
||||
# Add the CLI binary target
|
||||
add_executable(nextpnr-${family} ${COMMON_FILES} ${${ufamily}_FILES} ${GUI_SOURCE_FILES})
|
||||
target_compile_definitions(nextpnr-${family} PRIVATE MAIN_EXECUTABLE)
|
||||
|
||||
# Add the importable Python module target
|
||||
PYTHON_ADD_MODULE(nextpnrpy_${family} ${COMMON_FILES} ${${ufamily}_FILES})
|
||||
target_compile_definitions(nextpnrpy_${family} PRIVATE PYTHON_MODULE)
|
||||
# Add any new per-architecture targets here
|
||||
|
||||
add_executable(nextpnr-${family}-test ${${ufamily}_TEST_FILES} ${COMMON_FILES} ${${ufamily}_FILES})
|
||||
target_link_libraries(nextpnr-${family}-test PRIVATE gtest_main)
|
||||
add_test(${family}-test ${CMAKE_CURRENT_BINARY_DIR}/nextpnr-${family}-test)
|
||||
|
||||
# Set ${family_targets} to the list of targets being build for this family
|
||||
set(family_targets nextpnr-${family} nextpnrpy_${family})
|
||||
set(family_targets nextpnr-${family} nextpnrpy_${family} nextpnr-${family}-test)
|
||||
# Include the family-specific CMakeFile
|
||||
include(${family}/family.cmake)
|
||||
foreach (target ${family_targets})
|
||||
# Include family-specific source files to all family targets and set defines appropriately
|
||||
target_include_directories(${target} PRIVATE ${family}/)
|
||||
target_compile_definitions(${target} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family} -DQT_NO_KEYWORDS)
|
||||
target_compile_definitions(${target} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family} QT_NO_KEYWORDS)
|
||||
target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} ${GUI_LIBRARY_FILES})
|
||||
endforeach (target)
|
||||
|
||||
add_executable(nextpnr-${family}-test "")
|
||||
target_sources(nextpnr-${family}-test PRIVATE tests/${family}/main.cpp)
|
||||
target_link_libraries(nextpnr-${family}-test PRIVATE gtest_main)
|
||||
add_test(${family}-test ${CMAKE_CURRENT_BINARY_DIR}/nextpnr-${family}-test)
|
||||
endforeach (family)
|
||||
|
||||
file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h)
|
||||
string(REGEX REPLACE "[^;]*/ice40/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||
string(REGEX REPLACE "[^;]*/3rdparty[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||
string(REGEX REPLACE "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||
|
||||
add_custom_target(
|
||||
clangformat
|
||||
|
@ -22,6 +22,8 @@
|
||||
#ifndef DESIGN_UTILS_H
|
||||
#define DESIGN_UTILS_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
/*
|
||||
@ -35,23 +37,36 @@ void replace_port(CellInfo *old_cell, IdString old_name, CellInfo *rep_cell,
|
||||
// If a net drives a given port of a cell matching a predicate (in many
|
||||
// cases more than one cell type, e.g. SB_DFFxx so a predicate is used), return
|
||||
// the first instance of that cell (otherwise nullptr). If exclusive is set to
|
||||
// true, then this cell must be the only load
|
||||
// true, then this cell must be the only load. If ignore_cell is set, that cell
|
||||
// is not considered
|
||||
template <typename F1>
|
||||
CellInfo *net_only_drives(NetInfo *net, F1 cell_pred, IdString port,
|
||||
bool exclusive = false)
|
||||
bool exclusive = false, CellInfo *exclude = nullptr)
|
||||
{
|
||||
if (net == nullptr)
|
||||
return nullptr;
|
||||
if (exclusive && (net->users.size() != 1)) {
|
||||
if (exclusive) {
|
||||
if (exclude == nullptr) {
|
||||
if (net->users.size() != 1)
|
||||
return nullptr;
|
||||
} else {
|
||||
if (net->users.size() > 2) {
|
||||
return nullptr;
|
||||
} else if (net->users.size() == 2) {
|
||||
if (std::find_if(net->users.begin(), net->users.end(),
|
||||
[exclude](const PortRef &ref) {
|
||||
return ref.cell == exclude;
|
||||
}) == net->users.end())
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &load : net->users) {
|
||||
if (cell_pred(load.cell) && load.port == port) {
|
||||
if (load.cell != exclude && cell_pred(load.cell) && load.port == port) {
|
||||
return load.cell;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// If a net is driven by a given port of a cell matching a predicate, return
|
||||
|
@ -140,7 +140,7 @@ static wchar_t *program;
|
||||
|
||||
void init_python(const char *executable)
|
||||
{
|
||||
#ifndef PYTHON_MODULE
|
||||
#ifdef MAIN_EXECUTABLE
|
||||
program = Py_DecodeLocale(executable, NULL);
|
||||
if (program == NULL) {
|
||||
fprintf(stderr, "Fatal error: cannot decode executable filename\n");
|
||||
@ -162,7 +162,7 @@ void init_python(const char *executable)
|
||||
|
||||
void deinit_python()
|
||||
{
|
||||
#ifndef PYTHON_MODULE
|
||||
#ifdef MAIN_EXECUTABLE
|
||||
Py_Finalize();
|
||||
PyMem_RawFree(program);
|
||||
#endif
|
||||
|
@ -17,7 +17,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PYTHON_MODULE
|
||||
#ifdef MAIN_EXECUTABLE
|
||||
|
||||
#include <QApplication>
|
||||
#include "mainwindow.h"
|
||||
|
@ -341,24 +341,25 @@ void json_import_cell_params(Design *design, string &modname, CellInfo *cell,
|
||||
modname.c_str());
|
||||
}
|
||||
|
||||
void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
||||
string &port_name, JsonNode *dir_node,
|
||||
JsonNode *wire_group_node)
|
||||
template <typename F>
|
||||
void json_import_ports(Design *design, const string &modname,
|
||||
const string &obj_name, const string &port_name,
|
||||
JsonNode *dir_node, JsonNode *wire_group_node, F visitor)
|
||||
{
|
||||
// Examine and connect a single port of the given cell to its nets,
|
||||
// generating them as necessary
|
||||
|
||||
// Examine a port of a cell or the design. For every bit of the port,
|
||||
// the connected net will be processed and `visitor` will be called
|
||||
// with (PortType dir, std::string name, NetInfo *net)
|
||||
assert(dir_node);
|
||||
|
||||
if (json_debug)
|
||||
log_info(" Examining port %s, node %s\n", port_name.c_str(),
|
||||
cell->name.c_str());
|
||||
obj_name.c_str());
|
||||
|
||||
if (!wire_group_node)
|
||||
log_error("JSON no connection match "
|
||||
"for port_direction \'%s\' of node \'%s\' "
|
||||
"in module \'%s\'\n",
|
||||
port_name.c_str(), cell->name.c_str(), modname.c_str());
|
||||
port_name.c_str(), obj_name.c_str(), modname.c_str());
|
||||
|
||||
assert(wire_group_node);
|
||||
|
||||
@ -377,7 +378,7 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
||||
else
|
||||
log_error("JSON unknown port direction \'%s\' in node \'%s\' "
|
||||
"of module \'%s\'\n",
|
||||
dir_node->data_string.c_str(), cell->name.c_str(),
|
||||
dir_node->data_string.c_str(), obj_name.c_str(),
|
||||
modname.c_str());
|
||||
//
|
||||
// Find an update, or create a net to connect
|
||||
@ -398,18 +399,12 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
||||
// There is/are no connections to this port.
|
||||
//
|
||||
// Create the port, but leave the net NULL
|
||||
PortInfo this_port;
|
||||
|
||||
//
|
||||
this_port.name = port_info.name;
|
||||
this_port.type = port_info.type;
|
||||
this_port.net = NULL;
|
||||
|
||||
cell->ports[this_port.name] = this_port;
|
||||
visitor(port_info.type, port_info.name, nullptr);
|
||||
|
||||
if (json_debug)
|
||||
log_info(" Port \'%s\' has no connection in \'%s\'\n",
|
||||
this_port.name.c_str(), cell->name.c_str());
|
||||
port_info.name.c_str(), obj_name.c_str());
|
||||
|
||||
} else
|
||||
for (int index = 0; index < wire_group_node->data_array.size();
|
||||
@ -417,13 +412,10 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
||||
//
|
||||
JsonNode *wire_node;
|
||||
PortInfo this_port;
|
||||
PortRef port_ref;
|
||||
bool const_input = false;
|
||||
IdString net_id;
|
||||
//
|
||||
wire_node = wire_group_node->data_array[index];
|
||||
port_ref.cell = cell;
|
||||
|
||||
//
|
||||
// Pick a name for this port
|
||||
if (is_bus)
|
||||
@ -433,8 +425,6 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
||||
this_port.name = port_info.name;
|
||||
this_port.type = port_info.type;
|
||||
|
||||
port_ref.port = this_port.name;
|
||||
|
||||
if (wire_node->type == 'N') {
|
||||
int net_num;
|
||||
|
||||
@ -500,7 +490,7 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
||||
"\'%s\' of port \'%s\' "
|
||||
"in cell \'%s\' of module \'%s\'\n",
|
||||
wire_node->data_string.c_str(),
|
||||
port_name.c_str(), cell->name.c_str(),
|
||||
port_name.c_str(), obj_name.c_str(),
|
||||
modname.c_str());
|
||||
|
||||
} else
|
||||
@ -511,17 +501,8 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
||||
|
||||
if (json_debug)
|
||||
log_info(" Inserting port \'%s\' into cell \'%s\'\n",
|
||||
this_port.name.c_str(), cell->name.c_str());
|
||||
|
||||
this_port.net = this_net;
|
||||
|
||||
cell->ports[this_port.name] = this_port;
|
||||
|
||||
if (this_port.type == PORT_OUT) {
|
||||
assert(this_net->driver.cell == NULL);
|
||||
this_net->driver = port_ref;
|
||||
} else
|
||||
this_net->users.push_back(port_ref);
|
||||
this_port.name.c_str(), obj_name.c_str());
|
||||
visitor(this_port.type, this_port.name, this_net);
|
||||
|
||||
if (design->nets.count(this_net->name) == 0)
|
||||
design->nets[this_net->name] = this_net;
|
||||
@ -632,14 +613,98 @@ void json_import_cell(Design *design, string modname, JsonNode *cell_node,
|
||||
dir_node = pdir_node->data_dict.at(port_name);
|
||||
wire_group_node = connections->data_dict.at(port_name);
|
||||
|
||||
json_import_cell_ports(design, modname, cell, port_name, dir_node,
|
||||
wire_group_node);
|
||||
json_import_ports(
|
||||
design, modname, cell->name, port_name, dir_node,
|
||||
wire_group_node,
|
||||
[cell](PortType type, const std::string &name, NetInfo *net) {
|
||||
cell->ports[name] = PortInfo{name, net, type};
|
||||
PortRef pr;
|
||||
pr.cell = cell;
|
||||
pr.port = name;
|
||||
if (net != nullptr) {
|
||||
if (type == PORT_IN || type == PORT_INOUT) {
|
||||
net->users.push_back(pr);
|
||||
} else if (type == PORT_OUT) {
|
||||
assert(net->driver.cell == nullptr);
|
||||
net->driver = pr;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
design->cells[cell->name] = cell;
|
||||
// check_all_nets_driven(design);
|
||||
}
|
||||
|
||||
static void insert_iobuf(Design *design, NetInfo *net, PortType type,
|
||||
const string &name)
|
||||
{
|
||||
// Instantiate a architecture-independent IO buffer connected to a given
|
||||
// net, of a given type, and named after the IO port.
|
||||
//
|
||||
// During packing, this generic IO buffer will be converted to an
|
||||
// architecure primitive.
|
||||
//
|
||||
CellInfo *iobuf = new CellInfo();
|
||||
iobuf->name = name;
|
||||
std::copy(net->attrs.begin(), net->attrs.end(),
|
||||
std::inserter(iobuf->attrs, iobuf->attrs.begin()));
|
||||
if (type == PORT_IN) {
|
||||
log_info("processing input port %s\n", name.c_str());
|
||||
iobuf->type = "$nextpnr_ibuf";
|
||||
iobuf->ports["O"] = PortInfo{"O", net, PORT_OUT};
|
||||
|
||||
assert(net->driver.cell == nullptr);
|
||||
net->driver.port = "O";
|
||||
net->driver.cell = iobuf;
|
||||
} else if (type == PORT_OUT) {
|
||||
log_info("processing output port %s\n", name.c_str());
|
||||
iobuf->type = "$nextpnr_obuf";
|
||||
iobuf->ports["I"] = PortInfo{"I", net, PORT_IN};
|
||||
PortRef ref;
|
||||
ref.cell = iobuf;
|
||||
ref.port = "I";
|
||||
net->users.push_back(ref);
|
||||
} else if (type == PORT_INOUT) {
|
||||
log_info("processing inout port %s\n", name.c_str());
|
||||
iobuf->type = "$nextpnr_iobuf";
|
||||
iobuf->ports["I"] = PortInfo{"I", nullptr, PORT_IN};
|
||||
if (net->driver.cell != NULL) {
|
||||
// Split the input and output nets for bidir ports
|
||||
NetInfo *net2 = new NetInfo();
|
||||
net2->name = "$" + net->name.str() + "$iobuf_i";
|
||||
net2->driver = net->driver;
|
||||
net2->driver.cell->ports[net2->driver.port].net = net2;
|
||||
net->driver.cell = nullptr;
|
||||
design->nets[net2->name] = net2;
|
||||
iobuf->ports["I"].net = net2;
|
||||
PortRef ref;
|
||||
ref.cell = iobuf;
|
||||
ref.port = "I";
|
||||
net2->users.push_back(ref);
|
||||
}
|
||||
iobuf->ports["O"] = PortInfo{"O", net, PORT_OUT};
|
||||
assert(net->driver.cell == nullptr);
|
||||
net->driver.port = "O";
|
||||
net->driver.cell = iobuf;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
design->cells[iobuf->name] = iobuf;
|
||||
}
|
||||
|
||||
void json_import_toplevel_port(Design *design, const string &modname,
|
||||
const string &portname, JsonNode *node)
|
||||
{
|
||||
JsonNode *dir_node = node->data_dict.at("direction");
|
||||
JsonNode *nets_node = node->data_dict.at("bits");
|
||||
json_import_ports(
|
||||
design, modname, "Top Level IO", portname, dir_node, nets_node,
|
||||
[design](PortType type, const std::string &name, NetInfo *net) {
|
||||
insert_iobuf(design, net, type, name);
|
||||
});
|
||||
}
|
||||
|
||||
void json_import(Design *design, string modname, JsonNode *node)
|
||||
{
|
||||
if (is_blackbox(node))
|
||||
@ -665,6 +730,23 @@ void json_import(Design *design, string modname, JsonNode *node)
|
||||
}
|
||||
}
|
||||
|
||||
if (node->data_dict.count("ports")) {
|
||||
JsonNode *ports_parent = node->data_dict.at("ports");
|
||||
|
||||
// N.B. ports must be imported after cells for tristate behaviour
|
||||
// to be correct
|
||||
// Loop through all ports
|
||||
for (int portid = 0; portid < GetSize(ports_parent->data_dict_keys);
|
||||
portid++) {
|
||||
JsonNode *here, *param_node;
|
||||
|
||||
here = ports_parent->data_dict.at(
|
||||
ports_parent->data_dict_keys[portid]);
|
||||
json_import_toplevel_port(design, modname,
|
||||
ports_parent->data_dict_keys[portid],
|
||||
here);
|
||||
}
|
||||
}
|
||||
check_all_nets_driven(design);
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,6 @@
|
||||
#include <string>
|
||||
#include "emb.h"
|
||||
#include "pybindings.h"
|
||||
#include "qtpropertymanager.h"
|
||||
#include "qttreepropertybrowser.h"
|
||||
#include "qtvariantproperty.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QDate>
|
||||
@ -18,16 +15,43 @@ MainWindow::MainWindow(Design *_design, QWidget *parent)
|
||||
ui->setupUi(this);
|
||||
ui->treeWidget->setColumnCount(1);
|
||||
ui->treeWidget->setHeaderLabel(QString("Items"));
|
||||
QTreeWidgetItem *belroot = new QTreeWidgetItem(ui->treeWidget);
|
||||
belroot->setText(0, QString("Bels"));
|
||||
ui->treeWidget->insertTopLevelItem(0, belroot);
|
||||
QList<QTreeWidgetItem *> items;
|
||||
ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this,
|
||||
&MainWindow::prepareMenu);
|
||||
|
||||
QTreeWidgetItem *bel_root = new QTreeWidgetItem(ui->treeWidget);
|
||||
bel_root->setText(0, QString("Bels"));
|
||||
ui->treeWidget->insertTopLevelItem(0, bel_root);
|
||||
QList<QTreeWidgetItem *> bel_items;
|
||||
for (auto bel : design->chip.getBels()) {
|
||||
auto name = design->chip.getBelName(bel);
|
||||
items.append(new QTreeWidgetItem((QTreeWidget *)nullptr,
|
||||
QStringList(QString(name.c_str()))));
|
||||
bel_items.append(new QTreeWidgetItem(
|
||||
(QTreeWidget *)nullptr, QStringList(QString(name.c_str()))));
|
||||
}
|
||||
belroot->addChildren(items);
|
||||
bel_root->addChildren(bel_items);
|
||||
|
||||
QTreeWidgetItem *wire_root = new QTreeWidgetItem(ui->treeWidget);
|
||||
QList<QTreeWidgetItem *> wire_items;
|
||||
wire_root->setText(0, QString("Wires"));
|
||||
ui->treeWidget->insertTopLevelItem(0, wire_root);
|
||||
for (auto wire : design->chip.getWires()) {
|
||||
auto name = design->chip.getWireName(wire);
|
||||
wire_items.append(new QTreeWidgetItem(
|
||||
(QTreeWidget *)nullptr, QStringList(QString(name.c_str()))));
|
||||
}
|
||||
wire_root->addChildren(wire_items);
|
||||
|
||||
QTreeWidgetItem *pip_root = new QTreeWidgetItem(ui->treeWidget);
|
||||
QList<QTreeWidgetItem *> pip_items;
|
||||
pip_root->setText(0, QString("Pips"));
|
||||
ui->treeWidget->insertTopLevelItem(0, pip_root);
|
||||
for (auto pip : design->chip.getPips()) {
|
||||
auto name = design->chip.getPipName(pip);
|
||||
pip_items.append(new QTreeWidgetItem(
|
||||
(QTreeWidget *)nullptr, QStringList(QString(name.c_str()))));
|
||||
}
|
||||
pip_root->addChildren(pip_items);
|
||||
|
||||
PyImport_ImportModule("emb");
|
||||
|
||||
write = [this](std::string s) {
|
||||
@ -38,7 +62,8 @@ MainWindow::MainWindow(Design *_design, QWidget *parent)
|
||||
emb::set_stdout(write);
|
||||
std::string title = "nextpnr-ice40 - " + design->chip.getChipName();
|
||||
setWindowTitle(title.c_str());
|
||||
QtVariantPropertyManager *variantManager = new QtVariantPropertyManager();
|
||||
|
||||
variantManager = new QtVariantPropertyManager();
|
||||
|
||||
int i = 0;
|
||||
QtProperty *topItem = variantManager->addProperty(
|
||||
@ -197,9 +222,9 @@ MainWindow::MainWindow(Design *_design, QWidget *parent)
|
||||
QString::number(i++) + QLatin1String(" Color Property"));
|
||||
topItem->addSubProperty(item);
|
||||
|
||||
QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory();
|
||||
variantFactory = new QtVariantEditorFactory();
|
||||
|
||||
QtTreePropertyBrowser *variantEditor = new QtTreePropertyBrowser();
|
||||
variantEditor = new QtTreePropertyBrowser();
|
||||
variantEditor->setFactoryForManager(variantManager, variantFactory);
|
||||
variantEditor->addProperty(topItem);
|
||||
variantEditor->setPropertiesWithoutValueMarked(true);
|
||||
@ -210,13 +235,37 @@ MainWindow::MainWindow(Design *_design, QWidget *parent)
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
|
||||
// delete variantManager;
|
||||
// delete variantFactory;
|
||||
// delete variantEditor;
|
||||
delete variantManager;
|
||||
delete variantFactory;
|
||||
delete variantEditor;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::prepareMenu(const QPoint &pos)
|
||||
{
|
||||
QTreeWidget *tree = ui->treeWidget;
|
||||
|
||||
QTreeWidgetItem *item = tree->itemAt(pos);
|
||||
|
||||
QAction *selectAction = new QAction("&Select", this);
|
||||
selectAction->setStatusTip("Select item on view");
|
||||
connect(selectAction, SIGNAL(triggered()), this, SLOT(selectObject(item)));
|
||||
|
||||
QMenu menu(this);
|
||||
menu.addAction(selectAction);
|
||||
|
||||
QPoint pt(pos);
|
||||
menu.exec(tree->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void MainWindow::selectObject(QTreeWidgetItem *item)
|
||||
{
|
||||
ui->plainTextEdit->moveCursor(QTextCursor::End);
|
||||
ui->plainTextEdit->insertPlainText(
|
||||
std::string("selected " + item->text(0).toStdString() + "\n").c_str());
|
||||
ui->plainTextEdit->moveCursor(QTextCursor::End);
|
||||
}
|
||||
|
||||
void handle_system_exit() { exit(-1); }
|
||||
|
||||
int MainWindow::executePython(std::string command)
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
#include "emb.h"
|
||||
#include "nextpnr.h"
|
||||
#include "qtpropertymanager.h"
|
||||
#include "qttreepropertybrowser.h"
|
||||
#include "qtvariantproperty.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
@ -27,11 +30,16 @@ class MainWindow : public QMainWindow
|
||||
|
||||
private Q_SLOTS:
|
||||
void on_lineEdit_returnPressed();
|
||||
void prepareMenu(const QPoint &pos);
|
||||
void selectObject(QTreeWidgetItem *item);
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
emb::stdout_write_type write;
|
||||
Design *design;
|
||||
QtVariantPropertyManager *variantManager;
|
||||
QtVariantEditorFactory *variantFactory;
|
||||
QtTreePropertyBrowser *variantEditor;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
@ -60,6 +60,26 @@ CellInfo *create_ice_cell(Design *design, IdString type, IdString name)
|
||||
add_port(new_cell, "LO", PORT_OUT);
|
||||
add_port(new_cell, "O", PORT_OUT);
|
||||
add_port(new_cell, "OUT", PORT_OUT);
|
||||
} else if (type == "SB_IO") {
|
||||
new_cell->params["PIN_TYPE"] = "0";
|
||||
new_cell->params["PULLUP"] = "0";
|
||||
new_cell->params["NEG_TRIGGER"] = "0";
|
||||
new_cell->params["IOSTANDARD"] = "SB_LVCMOS";
|
||||
|
||||
add_port(new_cell, "PACKAGE_PIN", PORT_INOUT);
|
||||
|
||||
add_port(new_cell, "LATCH_INPUT_VALUE", PORT_IN);
|
||||
add_port(new_cell, "CLOCK_ENABLE", PORT_IN);
|
||||
add_port(new_cell, "INPUT_CLK", PORT_IN);
|
||||
add_port(new_cell, "OUTPUT_CLK", PORT_IN);
|
||||
|
||||
add_port(new_cell, "OUTPUT_ENABLE", PORT_IN);
|
||||
add_port(new_cell, "D_OUT_0", PORT_IN);
|
||||
add_port(new_cell, "D_OUT_1", PORT_IN);
|
||||
|
||||
add_port(new_cell, "D_IN_0", PORT_OUT);
|
||||
add_port(new_cell, "D_IN_1", PORT_OUT);
|
||||
|
||||
} else {
|
||||
log_error("unable to create iCE40 cell of type %s", type.c_str());
|
||||
}
|
||||
@ -128,4 +148,20 @@ void dff_to_lc(CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
|
||||
replace_port(dff, "Q", lc, "O");
|
||||
}
|
||||
|
||||
void nxio_to_sb(CellInfo *nxio, CellInfo *sbio)
|
||||
{
|
||||
if (nxio->type == "$nextpnr_ibuf") {
|
||||
sbio->params["PIN_TYPE"] = "1";
|
||||
auto pu_attr = nxio->attrs.find("PULLUP");
|
||||
if (pu_attr != nxio->attrs.end())
|
||||
sbio->params["PULLUP"] = pu_attr->second;
|
||||
replace_port(nxio, "O", sbio, "D_IN_0");
|
||||
} else if (nxio->type == "$nextpnr_obuf") {
|
||||
sbio->params["PIN_TYPE"] = "25";
|
||||
replace_port(nxio, "I", sbio, "D_OUT_0");
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -47,6 +47,9 @@ inline bool is_ff(const CellInfo *cell)
|
||||
cell->type == "SB_DFFNESS" || cell->type == "SB_DFFNES";
|
||||
}
|
||||
|
||||
// Return true if a cell is a SB_IO
|
||||
inline bool is_sb_io(const CellInfo *cell) { return cell->type == "SB_IO"; }
|
||||
|
||||
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
|
||||
// as needed. Set no_dff if a DFF is not being used, so that the output
|
||||
// can be reconnected
|
||||
@ -58,6 +61,9 @@ void lut_to_lc(CellInfo *lut, CellInfo *lc, bool no_dff = true);
|
||||
// ignored
|
||||
void dff_to_lc(CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
|
||||
|
||||
// Convert a nextpnr IO buffer to a SB_IO
|
||||
void nxio_to_sb(CellInfo *nxio, CellInfo *sbio);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
@ -99,6 +99,16 @@ Chip::Chip(ChipArgs args) : args(args)
|
||||
}
|
||||
#endif
|
||||
|
||||
package_info = nullptr;
|
||||
for (int i = 0; i < chip_info.num_packages; i++) {
|
||||
if (chip_info.packages_data[i].name == args.package) {
|
||||
package_info = &(chip_info.packages_data[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (package_info == nullptr)
|
||||
log_error("Unsupported package '%s'.\n", args.package.c_str());
|
||||
|
||||
bel_to_cell.resize(chip_info.num_bels);
|
||||
wire_to_net.resize(chip_info.num_wires);
|
||||
pip_to_net.resize(chip_info.num_pips);
|
||||
@ -231,6 +241,20 @@ PipId Chip::getPipByName(IdString name) const
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
BelId Chip::getPackagePinBel(const std::string &pin) const
|
||||
{
|
||||
for (int i = 0; i < package_info->num_pins; i++) {
|
||||
if (package_info->pins[i].name == pin) {
|
||||
BelId id;
|
||||
id.index = package_info->pins[i].bel_index;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return BelId();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
PosInfo Chip::getBelPosition(BelId bel) const
|
||||
{
|
||||
PosInfo pos;
|
||||
|
20
ice40/chip.h
20
ice40/chip.h
@ -118,6 +118,19 @@ struct WireInfoPOD
|
||||
float x, y;
|
||||
};
|
||||
|
||||
struct PackagePinPOD
|
||||
{
|
||||
const char *name;
|
||||
int32_t bel_index;
|
||||
};
|
||||
|
||||
struct PackageInfoPOD
|
||||
{
|
||||
const char *name;
|
||||
int num_pins;
|
||||
PackagePinPOD *pins;
|
||||
};
|
||||
|
||||
enum TileType
|
||||
{
|
||||
TILE_NONE = 0,
|
||||
@ -173,12 +186,13 @@ struct ChipInfoPOD
|
||||
{
|
||||
int width, height;
|
||||
int num_bels, num_wires, num_pips;
|
||||
int num_switches;
|
||||
int num_switches, num_packages;
|
||||
BelInfoPOD *bel_data;
|
||||
WireInfoPOD *wire_data;
|
||||
PipInfoPOD *pip_data;
|
||||
TileType *tile_grid;
|
||||
BitstreamInfoPOD *bits_info;
|
||||
PackageInfoPOD *packages_data;
|
||||
};
|
||||
|
||||
extern ChipInfoPOD chip_info_384;
|
||||
@ -417,11 +431,13 @@ struct ChipArgs
|
||||
HX8K,
|
||||
UP5K
|
||||
} type = NONE;
|
||||
std::string package;
|
||||
};
|
||||
|
||||
struct Chip
|
||||
{
|
||||
ChipInfoPOD chip_info;
|
||||
PackageInfoPOD *package_info;
|
||||
|
||||
mutable std::unordered_map<IdString, int> bel_by_name;
|
||||
mutable std::unordered_map<IdString, int> wire_by_name;
|
||||
@ -682,6 +698,8 @@ struct Chip
|
||||
return range;
|
||||
}
|
||||
|
||||
BelId getPackagePinBel(const std::string &pin) const;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
PosInfo getBelPosition(BelId bel) const;
|
||||
|
@ -23,6 +23,8 @@ switches = list()
|
||||
|
||||
ierens = list()
|
||||
|
||||
packages = list()
|
||||
|
||||
wire_uphill_belport = dict()
|
||||
wire_downhill_belports = dict()
|
||||
|
||||
@ -123,6 +125,11 @@ with open(sys.argv[1], "r") as f:
|
||||
mode = ("ieren",)
|
||||
continue
|
||||
|
||||
if line[0] == ".pins":
|
||||
mode = ("pins", line[1])
|
||||
packages.append((line[1], []))
|
||||
continue
|
||||
|
||||
if (line[0][0] == ".") or (mode is None):
|
||||
mode = None
|
||||
continue
|
||||
@ -157,9 +164,16 @@ with open(sys.argv[1], "r") as f:
|
||||
assert m
|
||||
bits.append((int(m.group(1)), int(m.group(2))))
|
||||
tile_bits[mode[1]].append((name, bits))
|
||||
continue
|
||||
|
||||
if mode[0] == "ieren":
|
||||
ierens.append(tuple([int(_) for _ in line]))
|
||||
continue
|
||||
|
||||
if mode[0] == "pins":
|
||||
packages[-1][1].append((line[0], int(line[1]), int(line[2]), int(line[3])))
|
||||
continue
|
||||
|
||||
def add_bel_input(bel, wire, port):
|
||||
if wire not in wire_downhill_belports:
|
||||
wire_downhill_belports[wire] = set()
|
||||
@ -392,6 +406,21 @@ for wire in range(num_wires):
|
||||
|
||||
wireinfo.append(info)
|
||||
|
||||
packageinfo = []
|
||||
|
||||
for package in packages:
|
||||
name, pins = package
|
||||
pins_info = []
|
||||
for pin in pins:
|
||||
pinname, x, y, z = pin
|
||||
pin_bel = "%d_%d_io%d" % (x, y, z)
|
||||
bel_idx = bel_name.index(pin_bel)
|
||||
pins_info.append('{"%s", %d}' % (pinname, bel_idx))
|
||||
print("static PackagePinPOD package_%s_pins[%d] = {" % (name, len(pins_info)))
|
||||
print(",\n".join(pins_info))
|
||||
print("};")
|
||||
packageinfo.append('{"%s", %d, package_%s_pins}' % (name, len(pins_info), name))
|
||||
|
||||
tilegrid = []
|
||||
for y in range(dev_height):
|
||||
for x in range(dev_width):
|
||||
@ -460,13 +489,18 @@ print("static TileType tile_grid_%s[%d] = {" % (dev_name, len(tilegrid)))
|
||||
print(",\n".join(tilegrid))
|
||||
print("};")
|
||||
|
||||
|
||||
print("static PackageInfoPOD package_info_%s[%d] = {" % (dev_name, len(packageinfo)))
|
||||
print(",\n".join(packageinfo))
|
||||
print("};")
|
||||
|
||||
print('}')
|
||||
print('NEXTPNR_NAMESPACE_BEGIN')
|
||||
|
||||
print("ChipInfoPOD chip_info_%s = {" % dev_name)
|
||||
print(" %d, %d, %d, %d, %d, %d," % (dev_width, dev_height, len(bel_name), num_wires, len(pipinfo), len(switchinfo)))
|
||||
print(" %d, %d, %d, %d, %d, %d, %d," % (dev_width, dev_height, len(bel_name), num_wires, len(pipinfo), len(switchinfo), len(packageinfo)))
|
||||
print(" bel_data_%s, wire_data_%s, pip_data_%s," % (dev_name, dev_name, dev_name))
|
||||
print(" tile_grid_%s, &bits_info_%s" % (dev_name, dev_name))
|
||||
print(" tile_grid_%s, &bits_info_%s, package_info_%s" % (dev_name, dev_name, dev_name))
|
||||
print("};")
|
||||
|
||||
print('NEXTPNR_NAMESPACE_END')
|
||||
|
@ -17,7 +17,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PYTHON_MODULE
|
||||
#ifdef MAIN_EXECUTABLE
|
||||
|
||||
#include <QApplication>
|
||||
#include <boost/filesystem/convenience.hpp>
|
||||
@ -30,6 +30,7 @@
|
||||
#include "mainwindow.h"
|
||||
#include "nextpnr.h"
|
||||
#include "pack.h"
|
||||
#include "pcf.h"
|
||||
#include "place.h"
|
||||
#include "pybindings.h"
|
||||
#include "route.h"
|
||||
@ -65,10 +66,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
po::options_description options("Allowed options");
|
||||
options.add_options()("help,h", "show help");
|
||||
options.add_options()("test", "just a check");
|
||||
options.add_options()("gui", "start gui");
|
||||
options.add_options()("svg", "dump SVG file");
|
||||
options.add_options()("pack", "pack design prior to place and route");
|
||||
options.add_options()("pack-only",
|
||||
"pack design only without placement or routing");
|
||||
|
||||
@ -76,6 +75,8 @@ int main(int argc, char *argv[])
|
||||
"python file to execute");
|
||||
options.add_options()("json", po::value<std::string>(),
|
||||
"JSON design file to ingest");
|
||||
options.add_options()("pcf", po::value<std::string>(),
|
||||
"PCF constraints file to ingest");
|
||||
options.add_options()("asc", po::value<std::string>(),
|
||||
"asc bitstream file to write");
|
||||
options.add_options()("version,v", "show version");
|
||||
@ -85,7 +86,8 @@ int main(int argc, char *argv[])
|
||||
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");
|
||||
|
||||
options.add_options()("package", po::value<std::string>(),
|
||||
"set device package");
|
||||
po::positional_options_description pos;
|
||||
pos.add("run", -1);
|
||||
|
||||
@ -129,41 +131,48 @@ int main(int argc, char *argv[])
|
||||
if (chipArgs.type != ChipArgs::NONE)
|
||||
goto help;
|
||||
chipArgs.type = ChipArgs::LP384;
|
||||
chipArgs.package = "qn32";
|
||||
}
|
||||
|
||||
if (vm.count("lp1k")) {
|
||||
if (chipArgs.type != ChipArgs::NONE)
|
||||
goto help;
|
||||
chipArgs.type = ChipArgs::LP1K;
|
||||
chipArgs.package = "tq144";
|
||||
}
|
||||
|
||||
if (vm.count("lp8k")) {
|
||||
if (chipArgs.type != ChipArgs::NONE)
|
||||
goto help;
|
||||
chipArgs.type = ChipArgs::LP8K;
|
||||
chipArgs.package = "ct256";
|
||||
}
|
||||
|
||||
if (vm.count("hx1k")) {
|
||||
if (chipArgs.type != ChipArgs::NONE)
|
||||
goto help;
|
||||
chipArgs.type = ChipArgs::HX1K;
|
||||
chipArgs.package = "tq144";
|
||||
}
|
||||
|
||||
if (vm.count("hx8k")) {
|
||||
if (chipArgs.type != ChipArgs::NONE)
|
||||
goto help;
|
||||
chipArgs.type = ChipArgs::HX8K;
|
||||
chipArgs.package = "ct256";
|
||||
}
|
||||
|
||||
if (vm.count("up5k")) {
|
||||
if (chipArgs.type != ChipArgs::NONE)
|
||||
goto help;
|
||||
chipArgs.type = ChipArgs::UP5K;
|
||||
chipArgs.package = "sg48";
|
||||
}
|
||||
|
||||
if (chipArgs.type == ChipArgs::NONE)
|
||||
if (chipArgs.type == ChipArgs::NONE) {
|
||||
chipArgs.type = ChipArgs::HX1K;
|
||||
|
||||
chipArgs.package = "tq144";
|
||||
}
|
||||
#ifdef ICE40_HX1K_ONLY
|
||||
if (chipArgs.type != ChipArgs::HX1K) {
|
||||
std::cout << "This version of nextpnr-ice40 is built with HX1K-support "
|
||||
@ -172,71 +181,14 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vm.count("package"))
|
||||
chipArgs.package = vm["package"].as<std::string>();
|
||||
|
||||
Design design(chipArgs);
|
||||
init_python(argv[0]);
|
||||
python_export_global("design", design);
|
||||
python_export_global("chip", design.chip);
|
||||
|
||||
if (vm.count("test")) {
|
||||
int bel_count = 0, wire_count = 0, pip_count = 0;
|
||||
|
||||
std::cout << "Checking bel names.\n";
|
||||
for (auto bel : design.chip.getBels()) {
|
||||
auto name = design.chip.getBelName(bel);
|
||||
assert(bel == design.chip.getBelByName(name));
|
||||
bel_count++;
|
||||
}
|
||||
std::cout << " checked " << bel_count << " bels.\n";
|
||||
|
||||
std::cout << "Checking wire names.\n";
|
||||
for (auto wire : design.chip.getWires()) {
|
||||
auto name = design.chip.getWireName(wire);
|
||||
assert(wire == design.chip.getWireByName(name));
|
||||
wire_count++;
|
||||
}
|
||||
std::cout << " checked " << wire_count << " wires.\n";
|
||||
|
||||
std::cout << "Checking pip names.\n";
|
||||
for (auto pip : design.chip.getPips()) {
|
||||
auto name = design.chip.getPipName(pip);
|
||||
assert(pip == design.chip.getPipByName(name));
|
||||
pip_count++;
|
||||
}
|
||||
std::cout << " checked " << pip_count << " pips.\n";
|
||||
|
||||
std::cout << "Checking uphill -> downhill consistency.\n";
|
||||
for (auto dst : design.chip.getWires()) {
|
||||
for (auto uphill_pip : design.chip.getPipsUphill(dst)) {
|
||||
bool found_downhill = false;
|
||||
for (auto downhill_pip : design.chip.getPipsDownhill(
|
||||
design.chip.getPipSrcWire(uphill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
assert(!found_downhill);
|
||||
found_downhill = true;
|
||||
}
|
||||
}
|
||||
assert(found_downhill);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Checking downhill -> uphill consistency.\n";
|
||||
for (auto dst : design.chip.getWires()) {
|
||||
for (auto downhill_pip : design.chip.getPipsDownhill(dst)) {
|
||||
bool found_uphill = false;
|
||||
for (auto uphill_pip : design.chip.getPipsUphill(
|
||||
design.chip.getPipDstWire(downhill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
assert(!found_uphill);
|
||||
found_uphill = true;
|
||||
}
|
||||
}
|
||||
assert(found_uphill);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vm.count("svg")) {
|
||||
std::cout << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
||||
"xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
|
||||
@ -256,9 +208,13 @@ int main(int argc, char *argv[])
|
||||
std::istream *f = new std::ifstream(filename);
|
||||
|
||||
parse_json_file(f, filename, &design);
|
||||
if (vm.count("pack") || vm.count("pack-only")) {
|
||||
pack_design(&design);
|
||||
|
||||
if (vm.count("pcf")) {
|
||||
std::ifstream pcf(vm["pcf"].as<std::string>());
|
||||
apply_pcf(&design, pcf);
|
||||
}
|
||||
|
||||
pack_design(&design);
|
||||
if (!vm.count("pack-only")) {
|
||||
place_design(&design);
|
||||
route_design(&design);
|
||||
|
@ -134,10 +134,65 @@ static void pack_constants(Design *design)
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_nextpnr_iob(CellInfo *cell)
|
||||
{
|
||||
return cell->type == "$nextpnr_ibuf" || cell->type == "$nextpnr_obuf" ||
|
||||
cell->type == "$nextpnr_iobuf";
|
||||
}
|
||||
|
||||
// Pack IO buffers
|
||||
static void pack_io(Design *design)
|
||||
{
|
||||
std::unordered_set<IdString> packed_cells;
|
||||
std::vector<CellInfo *> new_cells;
|
||||
|
||||
for (auto cell : design->cells) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (is_nextpnr_iob(ci)) {
|
||||
CellInfo *sb = nullptr;
|
||||
if (ci->type == "$nextpnr_ibuf" || ci->type == "$nextpnr_iobuf") {
|
||||
sb = net_only_drives(ci->ports.at("O").net, is_sb_io,
|
||||
"PACKAGE_PIN", true, ci);
|
||||
|
||||
} else if (ci->type == "$nextpnr_obuf") {
|
||||
sb = net_only_drives(ci->ports.at("I").net, is_sb_io,
|
||||
"PACKAGE_PIN", true, ci);
|
||||
}
|
||||
if (sb != nullptr) {
|
||||
// Trivial case, SB_IO used. Just destroy the net and the
|
||||
// iobuf
|
||||
log_info("%s feeds SB_IO %s, removing %s %s.\n",
|
||||
ci->name.c_str(), sb->name.c_str(), ci->type.c_str(),
|
||||
ci->name.c_str());
|
||||
NetInfo *net = sb->ports.at("PACKAGE_PIN").net;
|
||||
if (net != nullptr) {
|
||||
design->nets.erase(net->name);
|
||||
sb->ports.at("PACKAGE_PIN").net = nullptr;
|
||||
}
|
||||
} else {
|
||||
// Create a SB_IO buffer
|
||||
sb = create_ice_cell(design, "SB_IO");
|
||||
nxio_to_sb(ci, sb);
|
||||
new_cells.push_back(sb);
|
||||
}
|
||||
packed_cells.insert(ci->name);
|
||||
std::copy(ci->attrs.begin(), ci->attrs.end(),
|
||||
std::inserter(sb->attrs, sb->attrs.begin()));
|
||||
}
|
||||
}
|
||||
for (auto pcell : packed_cells) {
|
||||
design->cells.erase(pcell);
|
||||
}
|
||||
for (auto ncell : new_cells) {
|
||||
design->cells[ncell->name] = ncell;
|
||||
}
|
||||
}
|
||||
|
||||
// Main pack function
|
||||
void pack_design(Design *design)
|
||||
{
|
||||
pack_constants(design);
|
||||
pack_io(design);
|
||||
pack_lut_lutffs(design);
|
||||
pack_nonlut_ffs(design);
|
||||
}
|
||||
|
@ -1,169 +0,0 @@
|
||||
module io_wrapper(input clk_pin, cen_pin, rst_pin, ina_pin, inb_pin,
|
||||
output outa_pin, outb_pin, outc_pin, outd_pin);
|
||||
|
||||
wire clk, cen, rst, ina, inb, outa, outb, outc, outd;
|
||||
|
||||
(* BEL="0_14_io1" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0000_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) clk_iob (
|
||||
.PACKAGE_PIN(clk_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(clk),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
(* BEL="0_14_io0" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0000_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) cen_iob (
|
||||
.PACKAGE_PIN(cen_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(cen),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
(* BEL="0_13_io1" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0000_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) rst_iob (
|
||||
.PACKAGE_PIN(rst_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(rst),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
(* BEL="0_13_io0" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0000_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) ina_iob (
|
||||
.PACKAGE_PIN(ina_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(ina),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
(* BEL="0_12_io1" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0000_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) inb_iob (
|
||||
.PACKAGE_PIN(inb_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(inb),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
(* BEL="0_12_io0" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0110_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) outa_iob (
|
||||
.PACKAGE_PIN(outa_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(outa),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
(* BEL="0_11_io1" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0110_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) outb_iob (
|
||||
.PACKAGE_PIN(outb_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(outb),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
(* BEL="0_11_io0" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0110_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) outc_iob (
|
||||
.PACKAGE_PIN(outc_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(outc),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
(* BEL="0_10_io1" *)
|
||||
SB_IO #(
|
||||
.PIN_TYPE(6'b 0110_01),
|
||||
.PULLUP(1'b0),
|
||||
.NEG_TRIGGER(1'b0)
|
||||
) outd_iob (
|
||||
.PACKAGE_PIN(outa_pin),
|
||||
.LATCH_INPUT_VALUE(),
|
||||
.CLOCK_ENABLE(),
|
||||
.INPUT_CLK(),
|
||||
.OUTPUT_CLK(),
|
||||
.OUTPUT_ENABLE(),
|
||||
.D_OUT_0(outd),
|
||||
.D_OUT_1(),
|
||||
.D_IN_0(),
|
||||
.D_IN_1()
|
||||
);
|
||||
|
||||
top top_i(.clk(clk), .rst(rst), .cen(cen), .ina(ina), .inb(inb), .outa(outa), .outb(outb), .outc(outc), .outd(outd));
|
||||
endmodule
|
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
NAME=${1%.v}
|
||||
yosys -p "synth_ice40 -nocarry -top io_wrapper; write_json ${NAME}.json" $1 io_wrapper.v
|
||||
../../nextpnr-ice40 --json ${NAME}.json --pack --asc ${NAME}.asc
|
||||
yosys -p "synth_ice40 -nocarry -top top; write_json ${NAME}.json" $1
|
||||
../../nextpnr-ice40 --json ${NAME}.json --pcf test.pcf --asc ${NAME}.asc
|
||||
icebox_vlog -p test.pcf ${NAME}.asc > ${NAME}_out.v
|
||||
|
||||
yosys -p "read_verilog +/ice40/cells_sim.v;\
|
||||
|
71
ice40/pcf.cc
Normal file
71
ice40/pcf.cc
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* 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 "pcf.h"
|
||||
#include <sstream>
|
||||
#include "log.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// Read a w
|
||||
|
||||
// Apply PCF constraints to a pre-packing design
|
||||
void apply_pcf(Design *design, std::istream &in)
|
||||
{
|
||||
if (!in)
|
||||
log_error("failed to open PCF file");
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
size_t cstart = line.find("#");
|
||||
if (cstart != std::string::npos)
|
||||
line = line.substr(0, cstart);
|
||||
std::stringstream ss(line);
|
||||
std::vector<std::string> words;
|
||||
std::string tmp;
|
||||
while (ss >> tmp)
|
||||
words.push_back(tmp);
|
||||
if (words.size() == 0)
|
||||
continue;
|
||||
std::string cmd = words.at(0);
|
||||
if (cmd == "set_io") {
|
||||
size_t args_end = 1;
|
||||
while (args_end < words.size() && words.at(args_end).at(0) == '-')
|
||||
args_end++;
|
||||
std::string cell = words.at(args_end);
|
||||
std::string pin = words.at(args_end + 1);
|
||||
auto fnd_cell = design->cells.find(cell);
|
||||
if (fnd_cell == design->cells.end()) {
|
||||
log_warning("unmatched pcf constraint %s\n", cell.c_str());
|
||||
} else {
|
||||
BelId pin_bel = design->chip.getPackagePinBel(pin);
|
||||
if (pin_bel == BelId())
|
||||
log_error("package does not have a pin named %s\n",
|
||||
pin.c_str());
|
||||
fnd_cell->second->attrs["BEL"] =
|
||||
design->chip.getBelName(pin_bel).str();
|
||||
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
|
||||
fnd_cell->second->attrs["BEL"].c_str());
|
||||
}
|
||||
} else {
|
||||
log_error("unsupported pcf command '%s'\n", cmd.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
33
ice40/pcf.h
Normal file
33
ice40/pcf.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* 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 PCF_H
|
||||
#define PCF_H
|
||||
|
||||
#include <iostream>
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// Apply PCF constraints to a pre-packing design
|
||||
void apply_pcf(Design *design, std::istream &in);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // ROUTE_H
|
9
tests/dummy/main.cc
Normal file
9
tests/dummy/main.cc
Normal file
@ -0,0 +1,9 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
TEST(example, sum_zero) {
|
||||
auto result = 0;
|
||||
ASSERT_EQ(result, 0);
|
||||
}
|
||||
|
||||
TEST(example, sum_five) {
|
||||
auto result = 15;
|
||||
ASSERT_EQ(result, 15);
|
||||
}
|
87
tests/ice40/hx1k.cc
Normal file
87
tests/ice40/hx1k.cc
Normal file
@ -0,0 +1,87 @@
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
USING_NEXTPNR_NAMESPACE
|
||||
|
||||
class HX1KTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
chipArgs.type = ChipArgs::HX1K;
|
||||
design = new Design(chipArgs);
|
||||
}
|
||||
|
||||
virtual void TearDown() { delete design; }
|
||||
|
||||
ChipArgs chipArgs;
|
||||
Design *design;
|
||||
};
|
||||
|
||||
TEST_F(HX1KTest, bel_names)
|
||||
{
|
||||
int bel_count = 0;
|
||||
for (auto bel : design->chip.getBels()) {
|
||||
auto name = design->chip.getBelName(bel);
|
||||
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||
bel_count++;
|
||||
}
|
||||
ASSERT_EQ(bel_count, 1416);
|
||||
}
|
||||
|
||||
TEST_F(HX1KTest, wire_names)
|
||||
{
|
||||
int wire_count = 0;
|
||||
for (auto wire : design->chip.getWires()) {
|
||||
auto name = design->chip.getWireName(wire);
|
||||
assert(wire == design->chip.getWireByName(name));
|
||||
wire_count++;
|
||||
}
|
||||
ASSERT_EQ(wire_count, 27682);
|
||||
}
|
||||
|
||||
TEST_F(HX1KTest, pip_names)
|
||||
{
|
||||
int pip_count = 0;
|
||||
for (auto pip : design->chip.getPips()) {
|
||||
auto name = design->chip.getPipName(pip);
|
||||
assert(pip == design->chip.getPipByName(name));
|
||||
pip_count++;
|
||||
}
|
||||
ASSERT_EQ(pip_count, 319904);
|
||||
}
|
||||
|
||||
TEST_F(HX1KTest, uphill_to_downhill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||
bool found_downhill = false;
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||
design->chip.getPipSrcWire(uphill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_downhill);
|
||||
found_downhill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_downhill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(HX1KTest, downhill_to_uphill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||
bool found_uphill = false;
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||
design->chip.getPipDstWire(downhill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_uphill);
|
||||
found_uphill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_uphill);
|
||||
}
|
||||
}
|
||||
}
|
87
tests/ice40/hx8k.cc
Normal file
87
tests/ice40/hx8k.cc
Normal file
@ -0,0 +1,87 @@
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
USING_NEXTPNR_NAMESPACE
|
||||
|
||||
class HX8KTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
chipArgs.type = ChipArgs::HX8K;
|
||||
design = new Design(chipArgs);
|
||||
}
|
||||
|
||||
virtual void TearDown() { delete design; }
|
||||
|
||||
ChipArgs chipArgs;
|
||||
Design *design;
|
||||
};
|
||||
|
||||
TEST_F(HX8KTest, bel_names)
|
||||
{
|
||||
int bel_count = 0;
|
||||
for (auto bel : design->chip.getBels()) {
|
||||
auto name = design->chip.getBelName(bel);
|
||||
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||
bel_count++;
|
||||
}
|
||||
ASSERT_EQ(bel_count, 7968);
|
||||
}
|
||||
|
||||
TEST_F(HX8KTest, wire_names)
|
||||
{
|
||||
int wire_count = 0;
|
||||
for (auto wire : design->chip.getWires()) {
|
||||
auto name = design->chip.getWireName(wire);
|
||||
assert(wire == design->chip.getWireByName(name));
|
||||
wire_count++;
|
||||
}
|
||||
ASSERT_EQ(wire_count, 135174);
|
||||
}
|
||||
|
||||
TEST_F(HX8KTest, pip_names)
|
||||
{
|
||||
int pip_count = 0;
|
||||
for (auto pip : design->chip.getPips()) {
|
||||
auto name = design->chip.getPipName(pip);
|
||||
assert(pip == design->chip.getPipByName(name));
|
||||
pip_count++;
|
||||
}
|
||||
ASSERT_EQ(pip_count, 1652480);
|
||||
}
|
||||
|
||||
TEST_F(HX8KTest, uphill_to_downhill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||
bool found_downhill = false;
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||
design->chip.getPipSrcWire(uphill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_downhill);
|
||||
found_downhill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_downhill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(HX8KTest, downhill_to_uphill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||
bool found_uphill = false;
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||
design->chip.getPipDstWire(downhill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_uphill);
|
||||
found_uphill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_uphill);
|
||||
}
|
||||
}
|
||||
}
|
87
tests/ice40/lp1k.cc
Normal file
87
tests/ice40/lp1k.cc
Normal file
@ -0,0 +1,87 @@
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
USING_NEXTPNR_NAMESPACE
|
||||
|
||||
class LP1KTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
chipArgs.type = ChipArgs::LP1K;
|
||||
design = new Design(chipArgs);
|
||||
}
|
||||
|
||||
virtual void TearDown() { delete design; }
|
||||
|
||||
ChipArgs chipArgs;
|
||||
Design *design;
|
||||
};
|
||||
|
||||
TEST_F(LP1KTest, bel_names)
|
||||
{
|
||||
int bel_count = 0;
|
||||
for (auto bel : design->chip.getBels()) {
|
||||
auto name = design->chip.getBelName(bel);
|
||||
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||
bel_count++;
|
||||
}
|
||||
ASSERT_EQ(bel_count, 1416);
|
||||
}
|
||||
|
||||
TEST_F(LP1KTest, wire_names)
|
||||
{
|
||||
int wire_count = 0;
|
||||
for (auto wire : design->chip.getWires()) {
|
||||
auto name = design->chip.getWireName(wire);
|
||||
assert(wire == design->chip.getWireByName(name));
|
||||
wire_count++;
|
||||
}
|
||||
ASSERT_EQ(wire_count, 27682);
|
||||
}
|
||||
|
||||
TEST_F(LP1KTest, pip_names)
|
||||
{
|
||||
int pip_count = 0;
|
||||
for (auto pip : design->chip.getPips()) {
|
||||
auto name = design->chip.getPipName(pip);
|
||||
assert(pip == design->chip.getPipByName(name));
|
||||
pip_count++;
|
||||
}
|
||||
ASSERT_EQ(pip_count, 319904);
|
||||
}
|
||||
|
||||
TEST_F(LP1KTest, uphill_to_downhill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||
bool found_downhill = false;
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||
design->chip.getPipSrcWire(uphill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_downhill);
|
||||
found_downhill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_downhill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LP1KTest, downhill_to_uphill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||
bool found_uphill = false;
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||
design->chip.getPipDstWire(downhill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_uphill);
|
||||
found_uphill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_uphill);
|
||||
}
|
||||
}
|
||||
}
|
87
tests/ice40/lp384.cc
Normal file
87
tests/ice40/lp384.cc
Normal file
@ -0,0 +1,87 @@
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
USING_NEXTPNR_NAMESPACE
|
||||
|
||||
class LP384Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
chipArgs.type = ChipArgs::LP384;
|
||||
design = new Design(chipArgs);
|
||||
}
|
||||
|
||||
virtual void TearDown() { delete design; }
|
||||
|
||||
ChipArgs chipArgs;
|
||||
Design *design;
|
||||
};
|
||||
|
||||
TEST_F(LP384Test, bel_names)
|
||||
{
|
||||
int bel_count = 0;
|
||||
for (auto bel : design->chip.getBels()) {
|
||||
auto name = design->chip.getBelName(bel);
|
||||
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||
bel_count++;
|
||||
}
|
||||
ASSERT_EQ(bel_count, 440);
|
||||
}
|
||||
|
||||
TEST_F(LP384Test, wire_names)
|
||||
{
|
||||
int wire_count = 0;
|
||||
for (auto wire : design->chip.getWires()) {
|
||||
auto name = design->chip.getWireName(wire);
|
||||
assert(wire == design->chip.getWireByName(name));
|
||||
wire_count++;
|
||||
}
|
||||
ASSERT_EQ(wire_count, 8294);
|
||||
}
|
||||
|
||||
TEST_F(LP384Test, pip_names)
|
||||
{
|
||||
int pip_count = 0;
|
||||
for (auto pip : design->chip.getPips()) {
|
||||
auto name = design->chip.getPipName(pip);
|
||||
assert(pip == design->chip.getPipByName(name));
|
||||
pip_count++;
|
||||
}
|
||||
ASSERT_EQ(pip_count, 86864);
|
||||
}
|
||||
|
||||
TEST_F(LP384Test, uphill_to_downhill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||
bool found_downhill = false;
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||
design->chip.getPipSrcWire(uphill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_downhill);
|
||||
found_downhill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_downhill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LP384Test, downhill_to_uphill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||
bool found_uphill = false;
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||
design->chip.getPipDstWire(downhill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_uphill);
|
||||
found_uphill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_uphill);
|
||||
}
|
||||
}
|
||||
}
|
87
tests/ice40/lp8k.cc
Normal file
87
tests/ice40/lp8k.cc
Normal file
@ -0,0 +1,87 @@
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
USING_NEXTPNR_NAMESPACE
|
||||
|
||||
class LP8KTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
chipArgs.type = ChipArgs::LP8K;
|
||||
design = new Design(chipArgs);
|
||||
}
|
||||
|
||||
virtual void TearDown() { delete design; }
|
||||
|
||||
ChipArgs chipArgs;
|
||||
Design *design;
|
||||
};
|
||||
|
||||
TEST_F(LP8KTest, bel_names)
|
||||
{
|
||||
int bel_count = 0;
|
||||
for (auto bel : design->chip.getBels()) {
|
||||
auto name = design->chip.getBelName(bel);
|
||||
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||
bel_count++;
|
||||
}
|
||||
ASSERT_EQ(bel_count, 7968);
|
||||
}
|
||||
|
||||
TEST_F(LP8KTest, wire_names)
|
||||
{
|
||||
int wire_count = 0;
|
||||
for (auto wire : design->chip.getWires()) {
|
||||
auto name = design->chip.getWireName(wire);
|
||||
assert(wire == design->chip.getWireByName(name));
|
||||
wire_count++;
|
||||
}
|
||||
ASSERT_EQ(wire_count, 135174);
|
||||
}
|
||||
|
||||
TEST_F(LP8KTest, pip_names)
|
||||
{
|
||||
int pip_count = 0;
|
||||
for (auto pip : design->chip.getPips()) {
|
||||
auto name = design->chip.getPipName(pip);
|
||||
assert(pip == design->chip.getPipByName(name));
|
||||
pip_count++;
|
||||
}
|
||||
ASSERT_EQ(pip_count, 1652480);
|
||||
}
|
||||
|
||||
TEST_F(LP8KTest, uphill_to_downhill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||
bool found_downhill = false;
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||
design->chip.getPipSrcWire(uphill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_downhill);
|
||||
found_downhill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_downhill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LP8KTest, downhill_to_uphill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||
bool found_uphill = false;
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||
design->chip.getPipDstWire(downhill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_uphill);
|
||||
found_uphill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_uphill);
|
||||
}
|
||||
}
|
||||
}
|
8
tests/ice40/main.cc
Normal file
8
tests/ice40/main.cc
Normal file
@ -0,0 +1,8 @@
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
TEST(example, sum_zero) {
|
||||
auto result = 0;
|
||||
ASSERT_EQ(result, 0);
|
||||
}
|
||||
|
||||
TEST(example, sum_five) {
|
||||
auto result = 15;
|
||||
ASSERT_EQ(result, 15);
|
||||
}
|
87
tests/ice40/up5k.cc
Normal file
87
tests/ice40/up5k.cc
Normal file
@ -0,0 +1,87 @@
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
USING_NEXTPNR_NAMESPACE
|
||||
|
||||
class UP5KTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
chipArgs.type = ChipArgs::UP5K;
|
||||
design = new Design(chipArgs);
|
||||
}
|
||||
|
||||
virtual void TearDown() { delete design; }
|
||||
|
||||
ChipArgs chipArgs;
|
||||
Design *design;
|
||||
};
|
||||
|
||||
TEST_F(UP5KTest, bel_names)
|
||||
{
|
||||
int bel_count = 0;
|
||||
for (auto bel : design->chip.getBels()) {
|
||||
auto name = design->chip.getBelName(bel);
|
||||
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||
bel_count++;
|
||||
}
|
||||
ASSERT_EQ(bel_count, 5414);
|
||||
}
|
||||
|
||||
TEST_F(UP5KTest, wire_names)
|
||||
{
|
||||
int wire_count = 0;
|
||||
for (auto wire : design->chip.getWires()) {
|
||||
auto name = design->chip.getWireName(wire);
|
||||
assert(wire == design->chip.getWireByName(name));
|
||||
wire_count++;
|
||||
}
|
||||
ASSERT_EQ(wire_count, 103383);
|
||||
}
|
||||
|
||||
TEST_F(UP5KTest, pip_names)
|
||||
{
|
||||
int pip_count = 0;
|
||||
for (auto pip : design->chip.getPips()) {
|
||||
auto name = design->chip.getPipName(pip);
|
||||
assert(pip == design->chip.getPipByName(name));
|
||||
pip_count++;
|
||||
}
|
||||
ASSERT_EQ(pip_count, 1219104);
|
||||
}
|
||||
|
||||
TEST_F(UP5KTest, uphill_to_downhill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||
bool found_downhill = false;
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||
design->chip.getPipSrcWire(uphill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_downhill);
|
||||
found_downhill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_downhill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(UP5KTest, downhill_to_uphill)
|
||||
{
|
||||
for (auto dst : design->chip.getWires()) {
|
||||
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||
bool found_uphill = false;
|
||||
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||
design->chip.getPipDstWire(downhill_pip))) {
|
||||
if (uphill_pip == downhill_pip) {
|
||||
ASSERT_FALSE(found_uphill);
|
||||
found_uphill = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_uphill);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user