Merge remote-tracking branch 'origin/master' into eddieh/idstring_speedup

This commit is contained in:
Eddie Hung 2018-07-25 17:51:47 -07:00
commit e6015dc695
32 changed files with 4976 additions and 612 deletions

View File

@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
class QtPropertyPrivate
{
public:
QtPropertyPrivate(QtAbstractPropertyManager *manager) : m_enabled(true), m_modified(false), m_manager(manager) {}
QtPropertyPrivate(QtAbstractPropertyManager *manager) : m_enabled(true), m_selectable(true), m_modified(false), m_manager(manager) {}
QtProperty *q_ptr;
QSet<QtProperty *> m_parentItems;
@ -66,6 +66,7 @@ public:
QString m_name;
QString m_id;
bool m_enabled;
bool m_selectable;
bool m_modified;
QtAbstractPropertyManager * const m_manager;
@ -260,6 +261,11 @@ bool QtProperty::isEnabled() const
return d_ptr->m_enabled;
}
bool QtProperty::isSelectable() const
{
return d_ptr->m_selectable;
}
/*!
Returns whether the property is modified.
@ -409,6 +415,15 @@ void QtProperty::setEnabled(bool enable)
propertyChanged();
}
void QtProperty::setSelectable(bool selectable)
{
if (d_ptr->m_selectable == selectable)
return;
d_ptr->m_selectable = selectable;
propertyChanged();
}
/*!
Sets the property's modified state according to the passed \a modified value.

View File

@ -83,6 +83,7 @@ public:
QString propertyName() const;
QString propertyId() const;
bool isEnabled() const;
bool isSelectable() const;
bool isModified() const;
bool hasValue() const;
@ -97,6 +98,7 @@ public:
void setPropertyName(const QString &text);
void setPropertyId(const QString &text);
void setEnabled(bool enable);
void setSelectable(bool selectable);
void setModified(bool modified);
bool isSubProperty()const;

View File

@ -651,6 +651,11 @@ void QtTreePropertyBrowserPrivate::updateItem(QTreeWidgetItem *item)
else
disableItem(item);
}
if (property->isSelectable()) {
item->setFlags(item->flags() | Qt::ItemIsSelectable);
} else {
item->setFlags(item->flags() & ~Qt::ItemIsSelectable);
}
m_treeWidget->viewport()->update();
}

View File

@ -1339,6 +1339,7 @@ void addPropertyRecusively(QtVariantPropertyManager * manager,
newProp->setWhatsThis(prop->whatsThis());
newProp->setModified(prop->isModified());
newProp->setEnabled(prop->isEnabled());
newProp->setSelectable(prop->isSelectable());
newProp->setValue(prop->value());
foreach(QtProperty * subProp, prop->subProperties())

70
bba/README.md Normal file
View File

@ -0,0 +1,70 @@
Binary Blob Assembler (bba)
===========================
This tools read a text file describing binary data, and write that binary data
file. The usual flow is that the input to bbasm is generated by a python
script, and the output is linked or loaded into a C program, using (packed)
structs to interpret the binary data.
All references (pointers) are encoded als 32 bit byte offset relative to the
location of the pointer. This way the resulting binary blob is position
independent.
Valid commands for the input are as follows.
pre <string>
------------
When a C file is generated as output, all the "pre" strings will be included
before the binary blob.
post <string>
-------------
When a C file is generated as output, all the "post" strings will be included
after the binary blob.
push <name>
-----------
All following commands up until the matching "pop" will be writen to stream
<name>. Everything written to the same stream will end up in a continous
region of the output.
pop
---
End of a push..pop block.
label <name> [<comment>]
------------------------
Add a label for the current position.
ref <name> [<comment>]
----------------------
Add a 32-bit reference to the specified label. The reference will be a byte
offset relative to the memory location of the reference itself.
u8 <value> [<comment>]
----------------------
Add a 8-bit value to the binary blob.
u16 <value> [<comment>]
-----------------------
Add a 16-bit value to the binary blob. Note that the input must be structured
in a way that ensures that all u16 are aligned to 2-byte addresses.
u32 <value> [<comment>]
----------------------
Add a 32-bit value to the binary blob. Note that the input must be structured
in a way that ensures that all u32 are aligned to 4-byte addresses.
str <string>
------------
Add a reference to a zero-terminated copy of the specified string.

View File

@ -5,6 +5,7 @@ ENDIF(CMAKE_CROSSCOMPILING)
IF(NOT CMAKE_CROSSCOMPILING)
ADD_EXECUTABLE(bbasm bba/main.cc)
target_link_libraries(bbasm LINK_PUBLIC ${Boost_PROGRAM_OPTIONS_LIBRARY})
ENDIF(NOT CMAKE_CROSSCOMPILING)
IF(NOT CMAKE_CROSSCOMPILING)

View File

@ -1,15 +1,354 @@
#include <unistd.h>
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <assert.h>
int main()
#include <boost/program_options.hpp>
#include <iostream>
#include <map>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
enum TokenType : int8_t
{
TOK_LABEL,
TOK_REF,
TOK_U8,
TOK_U16,
TOK_U32
};
struct Stream
{
std::string name;
std::vector<TokenType> tokenTypes;
std::vector<uint32_t> tokenValues;
std::vector<std::string> tokenComments;
};
Stream stringStream;
std::vector<Stream> streams;
std::map<std::string, int> streamIndex;
std::vector<int> streamStack;
std::vector<int> labels;
std::map<std::string, int> labelIndex;
std::vector<std::string> preText, postText;
const char *skipWhitespace(const char *p)
{
if (p == nullptr)
return "";
while (*p == ' ' || *p == '\t')
p++;
return p;
}
int main(int argc, char **argv)
{
bool verbose = false;
bool bigEndian = false;
bool writeC = false;
char buffer[512];
int i, j;
while (1) {
i = read(0, buffer, 512);
if (i == 0) break;
assert(i > 0);
j = write(1, buffer, i);
assert(i == j);
namespace po = boost::program_options;
po::positional_options_description pos;
po::options_description options("Allowed options");
options.add_options()("v", "verbose output");
options.add_options()("b", "big endian");
options.add_options()("c", "write c strings");
options.add_options()("files", po::value<std::vector<std::string>>(), "file parameters");
pos.add("files", -1);
po::variables_map vm;
try {
po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run();
po::store(parsed, vm);
po::notify(vm);
} catch (std::exception &e) {
std::cout << e.what() << "\n";
return 1;
}
if (vm.count("v"))
verbose = true;
if (vm.count("b"))
bigEndian = true;
if (vm.count("c"))
writeC = true;
if (vm.count("files") == 0) {
printf("File parameters are mandatory\n");
exit(-1);
}
std::vector<std::string> files = vm["files"].as<std::vector<std::string>>();
if (files.size() != 2) {
printf("Input and output parameters must be set\n");
exit(-1);
}
FILE *fileIn = fopen(files.at(0).c_str(), "rt");
assert(fileIn != nullptr);
FILE *fileOut = fopen(files.at(1).c_str(), writeC ? "wt" : "wb");
assert(fileOut != nullptr);
while (fgets(buffer, 512, fileIn) != nullptr) {
std::string cmd = strtok(buffer, " \t\r\n");
if (cmd == "pre") {
const char *p = skipWhitespace(strtok(nullptr, "\r\n"));
preText.push_back(p);
continue;
}
if (cmd == "post") {
const char *p = skipWhitespace(strtok(nullptr, "\r\n"));
postText.push_back(p);
continue;
}
if (cmd == "push") {
const char *p = strtok(nullptr, " \t\r\n");
if (streamIndex.count(p) == 0) {
streamIndex[p] = streams.size();
streams.resize(streams.size() + 1);
streams.back().name = p;
}
streamStack.push_back(streamIndex.at(p));
continue;
}
if (cmd == "pop") {
streamStack.pop_back();
continue;
}
if (cmd == "label" || cmd == "ref") {
const char *label = strtok(nullptr, " \t\r\n");
const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
Stream &s = streams.at(streamStack.back());
if (labelIndex.count(label) == 0) {
labelIndex[label] = labels.size();
labels.push_back(-1);
}
s.tokenTypes.push_back(cmd == "label" ? TOK_LABEL : TOK_REF);
s.tokenValues.push_back(labelIndex.at(label));
if (verbose)
s.tokenComments.push_back(comment);
continue;
}
if (cmd == "u8" || cmd == "u16" || cmd == "u32") {
const char *value = strtok(nullptr, " \t\r\n");
const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
Stream &s = streams.at(streamStack.back());
s.tokenTypes.push_back(cmd == "u8" ? TOK_U8 : cmd == "u16" ? TOK_U16 : TOK_U32);
s.tokenValues.push_back(atoll(value));
if (verbose)
s.tokenComments.push_back(comment);
continue;
}
if (cmd == "str") {
const char *value = skipWhitespace(strtok(nullptr, "\r\n"));
std::string label = std::string("str:") + value;
Stream &s = streams.at(streamStack.back());
if (labelIndex.count(label) == 0) {
labelIndex[label] = labels.size();
labels.push_back(-1);
}
s.tokenTypes.push_back(TOK_REF);
s.tokenValues.push_back(labelIndex.at(label));
if (verbose)
s.tokenComments.push_back(value);
stringStream.tokenTypes.push_back(TOK_LABEL);
stringStream.tokenValues.push_back(labelIndex.at(label));
while (1) {
stringStream.tokenTypes.push_back(TOK_U8);
stringStream.tokenValues.push_back(*value);
if (*value == 0)
break;
value++;
}
continue;
}
assert(0);
}
if (verbose) {
printf("Constructed %d streams:\n", int(streams.size()));
for (auto &s : streams)
printf(" stream '%s' with %d tokens\n", s.name.c_str(), int(s.tokenTypes.size()));
}
assert(!streams.empty());
assert(streamStack.empty());
streams.push_back(Stream());
streams.back().tokenTypes.swap(stringStream.tokenTypes);
streams.back().tokenValues.swap(stringStream.tokenValues);
int cursor = 0;
for (auto &s : streams) {
for (int i = 0; i < int(s.tokenTypes.size()); i++) {
switch (s.tokenTypes[i]) {
case TOK_LABEL:
labels[s.tokenValues[i]] = cursor;
break;
case TOK_REF:
cursor += 4;
break;
case TOK_U8:
cursor += 1;
break;
case TOK_U16:
assert(cursor % 2 == 0);
cursor += 2;
break;
case TOK_U32:
assert(cursor % 4 == 0);
cursor += 4;
break;
default:
assert(0);
}
}
}
if (verbose) {
printf("resolved positions for %d labels.\n", int(labels.size()));
printf("total data (including strings): %.2f MB\n", double(cursor) / (1024 * 1024));
}
std::vector<uint8_t> data(cursor);
cursor = 0;
for (auto &s : streams) {
for (int i = 0; i < int(s.tokenTypes.size()); i++) {
uint32_t value = s.tokenValues[i];
int numBytes = 0;
switch (s.tokenTypes[i]) {
case TOK_LABEL:
break;
case TOK_REF:
value = labels[value] - cursor;
numBytes = 4;
break;
case TOK_U8:
numBytes = 1;
break;
case TOK_U16:
numBytes = 2;
break;
case TOK_U32:
numBytes = 4;
break;
default:
assert(0);
}
if (bigEndian) {
switch (numBytes) {
case 4:
data[cursor++] = value >> 24;
data[cursor++] = value >> 16;
/* fall-through */
case 2:
data[cursor++] = value >> 8;
/* fall-through */
case 1:
data[cursor++] = value;
/* fall-through */
case 0:
break;
default:
assert(0);
}
} else {
switch (numBytes) {
case 4:
data[cursor + 3] = value >> 24;
data[cursor + 2] = value >> 16;
/* fall-through */
case 2:
data[cursor + 1] = value >> 8;
/* fall-through */
case 1:
data[cursor] = value;
/* fall-through */
case 0:
break;
default:
assert(0);
}
cursor += numBytes;
}
}
}
assert(cursor == int(data.size()));
if (writeC) {
for (auto &s : preText)
fprintf(fileOut, "%s\n", s.c_str());
fprintf(fileOut, "const char %s[%d] =\n\"", streams[0].name.c_str(), int(data.size()) + 1);
cursor = 1;
for (auto d : data) {
if (cursor > 70) {
fputc('\"', fileOut);
fputc('\n', fileOut);
cursor = 0;
}
if (cursor == 0) {
fputc('\"', fileOut);
cursor = 1;
}
if (d < 32 || d >= 128) {
fprintf(fileOut, "\\%03o", int(d));
cursor += 4;
} else if (d == '\"' || d == '\'' || d == '\\') {
fputc('\\', fileOut);
fputc(d, fileOut);
cursor += 2;
} else {
fputc(d, fileOut);
cursor++;
}
}
fprintf(fileOut, "\";\n");
for (auto &s : postText)
fprintf(fileOut, "%s\n", s.c_str());
} else {
fwrite(data.data(), int(data.size()), 1, fileOut);
}
return 0;
}

