Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr
This commit is contained in:
commit
145c849596
@ -79,36 +79,42 @@ aux_source_directory(frontend/json/ JSON_PARSER_FILES)
|
|||||||
set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES})
|
set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES})
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
|
||||||
|
if(MINGW)
|
||||||
|
add_definitions("-Wa,-mbig-obj")
|
||||||
|
endif(MINGW)
|
||||||
|
|
||||||
foreach (family ${FAMILIES})
|
foreach (family ${FAMILIES})
|
||||||
string(TOUPPER ${family} ufamily)
|
string(TOUPPER ${family} ufamily)
|
||||||
aux_source_directory(${family}/ ${ufamily}_FILES)
|
aux_source_directory(${family}/ ${ufamily}_FILES)
|
||||||
|
aux_source_directory(tests/${family}/ ${ufamily}_TEST_FILES)
|
||||||
# Add the CLI binary target
|
# Add the CLI binary target
|
||||||
add_executable(nextpnr-${family} ${COMMON_FILES} ${${ufamily}_FILES} ${GUI_SOURCE_FILES})
|
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
|
# Add the importable Python module target
|
||||||
PYTHON_ADD_MODULE(nextpnrpy_${family} ${COMMON_FILES} ${${ufamily}_FILES})
|
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 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} 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 the family-specific CMakeFile
|
||||||
include(${family}/family.cmake)
|
include(${family}/family.cmake)
|
||||||
foreach (target ${family_targets})
|
foreach (target ${family_targets})
|
||||||
# Include family-specific source files to all family targets and set defines appropriately
|
# Include family-specific source files to all family targets and set defines appropriately
|
||||||
target_include_directories(${target} PRIVATE ${family}/)
|
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})
|
target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} ${GUI_LIBRARY_FILES})
|
||||||
endforeach (target)
|
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)
|
endforeach (family)
|
||||||
|
|
||||||
file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h)
|
file(GLOB_RECURSE CLANGFORMAT_FILES *.cc *.h)
|
||||||
string(REGEX REPLACE "[^;]*/ice40/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
string(REGEX REPLACE "[^;]*/ice40/chipdbs/chipdb-[^;]*.cc" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||||
string(REGEX REPLACE "[^;]*/3rdparty[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
string(REGEX REPLACE "[^;]*/3rdparty[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||||
|
string(REGEX REPLACE "[^;]*/generated[^;]*" "" CLANGFORMAT_FILES "${CLANGFORMAT_FILES}")
|
||||||
|
|
||||||
add_custom_target(
|
add_custom_target(
|
||||||
clangformat
|
clangformat
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#ifndef DESIGN_UTILS_H
|
#ifndef DESIGN_UTILS_H
|
||||||
#define DESIGN_UTILS_H
|
#define DESIGN_UTILS_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -35,24 +37,37 @@ 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
|
// 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
|
// 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
|
// 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>
|
template <typename F1>
|
||||||
CellInfo *net_only_drives(NetInfo *net, F1 cell_pred, IdString port,
|
CellInfo *net_only_drives(NetInfo *net, F1 cell_pred, IdString port,
|
||||||
bool exclusive = false)
|
bool exclusive = false, CellInfo *exclude = nullptr)
|
||||||
{
|
{
|
||||||
if (net == nullptr)
|
if (net == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (exclusive && (net->users.size() != 1)) {
|
if (exclusive) {
|
||||||
|
if (exclude == nullptr) {
|
||||||
|
if (net->users.size() != 1)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
|
if (net->users.size() > 2) {
|
||||||
|
return nullptr;
|
||||||
|
} else if (net->users.size() == 2) {
|
||||||
|
if (std::find_if(net->users.begin(), net->users.end(),
|
||||||
|
[exclude](const PortRef &ref) {
|
||||||
|
return ref.cell == exclude;
|
||||||
|
}) == net->users.end())
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const auto &load : net->users) {
|
for (const auto &load : net->users) {
|
||||||
if (cell_pred(load.cell) && load.port == port) {
|
if (load.cell != exclude && cell_pred(load.cell) && load.port == port) {
|
||||||
return load.cell;
|
return load.cell;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If a net is driven by a given port of a cell matching a predicate, return
|
// If a net is driven by a given port of a cell matching a predicate, return
|
||||||
// that cell, otherwise nullptr
|
// that cell, otherwise nullptr
|
||||||
|
@ -140,7 +140,7 @@ static wchar_t *program;
|
|||||||
|
|
||||||
void init_python(const char *executable)
|
void init_python(const char *executable)
|
||||||
{
|
{
|
||||||
#ifndef PYTHON_MODULE
|
#ifdef MAIN_EXECUTABLE
|
||||||
program = Py_DecodeLocale(executable, NULL);
|
program = Py_DecodeLocale(executable, NULL);
|
||||||
if (program == NULL) {
|
if (program == NULL) {
|
||||||
fprintf(stderr, "Fatal error: cannot decode executable filename\n");
|
fprintf(stderr, "Fatal error: cannot decode executable filename\n");
|
||||||
@ -162,7 +162,7 @@ void init_python(const char *executable)
|
|||||||
|
|
||||||
void deinit_python()
|
void deinit_python()
|
||||||
{
|
{
|
||||||
#ifndef PYTHON_MODULE
|
#ifdef MAIN_EXECUTABLE
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
PyMem_RawFree(program);
|
PyMem_RawFree(program);
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PYTHON_MODULE
|
#ifdef MAIN_EXECUTABLE
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
@ -341,24 +341,25 @@ void json_import_cell_params(Design *design, string &modname, CellInfo *cell,
|
|||||||
modname.c_str());
|
modname.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
template <typename F>
|
||||||
string &port_name, JsonNode *dir_node,
|
void json_import_ports(Design *design, const string &modname,
|
||||||
JsonNode *wire_group_node)
|
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,
|
// Examine a port of a cell or the design. For every bit of the port,
|
||||||
// generating them as necessary
|
// the connected net will be processed and `visitor` will be called
|
||||||
|
// with (PortType dir, std::string name, NetInfo *net)
|
||||||
assert(dir_node);
|
assert(dir_node);
|
||||||
|
|
||||||
if (json_debug)
|
if (json_debug)
|
||||||
log_info(" Examining port %s, node %s\n", port_name.c_str(),
|
log_info(" Examining port %s, node %s\n", port_name.c_str(),
|
||||||
cell->name.c_str());
|
obj_name.c_str());
|
||||||
|
|
||||||
if (!wire_group_node)
|
if (!wire_group_node)
|
||||||
log_error("JSON no connection match "
|
log_error("JSON no connection match "
|
||||||
"for port_direction \'%s\' of node \'%s\' "
|
"for port_direction \'%s\' of node \'%s\' "
|
||||||
"in module \'%s\'\n",
|
"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);
|
assert(wire_group_node);
|
||||||
|
|
||||||
@ -377,7 +378,7 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
|||||||
else
|
else
|
||||||
log_error("JSON unknown port direction \'%s\' in node \'%s\' "
|
log_error("JSON unknown port direction \'%s\' in node \'%s\' "
|
||||||
"of module \'%s\'\n",
|
"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());
|
modname.c_str());
|
||||||
//
|
//
|
||||||
// Find an update, or create a net to connect
|
// 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.
|
// There is/are no connections to this port.
|
||||||
//
|
//
|
||||||
// Create the port, but leave the net NULL
|
// Create the port, but leave the net NULL
|
||||||
PortInfo this_port;
|
|
||||||
|
|
||||||
//
|
visitor(port_info.type, port_info.name, nullptr);
|
||||||
this_port.name = port_info.name;
|
|
||||||
this_port.type = port_info.type;
|
|
||||||
this_port.net = NULL;
|
|
||||||
|
|
||||||
cell->ports[this_port.name] = this_port;
|
|
||||||
|
|
||||||
if (json_debug)
|
if (json_debug)
|
||||||
log_info(" Port \'%s\' has no connection in \'%s\'\n",
|
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
|
} else
|
||||||
for (int index = 0; index < wire_group_node->data_array.size();
|
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;
|
JsonNode *wire_node;
|
||||||
PortInfo this_port;
|
PortInfo this_port;
|
||||||
PortRef port_ref;
|
|
||||||
bool const_input = false;
|
bool const_input = false;
|
||||||
IdString net_id;
|
IdString net_id;
|
||||||
//
|
//
|
||||||
wire_node = wire_group_node->data_array[index];
|
wire_node = wire_group_node->data_array[index];
|
||||||
port_ref.cell = cell;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pick a name for this port
|
// Pick a name for this port
|
||||||
if (is_bus)
|
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.name = port_info.name;
|
||||||
this_port.type = port_info.type;
|
this_port.type = port_info.type;
|
||||||
|
|
||||||
port_ref.port = this_port.name;
|
|
||||||
|
|
||||||
if (wire_node->type == 'N') {
|
if (wire_node->type == 'N') {
|
||||||
int net_num;
|
int net_num;
|
||||||
|
|
||||||
@ -500,7 +490,7 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
|||||||
"\'%s\' of port \'%s\' "
|
"\'%s\' of port \'%s\' "
|
||||||
"in cell \'%s\' of module \'%s\'\n",
|
"in cell \'%s\' of module \'%s\'\n",
|
||||||
wire_node->data_string.c_str(),
|
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());
|
modname.c_str());
|
||||||
|
|
||||||
} else
|
} else
|
||||||
@ -511,17 +501,8 @@ void json_import_cell_ports(Design *design, string &modname, CellInfo *cell,
|
|||||||
|
|
||||||
if (json_debug)
|
if (json_debug)
|
||||||
log_info(" Inserting port \'%s\' into cell \'%s\'\n",
|
log_info(" Inserting port \'%s\' into cell \'%s\'\n",
|
||||||
this_port.name.c_str(), cell->name.c_str());
|
this_port.name.c_str(), obj_name.c_str());
|
||||||
|
visitor(this_port.type, this_port.name, this_net);
|
||||||
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);
|
|
||||||
|
|
||||||
if (design->nets.count(this_net->name) == 0)
|
if (design->nets.count(this_net->name) == 0)
|
||||||
design->nets[this_net->name] = this_net;
|
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);
|
dir_node = pdir_node->data_dict.at(port_name);
|
||||||
wire_group_node = connections->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,
|
json_import_ports(
|
||||||
wire_group_node);
|
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;
|
design->cells[cell->name] = cell;
|
||||||
// check_all_nets_driven(design);
|
// 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)
|
void json_import(Design *design, string modname, JsonNode *node)
|
||||||
{
|
{
|
||||||
if (is_blackbox(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);
|
check_all_nets_driven(design);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include "emb.h"
|
#include "emb.h"
|
||||||
#include "pybindings.h"
|
#include "pybindings.h"
|
||||||
#include "qtpropertymanager.h"
|
|
||||||
#include "qttreepropertybrowser.h"
|
|
||||||
#include "qtvariantproperty.h"
|
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
#include <QDate>
|
#include <QDate>
|
||||||
@ -18,16 +15,43 @@ MainWindow::MainWindow(Design *_design, QWidget *parent)
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->treeWidget->setColumnCount(1);
|
ui->treeWidget->setColumnCount(1);
|
||||||
ui->treeWidget->setHeaderLabel(QString("Items"));
|
ui->treeWidget->setHeaderLabel(QString("Items"));
|
||||||
QTreeWidgetItem *belroot = new QTreeWidgetItem(ui->treeWidget);
|
ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
belroot->setText(0, QString("Bels"));
|
connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this,
|
||||||
ui->treeWidget->insertTopLevelItem(0, belroot);
|
&MainWindow::prepareMenu);
|
||||||
QList<QTreeWidgetItem *> items;
|
|
||||||
|
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()) {
|
for (auto bel : design->chip.getBels()) {
|
||||||
auto name = design->chip.getBelName(bel);
|
auto name = design->chip.getBelName(bel);
|
||||||
items.append(new QTreeWidgetItem((QTreeWidget *)nullptr,
|
bel_items.append(new QTreeWidgetItem(
|
||||||
QStringList(QString(name.c_str()))));
|
(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");
|
PyImport_ImportModule("emb");
|
||||||
|
|
||||||
write = [this](std::string s) {
|
write = [this](std::string s) {
|
||||||
@ -38,7 +62,8 @@ MainWindow::MainWindow(Design *_design, QWidget *parent)
|
|||||||
emb::set_stdout(write);
|
emb::set_stdout(write);
|
||||||
std::string title = "nextpnr-ice40 - " + design->chip.getChipName();
|
std::string title = "nextpnr-ice40 - " + design->chip.getChipName();
|
||||||
setWindowTitle(title.c_str());
|
setWindowTitle(title.c_str());
|
||||||
QtVariantPropertyManager *variantManager = new QtVariantPropertyManager();
|
|
||||||
|
variantManager = new QtVariantPropertyManager();
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
QtProperty *topItem = variantManager->addProperty(
|
QtProperty *topItem = variantManager->addProperty(
|
||||||
@ -197,9 +222,9 @@ MainWindow::MainWindow(Design *_design, QWidget *parent)
|
|||||||
QString::number(i++) + QLatin1String(" Color Property"));
|
QString::number(i++) + QLatin1String(" Color Property"));
|
||||||
topItem->addSubProperty(item);
|
topItem->addSubProperty(item);
|
||||||
|
|
||||||
QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory();
|
variantFactory = new QtVariantEditorFactory();
|
||||||
|
|
||||||
QtTreePropertyBrowser *variantEditor = new QtTreePropertyBrowser();
|
variantEditor = new QtTreePropertyBrowser();
|
||||||
variantEditor->setFactoryForManager(variantManager, variantFactory);
|
variantEditor->setFactoryForManager(variantManager, variantFactory);
|
||||||
variantEditor->addProperty(topItem);
|
variantEditor->addProperty(topItem);
|
||||||
variantEditor->setPropertiesWithoutValueMarked(true);
|
variantEditor->setPropertiesWithoutValueMarked(true);
|
||||||
@ -210,13 +235,37 @@ MainWindow::MainWindow(Design *_design, QWidget *parent)
|
|||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
|
delete variantManager;
|
||||||
// delete variantManager;
|
delete variantFactory;
|
||||||
// delete variantFactory;
|
delete variantEditor;
|
||||||
// delete variantEditor;
|
|
||||||
delete ui;
|
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); }
|
void handle_system_exit() { exit(-1); }
|
||||||
|
|
||||||
int MainWindow::executePython(std::string command)
|
int MainWindow::executePython(std::string command)
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
#include "emb.h"
|
#include "emb.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
|
#include "qtpropertymanager.h"
|
||||||
|
#include "qttreepropertybrowser.h"
|
||||||
|
#include "qtvariantproperty.h"
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
||||||
@ -27,11 +30,16 @@ class MainWindow : public QMainWindow
|
|||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void on_lineEdit_returnPressed();
|
void on_lineEdit_returnPressed();
|
||||||
|
void prepareMenu(const QPoint &pos);
|
||||||
|
void selectObject(QTreeWidgetItem *item);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
emb::stdout_write_type write;
|
emb::stdout_write_type write;
|
||||||
Design *design;
|
Design *design;
|
||||||
|
QtVariantPropertyManager *variantManager;
|
||||||
|
QtVariantEditorFactory *variantFactory;
|
||||||
|
QtTreePropertyBrowser *variantEditor;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
@ -60,6 +60,26 @@ CellInfo *create_ice_cell(Design *design, IdString type, IdString name)
|
|||||||
add_port(new_cell, "LO", PORT_OUT);
|
add_port(new_cell, "LO", PORT_OUT);
|
||||||
add_port(new_cell, "O", PORT_OUT);
|
add_port(new_cell, "O", PORT_OUT);
|
||||||
add_port(new_cell, "OUT", 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 {
|
} else {
|
||||||
log_error("unable to create iCE40 cell of type %s", type.c_str());
|
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");
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -47,6 +47,9 @@ inline bool is_ff(const CellInfo *cell)
|
|||||||
cell->type == "SB_DFFNESS" || cell->type == "SB_DFFNES";
|
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
|
// 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
|
// as needed. Set no_dff if a DFF is not being used, so that the output
|
||||||
// can be reconnected
|
// can be reconnected
|
||||||
@ -58,6 +61,9 @@ void lut_to_lc(CellInfo *lut, CellInfo *lc, bool no_dff = true);
|
|||||||
// ignored
|
// ignored
|
||||||
void dff_to_lc(CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -99,6 +99,16 @@ Chip::Chip(ChipArgs args) : args(args)
|
|||||||
}
|
}
|
||||||
#endif
|
#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);
|
bel_to_cell.resize(chip_info.num_bels);
|
||||||
wire_to_net.resize(chip_info.num_wires);
|
wire_to_net.resize(chip_info.num_wires);
|
||||||
pip_to_net.resize(chip_info.num_pips);
|
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 Chip::getBelPosition(BelId bel) const
|
||||||
{
|
{
|
||||||
PosInfo pos;
|
PosInfo pos;
|
||||||
|
20
ice40/chip.h
20
ice40/chip.h
@ -118,6 +118,19 @@ struct WireInfoPOD
|
|||||||
float x, y;
|
float x, y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PackagePinPOD
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
int32_t bel_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PackageInfoPOD
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
int num_pins;
|
||||||
|
PackagePinPOD *pins;
|
||||||
|
};
|
||||||
|
|
||||||
enum TileType
|
enum TileType
|
||||||
{
|
{
|
||||||
TILE_NONE = 0,
|
TILE_NONE = 0,
|
||||||
@ -173,12 +186,13 @@ struct ChipInfoPOD
|
|||||||
{
|
{
|
||||||
int width, height;
|
int width, height;
|
||||||
int num_bels, num_wires, num_pips;
|
int num_bels, num_wires, num_pips;
|
||||||
int num_switches;
|
int num_switches, num_packages;
|
||||||
BelInfoPOD *bel_data;
|
BelInfoPOD *bel_data;
|
||||||
WireInfoPOD *wire_data;
|
WireInfoPOD *wire_data;
|
||||||
PipInfoPOD *pip_data;
|
PipInfoPOD *pip_data;
|
||||||
TileType *tile_grid;
|
TileType *tile_grid;
|
||||||
BitstreamInfoPOD *bits_info;
|
BitstreamInfoPOD *bits_info;
|
||||||
|
PackageInfoPOD *packages_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ChipInfoPOD chip_info_384;
|
extern ChipInfoPOD chip_info_384;
|
||||||
@ -417,11 +431,13 @@ struct ChipArgs
|
|||||||
HX8K,
|
HX8K,
|
||||||
UP5K
|
UP5K
|
||||||
} type = NONE;
|
} type = NONE;
|
||||||
|
std::string package;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Chip
|
struct Chip
|
||||||
{
|
{
|
||||||
ChipInfoPOD chip_info;
|
ChipInfoPOD chip_info;
|
||||||
|
PackageInfoPOD *package_info;
|
||||||
|
|
||||||
mutable std::unordered_map<IdString, int> bel_by_name;
|
mutable std::unordered_map<IdString, int> bel_by_name;
|
||||||
mutable std::unordered_map<IdString, int> wire_by_name;
|
mutable std::unordered_map<IdString, int> wire_by_name;
|
||||||
@ -682,6 +698,8 @@ struct Chip
|
|||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BelId getPackagePinBel(const std::string &pin) const;
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
PosInfo getBelPosition(BelId bel) const;
|
PosInfo getBelPosition(BelId bel) const;
|
||||||
|
@ -23,6 +23,8 @@ switches = list()
|
|||||||
|
|
||||||
ierens = list()
|
ierens = list()
|
||||||
|
|
||||||
|
packages = list()
|
||||||
|
|
||||||
wire_uphill_belport = dict()
|
wire_uphill_belport = dict()
|
||||||
wire_downhill_belports = dict()
|
wire_downhill_belports = dict()
|
||||||
|
|
||||||
@ -123,6 +125,11 @@ with open(sys.argv[1], "r") as f:
|
|||||||
mode = ("ieren",)
|
mode = ("ieren",)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if line[0] == ".pins":
|
||||||
|
mode = ("pins", line[1])
|
||||||
|
packages.append((line[1], []))
|
||||||
|
continue
|
||||||
|
|
||||||
if (line[0][0] == ".") or (mode is None):
|
if (line[0][0] == ".") or (mode is None):
|
||||||
mode = None
|
mode = None
|
||||||
continue
|
continue
|
||||||
@ -157,9 +164,16 @@ with open(sys.argv[1], "r") as f:
|
|||||||
assert m
|
assert m
|
||||||
bits.append((int(m.group(1)), int(m.group(2))))
|
bits.append((int(m.group(1)), int(m.group(2))))
|
||||||
tile_bits[mode[1]].append((name, bits))
|
tile_bits[mode[1]].append((name, bits))
|
||||||
|
continue
|
||||||
|
|
||||||
if mode[0] == "ieren":
|
if mode[0] == "ieren":
|
||||||
ierens.append(tuple([int(_) for _ in line]))
|
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):
|
def add_bel_input(bel, wire, port):
|
||||||
if wire not in wire_downhill_belports:
|
if wire not in wire_downhill_belports:
|
||||||
wire_downhill_belports[wire] = set()
|
wire_downhill_belports[wire] = set()
|
||||||
@ -392,6 +406,21 @@ for wire in range(num_wires):
|
|||||||
|
|
||||||
wireinfo.append(info)
|
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 = []
|
tilegrid = []
|
||||||
for y in range(dev_height):
|
for y in range(dev_height):
|
||||||
for x in range(dev_width):
|
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(",\n".join(tilegrid))
|
||||||
print("};")
|
print("};")
|
||||||
|
|
||||||
|
|
||||||
|
print("static PackageInfoPOD package_info_%s[%d] = {" % (dev_name, len(packageinfo)))
|
||||||
|
print(",\n".join(packageinfo))
|
||||||
|
print("};")
|
||||||
|
|
||||||
print('}')
|
print('}')
|
||||||
print('NEXTPNR_NAMESPACE_BEGIN')
|
print('NEXTPNR_NAMESPACE_BEGIN')
|
||||||
|
|
||||||
print("ChipInfoPOD chip_info_%s = {" % dev_name)
|
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(" 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("};")
|
||||||
|
|
||||||
print('NEXTPNR_NAMESPACE_END')
|
print('NEXTPNR_NAMESPACE_END')
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PYTHON_MODULE
|
#ifdef MAIN_EXECUTABLE
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <boost/filesystem/convenience.hpp>
|
#include <boost/filesystem/convenience.hpp>
|
||||||
@ -30,6 +30,7 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "pack.h"
|
#include "pack.h"
|
||||||
|
#include "pcf.h"
|
||||||
#include "place.h"
|
#include "place.h"
|
||||||
#include "pybindings.h"
|
#include "pybindings.h"
|
||||||
#include "route.h"
|
#include "route.h"
|
||||||
@ -65,10 +66,8 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
po::options_description options("Allowed options");
|
po::options_description options("Allowed options");
|
||||||
options.add_options()("help,h", "show help");
|
options.add_options()("help,h", "show help");
|
||||||
options.add_options()("test", "just a check");
|
|
||||||
options.add_options()("gui", "start gui");
|
options.add_options()("gui", "start gui");
|
||||||
options.add_options()("svg", "dump SVG file");
|
options.add_options()("svg", "dump SVG file");
|
||||||
options.add_options()("pack", "pack design prior to place and route");
|
|
||||||
options.add_options()("pack-only",
|
options.add_options()("pack-only",
|
||||||
"pack design only without placement or routing");
|
"pack design only without placement or routing");
|
||||||
|
|
||||||
@ -76,6 +75,8 @@ int main(int argc, char *argv[])
|
|||||||
"python file to execute");
|
"python file to execute");
|
||||||
options.add_options()("json", po::value<std::string>(),
|
options.add_options()("json", po::value<std::string>(),
|
||||||
"JSON design file to ingest");
|
"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>(),
|
options.add_options()("asc", po::value<std::string>(),
|
||||||
"asc bitstream file to write");
|
"asc bitstream file to write");
|
||||||
options.add_options()("version,v", "show version");
|
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()("hx1k", "set device type to iCE40HX1K");
|
||||||
options.add_options()("hx8k", "set device type to iCE40HX8K");
|
options.add_options()("hx8k", "set device type to iCE40HX8K");
|
||||||
options.add_options()("up5k", "set device type to iCE40UP5K");
|
options.add_options()("up5k", "set device type to iCE40UP5K");
|
||||||
|
options.add_options()("package", po::value<std::string>(),
|
||||||
|
"set device package");
|
||||||
po::positional_options_description pos;
|
po::positional_options_description pos;
|
||||||
pos.add("run", -1);
|
pos.add("run", -1);
|
||||||
|
|
||||||
@ -129,41 +131,48 @@ int main(int argc, char *argv[])
|
|||||||
if (chipArgs.type != ChipArgs::NONE)
|
if (chipArgs.type != ChipArgs::NONE)
|
||||||
goto help;
|
goto help;
|
||||||
chipArgs.type = ChipArgs::LP384;
|
chipArgs.type = ChipArgs::LP384;
|
||||||
|
chipArgs.package = "qn32";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("lp1k")) {
|
if (vm.count("lp1k")) {
|
||||||
if (chipArgs.type != ChipArgs::NONE)
|
if (chipArgs.type != ChipArgs::NONE)
|
||||||
goto help;
|
goto help;
|
||||||
chipArgs.type = ChipArgs::LP1K;
|
chipArgs.type = ChipArgs::LP1K;
|
||||||
|
chipArgs.package = "tq144";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("lp8k")) {
|
if (vm.count("lp8k")) {
|
||||||
if (chipArgs.type != ChipArgs::NONE)
|
if (chipArgs.type != ChipArgs::NONE)
|
||||||
goto help;
|
goto help;
|
||||||
chipArgs.type = ChipArgs::LP8K;
|
chipArgs.type = ChipArgs::LP8K;
|
||||||
|
chipArgs.package = "ct256";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("hx1k")) {
|
if (vm.count("hx1k")) {
|
||||||
if (chipArgs.type != ChipArgs::NONE)
|
if (chipArgs.type != ChipArgs::NONE)
|
||||||
goto help;
|
goto help;
|
||||||
chipArgs.type = ChipArgs::HX1K;
|
chipArgs.type = ChipArgs::HX1K;
|
||||||
|
chipArgs.package = "tq144";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("hx8k")) {
|
if (vm.count("hx8k")) {
|
||||||
if (chipArgs.type != ChipArgs::NONE)
|
if (chipArgs.type != ChipArgs::NONE)
|
||||||
goto help;
|
goto help;
|
||||||
chipArgs.type = ChipArgs::HX8K;
|
chipArgs.type = ChipArgs::HX8K;
|
||||||
|
chipArgs.package = "ct256";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("up5k")) {
|
if (vm.count("up5k")) {
|
||||||
if (chipArgs.type != ChipArgs::NONE)
|
if (chipArgs.type != ChipArgs::NONE)
|
||||||
goto help;
|
goto help;
|
||||||
chipArgs.type = ChipArgs::UP5K;
|
chipArgs.type = ChipArgs::UP5K;
|
||||||
|
chipArgs.package = "sg48";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chipArgs.type == ChipArgs::NONE)
|
if (chipArgs.type == ChipArgs::NONE) {
|
||||||
chipArgs.type = ChipArgs::HX1K;
|
chipArgs.type = ChipArgs::HX1K;
|
||||||
|
chipArgs.package = "tq144";
|
||||||
|
}
|
||||||
#ifdef ICE40_HX1K_ONLY
|
#ifdef ICE40_HX1K_ONLY
|
||||||
if (chipArgs.type != ChipArgs::HX1K) {
|
if (chipArgs.type != ChipArgs::HX1K) {
|
||||||
std::cout << "This version of nextpnr-ice40 is built with HX1K-support "
|
std::cout << "This version of nextpnr-ice40 is built with HX1K-support "
|
||||||
@ -172,71 +181,14 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (vm.count("package"))
|
||||||
|
chipArgs.package = vm["package"].as<std::string>();
|
||||||
|
|
||||||
Design design(chipArgs);
|
Design design(chipArgs);
|
||||||
init_python(argv[0]);
|
init_python(argv[0]);
|
||||||
python_export_global("design", design);
|
python_export_global("design", design);
|
||||||
python_export_global("chip", design.chip);
|
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")) {
|
if (vm.count("svg")) {
|
||||||
std::cout << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
std::cout << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
||||||
"xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
|
"xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
|
||||||
@ -256,9 +208,13 @@ int main(int argc, char *argv[])
|
|||||||
std::istream *f = new std::ifstream(filename);
|
std::istream *f = new std::ifstream(filename);
|
||||||
|
|
||||||
parse_json_file(f, filename, &design);
|
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")) {
|
if (!vm.count("pack-only")) {
|
||||||
place_design(&design);
|
place_design(&design);
|
||||||
route_design(&design);
|
route_design(&design);
|
||||||
|
@ -134,10 +134,65 @@ static void pack_constants(Design *design)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_nextpnr_iob(CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == "$nextpnr_ibuf" || cell->type == "$nextpnr_obuf" ||
|
||||||
|
cell->type == "$nextpnr_iobuf";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack IO buffers
|
||||||
|
static void pack_io(Design *design)
|
||||||
|
{
|
||||||
|
std::unordered_set<IdString> packed_cells;
|
||||||
|
std::vector<CellInfo *> new_cells;
|
||||||
|
|
||||||
|
for (auto cell : design->cells) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (is_nextpnr_iob(ci)) {
|
||||||
|
CellInfo *sb = nullptr;
|
||||||
|
if (ci->type == "$nextpnr_ibuf" || ci->type == "$nextpnr_iobuf") {
|
||||||
|
sb = net_only_drives(ci->ports.at("O").net, is_sb_io,
|
||||||
|
"PACKAGE_PIN", true, ci);
|
||||||
|
|
||||||
|
} else if (ci->type == "$nextpnr_obuf") {
|
||||||
|
sb = net_only_drives(ci->ports.at("I").net, is_sb_io,
|
||||||
|
"PACKAGE_PIN", true, ci);
|
||||||
|
}
|
||||||
|
if (sb != nullptr) {
|
||||||
|
// Trivial case, SB_IO used. Just destroy the net and the
|
||||||
|
// iobuf
|
||||||
|
log_info("%s feeds SB_IO %s, removing %s %s.\n",
|
||||||
|
ci->name.c_str(), sb->name.c_str(), ci->type.c_str(),
|
||||||
|
ci->name.c_str());
|
||||||
|
NetInfo *net = sb->ports.at("PACKAGE_PIN").net;
|
||||||
|
if (net != nullptr) {
|
||||||
|
design->nets.erase(net->name);
|
||||||
|
sb->ports.at("PACKAGE_PIN").net = nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create a SB_IO buffer
|
||||||
|
sb = create_ice_cell(design, "SB_IO");
|
||||||
|
nxio_to_sb(ci, sb);
|
||||||
|
new_cells.push_back(sb);
|
||||||
|
}
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
std::copy(ci->attrs.begin(), ci->attrs.end(),
|
||||||
|
std::inserter(sb->attrs, sb->attrs.begin()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto pcell : packed_cells) {
|
||||||
|
design->cells.erase(pcell);
|
||||||
|
}
|
||||||
|
for (auto ncell : new_cells) {
|
||||||
|
design->cells[ncell->name] = ncell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Main pack function
|
// Main pack function
|
||||||
void pack_design(Design *design)
|
void pack_design(Design *design)
|
||||||
{
|
{
|
||||||
pack_constants(design);
|
pack_constants(design);
|
||||||
|
pack_io(design);
|
||||||
pack_lut_lutffs(design);
|
pack_lut_lutffs(design);
|
||||||
pack_nonlut_ffs(design);
|
pack_nonlut_ffs(design);
|
||||||
}
|
}
|
||||||
|
@ -1,169 +0,0 @@
|
|||||||
module io_wrapper(input clk_pin, cen_pin, rst_pin, ina_pin, inb_pin,
|
|
||||||
output outa_pin, outb_pin, outc_pin, outd_pin);
|
|
||||||
|
|
||||||
wire clk, cen, rst, ina, inb, outa, outb, outc, outd;
|
|
||||||
|
|
||||||
(* BEL="0_14_io1" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0000_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) clk_iob (
|
|
||||||
.PACKAGE_PIN(clk_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(clk),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
(* BEL="0_14_io0" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0000_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) cen_iob (
|
|
||||||
.PACKAGE_PIN(cen_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(cen),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
(* BEL="0_13_io1" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0000_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) rst_iob (
|
|
||||||
.PACKAGE_PIN(rst_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(rst),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
(* BEL="0_13_io0" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0000_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) ina_iob (
|
|
||||||
.PACKAGE_PIN(ina_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(ina),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
(* BEL="0_12_io1" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0000_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) inb_iob (
|
|
||||||
.PACKAGE_PIN(inb_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(inb),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
(* BEL="0_12_io0" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0110_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) outa_iob (
|
|
||||||
.PACKAGE_PIN(outa_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(outa),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
(* BEL="0_11_io1" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0110_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) outb_iob (
|
|
||||||
.PACKAGE_PIN(outb_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(outb),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
(* BEL="0_11_io0" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0110_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) outc_iob (
|
|
||||||
.PACKAGE_PIN(outc_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(outc),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
(* BEL="0_10_io1" *)
|
|
||||||
SB_IO #(
|
|
||||||
.PIN_TYPE(6'b 0110_01),
|
|
||||||
.PULLUP(1'b0),
|
|
||||||
.NEG_TRIGGER(1'b0)
|
|
||||||
) outd_iob (
|
|
||||||
.PACKAGE_PIN(outa_pin),
|
|
||||||
.LATCH_INPUT_VALUE(),
|
|
||||||
.CLOCK_ENABLE(),
|
|
||||||
.INPUT_CLK(),
|
|
||||||
.OUTPUT_CLK(),
|
|
||||||
.OUTPUT_ENABLE(),
|
|
||||||
.D_OUT_0(outd),
|
|
||||||
.D_OUT_1(),
|
|
||||||
.D_IN_0(),
|
|
||||||
.D_IN_1()
|
|
||||||
);
|
|
||||||
|
|
||||||
top top_i(.clk(clk), .rst(rst), .cen(cen), .ina(ina), .inb(inb), .outa(outa), .outb(outb), .outc(outc), .outd(outd));
|
|
||||||
endmodule
|
|
@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -ex
|
set -ex
|
||||||
NAME=${1%.v}
|
NAME=${1%.v}
|
||||||
yosys -p "synth_ice40 -nocarry -top io_wrapper; write_json ${NAME}.json" $1 io_wrapper.v
|
yosys -p "synth_ice40 -nocarry -top top; write_json ${NAME}.json" $1
|
||||||
../../nextpnr-ice40 --json ${NAME}.json --pack --asc ${NAME}.asc
|
../../nextpnr-ice40 --json ${NAME}.json --pcf test.pcf --asc ${NAME}.asc
|
||||||
icebox_vlog -p test.pcf ${NAME}.asc > ${NAME}_out.v
|
icebox_vlog -p test.pcf ${NAME}.asc > ${NAME}_out.v
|
||||||
|
|
||||||
yosys -p "read_verilog +/ice40/cells_sim.v;\
|
yosys -p "read_verilog +/ice40/cells_sim.v;\
|
||||||
|
71
ice40/pcf.cc
Normal file
71
ice40/pcf.cc
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@clifford.at>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pcf.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Read a w
|
||||||
|
|
||||||
|
// Apply PCF constraints to a pre-packing design
|
||||||
|
void apply_pcf(Design *design, std::istream &in)
|
||||||
|
{
|
||||||
|
if (!in)
|
||||||
|
log_error("failed to open PCF file");
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(in, line)) {
|
||||||
|
size_t cstart = line.find("#");
|
||||||
|
if (cstart != std::string::npos)
|
||||||
|
line = line.substr(0, cstart);
|
||||||
|
std::stringstream ss(line);
|
||||||
|
std::vector<std::string> words;
|
||||||
|
std::string tmp;
|
||||||
|
while (ss >> tmp)
|
||||||
|
words.push_back(tmp);
|
||||||
|
if (words.size() == 0)
|
||||||
|
continue;
|
||||||
|
std::string cmd = words.at(0);
|
||||||
|
if (cmd == "set_io") {
|
||||||
|
size_t args_end = 1;
|
||||||
|
while (args_end < words.size() && words.at(args_end).at(0) == '-')
|
||||||
|
args_end++;
|
||||||
|
std::string cell = words.at(args_end);
|
||||||
|
std::string pin = words.at(args_end + 1);
|
||||||
|
auto fnd_cell = design->cells.find(cell);
|
||||||
|
if (fnd_cell == design->cells.end()) {
|
||||||
|
log_warning("unmatched pcf constraint %s\n", cell.c_str());
|
||||||
|
} else {
|
||||||
|
BelId pin_bel = design->chip.getPackagePinBel(pin);
|
||||||
|
if (pin_bel == BelId())
|
||||||
|
log_error("package does not have a pin named %s\n",
|
||||||
|
pin.c_str());
|
||||||
|
fnd_cell->second->attrs["BEL"] =
|
||||||
|
design->chip.getBelName(pin_bel).str();
|
||||||
|
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
|
||||||
|
fnd_cell->second->attrs["BEL"].c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("unsupported pcf command '%s'\n", cmd.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
33
ice40/pcf.h
Normal file
33
ice40/pcf.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@clifford.at>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PCF_H
|
||||||
|
#define PCF_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Apply PCF constraints to a pre-packing design
|
||||||
|
void apply_pcf(Design *design, std::istream &in);
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // ROUTE_H
|
9
tests/dummy/main.cc
Normal file
9
tests/dummy/main.cc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(example, sum_zero) {
|
|
||||||
auto result = 0;
|
|
||||||
ASSERT_EQ(result, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(example, sum_five) {
|
|
||||||
auto result = 15;
|
|
||||||
ASSERT_EQ(result, 15);
|
|
||||||
}
|
|
87
tests/ice40/hx1k.cc
Normal file
87
tests/ice40/hx1k.cc
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
class HX1KTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void SetUp()
|
||||||
|
{
|
||||||
|
chipArgs.type = ChipArgs::HX1K;
|
||||||
|
design = new Design(chipArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() { delete design; }
|
||||||
|
|
||||||
|
ChipArgs chipArgs;
|
||||||
|
Design *design;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(HX1KTest, bel_names)
|
||||||
|
{
|
||||||
|
int bel_count = 0;
|
||||||
|
for (auto bel : design->chip.getBels()) {
|
||||||
|
auto name = design->chip.getBelName(bel);
|
||||||
|
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||||
|
bel_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(bel_count, 1416);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HX1KTest, wire_names)
|
||||||
|
{
|
||||||
|
int wire_count = 0;
|
||||||
|
for (auto wire : design->chip.getWires()) {
|
||||||
|
auto name = design->chip.getWireName(wire);
|
||||||
|
assert(wire == design->chip.getWireByName(name));
|
||||||
|
wire_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(wire_count, 27682);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HX1KTest, pip_names)
|
||||||
|
{
|
||||||
|
int pip_count = 0;
|
||||||
|
for (auto pip : design->chip.getPips()) {
|
||||||
|
auto name = design->chip.getPipName(pip);
|
||||||
|
assert(pip == design->chip.getPipByName(name));
|
||||||
|
pip_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(pip_count, 319904);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HX1KTest, uphill_to_downhill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||||
|
bool found_downhill = false;
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||||
|
design->chip.getPipSrcWire(uphill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_downhill);
|
||||||
|
found_downhill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_downhill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HX1KTest, downhill_to_uphill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||||
|
bool found_uphill = false;
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||||
|
design->chip.getPipDstWire(downhill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_uphill);
|
||||||
|
found_uphill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_uphill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
tests/ice40/hx8k.cc
Normal file
87
tests/ice40/hx8k.cc
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
class HX8KTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void SetUp()
|
||||||
|
{
|
||||||
|
chipArgs.type = ChipArgs::HX8K;
|
||||||
|
design = new Design(chipArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() { delete design; }
|
||||||
|
|
||||||
|
ChipArgs chipArgs;
|
||||||
|
Design *design;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(HX8KTest, bel_names)
|
||||||
|
{
|
||||||
|
int bel_count = 0;
|
||||||
|
for (auto bel : design->chip.getBels()) {
|
||||||
|
auto name = design->chip.getBelName(bel);
|
||||||
|
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||||
|
bel_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(bel_count, 7968);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HX8KTest, wire_names)
|
||||||
|
{
|
||||||
|
int wire_count = 0;
|
||||||
|
for (auto wire : design->chip.getWires()) {
|
||||||
|
auto name = design->chip.getWireName(wire);
|
||||||
|
assert(wire == design->chip.getWireByName(name));
|
||||||
|
wire_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(wire_count, 135174);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HX8KTest, pip_names)
|
||||||
|
{
|
||||||
|
int pip_count = 0;
|
||||||
|
for (auto pip : design->chip.getPips()) {
|
||||||
|
auto name = design->chip.getPipName(pip);
|
||||||
|
assert(pip == design->chip.getPipByName(name));
|
||||||
|
pip_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(pip_count, 1652480);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HX8KTest, uphill_to_downhill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||||
|
bool found_downhill = false;
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||||
|
design->chip.getPipSrcWire(uphill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_downhill);
|
||||||
|
found_downhill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_downhill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HX8KTest, downhill_to_uphill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||||
|
bool found_uphill = false;
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||||
|
design->chip.getPipDstWire(downhill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_uphill);
|
||||||
|
found_uphill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_uphill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
tests/ice40/lp1k.cc
Normal file
87
tests/ice40/lp1k.cc
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
class LP1KTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void SetUp()
|
||||||
|
{
|
||||||
|
chipArgs.type = ChipArgs::LP1K;
|
||||||
|
design = new Design(chipArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() { delete design; }
|
||||||
|
|
||||||
|
ChipArgs chipArgs;
|
||||||
|
Design *design;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(LP1KTest, bel_names)
|
||||||
|
{
|
||||||
|
int bel_count = 0;
|
||||||
|
for (auto bel : design->chip.getBels()) {
|
||||||
|
auto name = design->chip.getBelName(bel);
|
||||||
|
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||||
|
bel_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(bel_count, 1416);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP1KTest, wire_names)
|
||||||
|
{
|
||||||
|
int wire_count = 0;
|
||||||
|
for (auto wire : design->chip.getWires()) {
|
||||||
|
auto name = design->chip.getWireName(wire);
|
||||||
|
assert(wire == design->chip.getWireByName(name));
|
||||||
|
wire_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(wire_count, 27682);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP1KTest, pip_names)
|
||||||
|
{
|
||||||
|
int pip_count = 0;
|
||||||
|
for (auto pip : design->chip.getPips()) {
|
||||||
|
auto name = design->chip.getPipName(pip);
|
||||||
|
assert(pip == design->chip.getPipByName(name));
|
||||||
|
pip_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(pip_count, 319904);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP1KTest, uphill_to_downhill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||||
|
bool found_downhill = false;
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||||
|
design->chip.getPipSrcWire(uphill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_downhill);
|
||||||
|
found_downhill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_downhill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP1KTest, downhill_to_uphill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||||
|
bool found_uphill = false;
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||||
|
design->chip.getPipDstWire(downhill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_uphill);
|
||||||
|
found_uphill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_uphill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
tests/ice40/lp384.cc
Normal file
87
tests/ice40/lp384.cc
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
class LP384Test : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void SetUp()
|
||||||
|
{
|
||||||
|
chipArgs.type = ChipArgs::LP384;
|
||||||
|
design = new Design(chipArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() { delete design; }
|
||||||
|
|
||||||
|
ChipArgs chipArgs;
|
||||||
|
Design *design;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(LP384Test, bel_names)
|
||||||
|
{
|
||||||
|
int bel_count = 0;
|
||||||
|
for (auto bel : design->chip.getBels()) {
|
||||||
|
auto name = design->chip.getBelName(bel);
|
||||||
|
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||||
|
bel_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(bel_count, 440);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP384Test, wire_names)
|
||||||
|
{
|
||||||
|
int wire_count = 0;
|
||||||
|
for (auto wire : design->chip.getWires()) {
|
||||||
|
auto name = design->chip.getWireName(wire);
|
||||||
|
assert(wire == design->chip.getWireByName(name));
|
||||||
|
wire_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(wire_count, 8294);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP384Test, pip_names)
|
||||||
|
{
|
||||||
|
int pip_count = 0;
|
||||||
|
for (auto pip : design->chip.getPips()) {
|
||||||
|
auto name = design->chip.getPipName(pip);
|
||||||
|
assert(pip == design->chip.getPipByName(name));
|
||||||
|
pip_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(pip_count, 86864);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP384Test, uphill_to_downhill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||||
|
bool found_downhill = false;
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||||
|
design->chip.getPipSrcWire(uphill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_downhill);
|
||||||
|
found_downhill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_downhill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP384Test, downhill_to_uphill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||||
|
bool found_uphill = false;
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||||
|
design->chip.getPipDstWire(downhill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_uphill);
|
||||||
|
found_uphill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_uphill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
tests/ice40/lp8k.cc
Normal file
87
tests/ice40/lp8k.cc
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
class LP8KTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void SetUp()
|
||||||
|
{
|
||||||
|
chipArgs.type = ChipArgs::LP8K;
|
||||||
|
design = new Design(chipArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() { delete design; }
|
||||||
|
|
||||||
|
ChipArgs chipArgs;
|
||||||
|
Design *design;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(LP8KTest, bel_names)
|
||||||
|
{
|
||||||
|
int bel_count = 0;
|
||||||
|
for (auto bel : design->chip.getBels()) {
|
||||||
|
auto name = design->chip.getBelName(bel);
|
||||||
|
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||||
|
bel_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(bel_count, 7968);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP8KTest, wire_names)
|
||||||
|
{
|
||||||
|
int wire_count = 0;
|
||||||
|
for (auto wire : design->chip.getWires()) {
|
||||||
|
auto name = design->chip.getWireName(wire);
|
||||||
|
assert(wire == design->chip.getWireByName(name));
|
||||||
|
wire_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(wire_count, 135174);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP8KTest, pip_names)
|
||||||
|
{
|
||||||
|
int pip_count = 0;
|
||||||
|
for (auto pip : design->chip.getPips()) {
|
||||||
|
auto name = design->chip.getPipName(pip);
|
||||||
|
assert(pip == design->chip.getPipByName(name));
|
||||||
|
pip_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(pip_count, 1652480);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP8KTest, uphill_to_downhill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||||
|
bool found_downhill = false;
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||||
|
design->chip.getPipSrcWire(uphill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_downhill);
|
||||||
|
found_downhill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_downhill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LP8KTest, downhill_to_uphill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||||
|
bool found_uphill = false;
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||||
|
design->chip.getPipDstWire(downhill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_uphill);
|
||||||
|
found_uphill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_uphill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
tests/ice40/main.cc
Normal file
8
tests/ice40/main.cc
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(example, sum_zero) {
|
|
||||||
auto result = 0;
|
|
||||||
ASSERT_EQ(result, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(example, sum_five) {
|
|
||||||
auto result = 15;
|
|
||||||
ASSERT_EQ(result, 15);
|
|
||||||
}
|
|
87
tests/ice40/up5k.cc
Normal file
87
tests/ice40/up5k.cc
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
class UP5KTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void SetUp()
|
||||||
|
{
|
||||||
|
chipArgs.type = ChipArgs::UP5K;
|
||||||
|
design = new Design(chipArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() { delete design; }
|
||||||
|
|
||||||
|
ChipArgs chipArgs;
|
||||||
|
Design *design;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(UP5KTest, bel_names)
|
||||||
|
{
|
||||||
|
int bel_count = 0;
|
||||||
|
for (auto bel : design->chip.getBels()) {
|
||||||
|
auto name = design->chip.getBelName(bel);
|
||||||
|
ASSERT_EQ(bel, design->chip.getBelByName(name));
|
||||||
|
bel_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(bel_count, 5414);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UP5KTest, wire_names)
|
||||||
|
{
|
||||||
|
int wire_count = 0;
|
||||||
|
for (auto wire : design->chip.getWires()) {
|
||||||
|
auto name = design->chip.getWireName(wire);
|
||||||
|
assert(wire == design->chip.getWireByName(name));
|
||||||
|
wire_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(wire_count, 103383);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UP5KTest, pip_names)
|
||||||
|
{
|
||||||
|
int pip_count = 0;
|
||||||
|
for (auto pip : design->chip.getPips()) {
|
||||||
|
auto name = design->chip.getPipName(pip);
|
||||||
|
assert(pip == design->chip.getPipByName(name));
|
||||||
|
pip_count++;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(pip_count, 1219104);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UP5KTest, uphill_to_downhill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(dst)) {
|
||||||
|
bool found_downhill = false;
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(
|
||||||
|
design->chip.getPipSrcWire(uphill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_downhill);
|
||||||
|
found_downhill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_downhill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UP5KTest, downhill_to_uphill)
|
||||||
|
{
|
||||||
|
for (auto dst : design->chip.getWires()) {
|
||||||
|
for (auto downhill_pip : design->chip.getPipsDownhill(dst)) {
|
||||||
|
bool found_uphill = false;
|
||||||
|
for (auto uphill_pip : design->chip.getPipsUphill(
|
||||||
|
design->chip.getPipDstWire(downhill_pip))) {
|
||||||
|
if (uphill_pip == downhill_pip) {
|
||||||
|
ASSERT_FALSE(found_uphill);
|
||||||
|
found_uphill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(found_uphill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user