Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr

This commit is contained in:
Clifford Wolf 2018-06-13 12:38:28 +02:00
commit 145c849596
28 changed files with 1078 additions and 351 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -17,7 +17,7 @@
*
*/
#ifndef PYTHON_MODULE
#ifdef MAIN_EXECUTABLE
#include <QApplication>
#include "mainwindow.h"

View File

@ -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);
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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')

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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
View 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
View 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
View 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();
}

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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();
}

View File

@ -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
View 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);
}
}
}