View File

@ -23,10 +23,10 @@
#include <condition_variable>
#include <memory>
#include <mutex>
#include <pthread.h>
#include <stdexcept>
#include <stdint.h>
#include <string>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@ -79,19 +79,19 @@ class assertion_failure : public std::runtime_error
};
NPNR_NORETURN
inline bool assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
inline void assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
{
throw assertion_failure(message, expr_str, filename, line);
}
NPNR_NORETURN
inline bool assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
inline void assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
{
throw assertion_failure(message, expr_str, filename, line);
}
#define NPNR_ASSERT(cond) ((void)((cond) || (assert_fail_impl(#cond, #cond, __FILE__, __LINE__))))
#define NPNR_ASSERT_MSG(cond, msg) ((void)((cond) || (assert_fail_impl(msg, #cond, __FILE__, __LINE__))))
#define NPNR_ASSERT(cond) (!(cond) ? assert_fail_impl(#cond, #cond, __FILE__, __LINE__) : (void)true)
#define NPNR_ASSERT_MSG(cond, msg) (!(cond) ? assert_fail_impl(msg, #cond, __FILE__, __LINE__) : (void)true)
#define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__))
#define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__))
@ -276,11 +276,11 @@ struct CellInfo : ArchCellInfo
// placement constraints
CellInfo *constr_parent;
std::vector<CellInfo*> constr_children;
std::vector<CellInfo *> constr_children;
const int UNCONSTR = INT_MIN;
int constr_x = UNCONSTR; // this.x - parent.x
int constr_y = UNCONSTR; // this.y - parent.y
int constr_z = UNCONSTR; // this.z - parent.z
int constr_x = UNCONSTR; // this.x - parent.x
int constr_y = UNCONSTR; // this.y - parent.y
int constr_z = UNCONSTR; // this.z - parent.z
bool constr_abs_z = false; // parent.z := 0
// parent.[xyz] := 0 when (constr_parent == nullptr)
};
@ -354,7 +354,7 @@ struct BaseCtx
{
// Lock to perform mutating actions on the Context.
std::mutex mutex;
pthread_t mutex_owner;
std::thread::id mutex_owner;
// Lock to be taken by UI when wanting to access context - the yield()
// method will lock/unlock it when its' released the main mutex to make
@ -387,12 +387,12 @@ struct BaseCtx
void lock(void)
{
mutex.lock();
mutex_owner = pthread_self();
mutex_owner = std::this_thread::get_id();
}
void unlock(void)
{
NPNR_ASSERT(pthread_equal(pthread_self(), mutex_owner) != 0);
NPNR_ASSERT(std::this_thread::get_id() == mutex_owner);
mutex.unlock();
}

View File

@ -94,7 +94,13 @@ class SAPlacer
BelType bel_type = ctx->getBelType(bel);
if (bel_type != ctx->belTypeFromId(cell->type)) {
log_error("Bel \'%s\' of type \'%s\' does not match cell "
"\'%s\' of type \'%s\'",
"\'%s\' of type \'%s\'\n",
loc_name.c_str(), ctx->belTypeToId(bel_type).c_str(ctx), cell->name.c_str(ctx),
cell->type.c_str(ctx));
}
if (!ctx->isValidBelForCell(cell, bel)) {
log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
"\'%s\' of type \'%s\'\n",
loc_name.c_str(), ctx->belTypeToId(bel_type).c_str(ctx), cell->name.c_str(ctx),
cell->type.c_str(ctx));
}

View File

@ -328,7 +328,8 @@ BelId Arch::getPackagePinBel(const std::string &pin) const
std::string Arch::getBelPackagePin(BelId bel) const
{
for (int i = 0; i < package_info->num_pins; i++) {
if (package_info->pin_data[i].abs_loc == bel.location && package_info->pin_data[i].bel_index == bel.index) {
if (Location(package_info->pin_data[i].abs_loc) == bel.location &&
package_info->pin_data[i].bel_index == bel.index) {
return package_info->pin_data[i].name.get();
}
}
@ -338,7 +339,7 @@ std::string Arch::getBelPackagePin(BelId bel) const
int Arch::getPioBelBank(BelId bel) const
{
for (int i = 0; i < chip_info->num_pios; i++) {
if (chip_info->pio_info[i].abs_loc == bel.location && chip_info->pio_info[i].bel_index == bel.index) {
if (Location(chip_info->pio_info[i].abs_loc) == bel.location && chip_info->pio_info[i].bel_index == bel.index) {
return chip_info->pio_info[i].bank;
}
}
@ -348,7 +349,7 @@ int Arch::getPioBelBank(BelId bel) const
std::string Arch::getPioFunctionName(BelId bel) const
{
for (int i = 0; i < chip_info->num_pios; i++) {
if (chip_info->pio_info[i].abs_loc == bel.location && chip_info->pio_info[i].bel_index == bel.index) {
if (Location(chip_info->pio_info[i].abs_loc) == bel.location && chip_info->pio_info[i].bel_index == bel.index) {
const char *func = chip_info->pio_info[i].function_name.get();
if (func == nullptr)
return "";

View File

@ -98,7 +98,7 @@ NPNR_PACKED_STRUCT(struct LocationTypePOD {
});
NPNR_PACKED_STRUCT(struct PIOInfoPOD {
Location abs_loc;
LocationPOD abs_loc;
int32_t bel_index;
RelPtr<char> function_name;
int16_t bank;
@ -107,7 +107,7 @@ NPNR_PACKED_STRUCT(struct PIOInfoPOD {
NPNR_PACKED_STRUCT(struct PackagePinPOD {
RelPtr<char> name;
Location abs_loc;
LocationPOD abs_loc;
int32_t bel_index;
});
@ -117,6 +117,26 @@ NPNR_PACKED_STRUCT(struct PackageInfoPOD {
RelPtr<PackagePinPOD> pin_data;
});
enum TapDirection : int8_t
{
TAP_DIR_LEFT = 0,
TAP_DIR_RIGHT = 1
};
enum GlobalQuadrant : int8_t
{
QUAD_UL = 0,
QUAD_UR = 1,
QUAD_LL = 2,
QUAD_LR = 3,
};
NPNR_PACKED_STRUCT(struct GlobalInfoPOD {
int16_t tap_col;
TapDirection tap_dir;
GlobalQuadrant quad;
});
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_tiles;
@ -124,6 +144,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t num_packages, num_pios;
RelPtr<LocationTypePOD> locations;
RelPtr<int32_t> location_type;
RelPtr<GlobalInfoPOD> location_glbinfo;
RelPtr<RelPtr<char>> tiletype_names;
RelPtr<PackageInfoPOD> package_info;
RelPtr<PIOInfoPOD> pio_info;

View File

@ -280,8 +280,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
other = "PIOD";
else
log_error("cannot place differential IO at location %s\n", pio.c_str());
//cc.tiles[pio_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
//cc.tiles[pic_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
// cc.tiles[pio_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
// cc.tiles[pic_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
cc.tiles[pio_tile].add_enum(other + ".PULLMODE", "NONE");
cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE");
}

View File

@ -24,11 +24,16 @@ if (MSVC)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resources/chipdb.rc PROPERTIES LANGUAGE RC)
foreach (dev ${devices})
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bin)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bba)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND ${ENV_CMD} python3 ${DB_PY} -b -p ${DEV_PORTS_INC} ${dev} ${DEV_CC_DB}
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${ENV_CMD} python3 ${DB_PY} -p ${DEV_PORTS_INC} ${dev} > ${DEV_CC_BBA_DB}
DEPENDS ${DB_PY}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm ${DEV_CC_BBA_DB} ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB})
set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE)
foreach (target ${family_targets})
@ -39,11 +44,18 @@ else()
target_compile_options(ecp5_chipdb PRIVATE -g0 -O0 -w)
foreach (dev ${devices})
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.cc)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bba)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND ${ENV_CMD} python3 ${DB_PY} -c -p ${DEV_PORTS_INC} ${dev} ${DEV_CC_DB}
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${ENV_CMD} python3 ${DB_PY} -p ${DEV_PORTS_INC} ${dev} > ${DEV_CC_BBA_DB}.new
COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
DEPENDS ${DB_PY}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm --c ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB})
foreach (target ${family_targets})
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ecp5_chipdb>)

View File

