diff --git a/3rdparty/QtPropertyBrowser/src/qtpropertybrowser.cpp b/3rdparty/QtPropertyBrowser/src/qtpropertybrowser.cpp index 63260fe4..9533fb5b 100644 --- a/3rdparty/QtPropertyBrowser/src/qtpropertybrowser.cpp +++ b/3rdparty/QtPropertyBrowser/src/qtpropertybrowser.cpp @@ -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 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. diff --git a/3rdparty/QtPropertyBrowser/src/qtpropertybrowser.h b/3rdparty/QtPropertyBrowser/src/qtpropertybrowser.h index c4c6275b..1ca87830 100644 --- a/3rdparty/QtPropertyBrowser/src/qtpropertybrowser.h +++ b/3rdparty/QtPropertyBrowser/src/qtpropertybrowser.h @@ -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; diff --git a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp index 673252d2..523856eb 100644 --- a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp +++ b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp @@ -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(); } diff --git a/3rdparty/QtPropertyBrowser/src/qtvariantproperty.cpp b/3rdparty/QtPropertyBrowser/src/qtvariantproperty.cpp index c41730c8..03f9688c 100644 --- a/3rdparty/QtPropertyBrowser/src/qtvariantproperty.cpp +++ b/3rdparty/QtPropertyBrowser/src/qtvariantproperty.cpp @@ -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()) diff --git a/bba/README.md b/bba/README.md index 7e64b582..166ff7ff 100644 --- a/bba/README.md +++ b/bba/README.md @@ -12,59 +12,63 @@ independent. Valid commands for the input are as follows. -pre ------------- +pre \ +-------------- When a C file is generated as output, all the "pre" strings will be included before the binary blob. -post -------------- +post \ +--------------- When a C file is generated as output, all the "post" strings will be included after the binary blob. -push ------------ +push \ +------------- All following commands up until the matching "pop" will be writen to stream -. Everything written to the same stream will end up in a continous -region of the output. +\. Everything written to the same stream will end up in a continous +region of the output. The statements `pop`, `label`, `ref`, `u8`, `u16`, +`u32`, and `str` are only valid within such a block. The name used in the +first push statement also determines the name of the variable in the generated +C output (when C is selected as output file format). pop --- End of a push..pop block. -label [] ------------------------- +label \ \[\\] +------------------------------ Add a label for the current position. -ref [] ----------------------- +ref \ \[\\] +---------------------------- 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 [] ----------------------- +u8 \ \[\\] +---------------------------- Add a 8-bit value to the binary blob. -u16 [] ------------------------ +u16 \ \[\\] +----------------------------- 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 [] ----------------------- +u32 \ \[\\] +----------------------------- 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 ------------- +str "\" \[\\] +-------------------------------- -Add a reference to a zero-terminated copy of the specified string. +Add a reference to a zero-terminated copy of that string. Any character may be +used to quote the string, but the most common choices are `"` and `|`. diff --git a/bba/main.cc b/bba/main.cc index cd09f804..dd0caf1a 100644 --- a/bba/main.cc +++ b/bba/main.cc @@ -52,6 +52,7 @@ std::map streamIndex; std::vector streamStack; std::vector labels; +std::vector labelNames; std::map labelIndex; std::vector preText, postText; @@ -67,6 +68,7 @@ const char *skipWhitespace(const char *p) int main(int argc, char **argv) { + bool debug = false; bool verbose = false; bool bigEndian = false; bool writeC = false; @@ -76,6 +78,7 @@ int main(int argc, char **argv) po::positional_options_description pos; po::options_description options("Allowed options"); options.add_options()("v", "verbose output"); + options.add_options()("d", "debug output"); options.add_options()("b", "big endian"); options.add_options()("c", "write c strings"); options.add_options()("files", po::value>(), "file parameters"); @@ -94,6 +97,8 @@ int main(int argc, char **argv) } if (vm.count("v")) verbose = true; + if (vm.count("d")) + debug = true; if (vm.count("b")) bigEndian = true; if (vm.count("c")) @@ -152,11 +157,13 @@ int main(int argc, char **argv) Stream &s = streams.at(streamStack.back()); if (labelIndex.count(label) == 0) { labelIndex[label] = labels.size(); + if (debug) + labelNames.push_back(label); labels.push_back(-1); } s.tokenTypes.push_back(cmd == "label" ? TOK_LABEL : TOK_REF); s.tokenValues.push_back(labelIndex.at(label)); - if (verbose) + if (debug) s.tokenComments.push_back(comment); continue; } @@ -167,28 +174,41 @@ int main(int argc, char **argv) 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) + if (debug) s.tokenComments.push_back(comment); continue; } if (cmd == "str") { const char *value = skipWhitespace(strtok(nullptr, "\r\n")); + char terminator[2] = {*value, 0}; + assert(terminator[0] != 0); + value = strtok((char *)value + 1, terminator); + const char *comment = 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(); + if (debug) + labelNames.push_back(label); labels.push_back(-1); } s.tokenTypes.push_back(TOK_REF); s.tokenValues.push_back(labelIndex.at(label)); - if (verbose) - s.tokenComments.push_back(value); + if (debug) + s.tokenComments.push_back(comment); stringStream.tokenTypes.push_back(TOK_LABEL); stringStream.tokenValues.push_back(labelIndex.at(label)); + stringStream.tokenComments.push_back(""); while (1) { stringStream.tokenTypes.push_back(TOK_U8); stringStream.tokenValues.push_back(*value); + if (debug) { + char char_comment[4] = {'\'', *value, '\'', 0}; + if (*value < 32 || *value >= 127) + char_comment[0] = 0; + stringStream.tokenComments.push_back(char_comment); + } if (*value == 0) break; value++; @@ -208,8 +228,10 @@ int main(int argc, char **argv) assert(!streams.empty()); assert(streamStack.empty()); streams.push_back(Stream()); + streams.back().name = "strings"; streams.back().tokenTypes.swap(stringStream.tokenTypes); streams.back().tokenValues.swap(stringStream.tokenValues); + streams.back().tokenComments.swap(stringStream.tokenComments); int cursor = 0; for (auto &s : streams) { @@ -247,6 +269,9 @@ int main(int argc, char **argv) cursor = 0; for (auto &s : streams) { + if (debug) + printf("-- %s --\n", s.name.c_str()); + for (int i = 0; i < int(s.tokenTypes.size()); i++) { uint32_t value = s.tokenValues[i]; int numBytes = 0; @@ -276,13 +301,13 @@ int main(int argc, char **argv) case 4: data[cursor++] = value >> 24; data[cursor++] = value >> 16; - /* fall-through */ + /* fall-through */ case 2: data[cursor++] = value >> 8; - /* fall-through */ + /* fall-through */ case 1: data[cursor++] = value; - /* fall-through */ + /* fall-through */ case 0: break; default: @@ -293,13 +318,13 @@ int main(int argc, char **argv) case 4: data[cursor + 3] = value >> 24; data[cursor + 2] = value >> 16; - /* fall-through */ + /* fall-through */ case 2: data[cursor + 1] = value >> 8; - /* fall-through */ + /* fall-through */ case 1: data[cursor] = value; - /* fall-through */ + /* fall-through */ case 0: break; default: @@ -307,6 +332,51 @@ int main(int argc, char **argv) } cursor += numBytes; } + + if (debug) { + printf("%08x ", cursor - numBytes); + for (int k = cursor - numBytes; k < cursor; k++) + printf("%02x ", data[k]); + for (int k = numBytes; k < 4; k++) + printf(" "); + + unsigned long long v = s.tokenValues[i]; + + switch (s.tokenTypes[i]) { + case TOK_LABEL: + if (s.tokenComments[i].empty()) + printf("label %s\n", labelNames[v].c_str()); + else + printf("label %-24s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str()); + break; + case TOK_REF: + if (s.tokenComments[i].empty()) + printf("ref %s %s\n", labelNames[v].c_str()); + else + printf("ref %-26s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str()); + break; + case TOK_U8: + if (s.tokenComments[i].empty()) + printf("u8 %llu\n", v); + else + printf("u8 %-27llu %s\n", v, s.tokenComments[i].c_str()); + break; + case TOK_U16: + if (s.tokenComments[i].empty()) + printf("u16 %-26llu\n", v); + else + printf("u16 %-26llu %s\n", v, s.tokenComments[i].c_str()); + break; + case TOK_U32: + if (s.tokenComments[i].empty()) + printf("u32 %-26llu\n", v); + else + printf("u32 %-26llu %s\n", v, s.tokenComments[i].c_str()); + break; + default: + assert(0); + } + } } } @@ -319,7 +389,8 @@ int main(int argc, char **argv) fprintf(fileOut, "const char %s[%d] =\n\"", streams[0].name.c_str(), int(data.size()) + 1); cursor = 1; - for (auto d : data) { + for (int i = 0; i < int(data.size()); i++) { + auto d = data[i]; if (cursor > 70) { fputc('\"', fileOut); fputc('\n', fileOut); @@ -329,9 +400,11 @@ int main(int argc, char **argv) fputc('\"', fileOut); cursor = 1; } - if (d < 32 || d >= 128) { - fprintf(fileOut, "\\%03o", int(d)); - cursor += 4; + if (d < 32 || d >= 127) { + if (i + 1 < int(data.size()) && (data[i + 1] < '0' || '9' < data[i + 1])) + cursor += fprintf(fileOut, "\\%o", int(d)); + else + cursor += fprintf(fileOut, "\\%03o", int(d)); } else if (d == '\"' || d == '\'' || d == '\\') { fputc('\\', fileOut); fputc(d, fileOut); diff --git a/common/nextpnr.h b/common/nextpnr.h index f7f1cebc..e9e491f4 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -155,10 +155,10 @@ struct GraphicElement enum style_t { - G_FRAME, - G_HIDDEN, - G_INACTIVE, - G_ACTIVE, + G_FRAME, // Static "frame". Contrast between G_INACTIVE and G_ACTIVE + G_HIDDEN, // Only display when object is selected or highlighted + G_INACTIVE, // Render using low-contrast color + G_ACTIVE, // Render using high-contast color } style = G_FRAME; float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0; @@ -276,11 +276,11 @@ struct CellInfo : ArchCellInfo // placement constraints CellInfo *constr_parent; - std::vector constr_children; + std::vector 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) }; diff --git a/common/router1.cc b/common/router1.cc index fbf3c467..8a05236f 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -810,14 +810,14 @@ bool router1(Context *ctx) log_info("Checksum: 0x%08x\n", ctx->checksum()); #ifndef NDEBUG ctx->check(); - ctx->unlock(); #endif + ctx->unlock(); return true; } catch (log_execution_error_exception) { #ifndef NDEBUG ctx->check(); - ctx->unlock(); #endif + ctx->unlock(); return false; } } diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 5507d1e3..55fe5704 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -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 ""; diff --git a/ecp5/arch.h b/ecp5/arch.h index 4400db5e..d9d29c76 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -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 function_name; int16_t bank; @@ -107,7 +107,7 @@ NPNR_PACKED_STRUCT(struct PIOInfoPOD { NPNR_PACKED_STRUCT(struct PackagePinPOD { RelPtr name; - Location abs_loc; + LocationPOD abs_loc; int32_t bel_index; }); @@ -117,6 +117,26 @@ NPNR_PACKED_STRUCT(struct PackageInfoPOD { RelPtr 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 locations; RelPtr location_type; + RelPtr location_glbinfo; RelPtr> tiletype_names; RelPtr package_info; RelPtr pio_info; @@ -517,6 +538,8 @@ struct Arch : BaseCtx return id(name.str()); } + IdString getWireType(WireId wire) const { return IdString(); } + uint32_t getWireChecksum(WireId wire) const { return wire.index; } void bindWire(WireId wire, IdString net, PlaceStrength strength) @@ -595,6 +618,8 @@ struct Arch : BaseCtx PipId getPipByName(IdString name) const; IdString getPipName(PipId pip) const; + IdString getPipType(PipId pip) const { return IdString(); } + uint32_t getPipChecksum(PipId pip) const { return pip.index; } void bindPip(PipId pip, IdString net, PlaceStrength strength) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index f1feba24..f87b7038 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -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"); } diff --git a/ecp5/family.cmake b/ecp5/family.cmake index 97e5d66b..1c388e99 100644 --- a/ecp5/family.cmake +++ b/ecp5/family.cmake @@ -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 $) diff --git a/ecp5/main.cc b/ecp5/main.cc index 90096855..f2db74d7 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -100,18 +100,16 @@ int main(int argc, char *argv[]) } if (vm.count("help") || argc == 1) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; + std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git " + "sha1 " GIT_COMMIT_HASH_STR ")\n"; std::cout << "\n"; std::cout << options << "\n"; return argc != 1; } if (vm.count("version")) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; + std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git " + "sha1 " GIT_COMMIT_HASH_STR ")\n"; return 1; } diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index b5cd53f1..cf12e775 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -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() diff --git a/generic/arch.cc b/generic/arch.cc index ac79ebea..5c9864ab 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -24,22 +24,24 @@ NEXTPNR_NAMESPACE_BEGIN -void Arch::addWire(IdString name, int x, int y) +void Arch::addWire(IdString name, IdString type, int x, int y) { NPNR_ASSERT(wires.count(name) == 0); WireInfo &wi = wires[name]; wi.name = name; + wi.type = type; wi.x = x; wi.y = y; wire_ids.push_back(name); } -void Arch::addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay) +void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay) { NPNR_ASSERT(pips.count(name) == 0); PipInfo &pi = pips[name]; pi.name = name; + pi.type = type; pi.srcWire = srcWire; pi.dstWire = dstWire; pi.delay = delay; @@ -49,11 +51,12 @@ void Arch::addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo d pip_ids.push_back(name); } -void Arch::addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay) +void Arch::addAlias(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay) { NPNR_ASSERT(pips.count(name) == 0); PipInfo &pi = pips[name]; pi.name = name; + pi.type = type; pi.srcWire = srcWire; pi.dstWire = dstWire; pi.delay = delay; @@ -207,15 +210,9 @@ BelId Arch::getBelByLocation(Loc loc) const return BelId(); } -const std::vector &Arch::getBelsByTile(int x, int y) const -{ - return bels_by_tile.at(x).at(y); -} +const std::vector &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 { @@ -272,6 +269,8 @@ WireId Arch::getWireByName(IdString name) const IdString Arch::getWireName(WireId wire) const { return wire; } +IdString Arch::getWireType(WireId wire) const { return wires.at(wire).type; } + uint32_t Arch::getWireChecksum(WireId wire) const { // FIXME @@ -322,6 +321,8 @@ PipId Arch::getPipByName(IdString name) const IdString Arch::getPipName(PipId pip) const { return pip; } +IdString Arch::getPipType(PipId pip) const { return pips.at(pip).type; } + uint32_t Arch::getPipChecksum(PipId wire) const { // FIXME diff --git a/generic/arch.h b/generic/arch.h index 97ed1ac2..01a90ee1 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -31,7 +31,7 @@ struct WireInfo; struct PipInfo { - IdString name, bound_net; + IdString name, type, bound_net; WireId srcWire, dstWire; DelayInfo delay; DecalXY decalxy; @@ -39,7 +39,7 @@ struct PipInfo struct WireInfo { - IdString name, bound_net; + IdString name, type, bound_net; std::vector downhill, uphill, aliases; BelPin uphill_bel_pin; std::vector downhill_bel_pins; @@ -96,9 +96,9 @@ struct Arch : BaseCtx float grid_distance_to_delay; - void addWire(IdString name, int x, int y); - void addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay); - void addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay); + void addWire(IdString name, IdString type, int x, int y); + void addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay); + void addAlias(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay); void addBel(IdString name, IdString type, Loc loc, bool gb); void addBelInput(IdString bel, IdString name, IdString wire); @@ -157,6 +157,7 @@ struct Arch : BaseCtx WireId getWireByName(IdString name) const; IdString getWireName(WireId wire) const; + IdString getWireType(WireId wire) const; uint32_t getWireChecksum(WireId wire) const; void bindWire(WireId wire, IdString net, PlaceStrength strength); void unbindWire(WireId wire); @@ -169,6 +170,7 @@ struct Arch : BaseCtx PipId getPipByName(IdString name) const; IdString getPipName(PipId pip) const; + IdString getPipType(PipId pip) const; uint32_t getPipChecksum(PipId pip) const; void bindPip(PipId pip, IdString net, PlaceStrength strength); void unbindPip(PipId pip); diff --git a/generic/main.cc b/generic/main.cc index 3b8b3fe6..d5a65102 100644 --- a/generic/main.cc +++ b/generic/main.cc @@ -75,18 +75,16 @@ int main(int argc, char *argv[]) } if (vm.count("help") || argc == 1) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; + std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git " + "sha1 " GIT_COMMIT_HASH_STR ")\n"; std::cout << "\n"; std::cout << options << "\n"; return argc != 1; } if (vm.count("version")) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; + std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git " + "sha1 " GIT_COMMIT_HASH_STR ")\n"; return 1; } diff --git a/gui/base.qrc b/gui/base.qrc index 1a848f54..7b3fa55c 100644 --- a/gui/base.qrc +++ b/gui/base.qrc @@ -10,5 +10,9 @@ resources/resultset_next.png resources/resultset_last.png resources/cross.png + resources/zoom_in.png + resources/zoom_out.png + resources/shape_handles.png + resources/shape_square.png diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 3cb2cc96..d44bd0f7 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -25,7 +25,6 @@ #include #include "designwidget.h" #include "fpgaviewwidget.h" -#include "jsonparse.h" #include "log.h" #include "mainwindow.h" #include "pythontab.h" @@ -76,7 +75,7 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr context, QWidget *parent centralTabWidget->setTabsClosable(true); connect(centralTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); - FPGAViewWidget *fpgaView = new FPGAViewWidget(); + fpgaView = new FPGAViewWidget(); centralTabWidget->addTab(fpgaView, "Graphics"); centralTabWidget->tabBar()->tabButton(0, QTabBar::RightSide)->resize(0, 0); @@ -164,4 +163,30 @@ void BaseMainWindow::createMenusAndBars() mainToolBar->addAction(actionSave); } +void BaseMainWindow::createGraphicsBar() +{ + QAction *actionZoomIn = new QAction("Zoom In", this); + actionZoomIn->setIcon(QIcon(":/icons/resources/zoom_in.png")); + connect(actionZoomIn, SIGNAL(triggered()), fpgaView, SLOT(zoom_in())); + + QAction *actionZoomOut = new QAction("Zoom Out", this); + actionZoomOut->setIcon(QIcon(":/icons/resources/zoom_out.png")); + connect(actionZoomOut, SIGNAL(triggered()), fpgaView, SLOT(zoom_out())); + + QAction *actionZoomSelected = new QAction("Zoom Selected", this); + actionZoomSelected->setIcon(QIcon(":/icons/resources/shape_handles.png")); + connect(actionZoomSelected, SIGNAL(triggered()), fpgaView, SLOT(zoom_selected())); + + QAction *actionZoomOutbound = new QAction("Zoom Outbound", this); + actionZoomOutbound->setIcon(QIcon(":/icons/resources/shape_square.png")); + connect(actionZoomOutbound, SIGNAL(triggered()), fpgaView, SLOT(zoom_outbound())); + + graphicsToolBar = new QToolBar(); + addToolBar(Qt::TopToolBarArea, graphicsToolBar); + graphicsToolBar->addAction(actionZoomIn); + graphicsToolBar->addAction(actionZoomOut); + graphicsToolBar->addAction(actionZoomSelected); + graphicsToolBar->addAction(actionZoomOutbound); +} + NEXTPNR_NAMESPACE_END diff --git a/gui/basewindow.h b/gui/basewindow.h index 1184fa80..a25a2854 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -37,6 +37,7 @@ NEXTPNR_NAMESPACE_BEGIN class PythonTab; class DesignWidget; +class FPGAViewWidget; class BaseMainWindow : public QMainWindow { @@ -49,6 +50,7 @@ class BaseMainWindow : public QMainWindow protected: void createMenusAndBars(); + void createGraphicsBar(); protected Q_SLOTS: void writeInfo(std::string text); @@ -70,12 +72,14 @@ class BaseMainWindow : public QMainWindow QMenuBar *menuBar; QToolBar *mainToolBar; + QToolBar *graphicsToolBar; QStatusBar *statusBar; QAction *actionNew; QAction *actionOpen; QAction *actionSave; QProgressBar *progressBar; DesignWidget *designview; + FPGAViewWidget *fpgaView; }; NEXTPNR_NAMESPACE_END diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 6d2a6d3d..989b55fe 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -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 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 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,8 +317,9 @@ void DesignWidget::newContext(Context *ctx) QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget); QMap pip_items; pip_root->setText(0, "Pips"); + pip_root->setFlags(pip_root->flags() & ~Qt::ItemIsSelectable); treeWidget->insertTopLevelItem(0, pip_root); -#ifndef ARCH_ECP5 +#ifndef ARCH_ECP5 if (ctx) { for (auto pip : ctx->getPips()) { auto id = ctx->getPipName(pip); @@ -339,14 +346,16 @@ void DesignWidget::newContext(Context *ctx) for (auto pip : nameToItem[2].toStdMap()) { pip_root->addChild(pip.second); } -#endif +#endif 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; } @@ -507,6 +519,18 @@ void DesignWidget::onItemSelectionChanged() if (treeWidget->selectedItems().size() == 0) return; + if (treeWidget->selectedItems().size() > 1) { + std::vector decals; + for (auto clickItem : treeWidget->selectedItems()) { + IdString value = static_cast(clickItem)->getData(); + ElementType type = static_cast(clickItem)->getType(); + std::vector 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()) @@ -550,6 +574,7 @@ void DesignWidget::onItemSelectionChanged() QtProperty *topItem = addTopLevelProperty("Wire"); addProperty(topItem, QVariant::String, "Name", c.c_str(ctx)); + addProperty(topItem, QVariant::String, "Type", ctx->getWireType(wire).c_str(ctx)); addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire)); addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundWireNet(wire).c_str(ctx), ElementType::NET); addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx), @@ -601,6 +626,7 @@ void DesignWidget::onItemSelectionChanged() QtProperty *topItem = addTopLevelProperty("Pip"); addProperty(topItem, QVariant::String, "Name", c.c_str(ctx)); + addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx)); addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip)); addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundPipNet(pip).c_str(ctx), ElementType::NET); addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingPipNet(pip).c_str(ctx), @@ -758,54 +784,53 @@ std::vector DesignWidget::getDecals(ElementType type, IdString value) return decals; } -void DesignWidget::updateHighlightGroup(QTreeWidgetItem *item, int group) +void DesignWidget::updateHighlightGroup(QList items, int group) { - if (highlightSelected.contains(item)) { - if (highlightSelected[item] == group) { - highlightSelected.remove(item); + const bool shouldClear = items.size() == 1; + for (auto item : items) { + if (highlightSelected.contains(item)) { + if (shouldClear && highlightSelected[item] == group) { + highlightSelected.remove(item); + } else + highlightSelected[item] = group; } else - highlightSelected[item] = group; - } else - highlightSelected.insert(item, group); - - std::vector decals; + highlightSelected.insert(item, group); + } + std::vector decals[8]; for (auto it : highlightSelected.toStdMap()) { - if (it.second == group) { - ElementType type = static_cast(it.first)->getType(); - IdString value = static_cast(it.first)->getData(); - std::vector d = getDecals(type, value); - std::move(d.begin(), d.end(), std::back_inserter(decals)); - } + ElementType type = static_cast(it.first)->getType(); + IdString value = static_cast(it.first)->getData(); + std::vector d = getDecals(type, value); + std::move(d.begin(), d.end(), std::back_inserter(decals[it.second])); } - - Q_EMIT highlight(decals, group); + for (int i = 0; i < 8; i++) + Q_EMIT highlight(decals[i], i); } void DesignWidget::prepareMenuProperty(const QPoint &pos) { QTreeWidget *tree = propertyEditor->treeWidget(); - - itemContextMenu = tree->itemAt(pos); - if (itemContextMenu->parent() == nullptr) - return; - - QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); - if (!browserItem) - return; - QtProperty *selectedProperty = browserItem->property(); - ElementType type = getElementTypeByName(selectedProperty->propertyId()); - if (type == ElementType::NONE) - return; - IdString value = ctx->id(selectedProperty->valueText().toStdString()); - - QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx)); + QList items; + for (auto itemContextMenu : tree->selectedItems()) { + QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); + if (!browserItem) + continue; + QtProperty *selectedProperty = browserItem->property(); + ElementType type = getElementTypeByName(selectedProperty->propertyId()); + if (type == ElementType::NONE) + continue; + IdString value = ctx->id(selectedProperty->valueText().toStdString()); + items.append(nameToItem[getElementIndex(type)].value(value.c_str(ctx))); + } + int selectedIndex = -1; + if (items.size() == 1) { + QTreeWidgetItem *item = items.at(0); + if (highlightSelected.contains(item)) + selectedIndex = highlightSelected[item]; + } QMenu menu(this); - QAction *selectAction = new QAction("&Select", this); - connect(selectAction, &QAction::triggered, this, [this, type, value] { Q_EMIT selected(getDecals(type, value)); }); - menu.addAction(selectAction); - QMenu *subMenu = menu.addMenu("Highlight"); QActionGroup *group = new QActionGroup(this); group->setExclusive(true); @@ -816,27 +841,24 @@ void DesignWidget::prepareMenuProperty(const QPoint &pos) action->setCheckable(true); subMenu->addAction(action); group->addAction(action); - if (highlightSelected.contains(item) && highlightSelected[item] == i) + if (selectedIndex == i) action->setChecked(true); - connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); }); + connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); }); } menu.exec(tree->mapToGlobal(pos)); } void DesignWidget::prepareMenuTree(const QPoint &pos) { - QTreeWidget *tree = treeWidget; - - itemContextMenu = tree->itemAt(pos); - - ElementType type = static_cast(itemContextMenu)->getType(); - IdString value = static_cast(itemContextMenu)->getData(); - - if (type == ElementType::NONE) + if (treeWidget->selectedItems().size() == 0) return; - - QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx)); - + int selectedIndex = -1; + QList items = treeWidget->selectedItems(); + if (treeWidget->selectedItems().size() == 1) { + QTreeWidgetItem *item = treeWidget->selectedItems().at(0); + if (highlightSelected.contains(item)) + selectedIndex = highlightSelected[item]; + } QMenu menu(this); QMenu *subMenu = menu.addMenu("Highlight"); QActionGroup *group = new QActionGroup(this); @@ -848,11 +870,11 @@ void DesignWidget::prepareMenuTree(const QPoint &pos) action->setCheckable(true); subMenu->addAction(action); group->addAction(action); - if (highlightSelected.contains(item) && highlightSelected[item] == i) + if (selectedIndex == i) action->setChecked(true); - connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); }); + connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); }); } - menu.exec(tree->mapToGlobal(pos)); + menu.exec(treeWidget->mapToGlobal(pos)); } void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column) diff --git a/gui/designwidget.h b/gui/designwidget.h index 61681541..85c326d0 100644 --- a/gui/designwidget.h +++ b/gui/designwidget.h @@ -60,7 +60,7 @@ class DesignWidget : public QWidget void updateButtons(); void addToHistory(QTreeWidgetItem *item); std::vector getDecals(ElementType type, IdString value); - void updateHighlightGroup(QTreeWidgetItem *item, int group); + void updateHighlightGroup(QList item, int group); Q_SIGNALS: void info(std::string text); void selected(std::vector decal); @@ -86,7 +86,6 @@ class DesignWidget : public QWidget QtGroupPropertyManager *groupManager; QtVariantEditorFactory *variantFactory; QtTreePropertyBrowser *propertyEditor; - QTreeWidgetItem *itemContextMenu; QMap propertyToId; QMap idToProperty; diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index b9e05a04..e81fe3bd 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -624,21 +624,32 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event) { QPoint degree = event->angleDelta() / 8; - if (!degree.isNull()) { - - if (zoom_ < zoomNear_) { - zoom_ = zoomNear_; - } else if (zoom_ < zoomLvl1_) { - zoom_ -= degree.y() / 10.0; - } else if (zoom_ < zoomLvl2_) { - zoom_ -= degree.y() / 5.0; - } else if (zoom_ < zoomFar_) { - zoom_ -= degree.y(); - } else { - zoom_ = zoomFar_; - } - update(); - } + if (!degree.isNull()) + zoom(degree.y()); } +void FPGAViewWidget::zoom(int level) +{ + if (zoom_ < zoomNear_) { + zoom_ = zoomNear_; + } else if (zoom_ < zoomLvl1_) { + zoom_ -= level / 10.0; + } else if (zoom_ < zoomLvl2_) { + zoom_ -= level / 5.0; + } else if (zoom_ < zoomFar_) { + zoom_ -= level; + } else { + zoom_ = zoomFar_; + } + update(); +} + +void FPGAViewWidget::zoom_in() { zoom(10); } + +void FPGAViewWidget::zoom_out() { zoom(-10); } + +void FPGAViewWidget::zoom_selected() {} + +void FPGAViewWidget::zoom_outbound() {} + NEXTPNR_NAMESPACE_END diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index f69fa786..cc79cd03 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -293,11 +293,17 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions void onSelectedArchItem(std::vector decals); void onHighlightGroupChanged(std::vector decals, int group); void pokeRenderer(void); + void zoom_in(); + void zoom_out(); + void zoom_selected(); + void zoom_outbound(); + Q_SIGNALS: void clickedBel(BelId bel); private: void renderLines(void); + void zoom(int level); QPoint lastPos_; LineShader lineShader_; diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index 810a98ae..f06971b6 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -159,6 +159,8 @@ void MainWindow::createMenu() taskToolBar->addAction(actionPlay); taskToolBar->addAction(actionPause); taskToolBar->addAction(actionStop); + + createGraphicsBar(); } #if defined(_MSC_VER) diff --git a/gui/resources/shape_handles.png b/gui/resources/shape_handles.png new file mode 100644 index 00000000..ce27fe3a Binary files /dev/null and b/gui/resources/shape_handles.png differ diff --git a/gui/resources/shape_square.png b/gui/resources/shape_square.png new file mode 100644 index 00000000..33af0460 Binary files /dev/null and b/gui/resources/shape_square.png differ diff --git a/gui/resources/zoom_in.png b/gui/resources/zoom_in.png new file mode 100644 index 00000000..cdf0a52f Binary files /dev/null and b/gui/resources/zoom_in.png differ diff --git a/gui/resources/zoom_out.png b/gui/resources/zoom_out.png new file mode 100644 index 00000000..07bf98a7 Binary files /dev/null and b/gui/resources/zoom_out.png differ diff --git a/ice40/arch.cc b/ice40/arch.cc index 1d7e9546..d08463d2 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -188,6 +188,13 @@ Arch::Arch(ArchArgs args) : args(args) id_i3 = id("I3"); id_dff_en = id("DFF_ENABLE"); id_neg_clk = id("NEG_CLK"); + id_cin = id("CIN"); + id_cout = id("COUT"); + id_o = id("O"); + id_lo = id("LO"); + id_icestorm_ram = id("ICESTORM_RAM"); + id_rclk = id("RCLK"); + id_wclk = id("WCLK"); } // ----------------------------------------------------------------------- @@ -433,11 +440,103 @@ GroupId Arch::getGroupByName(IdString name) const return GroupId(); } -IdString Arch::getGroupName(GroupId group) const { return IdString(); } +IdString Arch::getGroupName(GroupId group) const +{ + std::string suffix; + + switch (group.type) { + case GroupId::TYPE_FRAME: + suffix = "tile"; + break; + case GroupId::TYPE_MAIN_SW: + suffix = "main_sw"; + break; + case GroupId::TYPE_LOCAL_SW: + suffix = "local_sw"; + break; + case GroupId::TYPE_LC0_SW: + suffix = "lc0_sw"; + break; + case GroupId::TYPE_LC1_SW: + suffix = "lc1_sw"; + break; + case GroupId::TYPE_LC2_SW: + suffix = "lc2_sw"; + break; + case GroupId::TYPE_LC3_SW: + suffix = "lc3_sw"; + break; + case GroupId::TYPE_LC4_SW: + suffix = "lc4_sw"; + break; + case GroupId::TYPE_LC5_SW: + suffix = "lc5_sw"; + break; + case GroupId::TYPE_LC6_SW: + suffix = "lc6_sw"; + break; + case GroupId::TYPE_LC7_SW: + suffix = "lc7_sw"; + break; + default: + return IdString(); + } + + return id("X" + std::to_string(group.x) + "/Y" + std::to_string(group.y) + "/" + suffix); +} std::vector Arch::getGroups() const { std::vector ret; + + for (int y = 0; y < chip_info->height; y++) { + for (int x = 0; x < chip_info->width; x++) { + TileType type = chip_info->tile_grid[y * chip_info->width + x]; + if (type == TILE_NONE) + continue; + + GroupId group; + group.type = GroupId::TYPE_FRAME; + group.x = x; + group.y = y; + // ret.push_back(group); + + group.type = GroupId::TYPE_MAIN_SW; + ret.push_back(group); + + group.type = GroupId::TYPE_LOCAL_SW; + ret.push_back(group); + +#if 0 + if (type == TILE_LOGIC) + { + group.type = GroupId::TYPE_LC0_SW; + ret.push_back(group); + + group.type = GroupId::TYPE_LC1_SW; + ret.push_back(group); + + group.type = GroupId::TYPE_LC2_SW; + ret.push_back(group); + + group.type = GroupId::TYPE_LC3_SW; + ret.push_back(group); + + group.type = GroupId::TYPE_LC4_SW; + ret.push_back(group); + + group.type = GroupId::TYPE_LC5_SW; + ret.push_back(group); + + group.type = GroupId::TYPE_LC6_SW; + ret.push_back(group); + + group.type = GroupId::TYPE_LC7_SW; + ret.push_back(group); + } +#endif + } + } return ret; } @@ -545,15 +644,63 @@ std::vector Arch::getDecalGraphics(DecalId decal) const std::vector ret; if (decal.type == DecalId::TYPE_FRAME) { - for (int x = 0; x <= chip_info->width; x++) - for (int y = 0; y <= chip_info->height; y++) { - GraphicElement el; - el.type = GraphicElement::G_LINE; - el.x1 = x - 0.05, el.x2 = x + 0.05, el.y1 = y, el.y2 = y, el.z = 0; - ret.push_back(el); - el.x1 = x, el.x2 = x, el.y1 = y - 0.05, el.y2 = y + 0.05, el.z = 0; - ret.push_back(el); - } + /* nothing */ + } + + if (decal.type == DecalId::TYPE_GROUP) { + int type = (decal.index >> 16) & 255; + int x = (decal.index >> 8) & 255; + int y = decal.index & 255; + + if (type == GroupId::TYPE_FRAME) { + GraphicElement el; + el.type = GraphicElement::G_LINE; + el.style = GraphicElement::G_FRAME; + + el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.01, el.y2 = y + 0.01; + ret.push_back(el); + el.x1 = x + 0.01, el.x2 = x + 0.01, el.y1 = y + 0.01, el.y2 = y + 0.02; + ret.push_back(el); + + el.x1 = x + 0.99, el.x2 = x + 0.98, el.y1 = y + 0.01, el.y2 = y + 0.01; + ret.push_back(el); + el.x1 = x + 0.99, el.x2 = x + 0.99, el.y1 = y + 0.01, el.y2 = y + 0.02; + ret.push_back(el); + + el.x1 = x + 0.99, el.x2 = x + 0.98, el.y1 = y + 0.99, el.y2 = y + 0.99; + ret.push_back(el); + el.x1 = x + 0.99, el.x2 = x + 0.99, el.y1 = y + 0.99, el.y2 = y + 0.98; + ret.push_back(el); + + el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.99, el.y2 = y + 0.99; + ret.push_back(el); + el.x1 = x + 0.01, el.x2 = x + 0.01, el.y1 = y + 0.99, el.y2 = y + 0.98; + ret.push_back(el); + } + + if (type == GroupId::TYPE_MAIN_SW) { + GraphicElement el; + el.type = GraphicElement::G_BOX; + el.style = GraphicElement::G_FRAME; + + el.x1 = x + main_swbox_x1; + el.x2 = x + main_swbox_x2; + el.y1 = y + main_swbox_y1; + el.y2 = y + main_swbox_y2; + ret.push_back(el); + } + + if (type == GroupId::TYPE_LOCAL_SW) { + GraphicElement el; + el.type = GraphicElement::G_BOX; + el.style = GraphicElement::G_FRAME; + + el.x1 = x + local_swbox_x1; + el.x2 = x + local_swbox_x2; + el.y1 = y + local_swbox_y1; + el.y2 = y + local_swbox_y2; + ret.push_back(el); + } } if (decal.type == DecalId::TYPE_WIRE) { @@ -588,103 +735,32 @@ std::vector Arch::getDecalGraphics(DecalId decal) const (chip_info->bel_data[bel.index].z) * logic_cell_pitch; el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + (chip_info->bel_data[bel.index].z) * logic_cell_pitch; - el.z = 0; ret.push_back(el); - - if (chip_info->bel_data[bel.index].z == 0) { - int tx = chip_info->bel_data[bel.index].x; - int ty = chip_info->bel_data[bel.index].y; - - // Main switchbox - GraphicElement main_sw; - main_sw.type = GraphicElement::G_BOX; - main_sw.style = GraphicElement::G_FRAME; - main_sw.x1 = tx + main_swbox_x1; - main_sw.x2 = tx + main_swbox_x2; - main_sw.y1 = ty + main_swbox_y1; - main_sw.y2 = ty + main_swbox_y2; - ret.push_back(main_sw); - - // Local tracks to LUT input switchbox - GraphicElement local_sw; - local_sw.type = GraphicElement::G_BOX; - local_sw.style = GraphicElement::G_FRAME; - local_sw.x1 = tx + local_swbox_x1; - local_sw.x2 = tx + local_swbox_x2; - local_sw.y1 = ty + local_swbox_y1; - local_sw.y2 = ty + local_swbox_y2; - local_sw.z = 0; - ret.push_back(local_sw); - } } if (bel_type == TYPE_SB_IO) { - if (chip_info->bel_data[bel.index].x == 0 || chip_info->bel_data[bel.index].x == chip_info->width - 1) { - GraphicElement el; - el.type = GraphicElement::G_BOX; - el.x1 = chip_info->bel_data[bel.index].x + 0.1; - el.x2 = chip_info->bel_data[bel.index].x + 0.9; - if (chip_info->bel_data[bel.index].z == 0) { - el.y1 = chip_info->bel_data[bel.index].y + 0.10; - el.y2 = chip_info->bel_data[bel.index].y + 0.45; - } else { - el.y1 = chip_info->bel_data[bel.index].y + 0.55; - el.y2 = chip_info->bel_data[bel.index].y + 0.90; - } - el.z = 0; - ret.push_back(el); - } else { - GraphicElement el; - el.type = GraphicElement::G_BOX; - if (chip_info->bel_data[bel.index].z == 0) { - el.x1 = chip_info->bel_data[bel.index].x + 0.10; - el.x2 = chip_info->bel_data[bel.index].x + 0.45; - } else { - el.x1 = chip_info->bel_data[bel.index].x + 0.55; - el.x2 = chip_info->bel_data[bel.index].x + 0.90; - } - el.y1 = chip_info->bel_data[bel.index].y + 0.1; - el.y2 = chip_info->bel_data[bel.index].y + 0.9; - el.z = 0; - ret.push_back(el); - } + GraphicElement el; + el.type = GraphicElement::G_BOX; + el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; + el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1; + el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2; + el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + + (4 * chip_info->bel_data[bel.index].z) * logic_cell_pitch; + el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + + (4 * chip_info->bel_data[bel.index].z + 3) * logic_cell_pitch; + ret.push_back(el); } if (bel_type == TYPE_ICESTORM_RAM) { for (int i = 0; i < 2; i++) { - int tx = chip_info->bel_data[bel.index].x; - int ty = chip_info->bel_data[bel.index].y + i; - GraphicElement el; el.type = GraphicElement::G_BOX; el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1; el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2; - el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1; - el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + 7 * logic_cell_pitch; - el.z = 0; + el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i; + el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + i + 7 * logic_cell_pitch; ret.push_back(el); - - // Main switchbox - GraphicElement main_sw; - main_sw.type = GraphicElement::G_BOX; - main_sw.style = GraphicElement::G_FRAME; - main_sw.x1 = tx + main_swbox_x1; - main_sw.x2 = tx + main_swbox_x2; - main_sw.y1 = ty + main_swbox_y1; - main_sw.y2 = ty + main_swbox_y2; - ret.push_back(main_sw); - - // Local tracks to LUT input switchbox - GraphicElement local_sw; - local_sw.type = GraphicElement::G_BOX; - local_sw.style = GraphicElement::G_FRAME; - local_sw.x1 = tx + local_swbox_x1; - local_sw.x2 = tx + local_swbox_x2; - local_sw.y1 = ty + local_swbox_y1; - local_sw.y2 = ty + local_swbox_y2; - local_sw.z = 0; - ret.push_back(local_sw); } } } @@ -696,26 +772,26 @@ std::vector Arch::getDecalGraphics(DecalId decal) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const { - if (cell->type == id("ICESTORM_LC")) { - if ((fromPort == id("I0") || fromPort == id("I1") || fromPort == id("I2") || fromPort == id("I3")) && - (toPort == id("O") || toPort == id("LO"))) { + if (cell->type == id_icestorm_lc) { + if ((fromPort == id_i0 || fromPort == id_i1 || fromPort == id_i2 || fromPort == id_i3) && + (toPort == id_o || toPort == id_lo)) { delay = 450; return true; - } else if (fromPort == id("CIN") && toPort == id("COUT")) { + } else if (fromPort == id_cin && toPort == id_cout) { delay = 120; return true; - } else if (fromPort == id("I1") && toPort == id("COUT")) { + } else if (fromPort == id_i1 && toPort == id_cout) { delay = 260; return true; - } else if (fromPort == id("I2") && toPort == id("COUT")) { + } else if (fromPort == id_i2 && toPort == id_cout) { delay = 230; return true; - } else if (fromPort == id("CLK") && toPort == id("O")) { + } else if (fromPort == id_clk && toPort == id_o) { delay = 540; return true; } - } else if (cell->type == id("ICESTORM_RAM")) { - if (fromPort == id("RCLK")) { + } else if (cell->type == id_icestorm_ram) { + if (fromPort == id_rclk) { delay = 2140; return true; } @@ -725,14 +801,14 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { - if (cell->type == id("ICESTORM_LC") && bool_or_default(cell->params, id("DFF_ENABLE"))) { - if (port != id("LO") && port != id("CIN") && port != id("COUT")) - return id("CLK"); - } else if (cell->type == id("ICESTORM_RAM")) { + if (cell->type == id_icestorm_lc && cell->lcInfo.dffEnable) { + if (port != id_lo && port != id_cin && port != id_cout) + return id_clk; + } else if (cell->type == id_icestorm_ram) { if (port.str(this)[0] == 'R') - return id("RCLK"); + return id_rclk; else - return id("WCLK"); + return id_wclk; } return IdString(); } diff --git a/ice40/arch.h b/ice40/arch.h index bd937371..51cbe725 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -469,6 +469,8 @@ struct Arch : BaseCtx return id(chip_info->wire_data[wire.index].name.get()); } + IdString getWireType(WireId wire) const { return IdString(); } + uint32_t getWireChecksum(WireId wire) const { return wire.index; } void bindWire(WireId wire, IdString net, PlaceStrength strength) @@ -611,6 +613,8 @@ struct Arch : BaseCtx IdString getPipName(PipId pip) const; + IdString getPipType(PipId pip) const { return IdString(); } + uint32_t getPipChecksum(PipId pip) const { return pip.index; } WireId getPipSrcWire(PipId pip) const @@ -739,6 +743,9 @@ struct Arch : BaseCtx IdString id_cen, id_clk, id_sr; IdString id_i0, id_i1, id_i2, id_i3; IdString id_dff_en, id_neg_clk; + 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 diff --git a/ice40/benchmark/.gitignore b/ice40/benchmark/.gitignore new file mode 100644 index 00000000..306b5c6e --- /dev/null +++ b/ice40/benchmark/.gitignore @@ -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 diff --git a/ice40/benchmark/Makefile b/ice40/benchmark/Makefile index 38cc2b0d..5e16d9b0 100644 --- a/ice40/benchmark/Makefile +++ b/ice40/benchmark/Makefile @@ -10,14 +10,18 @@ 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 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 + ../../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,1 2 3 4 5 6 7 8,$(eval $(call mkreport,$(i)))) +$(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 diff --git a/ice40/benchmark/report.ipynb b/ice40/benchmark/report.ipynb new file mode 100644 index 00000000..3232f38c --- /dev/null +++ b/ice40/benchmark/report.ipynb @@ -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 +} diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index e9851a83..af5febce 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -566,9 +566,8 @@ void write_asc(const Context *ctx, std::ostream &out) set_config(ti, config.at(y).at(x), "Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true); else - set_config(ti, config.at(y).at(x), - "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" + - std::to_string(lc_idx) + "_inmux02_5", + set_config(ti, config.at(y).at(x), "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + + "_LC0" + std::to_string(lc_idx) + "_inmux02_5", true); } } diff --git a/ice40/chipdb.py b/ice40/chipdb.py index b5fee359..b6af8fcf 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -675,7 +675,8 @@ class BinaryBlobAssembler: print("ref %s %s" % (name, comment)) def s(self, s, comment): - print("str %s" % s) + assert "|" not in s + print("str |%s| %s" % (s, comment)) def u8(self, v, comment): if comment is None: diff --git a/ice40/gfx.cc b/ice40/gfx.cc index 1b01cbd8..0a583e8e 100644 --- a/ice40/gfx.cc +++ b/ice40/gfx.cc @@ -701,6 +701,17 @@ void gfxTilePip(std::vector &g, int x, int y, GfxTileWireId src, if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2)) pipGfx(g, x, y, x1, y1, x2, y2, local_swbox_x1, local_swbox_y1, local_swbox_x2, local_swbox_y2, style); + + if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) { + GraphicElement el; + el.type = GraphicElement::G_ARROW; + el.style = style; + el.x1 = x + logic_cell_x1 + 0.005 * 3; + el.x2 = el.x1; + el.y1 = y + 0.01; + el.y2 = y + 0.02; + g.push_back(el); + } } NEXTPNR_NAMESPACE_END diff --git a/ice40/main.cc b/ice40/main.cc index 4de05d00..6201831a 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -144,18 +144,16 @@ int main(int argc, char *argv[]) #endif if (vm.count("help") || argc == 1) { help: - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; + std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git " + "sha1 " GIT_COMMIT_HASH_STR ")\n"; std::cout << "\n"; std::cout << options << "\n"; return argc != 1; } if (vm.count("version")) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; + std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git " + "sha1 " GIT_COMMIT_HASH_STR ")\n"; return 1; } diff --git a/ice40/pack.cc b/ice40/pack.cc index 91dcf846..8182eb70 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -662,12 +662,12 @@ static void pack_special(Context *ctx) auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")]; packed->params[ctx->id("FEEDBACK_PATH")] = - feedback_path == "DELAY" - ? "0" - : feedback_path == "SIMPLE" ? "1" - : feedback_path == "PHASE_AND_DELAY" - ? "2" - : feedback_path == "EXTERNAL" ? "6" : feedback_path; + feedback_path == "DELAY" ? "0" : feedback_path == "SIMPLE" + ? "1" + : feedback_path == "PHASE_AND_DELAY" + ? "2" + : feedback_path == "EXTERNAL" ? "6" + : feedback_path; packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci)); NetInfo *pad_packagepin_net = nullptr; diff --git a/ice40/picorv32_benchmark.py b/ice40/picorv32_benchmark.py index 9544db50..a4ec581e 100755 --- a/ice40/picorv32_benchmark.py +++ b/ice40/picorv32_benchmark.py @@ -7,7 +7,6 @@ import re num_runs = 8 if not path.exists("picorv32.json"): - os.remove("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) @@ -23,7 +22,7 @@ for i in range(num_runs): 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], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) + 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: diff --git a/ice40/place_legaliser.cc b/ice40/place_legaliser.cc index ebc2b865..9fde179d 100644 --- a/ice40/place_legaliser.cc +++ b/ice40/place_legaliser.cc @@ -90,21 +90,20 @@ static int get_cell_evilness(const Context *ctx, const CellInfo *cell) // This returns how "evil" a logic cell is, and thus how likely it is to be ripped up // during logic tile legalisation int score = 0; - if (get_net_or_empty(cell, ctx->id("I0"))) + if (get_net_or_empty(cell, ctx->id_i0)) ++score; - if (get_net_or_empty(cell, ctx->id("I1"))) + if (get_net_or_empty(cell, ctx->id_i1)) ++score; - if (get_net_or_empty(cell, ctx->id("I2"))) + if (get_net_or_empty(cell, ctx->id_i2)) ++score; - if (get_net_or_empty(cell, ctx->id("I3"))) + if (get_net_or_empty(cell, ctx->id_i3)) ++score; - if (bool_or_default(cell->params, ctx->id("DFF_ENABLE"))) { - const NetInfo *cen = get_net_or_empty(cell, ctx->id("CEN")), *sr = get_net_or_empty(cell, ctx->id("SR")); - if (cen) + if (cell->lcInfo.dffEnable) { + if (cell->lcInfo.cen) score += 10; - if (sr) + if (cell->lcInfo.sr) score += 10; - if (bool_or_default(cell->params, ctx->id("NEG_CLK"))) + if (cell->lcInfo.negClk) score += 5; } return score;