From 1b7b4ece06645c5d98b009b9a53e368817163c4b Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 17:59:20 +0200 Subject: [PATCH 1/9] Add bba parser Signed-off-by: Clifford Wolf --- bba/main.cc | 142 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 8 deletions(-) diff --git a/bba/main.cc b/bba/main.cc index 42b79504..3ef4c1ca 100644 --- a/bba/main.cc +++ b/bba/main.cc @@ -1,15 +1,141 @@ -#include +#include +#include +#include +#include +#include +#include #include + +enum TokenType : int8_t +{ + TOK_LABEL, + TOK_REF, + TOK_U8, + TOK_U16, + TOK_U32 +}; + +struct Stream +{ + std::string name; + std::vector tokenTypes; + std::vector tokenValues; + std::vector tokenComments; +}; + +Stream stringStream; +std::vector streams; +std::map streamIndex; +std::vector streamStack; + +std::vector labels; +std::map labelIndex; + +std::vector preText, postText; + +const char *skipWhitespace(const char *p) +{ + while (*p == ' ' || *p == '\t') + p++; + return p; +} + int main() { + bool verbose = true; + // bool bigEndian = 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); + + while (fgets(buffer, 512, stdin) != 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(buffer, " \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(buffer, " \t\r\n"); + const char *comment = skipWhitespace(strtok(buffer, "\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(buffer, " \t\r\n"); + const char *comment = skipWhitespace(strtok(buffer, "\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 == "s") { + const char *value = skipWhitespace(strtok(buffer, "\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; + } + + abort(); } + + assert(!streams.empty()); + assert(streamStack.empty()); + streams.push_back(Stream()); + streams.back().tokenTypes.swap(stringStream.tokenTypes); + streams.back().tokenValues.swap(stringStream.tokenValues); + return 0; } From c3859072d4e621186e7b4ded3d2311ce0e8d31ba Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 21:10:42 +0200 Subject: [PATCH 2/9] Use bbasm to create iCE40 chipdb Signed-off-by: Clifford Wolf --- bba/main.cc | 206 +++++++++++++++++++++++++++++-- ice40/chipdb.py | 301 +++++---------------------------------------- ice40/family.cmake | 8 +- 3 files changed, 231 insertions(+), 284 deletions(-) diff --git a/bba/main.cc b/bba/main.cc index 3ef4c1ca..53c70b83 100644 --- a/bba/main.cc +++ b/bba/main.cc @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include enum TokenType : int8_t { @@ -35,18 +37,48 @@ std::vector preText, postText; const char *skipWhitespace(const char *p) { + if (p == nullptr) + return ""; while (*p == ' ' || *p == '\t') p++; return p; } -int main() +int main(int argc, char **argv) { - bool verbose = true; - // bool bigEndian = false; + bool verbose = false; + bool bigEndian = false; + bool writeC = false; char buffer[512]; - while (fgets(buffer, 512, stdin) != nullptr) + int opt; + while ((opt = getopt(argc, argv, "vbc")) != -1) + { + switch (opt) + { + case 'v': + verbose = true; + break; + case 'b': + bigEndian = true; + break; + case 'c': + writeC = true; + break; + default: + assert(0); + } + } + + assert(optind+2 == argc); + + FILE *fileIn = fopen(argv[optind], "rt"); + assert(fileIn != nullptr); + + FILE *fileOut = fopen(argv[optind+1], writeC ? "wt" : "wb"); + assert(fileOut != nullptr); + + while (fgets(buffer, 512, fileIn) != nullptr) { std::string cmd = strtok(buffer, " \t\r\n"); @@ -63,7 +95,7 @@ int main() } if (cmd == "push") { - const char *p = strtok(buffer, " \t\r\n"); + const char *p = strtok(nullptr, " \t\r\n"); if (streamIndex.count(p) == 0) { streamIndex[p] = streams.size(); streams.resize(streams.size() + 1); @@ -79,8 +111,8 @@ int main() } if (cmd == "label" || cmd == "ref") { - const char *label = strtok(buffer, " \t\r\n"); - const char *comment = skipWhitespace(strtok(buffer, "\r\n")); + 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(); @@ -94,8 +126,8 @@ int main() } if (cmd == "u8" || cmd == "u16" || cmd == "u32") { - const char *value = strtok(buffer, " \t\r\n"); - const char *comment = skipWhitespace(strtok(buffer, "\r\n")); + 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)); @@ -104,8 +136,8 @@ int main() continue; } - if (cmd == "s") { - const char *value = skipWhitespace(strtok(buffer, "\r\n")); + 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) { @@ -128,7 +160,13 @@ int main() continue; } - abort(); + 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()); @@ -137,5 +175,149 @@ int main() 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 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; } diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 108197c1..b5fee359 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -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() diff --git a/ice40/family.cmake b/ice40/family.cmake index 95cdf331..374c62eb 100644 --- a/ice40/family.cmake +++ b/ice40/family.cmake @@ -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} ) From 8b60ed5fd1bda845b4d3f8ac727f5b285713ff0e Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 25 Jul 2018 09:31:44 +0200 Subject: [PATCH 3/9] Fix bba to compile on windows (no unistd there) --- bba/bba.cmake | 1 + bba/main.cc | 97 ++++++++++++++++++++++++++++----------------------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/bba/bba.cmake b/bba/bba.cmake index 4bb9ea0e..dd803d5d 100644 --- a/bba/bba.cmake +++ b/bba/bba.cmake @@ -5,6 +5,7 @@ ENDIF(CMAKE_CROSSCOMPILING) IF(NOT CMAKE_CROSSCOMPILING) ADD_EXECUTABLE(bbasm bba/main.cc) + target_link_libraries(bbasm LINK_PUBLIC ${Boost_LIBRARIES}) ENDIF(NOT CMAKE_CROSSCOMPILING) IF(NOT CMAKE_CROSSCOMPILING) diff --git a/bba/main.cc b/bba/main.cc index 53c70b83..ee6129ac 100644 --- a/bba/main.cc +++ b/bba/main.cc @@ -1,12 +1,13 @@ +#include +#include +#include #include -#include -#include +#include #include #include -#include #include -#include -#include +#include +#include enum TokenType : int8_t { @@ -51,35 +52,50 @@ int main(int argc, char **argv) bool writeC = false; char buffer[512]; - int opt; - while ((opt = getopt(argc, argv, "vbc")) != -1) - { - switch (opt) - { - case 'v': - verbose = true; - break; - case 'b': - bigEndian = true; - break; - case 'c': - writeC = true; - break; - default: - assert(0); - } + 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>(), "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 files = vm["files"].as>(); + if (files.size() != 2) { + printf("Input and output parameters must be set\n"); + exit(-1); } - assert(optind+2 == argc); - - FILE *fileIn = fopen(argv[optind], "rt"); + FILE *fileIn = fopen(files.at(0).c_str(), "rt"); assert(fileIn != nullptr); - FILE *fileOut = fopen(argv[optind+1], writeC ? "wt" : "wb"); + FILE *fileOut = fopen(files.at(1).c_str(), writeC ? "wt" : "wb"); assert(fileOut != nullptr); - while (fgets(buffer, 512, fileIn) != nullptr) - { + while (fgets(buffer, 512, fileIn) != nullptr) { std::string cmd = strtok(buffer, " \t\r\n"); if (cmd == "pre") { @@ -178,8 +194,7 @@ int main(int argc, char **argv) int cursor = 0; for (auto &s : streams) { for (int i = 0; i < int(s.tokenTypes.size()); i++) { - switch (s.tokenTypes[i]) - { + switch (s.tokenTypes[i]) { case TOK_LABEL: labels[s.tokenValues[i]] = cursor; break; @@ -205,7 +220,7 @@ int main(int argc, char **argv) if (verbose) { printf("resolved positions for %d labels.\n", int(labels.size())); - printf("total data (including strings): %.2f MB\n", double(cursor) / (1024*1024)); + printf("total data (including strings): %.2f MB\n", double(cursor) / (1024 * 1024)); } std::vector data(cursor); @@ -216,8 +231,7 @@ int main(int argc, char **argv) uint32_t value = s.tokenValues[i]; int numBytes = 0; - switch (s.tokenTypes[i]) - { + switch (s.tokenTypes[i]) { case TOK_LABEL: break; case TOK_REF: @@ -238,8 +252,7 @@ int main(int argc, char **argv) } if (bigEndian) { - switch (numBytes) - { + switch (numBytes) { case 4: data[cursor++] = value >> 24; data[cursor++] = value >> 16; @@ -256,14 +269,13 @@ int main(int argc, char **argv) assert(0); } } else { - switch (numBytes) - { + switch (numBytes) { case 4: - data[cursor+3] = value >> 24; - data[cursor+2] = value >> 16; + data[cursor + 3] = value >> 24; + data[cursor + 2] = value >> 16; /* fall-through */ case 2: - data[cursor+1] = value >> 8; + data[cursor + 1] = value >> 8; /* fall-through */ case 1: data[cursor] = value; @@ -284,7 +296,7 @@ int main(int argc, char **argv) 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); + fprintf(fileOut, "const char %s[%d] =\n\"", streams[0].name.c_str(), int(data.size()) + 1); cursor = 1; for (auto d : data) { @@ -300,8 +312,7 @@ int main(int argc, char **argv) if (d < 32 || d >= 128) { fprintf(fileOut, "\\%03o", int(d)); cursor += 4; - } else - if (d == '\"' || d == '\'' || d == '\\') { + } else if (d == '\"' || d == '\'' || d == '\\') { fputc('\\', fileOut); fputc(d, fileOut); cursor += 2; From 5e58a329e6826fe6262077c1276ecf375b014f92 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 25 Jul 2018 09:32:13 +0200 Subject: [PATCH 4/9] Make thread check portable --- common/nextpnr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 3ba4f3b3..e89512f2 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -23,10 +23,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -344,7 +344,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 @@ -377,12 +377,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(); } From 6b8b067b1a7f0ae165c79e64558f5e0646574457 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 25 Jul 2018 09:37:22 +0200 Subject: [PATCH 5/9] Fix for zero length case --- gui/fpgaviewwidget.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 7ccb3445..15f37ce0 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -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(); From e3ce2f544d8a5ea3e23f6c7b43232bf2dfcee19c Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 25 Jul 2018 11:58:23 +0200 Subject: [PATCH 6/9] noreturn have to be void, so there is no UB --- common/nextpnr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index e89512f2..2eb46bd3 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -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__)) From 8a498c501f3e86dbb41423b11d2af28c46a3435d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 25 Jul 2018 12:08:13 +0200 Subject: [PATCH 7/9] just Boost_PROGRAM_OPTIONS_LIBRARY --- bba/bba.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bba/bba.cmake b/bba/bba.cmake index dd803d5d..3e094277 100644 --- a/bba/bba.cmake +++ b/bba/bba.cmake @@ -5,7 +5,7 @@ ENDIF(CMAKE_CROSSCOMPILING) IF(NOT CMAKE_CROSSCOMPILING) ADD_EXECUTABLE(bbasm bba/main.cc) - target_link_libraries(bbasm LINK_PUBLIC ${Boost_LIBRARIES}) + target_link_libraries(bbasm LINK_PUBLIC ${Boost_PROGRAM_OPTIONS_LIBRARY}) ENDIF(NOT CMAKE_CROSSCOMPILING) IF(NOT CMAKE_CROSSCOMPILING) From 6ac2a4de4829fcda0f397a482967d4bf68ba9620 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 25 Jul 2018 12:13:06 +0200 Subject: [PATCH 8/9] proper options for linux build --- ice40/family.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ice40/family.cmake b/ice40/family.cmake index 374c62eb..75061f44 100644 --- a/ice40/family.cmake +++ b/ice40/family.cmake @@ -51,7 +51,7 @@ else() DEPENDS ${DEV_TXT_DB} ${DB_PY} ) add_custom_command(OUTPUT ${DEV_CC_DB} - COMMAND bbasm -c ${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} ) From 1e71621f6517faca01f1351ec60dd51288e14ec8 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 25 Jul 2018 13:06:21 +0200 Subject: [PATCH 9/9] Add bba README Signed-off-by: Clifford Wolf --- bba/README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ bba/main.cc | 20 +++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 bba/README.md diff --git a/bba/README.md b/bba/README.md new file mode 100644 index 00000000..7e64b582 --- /dev/null +++ b/bba/README.md @@ -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 +------------ + +When a C file is generated as output, all the "pre" strings will be included +before the binary blob. + +post +------------- + +When a C file is generated as output, all the "post" strings will be included +after the binary blob. + +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. + +pop +--- + +End of a push..pop block. + +label [] +------------------------ + +Add a label for the current position. + +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 [] +---------------------- + +Add a 8-bit value to the binary blob. + +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 [] +---------------------- + +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 +------------ + +Add a reference to a zero-terminated copy of the specified string. diff --git a/bba/main.cc b/bba/main.cc index ee6129ac..cd09f804 100644 --- a/bba/main.cc +++ b/bba/main.cc @@ -1,3 +1,23 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 #include #include