@ -10,11 +10,7 @@ type_at_location = dict()
tiletype_names = dict()
parser = argparse.ArgumentParser(description="import ECP5 routing and bels from Project Trellis")
group = parser.add_mutually_exclusive_group()
group.add_argument("-b", "--binary", action="store_true")
group.add_argument("-c", "--c_file", action="store_true")
parser.add_argument("device", type=str, help="target device")
parser.add_argument("outfile", type=argparse.FileType('w'), help="output filename")
parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc")
args = parser.parse_args()
@ -36,284 +32,50 @@ portpins = dict()
class BinaryBlobAssembler:
def __init__(self, cname, endianness, nodebug=False):
assert endianness in ["le", "be"]
self.cname = cname
self.endianness = endianness
self.finalized = False
self.data = bytearray()
self.comments = dict()
self.labels = dict()
self.exports = set()
self.labels_byaddr = dict()
self.ltypes_byaddr = dict()
self.strings = dict()
self.refs = dict()
self.nodebug = nodebug
def l(self, name, ltype=None, export=False):
assert not self.finalized
assert name not in self.labels
assert len(self.data) not in self.labels_byaddr
self.labels[name] = len(self.data)
if ltype is not None:
self.ltypes_byaddr[len(self.data)] = ltype
self.labels_byaddr[len(self.data)] = name
if export:
assert ltype is not None
self.exports.add(len(self.data))
def l(self, name, ltype = None, export = False):
if ltype is None:
print("label %s" % (name,))
else:
print("label %s %s" % (name, ltype))
def r(self, name, comment):
assert not self.finalized
assert len(self.data) % 4 == 0
assert len(self.data) not in self.refs
if self.nodebug:
comment = None
if name is not None:
self.refs[len(self.data)] = (name, comment)
self.data.append(0)
self.data.append(0)
self.data.append(0)
self.data.append(0)
if (name is None) and (comment is not None):
self.comments[len(self.data)] = comment + " (null reference)"
if comment is None:
print("ref %s" % (name,))
else:
print("ref %s %s" % (name, comment))
def s(self, s, comment):
assert not self.finalized
if self.nodebug:
comment = None
if s not in self.strings:
index = len(self.strings)
self.strings[s] = index
else:
index = self.strings[s]
if comment is not None:
self.r("str%d" % index, '%s: "%s"' % (comment, s))
else:
self.r("str%d" % index, None)
print("str %s" % s)
def u8(self, v, comment):
assert not self.finalized
if self.nodebug:
comment = None
self.data.append(v)
if comment is not None:
self.comments[len(self.data)] = comment
if comment is None:
print("u8 %d" % (v,))
else:
print("u8 %d %s" % (v, comment))
def u16(self, v, comment):
assert not self.finalized
assert len(self.data) % 2 == 0
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
elif self.endianness == "be":
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
if comment is None:
print("u16 %d" % (v,))
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
def s16(self, v, comment):
assert not self.finalized
assert len(self.data) % 2 == 0
if self.nodebug:
comment = None
c2val = (((-v) ^ 0xffff) + 1) if v < 0 else v
if self.endianness == "le":
self.data.append(c2val & 255)
self.data.append((c2val >> 8) & 255)
elif self.endianness == "be":
self.data.append((c2val >> 8) & 255)
self.data.append(c2val & 255)
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
print("u16 %d %s" % (v, comment))
def u32(self, v, comment):
assert not self.finalized
assert len(self.data) % 4 == 0
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 24) & 255)
elif self.endianness == "be":
self.data.append((v >> 24) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
if comment is None:
print("u32 %d" % (v,))
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
print("u32 %d %s" % (v, comment))
def finalize(self):
assert not self.finalized
for s, index in sorted(self.strings.items()):
self.l("str%d" % index, "char")
for c in s:
self.data.append(ord(c))
self.data.append(0)
self.finalized = True
cursor = 0
while cursor < len(self.data):
if cursor in self.refs:
v = self.labels[self.refs[cursor][0]] - cursor
if self.endianness == "le":
self.data[cursor + 0] = (v & 255)
self.data[cursor + 1] = ((v >> 8) & 255)
self.data[cursor + 2] = ((v >> 16) & 255)
self.data[cursor + 3] = ((v >> 24) & 255)
elif self.endianness == "be":
self.data[cursor + 0] = ((v >> 24) & 255)
self.data[cursor + 1] = ((v >> 16) & 255)
self.data[cursor + 2] = ((v >> 8) & 255)
self.data[cursor + 3] = (v & 255)
else:
assert 0
cursor += 4
else:
cursor += 1
def pre(self, s):
print("pre %s" % s)
def write_verbose_c(self, f, ctype="const unsigned char"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
cursor = 0
bytecnt = 0
while cursor < len(self.data):
if cursor in self.comments:
if bytecnt == 0:
print(" ", end="", file=f)
print(" // %s" % self.comments[cursor], file=f)
bytecnt = 0
if cursor in self.labels_byaddr:
if bytecnt != 0:
print(file=f)
if cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (
self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
else:
print(" // [%d] %s" % (cursor, self.labels_byaddr[cursor]), file=f)
bytecnt = 0
if cursor in self.refs:
if bytecnt != 0:
print(file=f)
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 0]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 1]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 2]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 3]), end="", file=f)
print(" // [%d] %s (reference to %s)" % (cursor, self.refs[cursor][1], self.refs[cursor][0]), file=f)
bytecnt = 0
cursor += 4
else:
if bytecnt == 0:
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor]), end=("" if bytecnt < 15 else "\n"), file=f)
bytecnt = (bytecnt + 1) & 15
cursor += 1
if bytecnt != 0:
print(file=f)
print("};", file=f)
def post(self, s):
print("post %s" % s)
def write_compact_c(self, f, ctype="const unsigned char"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
column = 0
for v in self.data:
if column == 0:
print(" ", end="", file=f)
column += 2
s = "%d," % v
print(s, end="", file=f)
column += len(s)
if column > 75:
print(file=f)
column = 0
if column != 0:
print(file=f)
for cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (
self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
print("};", file=f)
def write_uint64_c(self, f, ctype="const uint64_t"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, (len(self.data) + 7) // 8), file=f)
column = 0
for i in range((len(self.data) + 7) // 8):
v0 = self.data[8 * i + 0] if 8 * i + 0 < len(self.data) else 0
v1 = self.data[8 * i + 1] if 8 * i + 1 < len(self.data) else 0
v2 = self.data[8 * i + 2] if 8 * i + 2 < len(self.data) else 0
v3 = self.data[8 * i + 3] if 8 * i + 3 < len(self.data) else 0
v4 = self.data[8 * i + 4] if 8 * i + 4 < len(self.data) else 0
v5 = self.data[8 * i + 5] if 8 * i + 5 < len(self.data) else 0
v6 = self.data[8 * i + 6] if 8 * i + 6 < len(self.data) else 0
v7 = self.data[8 * i + 7] if 8 * i + 7 < len(self.data) else 0
if self.endianness == "le":
v = v0 << 0
v |= v1 << 8
v |= v2 << 16
v |= v3 << 24
v |= v4 << 32
v |= v5 << 40
v |= v6 << 48
v |= v7 << 56
elif self.endianness == "be":
v = v7 << 0
v |= v6 << 8
v |= v5 << 16
v |= v4 << 24
v |= v3 << 32
v |= v2 << 40
v |= v1 << 48
v |= v0 << 56
else:
assert 0
if column == 3:
print(" 0x%016x," % v, file=f)
column = 0
else:
if column == 0:
print(" ", end="", file=f)
print(" 0x%016x," % v, end="", file=f)
column += 1
if column != 0:
print("", file=f)
print("};", file=f)
def write_string_c(self, f, ctype="const char"):
assert self.finalized
assert self.data[len(self.data) - 1] == 0
print("%s %s[%d] =" % (ctype, self.cname, len(self.data)), file=f)
print(" \"", end="", file=f)
column = 0
for i in range(len(self.data) - 1):
if (self.data[i] < 32) or (self.data[i] > 126):
print("\\%03o" % self.data[i], end="", file=f)
column += 4
elif self.data[i] == ord('"') or self.data[i] == ord('\\'):
print("\\" + chr(self.data[i]), end="", file=f)
column += 2
else:
print(chr(self.data[i]), end="", file=f)
column += 1
if column > 70 and (i != len(self.data) - 2):
print("\"\n \"", end="", file=f)
column = 0
print("\";", file=f)
def write_binary(self, f):
assert self.finalized
assert self.data[len(self.data) - 1] == 0
f.buffer.write(self.data)
def push(self, name):
print("push %s" % name)
def pop(self):
print("pop")
bel_types = {
"NONE": 0,
@ -364,13 +126,25 @@ def process_pio_db(ddrg, device):
if bel_idx is not None:
pindata.append((loc, bel_idx, bank, pinfunc))
global_data = {}
quadrants = ["UL", "UR", "LL", "LR"]
def process_loc_globals(chip):
for y in range(0, max_row+1):
for x in range(0, max_col+1):
quad = chip.global_data.get_quadrant(y, x)
tapdrv = chip.global_data.get_tap_driver(y, x)
global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col)
def write_database(dev_name, ddrg, endianness):
def write_loc(loc, sym_name):
bba.s16(loc.x, "%s.x" % sym_name)
bba.s16(loc.y, "%s.y" % sym_name)
bba.u16(loc.x, "%s.x" % sym_name)
bba.u16(loc.y, "%s.y" % sym_name)
bba = BinaryBlobAssembler("chipdb_blob_%s" % dev_name, endianness)
bba = BinaryBlobAssembler()
bba.pre('#include "nextpnr.h"')
bba.pre('NEXTPNR_NAMESPACE_BEGIN')
bba.post('NEXTPNR_NAMESPACE_END')
bba.push("chipdb_blob_%s" % dev_name)
bba.r("chip_info", "chip_info")
loctypes = list([_.key() for _ in ddrg.locationTypes])
@ -450,6 +224,14 @@ def write_database(dev_name, ddrg, endianness):
for y in range(0, max_row+1):
for x in range(0, max_col+1):
bba.u32(loctypes.index(ddrg.typeAtLocation[pytrellis.Location(x, y)]), "loctype")
bba.l("location_glbinfo", "GlobalInfoPOD")
for y in range(0, max_row+1):
for x in range(0, max_col+1):
bba.u16(global_data[x, y][2], "tap_col")
bba.u8(global_data[x, y][1], "tap_dir")
bba.u8(global_data[x, y][0], "quad")
for package, pkgdata in sorted(packages.items()):
bba.l("package_data_%s" % package, "PackagePinPOD")
for pin in pkgdata:
@ -491,11 +273,12 @@ def write_database(dev_name, ddrg, endianness):
bba.r("locations", "locations")
bba.r("location_types", "location_type")
bba.r("location_glbinfo", "location_glbinfo")
bba.r("tiletype_names", "tiletype_names")
bba.r("package_data", "package_info")
bba.r("pio_info", "pio_info")
bba.finalize()
bba.pop()
return bba
dev_names = {"25k": "LFE5U-25F", "45k": "LFE5U-45F", "85k": "LFE5U-85F"}
@ -518,30 +301,18 @@ def main():
idx = len(portpins) + 1
portpins[line[1]] = idx
print("Initialising chip...")
# print("Initialising chip...")
chip = pytrellis.Chip(dev_names[args.device])
print("Building routing graph...")
# print("Building routing graph...")
ddrg = pytrellis.make_dedup_chipdb(chip)
max_row = chip.get_max_row()
max_col = chip.get_max_col()
process_pio_db(ddrg, args.device)
print("{} unique location types".format(len(ddrg.locationTypes)))
process_loc_globals(chip)
# print("{} unique location types".format(len(ddrg.locationTypes)))
bba = write_database(args.device, ddrg, "le")
if args.c_file:
print('#include "nextpnr.h"', file=args.outfile)
print('NEXTPNR_NAMESPACE_BEGIN', file=args.outfile)
if args.binary:
bba.write_binary(args.outfile)
if args.c_file:
bba.write_string_c(args.outfile)
if args.c_file:
print('NEXTPNR_NAMESPACE_END', file=args.outfile)
if __name__ == "__main__":
main()

View File

@ -207,15 +207,9 @@ BelId Arch::getBelByLocation(Loc loc) const
return BelId();
}
const std::vector<BelId> &Arch::getBelsByTile(int x, int y) const
{
return bels_by_tile.at(x).at(y);
}
const std::vector<BelId> &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); }
bool Arch::getBelGlobalBuf(BelId bel) const
{
return bels.at(bel).gb;
}
bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; }
uint32_t Arch::getBelChecksum(BelId bel) const
{

View File

@ -35,6 +35,7 @@ class ElementTreeItem : public QTreeWidgetItem
ElementTreeItem(ElementType t, QString str, QTreeWidgetItem *parent)
: QTreeWidgetItem(parent, QStringList(str)), type(t)
{
this->setFlags(this->flags() & ~Qt::ItemIsSelectable);
}
virtual ~ElementTreeItem(){};
@ -49,6 +50,7 @@ class IdStringTreeItem : public ElementTreeItem
public:
IdStringTreeItem(IdString d, ElementType t, QString str, QTreeWidgetItem *parent) : ElementTreeItem(t, str, parent)
{
this->setFlags(this->flags() | Qt::ItemIsSelectable);
this->data = d;
}
virtual ~IdStringTreeItem(){};
@ -68,6 +70,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
treeWidget->setColumnCount(1);
treeWidget->setHeaderLabel("Items");
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
treeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
// Add property view
variantManager = new QtVariantPropertyManager(this);
@ -79,6 +82,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
propertyEditor->setPropertiesWithoutValueMarked(true);
propertyEditor->show();
propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu);
propertyEditor->treeWidget()->setSelectionMode(QAbstractItemView::ExtendedSelection);
QLineEdit *lineEdit = new QLineEdit();
lineEdit->setClearButtonEnabled(true);
@ -248,6 +252,7 @@ void DesignWidget::newContext(Context *ctx)
QTreeWidgetItem *bel_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> bel_items;
bel_root->setText(0, "Bels");
bel_root->setFlags(bel_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, bel_root);
if (ctx) {
for (auto bel : ctx->getBels()) {
@ -280,6 +285,7 @@ void DesignWidget::newContext(Context *ctx)
QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> wire_items;
wire_root->setText(0, "Wires");
wire_root->setFlags(wire_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, wire_root);
if (ctx) {
for (auto wire : ctx->getWires()) {
@ -311,6 +317,7 @@ void DesignWidget::newContext(Context *ctx)
QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> pip_items;
pip_root->setText(0, "Pips");
pip_root->setFlags(pip_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, pip_root);
#ifndef ARCH_ECP5
if (ctx) {
@ -343,10 +350,12 @@ void DesignWidget::newContext(Context *ctx)
nets_root = new QTreeWidgetItem(treeWidget);
nets_root->setText(0, "Nets");
nets_root->setFlags(nets_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, nets_root);
cells_root = new QTreeWidgetItem(treeWidget);
cells_root->setText(0, "Cells");
cells_root->setFlags(cells_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, cells_root);
updateTree();
@ -418,6 +427,7 @@ QtProperty *DesignWidget::addTopLevelProperty(const QString &id)
QtProperty *topItem = groupManager->addProperty(id);
propertyToId[topItem] = id;
idToProperty[id] = topItem;
topItem->setSelectable(false);
propertyEditor->addProperty(topItem);
return topItem;
}
@ -485,12 +495,14 @@ void DesignWidget::addProperty(QtProperty *topItem, int propertyType, const QStr
QtVariantProperty *item = readOnlyManager->addProperty(propertyType, name);
item->setValue(value);
item->setPropertyId(getElementTypeName(type));
item->setSelectable(type != ElementType::NONE);
topItem->addSubProperty(item);
}
QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name)
{
QtProperty *item = groupManager->addProperty(name);
item->setSelectable(false);
topItem->addSubProperty(item);
return item;
}
@ -500,6 +512,19 @@ void DesignWidget::onItemSelectionChanged()
if (treeWidget->selectedItems().size() == 0)
return;
if (treeWidget->selectedItems().size() > 1)
{
std::vector<DecalXY> decals;
for (auto clickItem : treeWidget->selectedItems()) {
IdString value = static_cast<IdStringTreeItem *>(clickItem)->getData();
ElementType type = static_cast<ElementTreeItem *>(clickItem)->getType();
std::vector<DecalXY> d = getDecals(type, value);
std::move(d.begin(), d.end(), std::back_inserter(decals));
}
Q_EMIT selected(decals);
return;
}
QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0);
if (!clickItem->parent())

View File

@ -198,6 +198,8 @@ bool LineShader::compile(void)
void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection)
{
auto gl = QOpenGLContext::currentContext()->functions();
if (line.vertices.size() == 0)
return;
vao_.bind();
program_->bind();

View File

@ -742,6 +742,18 @@ struct Arch : BaseCtx
IdString id_cin, id_cout;
IdString id_o, id_lo;
IdString id_icestorm_ram, id_rclk, id_wclk;
// -------------------------------------------------
BelPin getIOBSharingPLLPin(BelId pll, PortPin pll_pin) const
{
auto wire = getBelPinWire(pll, pll_pin);
for (auto src_bel : getWireBelPins(wire)) {
if (getBelType(src_bel.bel) == TYPE_SB_IO && src_bel.pin == PIN_D_IN_0) {
return src_bel;
}
}
NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
}
};
NEXTPNR_NAMESPACE_END

View File

@ -124,12 +124,16 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
}
// Is there a PLL that shares this IO buffer?
if (pll_bel.index != -1) {
auto pll_cell = getBoundBelCell(pll_bel);
// Is a PLL placed in this PLL bel?
if (!checkBelAvail(pll_bel)) {
if (pll_cell != IdString()) {
// Is the shared port driving a net?
auto pll_cell = getBoundBelCell(pll_bel);
auto pi = cells.at(pll_cell)->ports[portPinToId(pll_bel_pin)];
if (pi.net != nullptr) {
// Are we perhaps a PAD INPUT Bel that can be placed here?
if (cells.at(pll_cell)->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this)) {
return true;
}
return false;
}
}

6
ice40/benchmark/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
hx8kdemo.log
hx8kdemo.blif
hx8kdemo.json
hx8kdemo_[an][0-9].asc
hx8kdemo_[an][0-9].log
report_[an][0-9].txt

27
ice40/benchmark/Makefile Normal file
View File

@ -0,0 +1,27 @@
reports::
define mkreport
reports:: report_a$1.txt report_n$1.txt
report_a$1.txt: hx8kdemo_a$1.asc
icetime -m -r report_a$1.txt -d hx8k hx8kdemo_a$1.asc
report_n$1.txt: hx8kdemo_n$1.asc
icetime -m -r report_n$1.txt -d hx8k hx8kdemo_n$1.asc
hx8kdemo_a$1.asc: hx8kdemo.blif
arachne-pnr -d 8k -p hx8kdemo.pcf -o hx8kdemo_a$1.asc -s 1$1 hx8kdemo.blif > hx8kdemo_a$1.log 2>&1
hx8kdemo_n$1.asc: hx8kdemo.json
../../nextpnr-ice40 --asc hx8kdemo_n$1.asc --json hx8kdemo.json --pcf hx8kdemo.pcf --hx8k --seed 1$1 > hx8kdemo_n$1.log 2>&1
endef
$(foreach i,0 1 2 3 4 5 6 7 8 9,$(eval $(call mkreport,$(i))))
hx8kdemo.blif: hx8kdemo.json
hx8kdemo.json: hx8kdemo.v spimemio.v simpleuart.v picosoc.v picorv32.v
yosys -ql hx8kdemo.log -p 'synth_ice40 -top hx8kdemo -blif hx8kdemo.blif -json hx8kdemo.json' $^
clean:
rm -f hx8kdemo.log hx8kdemo.blif hx8kdemo.json
rm -f hx8kdemo_[an][0-9].asc hx8kdemo_[an][0-9].log report_[an][0-9].txt

View File

@ -0,0 +1,40 @@
# Pinout for the iCE40-HX8K Breakout Board
set_io clk J3
set_io flash_csb R12
set_io flash_clk R11
set_io flash_io0 P12
set_io flash_io1 P11
# for QSPI mode the flash chip on the iCE40-HX8K Breakout Board
# must be replaced with one that supports QSPI and the IO2 and IO3
# pins must be soldered to T9 and P8 (center on J3)
set_io flash_io2 T9
set_io flash_io3 P8
set_io ser_tx B12
set_io ser_rx B10
# left on J3
set_io debug_ser_tx T1
set_io debug_ser_rx R3
# right on J3
set_io debug_flash_csb T15
set_io debug_flash_clk R16
set_io debug_flash_io0 N12
set_io debug_flash_io1 P13
set_io debug_flash_io2 T13
set_io debug_flash_io3 T14
set_io leds[7] B5 # D9
set_io leds[6] B4 # D8
set_io leds[5] A2 # D7
set_io leds[4] A1 # D6
set_io leds[3] C5 # D5
set_io leds[2] C4 # D4
set_io leds[1] B3 # D3
set_io leds[0] C3 # D2

139
ice40/benchmark/hx8kdemo.v Normal file
View File

@ -0,0 +1,139 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 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.
*
*/
module hx8kdemo (
input clk,
output ser_tx,
input ser_rx,
output [7:0] leds,
output flash_csb,
output flash_clk,
inout flash_io0,
inout flash_io1,
inout flash_io2,
inout flash_io3,
output debug_ser_tx,
output debug_ser_rx,
output debug_flash_csb,
output debug_flash_clk,
output debug_flash_io0,
output debug_flash_io1,
output debug_flash_io2,
output debug_flash_io3
);
reg [5:0] reset_cnt = 0;
wire resetn = &reset_cnt;
always @(posedge clk) begin
reset_cnt <= reset_cnt + !resetn;
end
wire flash_io0_oe, flash_io0_do, flash_io0_di;
wire flash_io1_oe, flash_io1_do, flash_io1_di;
wire flash_io2_oe, flash_io2_do, flash_io2_di;
wire flash_io3_oe, flash_io3_do, flash_io3_di;
SB_IO #(
.PIN_TYPE(6'b 1010_01),
.PULLUP(1'b 0)
) flash_io_buf [3:0] (
.PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}),
.OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}),
.D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}),
.D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di})
);
wire iomem_valid;
reg iomem_ready;
wire [3:0] iomem_wstrb;
wire [31:0] iomem_addr;
wire [31:0] iomem_wdata;
reg [31:0] iomem_rdata;
reg [31:0] gpio;
assign leds = gpio;
always @(posedge clk) begin
if (!resetn) begin
gpio <= 0;
end else begin
iomem_ready <= 0;
if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin
iomem_ready <= 1;
iomem_rdata <= gpio;
if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0];
if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8];
if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16];
if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24];
end
end
end
picosoc soc (
.clk (clk ),
.resetn (resetn ),
.ser_tx (ser_tx ),
.ser_rx (ser_rx ),
.flash_csb (flash_csb ),
.flash_clk (flash_clk ),
.flash_io0_oe (flash_io0_oe),
.flash_io1_oe (flash_io1_oe),
.flash_io2_oe (flash_io2_oe),
.flash_io3_oe (flash_io3_oe),
.flash_io0_do (flash_io0_do),
.flash_io1_do (flash_io1_do),
.flash_io2_do (flash_io2_do),
.flash_io3_do (flash_io3_do),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di),
.irq_5 (1'b0 ),
.irq_6 (1'b0 ),
.irq_7 (1'b0 ),
.iomem_valid (iomem_valid ),
.iomem_ready (iomem_ready ),
.iomem_wstrb (iomem_wstrb ),
.iomem_addr (iomem_addr ),
.iomem_wdata (iomem_wdata ),
.iomem_rdata (iomem_rdata )
);
assign debug_ser_tx = ser_tx;
assign debug_ser_rx = ser_rx;
assign debug_flash_csb = flash_csb;
assign debug_flash_clk = flash_clk;
assign debug_flash_io0 = flash_io0_di;
assign debug_flash_io1 = flash_io1_di;
assign debug_flash_io2 = flash_io2_di;
assign debug_flash_io3 = flash_io3_di;
endmodule

2977
ice40/benchmark/picorv32.v Normal file

File diff suppressed because it is too large Load Diff

241
ice40/benchmark/picosoc.v Normal file
View File

@ -0,0 +1,241 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 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 PICORV32_REGS
`ifdef PICORV32_V
`error "picosoc.v must be read before picorv32.v!"
`endif
`define PICORV32_REGS picosoc_regs
`endif
module picosoc (
input clk,
input resetn,
output iomem_valid,
input iomem_ready,
output [ 3:0] iomem_wstrb,
output [31:0] iomem_addr,
output [31:0] iomem_wdata,
input [31:0] iomem_rdata,
input irq_5,
input irq_6,
input irq_7,
output ser_tx,
input ser_rx,
output flash_csb,
output flash_clk,
output flash_io0_oe,
output flash_io1_oe,
output flash_io2_oe,
output flash_io3_oe,
output flash_io0_do,
output flash_io1_do,
output flash_io2_do,
output flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di
);
parameter integer MEM_WORDS = 256;
parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory
parameter [31:0] PROGADDR_RESET = 32'h 0010_0000; // 1 MB into flash
reg [31:0] irq;
wire irq_stall = 0;
wire irq_uart = 0;
always @* begin
irq = 0;
irq[3] = irq_stall;
irq[4] = irq_uart;
irq[5] = irq_5;
irq[6] = irq_6;
irq[7] = irq_7;
end
wire mem_valid;
wire mem_instr;
wire mem_ready;
wire [31:0] mem_addr;
wire [31:0] mem_wdata;
wire [3:0] mem_wstrb;
wire [31:0] mem_rdata;
wire spimem_ready;
wire [31:0] spimem_rdata;
reg ram_ready;
wire [31:0] ram_rdata;
assign iomem_valid = mem_valid && (mem_addr[31:24] > 8'h 01);
assign iomem_wstrb = mem_wstrb;
assign iomem_addr = mem_addr;
assign iomem_wdata = mem_wdata;
wire spimemio_cfgreg_sel = mem_valid && (mem_addr == 32'h 0200_0000);
wire [31:0] spimemio_cfgreg_do;
wire simpleuart_reg_div_sel = mem_valid && (mem_addr == 32'h 0200_0004);
wire [31:0] simpleuart_reg_div_do;
wire simpleuart_reg_dat_sel = mem_valid && (mem_addr == 32'h 0200_0008);
wire [31:0] simpleuart_reg_dat_do;
wire simpleuart_reg_dat_wait;
assign mem_ready = (iomem_valid && iomem_ready) || spimem_ready || ram_ready || spimemio_cfgreg_sel ||
simpleuart_reg_div_sel || (simpleuart_reg_dat_sel && !simpleuart_reg_dat_wait);
assign mem_rdata = (iomem_valid && iomem_ready) ? iomem_rdata : spimem_ready ? spimem_rdata : ram_ready ? ram_rdata :
spimemio_cfgreg_sel ? spimemio_cfgreg_do : simpleuart_reg_div_sel ? simpleuart_reg_div_do :
simpleuart_reg_dat_sel ? simpleuart_reg_dat_do : 32'h 0000_0000;
picorv32 #(
.STACKADDR(STACKADDR),
.PROGADDR_RESET(PROGADDR_RESET),
.PROGADDR_IRQ(32'h 0000_0000),
.BARREL_SHIFTER(1),
.COMPRESSED_ISA(1),
.ENABLE_MUL(1),
.ENABLE_DIV(1),
.ENABLE_IRQ(1),
.ENABLE_IRQ_QREGS(0)
) cpu (
.clk (clk ),
.resetn (resetn ),
.mem_valid (mem_valid ),
.mem_instr (mem_instr ),
.mem_ready (mem_ready ),
.mem_addr (mem_addr ),
.mem_wdata (mem_wdata ),
.mem_wstrb (mem_wstrb ),
.mem_rdata (mem_rdata ),
.irq (irq )
);
spimemio spimemio (
.clk (clk),
.resetn (resetn),
.valid (mem_valid && mem_addr >= 4*MEM_WORDS && mem_addr < 32'h 0200_0000),
.ready (spimem_ready),
.addr (mem_addr[23:0]),
.rdata (spimem_rdata),
.flash_csb (flash_csb ),
.flash_clk (flash_clk ),
.flash_io0_oe (flash_io0_oe),
.flash_io1_oe (flash_io1_oe),
.flash_io2_oe (flash_io2_oe),
.flash_io3_oe (flash_io3_oe),
.flash_io0_do (flash_io0_do),
.flash_io1_do (flash_io1_do),
.flash_io2_do (flash_io2_do),
.flash_io3_do (flash_io3_do),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di),
.cfgreg_we(spimemio_cfgreg_sel ? mem_wstrb : 4'b 0000),
.cfgreg_di(mem_wdata),
.cfgreg_do(spimemio_cfgreg_do)
);
simpleuart simpleuart (
.clk (clk ),
.resetn (resetn ),
.ser_tx (ser_tx ),
.ser_rx (ser_rx ),
.reg_div_we (simpleuart_reg_div_sel ? mem_wstrb : 4'b 0000),
.reg_div_di (mem_wdata),
.reg_div_do (simpleuart_reg_div_do),
.reg_dat_we (simpleuart_reg_dat_sel ? mem_wstrb[0] : 1'b 0),
.reg_dat_re (simpleuart_reg_dat_sel && !mem_wstrb),
.reg_dat_di (mem_wdata),
.reg_dat_do (simpleuart_reg_dat_do),
.reg_dat_wait(simpleuart_reg_dat_wait)
);
always @(posedge clk)
ram_ready <= mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS;
picosoc_mem #(.WORDS(MEM_WORDS)) memory (
.clk(clk),
.wen((mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS) ? mem_wstrb : 4'b0),
.addr(mem_addr[23:2]),
.wdata(mem_wdata),
.rdata(ram_rdata)
);
endmodule
// Implementation note:
// Replace the following two modules with wrappers for your SRAM cells.
module picosoc_regs (
input clk, wen,
input [5:0] waddr,
input [5:0] raddr1,
input [5:0] raddr2,
input [31:0] wdata,
output [31:0] rdata1,
output [31:0] rdata2
);
reg [31:0] regs [0:31];
always @(posedge clk)
if (wen) regs[waddr[4:0]] <= wdata;
assign rdata1 = regs[raddr1[4:0]];
assign rdata2 = regs[raddr2[4:0]];
endmodule
module picosoc_mem #(
parameter integer WORDS = 256
) (
input clk,
input [3:0] wen,
input [21:0] addr,
input [31:0] wdata,
output reg [31:0] rdata
);
reg [31:0] mem [0:WORDS-1];
always @(posedge clk) begin
rdata <= mem[addr];
if (wen[0]) mem[addr][ 7: 0] <= wdata[ 7: 0];
if (wen[1]) mem[addr][15: 8] <= wdata[15: 8];
if (wen[2]) mem[addr][23:16] <= wdata[23:16];
if (wen[3]) mem[addr][31:24] <= wdata[31:24];
end
endmodule

View File

@ -0,0 +1,69 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import subprocess\n",
"\n",
"gitrev = subprocess.getoutput(\"git rev-parse --short HEAD\")\n",
"\n",
"data_a = 1 + np.zeros(10)\n",
"data_n = 1 + np.zeros(10)\n",
"\n",
"for i in range(10):\n",
" try:\n",
" with open(\"report_a%d.txt\" % i, \"r\") as f:\n",
" for line in f:\n",
" if line.startswith(\"Total path delay:\"):\n",
" data_a[i] = float(line.split()[3])\n",
" except:\n",
" pass\n",
" try:\n",
" with open(\"report_n%d.txt\" % i, \"r\") as f:\n",
" for line in f:\n",
" if line.startswith(\"Total path delay:\"):\n",
" data_n[i] = float(line.split()[3])\n",
" except:\n",
" pass\n",
"\n",
"plt.figure(figsize=(9,3))\n",
"plt.title(\"nextpnr -- ice40/benchmark/ -- %s\" % gitrev)\n",
"plt.bar(np.arange(10), data_a, color='blue')\n",
"plt.bar(15+np.arange(10), data_n, color='red')\n",
"plt.ylabel('Longest path (ns)')\n",
"plt.xticks([5, 20], [\"arachne-pnr\", \"nextpnr\"])\n",
"plt.xlim(-2, 27)\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

View File

@ -0,0 +1,137 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 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.
*
*/
module simpleuart (
input clk,
input resetn,
output ser_tx,
input ser_rx,
input [3:0] reg_div_we,
input [31:0] reg_div_di,
output [31:0] reg_div_do,
input reg_dat_we,
input reg_dat_re,
input [31:0] reg_dat_di,
output [31:0] reg_dat_do,
output reg_dat_wait
);
reg [31:0] cfg_divider;
reg [3:0] recv_state;
reg [31:0] recv_divcnt;
reg [7:0] recv_pattern;
reg [7:0] recv_buf_data;
reg recv_buf_valid;
reg [9:0] send_pattern;
reg [3:0] send_bitcnt;
reg [31:0] send_divcnt;
reg send_dummy;
assign reg_div_do = cfg_divider;
assign reg_dat_wait = reg_dat_we && (send_bitcnt || send_dummy);
assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0;
always @(posedge clk) begin
if (!resetn) begin
cfg_divider <= 1;
end else begin
if (reg_div_we[0]) cfg_divider[ 7: 0] <= reg_div_di[ 7: 0];
if (reg_div_we[1]) cfg_divider[15: 8] <= reg_div_di[15: 8];
if (reg_div_we[2]) cfg_divider[23:16] <= reg_div_di[23:16];
if (reg_div_we[3]) cfg_divider[31:24] <= reg_div_di[31:24];
end
end
always @(posedge clk) begin
if (!resetn) begin
recv_state <= 0;
recv_divcnt <= 0;
recv_pattern <= 0;
recv_buf_data <= 0;
recv_buf_valid <= 0;
end else begin
recv_divcnt <= recv_divcnt + 1;
if (reg_dat_re)
recv_buf_valid <= 0;
case (recv_state)
0: begin
if (!ser_rx)
recv_state <= 1;
recv_divcnt <= 0;
end
1: begin
if (2*recv_divcnt > cfg_divider) begin
recv_state <= 2;
recv_divcnt <= 0;
end
end
10: begin
if (recv_divcnt > cfg_divider) begin
recv_buf_data <= recv_pattern;
recv_buf_valid <= 1;
recv_state <= 0;
end
end
default: begin
if (recv_divcnt > cfg_divider) begin
recv_pattern <= {ser_rx, recv_pattern[7:1]};
recv_state <= recv_state + 1;
recv_divcnt <= 0;
end
end
endcase
end
end
assign ser_tx = send_pattern[0];
always @(posedge clk) begin
if (reg_div_we)
send_dummy <= 1;
send_divcnt <= send_divcnt + 1;
if (!resetn) begin
send_pattern <= ~0;
send_bitcnt <= 0;
send_divcnt <= 0;
send_dummy <= 1;
end else begin
if (send_dummy && !send_bitcnt) begin
send_pattern <= ~0;
send_bitcnt <= 15;
send_divcnt <= 0;
send_dummy <= 0;
end else
if (reg_dat_we && !send_bitcnt) begin
send_pattern <= {1'b1, reg_dat_di[7:0], 1'b0};
send_bitcnt <= 10;
send_divcnt <= 0;
end else
if (send_divcnt > cfg_divider && send_bitcnt) begin
send_pattern <= {1'b1, send_pattern[9:1]};
send_bitcnt <= send_bitcnt - 1;
send_divcnt <= 0;
end
end
end
endmodule

579
ice40/benchmark/spimemio.v Normal file
View File

@ -0,0 +1,579 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 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.
*
*/
module spimemio (
input clk, resetn,
input valid,
output ready,
input [23:0] addr,
output reg [31:0] rdata,
output flash_csb,
output flash_clk,
output flash_io0_oe,
output flash_io1_oe,
output flash_io2_oe,
output flash_io3_oe,
output flash_io0_do,
output flash_io1_do,
output flash_io2_do,
output flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di,
input [3:0] cfgreg_we,
input [31:0] cfgreg_di,
output [31:0] cfgreg_do
);
reg xfer_resetn;
reg din_valid;
wire din_ready;
reg [7:0] din_data;
reg [3:0] din_tag;
reg din_cont;
reg din_qspi;
reg din_ddr;
reg din_rd;
wire dout_valid;
wire [7:0] dout_data;
wire [3:0] dout_tag;
reg [23:0] buffer;
reg [23:0] rd_addr;
reg rd_valid;
reg rd_wait;
reg rd_inc;
assign ready = valid && (addr == rd_addr) && rd_valid;
wire jump = valid && !ready && (addr != rd_addr+4) && rd_valid;
reg softreset;
reg config_en; // cfgreg[31]
reg config_ddr; // cfgreg[22]
reg config_qspi; // cfgreg[21]
reg config_cont; // cfgreg[20]
reg [3:0] config_dummy; // cfgreg[19:16]
reg [3:0] config_oe; // cfgreg[11:8]
reg config_csb; // cfgreg[5]
reg config_clk; // cfgref[4]
reg [3:0] config_do; // cfgreg[3:0]
assign cfgreg_do[31] = config_en;
assign cfgreg_do[30:23] = 0;
assign cfgreg_do[22] = config_ddr;
assign cfgreg_do[21] = config_qspi;
assign cfgreg_do[20] = config_cont;
assign cfgreg_do[19:16] = config_dummy;
assign cfgreg_do[15:12] = 0;
assign cfgreg_do[11:8] = {flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe};
assign cfgreg_do[7:6] = 0;
assign cfgreg_do[5] = flash_csb;
assign cfgreg_do[4] = flash_clk;
assign cfgreg_do[3:0] = {flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
always @(posedge clk) begin
softreset <= !config_en || cfgreg_we;
if (!resetn) begin
softreset <= 1;
config_en <= 1;
config_csb <= 0;
config_clk <= 0;
config_oe <= 0;
config_do <= 0;
config_ddr <= 0;
config_qspi <= 0;
config_cont <= 0;
config_dummy <= 8;
end else begin
if (cfgreg_we[0]) begin
config_csb <= cfgreg_di[5];
config_clk <= cfgreg_di[4];
config_do <= cfgreg_di[3:0];
end
if (cfgreg_we[1]) begin
config_oe <= cfgreg_di[11:8];
end
if (cfgreg_we[2]) begin
config_ddr <= cfgreg_di[22];
config_qspi <= cfgreg_di[21];
config_cont <= cfgreg_di[20];
config_dummy <= cfgreg_di[19:16];
end
if (cfgreg_we[3]) begin
config_en <= cfgreg_di[31];
end
end
end
wire xfer_csb;
wire xfer_clk;
wire xfer_io0_oe;
wire xfer_io1_oe;
wire xfer_io2_oe;
wire xfer_io3_oe;
wire xfer_io0_do;
wire xfer_io1_do;
wire xfer_io2_do;
wire xfer_io3_do;
reg xfer_io0_90;
reg xfer_io1_90;
reg xfer_io2_90;
reg xfer_io3_90;
always @(negedge clk) begin
xfer_io0_90 <= xfer_io0_do;
xfer_io1_90 <= xfer_io1_do;
xfer_io2_90 <= xfer_io2_do;
xfer_io3_90 <= xfer_io3_do;
end
assign flash_csb = config_en ? xfer_csb : config_csb;
assign flash_clk = config_en ? xfer_clk : config_clk;
assign flash_io0_oe = config_en ? xfer_io0_oe : config_oe[0];
assign flash_io1_oe = config_en ? xfer_io1_oe : config_oe[1];
assign flash_io2_oe = config_en ? xfer_io2_oe : config_oe[2];
assign flash_io3_oe = config_en ? xfer_io3_oe : config_oe[3];
assign flash_io0_do = config_en ? (config_ddr ? xfer_io0_90 : xfer_io0_do) : config_do[0];
assign flash_io1_do = config_en ? (config_ddr ? xfer_io1_90 : xfer_io1_do) : config_do[1];
assign flash_io2_do = config_en ? (config_ddr ? xfer_io2_90 : xfer_io2_do) : config_do[2];
assign flash_io3_do = config_en ? (config_ddr ? xfer_io3_90 : xfer_io3_do) : config_do[3];
wire xfer_dspi = din_ddr && !din_qspi;
wire xfer_ddr = din_ddr && din_qspi;
spimemio_xfer xfer (
.clk (clk ),
.resetn (xfer_resetn ),
.din_valid (din_valid ),
.din_ready (din_ready ),
.din_data (din_data ),
.din_tag (din_tag ),
.din_cont (din_cont ),
.din_dspi (xfer_dspi ),
.din_qspi (din_qspi ),
.din_ddr (xfer_ddr ),
.din_rd (din_rd ),
.dout_valid (dout_valid ),
.dout_data (dout_data ),
.dout_tag (dout_tag ),
.flash_csb (xfer_csb ),
.flash_clk (xfer_clk ),
.flash_io0_oe (xfer_io0_oe ),
.flash_io1_oe (xfer_io1_oe ),
.flash_io2_oe (xfer_io2_oe ),
.flash_io3_oe (xfer_io3_oe ),
.flash_io0_do (xfer_io0_do ),
.flash_io1_do (xfer_io1_do ),
.flash_io2_do (xfer_io2_do ),
.flash_io3_do (xfer_io3_do ),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di)
);
reg [3:0] state;
always @(posedge clk) begin
xfer_resetn <= 1;
din_valid <= 0;
if (!resetn || softreset) begin
state <= 0;
xfer_resetn <= 0;
rd_valid <= 0;
din_tag <= 0;
din_cont <= 0;
din_qspi <= 0;
din_ddr <= 0;
din_rd <= 0;
end else begin
if (dout_valid && dout_tag == 1) buffer[ 7: 0] <= dout_data;
if (dout_valid && dout_tag == 2) buffer[15: 8] <= dout_data;
if (dout_valid && dout_tag == 3) buffer[23:16] <= dout_data;
if (dout_valid && dout_tag == 4) begin
rdata <= {dout_data, buffer};
rd_addr <= rd_inc ? rd_addr + 4 : addr;
rd_valid <= 1;
rd_wait <= rd_inc;
rd_inc <= 1;
end
if (valid)
rd_wait <= 0;
case (state)
0: begin
din_valid <= 1;
din_data <= 8'h ff;
din_tag <= 0;
if (din_ready) begin
din_valid <= 0;
state <= 1;
end
end
1: begin
if (dout_valid) begin
xfer_resetn <= 0;
state <= 2;
end
end
2: begin
din_valid <= 1;
din_data <= 8'h ab;
din_tag <= 0;
if (din_ready) begin
din_valid <= 0;
state <= 3;
end
end
3: begin
if (dout_valid) begin
xfer_resetn <= 0;
state <= 4;
end
end
4: begin
rd_inc <= 0;
din_valid <= 1;
din_tag <= 0;
case ({config_ddr, config_qspi})
2'b11: din_data <= 8'h ED;
2'b01: din_data <= 8'h EB;
2'b10: din_data <= 8'h BB;
2'b00: din_data <= 8'h 03;
endcase
if (din_ready) begin
din_valid <= 0;
state <= 5;
end
end
5: begin
if (valid && !ready) begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[23:16];
din_qspi <= config_qspi;
din_ddr <= config_ddr;
if (din_ready) begin
din_valid <= 0;
state <= 6;
end
end
end
6: begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[15:8];
if (din_ready) begin
din_valid <= 0;
state <= 7;
end
end
7: begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[7:0];
if (din_ready) begin
din_valid <= 0;
din_data <= 0;
state <= config_qspi || config_ddr ? 8 : 9;
end
end
8: begin
din_valid <= 1;
din_tag <= 0;
din_data <= config_cont ? 8'h A5 : 8'h FF;
if (din_ready) begin
din_rd <= 1;
din_data <= config_dummy;
din_valid <= 0;
state <= 9;
end
end
9: begin
din_valid <= 1;
din_tag <= 1;
if (din_ready) begin
din_valid <= 0;
state <= 10;
end
end
10: begin
din_valid <= 1;
din_data <= 8'h 00;
din_tag <= 2;
if (din_ready) begin
din_valid <= 0;
state <= 11;
end
end
11: begin
din_valid <= 1;
din_tag <= 3;
if (din_ready) begin
din_valid <= 0;
state <= 12;
end
end
12: begin
if (!rd_wait || valid) begin
din_valid <= 1;
din_tag <= 4;
if (din_ready) begin
din_valid <= 0;
state <= 9;
end
end
end
endcase
if (jump) begin
rd_inc <= 0;
rd_valid <= 0;
xfer_resetn <= 0;
if (config_cont) begin
state <= 5;
end else begin
state <= 4;
din_qspi <= 0;
din_ddr <= 0;
end
din_rd <= 0;
end
end
end
endmodule
module spimemio_xfer (
input clk, resetn,
input din_valid,
output din_ready,
input [7:0] din_data,
input [3:0] din_tag,
input din_cont,
input din_dspi,
input din_qspi,
input din_ddr,
input din_rd,
output dout_valid,
output [7:0] dout_data,
output [3:0] dout_tag,
output reg flash_csb,
output reg flash_clk,
output reg flash_io0_oe,
output reg flash_io1_oe,
output reg flash_io2_oe,
output reg flash_io3_oe,
output reg flash_io0_do,
output reg flash_io1_do,
output reg flash_io2_do,
output reg flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di
);
reg [7:0] obuffer;
reg [7:0] ibuffer;
reg [3:0] count;
reg [3:0] dummy_count;
reg xfer_cont;
reg xfer_dspi;
reg xfer_qspi;
reg xfer_ddr;
reg xfer_ddr_q;
reg xfer_rd;
reg [3:0] xfer_tag;
reg [3:0] xfer_tag_q;
reg [7:0] next_obuffer;
reg [7:0] next_ibuffer;
reg [3:0] next_count;
reg fetch;
reg next_fetch;
reg last_fetch;
always @(posedge clk) begin
xfer_ddr_q <= xfer_ddr;
xfer_tag_q <= xfer_tag;
end
assign din_ready = din_valid && resetn && next_fetch;
assign dout_valid = (xfer_ddr_q ? fetch && !last_fetch : next_fetch && !fetch) && resetn;
assign dout_data = ibuffer;
assign dout_tag = xfer_tag_q;
always @* begin
flash_io0_oe = 0;
flash_io1_oe = 0;
flash_io2_oe = 0;
flash_io3_oe = 0;
flash_io0_do = 0;
flash_io1_do = 0;
flash_io2_do = 0;
flash_io3_do = 0;
next_obuffer = obuffer;
next_ibuffer = ibuffer;
next_count = count;
next_fetch = 0;
if (dummy_count == 0) begin
casez ({xfer_ddr, xfer_qspi, xfer_dspi})
3'b 000: begin
flash_io0_oe = 1;
flash_io0_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[6:0], 1'b 0};
next_count = count - |count;
end else begin
next_ibuffer = {ibuffer[6:0], flash_io1_di};
end
next_fetch = (next_count == 0);
end
3'b 01?: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io2_oe = !xfer_rd;
flash_io3_oe = !xfer_rd;
flash_io0_do = obuffer[4];
flash_io1_do = obuffer[5];
flash_io2_do = obuffer[6];
flash_io3_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[3:0], 4'b 0000};
next_count = count - {|count, 2'b00};
end else begin
next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
end
next_fetch = (next_count == 0);
end
3'b 11?: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io2_oe = !xfer_rd;
flash_io3_oe = !xfer_rd;
flash_io0_do = obuffer[4];
flash_io1_do = obuffer[5];
flash_io2_do = obuffer[6];
flash_io3_do = obuffer[7];
next_obuffer = {obuffer[3:0], 4'b 0000};
next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
next_count = count - {|count, 2'b00};
next_fetch = (next_count == 0);
end
3'b ??1: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io0_do = obuffer[6];
flash_io1_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[5:0], 2'b 00};
next_count = count - {|count, 1'b0};
end else begin
next_ibuffer = {ibuffer[5:0], flash_io1_di, flash_io0_di};
end
next_fetch = (next_count == 0);
end
endcase
end
end
always @(posedge clk) begin
if (!resetn) begin
fetch <= 1;
last_fetch <= 1;
flash_csb <= 1;
flash_clk <= 0;
count <= 0;
dummy_count <= 0;
xfer_tag <= 0;
xfer_cont <= 0;
xfer_dspi <= 0;
xfer_qspi <= 0;
xfer_ddr <= 0;
xfer_rd <= 0;
end else begin
fetch <= next_fetch;
last_fetch <= xfer_ddr ? fetch : 1;
if (dummy_count) begin
flash_clk <= !flash_clk && !flash_csb;
dummy_count <= dummy_count - flash_clk;
end else
if (count) begin
flash_clk <= !flash_clk && !flash_csb;
obuffer <= next_obuffer;
ibuffer <= next_ibuffer;
count <= next_count;
end
if (din_valid && din_ready) begin
flash_csb <= 0;
flash_clk <= 0;
count <= 8;
dummy_count <= din_rd ? din_data : 0;
obuffer <= din_data;
xfer_tag <= din_tag;
xfer_cont <= din_cont;
xfer_dspi <= din_dspi;
xfer_qspi <= din_qspi;
xfer_ddr <= din_ddr;
xfer_rd <= din_rd;
end
end
end
endmodule

View File

@ -6,17 +6,11 @@ import textwrap
import argparse
parser = argparse.ArgumentParser(description="convert ICE40 chip database")
group = parser.add_mutually_exclusive_group()
group.add_argument("-b", "--binary", action="store_true")
group.add_argument("-c", "--c_file", action="store_true")
parser.add_argument("filename", type=str, help="chipdb input filename")
parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc")
parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h")
args = parser.parse_args()
endianness = "le"
nodebug = True
dev_name = None
dev_width = None
dev_height = None
@ -668,266 +662,56 @@ for ec in sorted(extra_cells.keys()):
add_bel_ec(ec)
class BinaryBlobAssembler:
def __init__(self, cname, endianness, nodebug = False):
assert endianness in ["le", "be"]
self.cname = cname
self.endianness = endianness
self.finalized = False
self.data = bytearray()
self.comments = dict()
self.labels = dict()
self.exports = set()
self.labels_byaddr = dict()
self.ltypes_byaddr = dict()
self.strings = dict()
self.refs = dict()
self.nodebug = nodebug
def l(self, name, ltype = None, export = False):
assert not self.finalized
assert name not in self.labels
assert len(self.data) not in self.labels_byaddr
self.labels[name] = len(self.data)
if ltype is not None:
self.ltypes_byaddr[len(self.data)] = ltype
self.labels_byaddr[len(self.data)] = name
if export:
assert ltype is not None
self.exports.add(len(self.data))
if ltype is None:
print("label %s" % (name,))
else:
print("label %s %s" % (name, ltype))
def r(self, name, comment):
assert not self.finalized
assert len(self.data) % 4 == 0
assert len(self.data) not in self.refs
if self.nodebug:
comment = None
if name is not None:
self.refs[len(self.data)] = (name, comment)
self.data.append(0)
self.data.append(0)
self.data.append(0)
self.data.append(0)
if (name is None) and (comment is not None):
self.comments[len(self.data)] = comment + " (null reference)"
if comment is None:
print("ref %s" % (name,))
else:
print("ref %s %s" % (name, comment))
def s(self, s, comment):
assert not self.finalized
if self.nodebug:
comment = None
if s not in self.strings:
index = len(self.strings)
self.strings[s] = index
else:
index = self.strings[s]
if comment is not None:
self.r("str%d" % index, '%s: "%s"' % (comment, s))
else:
self.r("str%d" % index, None)
print("str %s" % s)
def u8(self, v, comment):
assert not self.finalized
if self.nodebug:
comment = None
self.data.append(v)
if comment is not None:
self.comments[len(self.data)] = comment
if comment is None:
print("u8 %d" % (v,))
else:
print("u8 %d %s" % (v, comment))
def u16(self, v, comment):
assert not self.finalized
assert len(self.data) % 2 == 0
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
elif self.endianness == "be":
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
if comment is None:
print("u16 %d" % (v,))
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
print("u16 %d %s" % (v, comment))
def u32(self, v, comment):
assert not self.finalized
assert len(self.data) % 4 == 0
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 24) & 255)
elif self.endianness == "be":
self.data.append((v >> 24) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
if comment is None:
print("u32 %d" % (v,))
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
print("u32 %d %s" % (v, comment))
def finalize(self):
assert not self.finalized
for s, index in sorted(self.strings.items()):
self.l("str%d" % index, "char")
for c in s:
self.data.append(ord(c))
self.data.append(0)
self.finalized = True
cursor = 0
while cursor < len(self.data):
if cursor in self.refs:
v = self.labels[self.refs[cursor][0]] - cursor
if self.endianness == "le":
self.data[cursor+0] = (v & 255)
self.data[cursor+1] = ((v >> 8) & 255)
self.data[cursor+2] = ((v >> 16) & 255)
self.data[cursor+3] = ((v >> 24) & 255)
elif self.endianness == "be":
self.data[cursor+0] = ((v >> 24) & 255)
self.data[cursor+1] = ((v >> 16) & 255)
self.data[cursor+2] = ((v >> 8) & 255)
self.data[cursor+3] = (v & 255)
else:
assert 0
cursor += 4
else:
cursor += 1
def pre(self, s):
print("pre %s" % s)
def write_verbose_c(self, f, ctype = "const unsigned char"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
cursor = 0
bytecnt = 0
while cursor < len(self.data):
if cursor in self.comments:
if bytecnt == 0:
print(" ", end="", file=f)
print(" // %s" % self.comments[cursor], file=f)
bytecnt = 0
if cursor in self.labels_byaddr:
if bytecnt != 0:
print(file=f)
if cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
else:
print(" // [%d] %s" % (cursor, self.labels_byaddr[cursor]), file=f)
bytecnt = 0
if cursor in self.refs:
if bytecnt != 0:
print(file=f)
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor+0]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor+1]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor+2]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor+3]), end="", file=f)
print(" // [%d] %s (reference to %s)" % (cursor, self.refs[cursor][1], self.refs[cursor][0]), file=f)
bytecnt = 0
cursor += 4
else:
if bytecnt == 0:
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor]), end=("" if bytecnt < 15 else "\n"), file=f)
bytecnt = (bytecnt + 1) & 15
cursor += 1
if bytecnt != 0:
print(file=f)
print("};", file=f)
def post(self, s):
print("post %s" % s)
def write_compact_c(self, f, ctype = "const unsigned char"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
column = 0
for v in self.data:
if column == 0:
print(" ", end="", file=f)
column += 2
s = "%d," % v
print(s, end="", file=f)
column += len(s)
if column > 75:
print(file=f)
column = 0
if column != 0:
print(file=f)
for cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
print("};", file=f)
def push(self, name):
print("push %s" % name)
def write_uint64_c(self, f, ctype = "const uint64_t"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, (len(self.data)+7) // 8), file=f)
column = 0
for i in range((len(self.data)+7) // 8):
v0 = self.data[8*i+0] if 8*i+0 < len(self.data) else 0
v1 = self.data[8*i+1] if 8*i+1 < len(self.data) else 0
v2 = self.data[8*i+2] if 8*i+2 < len(self.data) else 0
v3 = self.data[8*i+3] if 8*i+3 < len(self.data) else 0
v4 = self.data[8*i+4] if 8*i+4 < len(self.data) else 0
v5 = self.data[8*i+5] if 8*i+5 < len(self.data) else 0
v6 = self.data[8*i+6] if 8*i+6 < len(self.data) else 0
v7 = self.data[8*i+7] if 8*i+7 < len(self.data) else 0
if self.endianness == "le":
v = v0 << 0
v |= v1 << 8
v |= v2 << 16
v |= v3 << 24
v |= v4 << 32
v |= v5 << 40
v |= v6 << 48
v |= v7 << 56
elif self.endianness == "be":
v = v7 << 0
v |= v6 << 8
v |= v5 << 16
v |= v4 << 24
v |= v3 << 32
v |= v2 << 40
v |= v1 << 48
v |= v0 << 56
else:
assert 0
if column == 3:
print(" 0x%016x," % v, file=f)
column = 0
else:
if column == 0:
print(" ", end="", file=f)
print(" 0x%016x," % v, end="", file=f)
column += 1
if column != 0:
print("", file=f)
print("};", file=f)
def pop(self):
print("pop")
def write_string_c(self, f, ctype = "const char"):
assert self.finalized
assert self.data[len(self.data)-1] == 0
print("%s %s[%d] =" % (ctype, self.cname, len(self.data)), file=f)
print(" \"", end="", file=f)
column = 0
for i in range(len(self.data)-1):
if (self.data[i] < 32) or (self.data[i] > 126):
print("\\%03o" % self.data[i], end="", file=f)
column += 4
elif self.data[i] == ord('"') or self.data[i] == ord('\\'):
print("\\" + chr(self.data[i]), end="", file=f)
column += 2
else:
print(chr(self.data[i]), end="", file=f)
column += 1
if column > 70 and (i != len(self.data)-2):
print("\"\n \"", end="", file=f)
column = 0
print("\";", file=f)
def write_binary(self, f):
assert self.finalized
assert self.data[len(self.data)-1] == 0
f.buffer.write(self.data)
bba = BinaryBlobAssembler("chipdb_blob_%s" % dev_name, endianness)
bba = BinaryBlobAssembler()
bba.pre('#include "nextpnr.h"')
bba.pre('NEXTPNR_NAMESPACE_BEGIN')
bba.post('NEXTPNR_NAMESPACE_END')
bba.push("chipdb_blob_%s" % dev_name)
bba.r("chip_info_%s" % dev_name, "chip_info")
index = 0
@ -1243,23 +1027,4 @@ bba.r("bits_info_%s" % dev_name, "bits_info")
bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config")
bba.r("package_info_%s" % dev_name, "packages_data")
bba.finalize()
if args.c_file:
print('#include "nextpnr.h"')
print('NEXTPNR_NAMESPACE_BEGIN')
if args.binary:
bba.write_binary(sys.stdout)
if args.c_file:
bba.write_string_c(sys.stdout)
# bba.write_uint64_c(sys.stdout)
# bba.write_compact_c(sys.stdout, "uint8_t")
# bba.write_verbose_c(sys.stdout, "uint8_t")
if args.c_file:
print('NEXTPNR_NAMESPACE_END')
bba.pop()

View File

@ -24,11 +24,11 @@ if (MSVC)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm < ${DEV_CC_BBA_DB} > ${DEV_CC_DB}
COMMAND bbasm ${DEV_CC_BBA_DB} ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
@ -46,12 +46,12 @@ else()
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new
COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm < ${DEV_CC_BBA_DB} > ${DEV_CC_DB}.new
COMMAND bbasm --c ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)

View File

@ -550,13 +550,13 @@ static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString
NPNR_ASSERT(port.net != nullptr);
// Create pass-through LUT.
std::unique_ptr<CellInfo> pt =
create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc");
pt->params[ctx->id("LUT_INIT")] = "255"; // output is always I3
std::unique_ptr<CellInfo> pt = create_ice_cell(ctx, ctx->id("ICESTORM_LC"),
ci->name.str(ctx) + "$nextpnr_" + portId.str(ctx) + "_lut_through");
pt->params[ctx->id("LUT_INIT")] = "65280"; // output is always I3
// Create LUT output net.
std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net");
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net");
out_net->driver.cell = pt.get();
out_net->driver.port = ctx->id("O");
pt->ports.at(ctx->id("O")).net = out_net.get();
@ -647,16 +647,14 @@ static void pack_special(Context *ctx)
}
new_cells.push_back(std::move(packed));
} else if (is_sb_pll40(ctx, ci)) {
bool is_pad = is_sb_pll40_pad(ctx, ci);
bool is_core = !is_pad;
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL");
packed->attrs[ctx->id("TYPE")] = ci->type.str(ctx);
packed_cells.insert(ci->name);
if (is_sb_pll40_pad(ctx, ci)) {
// TODO(q3k): Implement these after checking their behaviour on
// a board with exposed 'clock pads'.
log_error("SB_PLL40_*_PAD cells are not supported yet.\n");
}
for (auto attr : ci->attrs)
packed->attrs[attr.first] = attr.second;
for (auto param : ci->params)
@ -672,6 +670,8 @@ static void pack_special(Context *ctx)
: feedback_path == "EXTERNAL" ? "6" : feedback_path;
packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
NetInfo *pad_packagepin_net = nullptr;
for (auto port : ci->ports) {
PortInfo &pi = port.second;
std::string newname = pi.name.str(ctx);
@ -685,6 +685,22 @@ static void pack_special(Context *ctx)
newname = "PLLOUT_B";
if (pi.name == ctx->id("PLLOUTCORE"))
newname = "PLLOUT_A";
if (pi.name == ctx->id("PACKAGEPIN")) {
if (!is_pad) {
log_error(" PLL '%s' has a PACKAGEPIN but is not a PAD PLL", ci->name.c_str(ctx));
} else {
// We drop this port and instead place the PLL adequately below.
pad_packagepin_net = port.second.net;
NPNR_ASSERT(pad_packagepin_net != nullptr);
continue;
}
}
if (pi.name == ctx->id("REFERENCECLK")) {
if (!is_core)
log_error(" PLL '%s' has a REFERENCECLK but is not a CORE PLL", ci->name.c_str(ctx));
}
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
@ -696,6 +712,42 @@ static void pack_special(Context *ctx)
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != TYPE_ICESTORM_PLL)
continue;
// A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared
// with PLLOUT_A.
if (is_pad) {
auto pll_sb_io_belpin = ctx->getIOBSharingPLLPin(bel, PIN_PLLOUT_A);
NPNR_ASSERT(pad_packagepin_net != nullptr);
auto pll_packagepin_driver = pad_packagepin_net->driver;
NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
if (pll_packagepin_driver.cell->type != ctx->id("SB_IO")) {
log_error(" PLL '%s' has a PACKAGEPIN driven by "
"an %s, should be directly connected to an input SB_IO\n",
ci->name.c_str(ctx), pll_packagepin_driver.cell->type.c_str(ctx));
}
auto packagepin_cell = pll_packagepin_driver.cell;
auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
if (packagepin_bel_name == packagepin_cell->attrs.end()) {
log_error(" PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
packagepin_cell->name.c_str(ctx));
}
auto packagepin_bel = ctx->getBelByName(ctx->id(packagepin_bel_name->second));
if (pll_sb_io_belpin.bel != packagepin_bel) {
log_error(" PLL '%s' PACKAGEPIN is connected to pin %s, can only be pin %s\n",
ci->name.c_str(ctx), ctx->getBelPackagePin(packagepin_bel).c_str(),
ctx->getBelPackagePin(pll_sb_io_belpin.bel).c_str());
}
if (pad_packagepin_net->users.size() != 1) {
log_error(" PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx),
pad_packagepin_net->name.c_str(ctx));
}
// Set an attribute about this PLL's PAD SB_IO.
packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second;
// Remove the connection from the SB_IO to the PLL.
packagepin_cell->ports.erase(pll_packagepin_driver.port);
}
log_info(" constrained '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx));
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
pll_bel = bel;
@ -706,6 +758,15 @@ static void pack_special(Context *ctx)
}
}
// Delete the original PACKAGEPIN net if needed.
if (pad_packagepin_net != nullptr) {
for (auto user : pad_packagepin_net->users) {
user.cell->ports.erase(user.port);
}
ctx->nets.erase(pad_packagepin_net->name);
pad_packagepin_net = nullptr;
}
// The LOCK signal on iCE40 PLLs goes through the neigh_op_bnl_1 wire.
// In practice, this means the LOCK signal can only directly reach LUT
// inputs.

42
ice40/picorv32_benchmark.py Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
import os, sys, threading
from os import path
import subprocess
import re
num_runs = 8
if not path.exists("picorv32.json"):
subprocess.run(["wget", "https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v"], check=True)
subprocess.run(["yosys", "-q", "-p", "synth_ice40 -json picorv32.json -top top", "picorv32.v", "picorv32_top.v"], check=True)
fmax = {}
if not path.exists("picorv32_work"):
os.mkdir("picorv32_work")
threads = []
for i in range(num_runs):
def runner(run):
ascfile = "picorv32_work/picorv32_s{}.asc".format(run)
if path.exists(ascfile):
os.remove(ascfile)
result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "70"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
if result.returncode != 0:
print("Run {} failed!".format(run))
else:
icetime_res = subprocess.check_output(["icetime", "-d", "hx8k", ascfile])
fmax_m = re.search(r'\(([0-9.]+) MHz\)', icetime_res.decode('utf-8'))
fmax[run] = float(fmax_m.group(1))
threads.append(threading.Thread(target=runner, args=[i+1]))
for t in threads: t.start()
for t in threads: t.join()
fmax_min = min(fmax.values())
fmax_max = max(fmax.values())
fmax_avg = sum(fmax.values()) / len(fmax)
print("{}/{} runs passed".format(len(fmax), num_runs))
print("icetime: min = {} MHz, avg = {} MHz, max = {} MHz".format(fmax_min, fmax_avg, fmax_max))