diff options
-rw-r--r-- | CMakeLists.txt | 22 | ||||
-rw-r--r-- | common/design_utils.h | 33 | ||||
-rw-r--r-- | common/pybindings.cc | 4 | ||||
-rw-r--r-- | dummy/main.cc | 2 | ||||
-rw-r--r-- | frontend/json/jsonparse.cc | 154 | ||||
-rw-r--r-- | gui/mainwindow.cc | 83 | ||||
-rw-r--r-- | gui/mainwindow.h | 8 | ||||
-rw-r--r-- | ice40/cells.cc | 36 | ||||
-rw-r--r-- | ice40/cells.h | 6 | ||||
-rw-r--r-- | ice40/chip.cc | 24 | ||||
-rw-r--r-- | ice40/chip.h | 20 | ||||
-rw-r--r-- | ice40/chipdb.py | 38 | ||||
-rw-r--r-- | ice40/main.cc | 92 | ||||
-rw-r--r-- | ice40/pack.cc | 55 | ||||
-rw-r--r-- | ice40/pack_tests/io_wrapper.v | 169 | ||||
-rwxr-xr-x | ice40/pack_tests/test.sh | 4 | ||||
-rw-r--r-- | ice40/pcf.cc | 71 | ||||
-rw-r--r-- | ice40/pcf.h | 33 | ||||
-rw-r--r-- | tests/dummy/main.cc | 9 | ||||
-rw-r--r-- | tests/dummy/main.cpp | 18 | ||||
-rw-r--r-- | tests/ice40/hx1k.cc | 87 | ||||
-rw-r--r-- | tests/ice40/hx8k.cc | 87 | ||||
-rw-r--r-- | tests/ice40/lp1k.cc | 87 | ||||
-rw-r--r-- | tests/ice40/lp384.cc | 87 | ||||
-rw-r--r-- | tests/ice40/lp8k.cc | 87 | ||||
-rw-r--r-- | tests/ice40/main.cc | 8 | ||||
-rw-r--r-- | tests/ice40/main.cpp | 18 | ||||
-rw-r--r-- | tests/ice40/up5k.cc | 87 |
28 files changed, 1078 insertions, 351 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index b0976d9a..74a94ae4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/common/design_utils.h b/common/design_utils.h index 2acc7d20..daf6e050 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -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)) { - return nullptr; - } else { - for (const auto &load : net->users) { - if (cell_pred(load.cell) && load.port == port) { - return load.cell; + 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; } } - return nullptr; } + for (const auto &load : net->users) { + 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 diff --git a/common/pybindings.cc b/common/pybindings.cc index 7c43c84f..761d6571 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -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 diff --git a/dummy/main.cc b/dummy/main.cc index 3b9e6ba3..7aa2f08f 100644 --- a/dummy/main.cc +++ b/dummy/main.cc @@ -17,7 +17,7 @@ * */ -#ifndef PYTHON_MODULE +#ifdef MAIN_EXECUTABLE #include <QApplication> #include "mainwindow.h" diff --git a/frontend/json/jsonparse.cc b/frontend/json/jsonparse.cc index 3f965ce4..79ee0a4d 100644 --- a/frontend/json/jsonparse.cc +++ b/frontend/json/jsonparse.cc @@ -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); } diff --git a/gui/mainwindow.cc b/gui/mainwindow.cc index c436fd6c..5f62ecec 100644 --- a/gui/mainwindow.cc +++ b/gui/mainwindow.cc @@ -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) diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 6f3e515f..27918486 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -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 diff --git a/ice40/cells.cc b/ice40/cells.cc index ad728d2c..b038db68 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -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 diff --git a/ice40/cells.h b/ice40/cells.h index 34a034cd..3cf0b718 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -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 diff --git a/ice40/chip.cc b/ice40/chip.cc index de33b020..441e65f2 100644 --- a/ice40/chip.cc +++ b/ice40/chip.cc @@ -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; diff --git a/ice40/chip.h b/ice40/chip.h index 2c95bf4e..5eea1b8e 100644 --- a/ice40/chip.h +++ b/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; diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 9b246f8b..946197d3 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -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') diff --git a/ice40/main.cc b/ice40/main.cc index 0e989819..8ccec77b 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -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); diff --git a/ice40/pack.cc b/ice40/pack.cc index 8f770a07..72dcadea 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -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); } diff --git a/ice40/pack_tests/io_wrapper.v b/ice40/pack_tests/io_wrapper.v deleted file mode 100644 index b58d6c0c..00000000 --- a/ice40/pack_tests/io_wrapper.v +++ /dev/null @@ -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 diff --git a/ice40/pack_tests/test.sh b/ice40/pack_tests/test.sh index dd1f345c..b36c01dc 100755 --- a/ice40/pack_tests/test.sh +++ b/ice40/pack_tests/test.sh @@ -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;\ diff --git a/ice40/pcf.cc b/ice40/pcf.cc new file mode 100644 index 00000000..75c32731 --- /dev/null +++ b/ice40/pcf.cc @@ -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 diff --git a/ice40/pcf.h b/ice40/pcf.h new file mode 100644 index 00000000..c4a7d991 --- /dev/null +++ b/ice40/pcf.h @@ -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 diff --git a/tests/dummy/main.cc b/tests/dummy/main.cc new file mode 100644 index 00000000..1270ccdf --- /dev/null +++ b/tests/dummy/main.cc @@ -0,0 +1,9 @@ +#include "gtest/gtest.h" + +#include <vector> + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/dummy/main.cpp b/tests/dummy/main.cpp deleted file mode 100644 index b8d22138..00000000 --- a/tests/dummy/main.cpp +++ /dev/null @@ -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); -} diff --git a/tests/ice40/hx1k.cc b/tests/ice40/hx1k.cc new file mode 100644 index 00000000..ceae4c45 --- /dev/null +++ b/tests/ice40/hx1k.cc @@ -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); + } + } +} diff --git a/tests/ice40/hx8k.cc b/tests/ice40/hx8k.cc new file mode 100644 index 00000000..060e34dd --- /dev/null +++ b/tests/ice40/hx8k.cc @@ -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); + } + } +} diff --git a/tests/ice40/lp1k.cc b/tests/ice40/lp1k.cc new file mode 100644 index 00000000..85b35652 --- /dev/null +++ b/tests/ice40/lp1k.cc @@ -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); + } + } +} diff --git a/tests/ice40/lp384.cc b/tests/ice40/lp384.cc new file mode 100644 index 00000000..c29938f1 --- /dev/null +++ b/tests/ice40/lp384.cc @@ -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); + } + } +} diff --git a/tests/ice40/lp8k.cc b/tests/ice40/lp8k.cc new file mode 100644 index 00000000..ca2fc424 --- /dev/null +++ b/tests/ice40/lp8k.cc @@ -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); + } + } +} diff --git a/tests/ice40/main.cc b/tests/ice40/main.cc new file mode 100644 index 00000000..e85c836a --- /dev/null +++ b/tests/ice40/main.cc @@ -0,0 +1,8 @@ +#include <vector> +#include "gtest/gtest.h" + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/ice40/main.cpp b/tests/ice40/main.cpp deleted file mode 100644 index b8d22138..00000000 --- a/tests/ice40/main.cpp +++ /dev/null @@ -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); -} diff --git a/tests/ice40/up5k.cc b/tests/ice40/up5k.cc new file mode 100644 index 00000000..38bb30fd --- /dev/null +++ b/tests/ice40/up5k.cc @@ -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); + } + } +} |