Use bbasm to create iCE40 chipdb

Signed-off-by: Clifford Wolf <clifford@clifford.at>
This commit is contained in:
Clifford Wolf 2018-07-24 21:10:42 +02:00
parent 1b7b4ece06
commit c3859072d4
3 changed files with 231 additions and 284 deletions

View File

@ -3,8 +3,10 @@
#include <string> #include <string>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <unistd.h>
enum TokenType : int8_t enum TokenType : int8_t
{ {
@ -35,18 +37,48 @@ std::vector<std::string> preText, postText;
const char *skipWhitespace(const char *p) const char *skipWhitespace(const char *p)
{ {
if (p == nullptr)
return "";
while (*p == ' ' || *p == '\t') while (*p == ' ' || *p == '\t')
p++; p++;
return p; return p;
} }
int main() int main(int argc, char **argv)
{ {
bool verbose = true; bool verbose = false;
// bool bigEndian = false; bool bigEndian = false;
bool writeC = false;
char buffer[512]; 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"); std::string cmd = strtok(buffer, " \t\r\n");
@ -63,7 +95,7 @@ int main()
} }
if (cmd == "push") { if (cmd == "push") {
const char *p = strtok(buffer, " \t\r\n"); const char *p = strtok(nullptr, " \t\r\n");
if (streamIndex.count(p) == 0) { if (streamIndex.count(p) == 0) {
streamIndex[p] = streams.size(); streamIndex[p] = streams.size();
streams.resize(streams.size() + 1); streams.resize(streams.size() + 1);
@ -79,8 +111,8 @@ int main()
} }
if (cmd == "label" || cmd == "ref") { if (cmd == "label" || cmd == "ref") {
const char *label = strtok(buffer, " \t\r\n"); const char *label = strtok(nullptr, " \t\r\n");
const char *comment = skipWhitespace(strtok(buffer, "\r\n")); const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
Stream &s = streams.at(streamStack.back()); Stream &s = streams.at(streamStack.back());
if (labelIndex.count(label) == 0) { if (labelIndex.count(label) == 0) {
labelIndex[label] = labels.size(); labelIndex[label] = labels.size();
@ -94,8 +126,8 @@ int main()
} }
if (cmd == "u8" || cmd == "u16" || cmd == "u32") { if (cmd == "u8" || cmd == "u16" || cmd == "u32") {
const char *value = strtok(buffer, " \t\r\n"); const char *value = strtok(nullptr, " \t\r\n");
const char *comment = skipWhitespace(strtok(buffer, "\r\n")); const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
Stream &s = streams.at(streamStack.back()); Stream &s = streams.at(streamStack.back());
s.tokenTypes.push_back(cmd == "u8" ? TOK_U8 : cmd == "u16" ? TOK_U16 : TOK_U32); s.tokenTypes.push_back(cmd == "u8" ? TOK_U8 : cmd == "u16" ? TOK_U16 : TOK_U32);
s.tokenValues.push_back(atoll(value)); s.tokenValues.push_back(atoll(value));
@ -104,8 +136,8 @@ int main()
continue; continue;
} }
if (cmd == "s") { if (cmd == "str") {
const char *value = skipWhitespace(strtok(buffer, "\r\n")); const char *value = skipWhitespace(strtok(nullptr, "\r\n"));
std::string label = std::string("str:") + value; std::string label = std::string("str:") + value;
Stream &s = streams.at(streamStack.back()); Stream &s = streams.at(streamStack.back());
if (labelIndex.count(label) == 0) { if (labelIndex.count(label) == 0) {
@ -128,7 +160,13 @@ int main()
continue; 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()); assert(!streams.empty());
@ -137,5 +175,149 @@ int main()
streams.back().tokenTypes.swap(stringStream.tokenTypes); streams.back().tokenTypes.swap(stringStream.tokenTypes);
streams.back().tokenValues.swap(stringStream.tokenValues); streams.back().tokenValues.swap(stringStream.tokenValues);
int cursor = 0;
for (auto &s : streams) {
for (int i = 0; i < int(s.tokenTypes.size()); i++) {
switch (s.tokenTypes[i])
{
case TOK_LABEL:
labels[s.tokenValues[i]] = cursor;
break;
case TOK_REF:
cursor += 4;
break;
case TOK_U8:
cursor += 1;
break;
case TOK_U16:
assert(cursor % 2 == 0);
cursor += 2;
break;
case TOK_U32:
assert(cursor % 4 == 0);
cursor += 4;
break;
default:
assert(0);
}
}
}
if (verbose) {
printf("resolved positions for %d labels.\n", int(labels.size()));
printf("total data (including strings): %.2f MB\n", double(cursor) / (1024*1024));
}
std::vector<uint8_t> data(cursor);
cursor = 0;
for (auto &s : streams) {
for (int i = 0; i < int(s.tokenTypes.size()); i++) {
uint32_t value = s.tokenValues[i];
int numBytes = 0;
switch (s.tokenTypes[i])
{
case TOK_LABEL:
break;
case TOK_REF:
value = labels[value] - cursor;
numBytes = 4;
break;
case TOK_U8:
numBytes = 1;
break;
case TOK_U16:
numBytes = 2;
break;
case TOK_U32:
numBytes = 4;
break;
default:
assert(0);
}
if (bigEndian) {
switch (numBytes)
{
case 4:
data[cursor++] = value >> 24;
data[cursor++] = value >> 16;
/* fall-through */
case 2:
data[cursor++] = value >> 8;
/* fall-through */
case 1:
data[cursor++] = value;
/* fall-through */
case 0:
break;
default:
assert(0);
}
} else {
switch (numBytes)
{
case 4:
data[cursor+3] = value >> 24;
data[cursor+2] = value >> 16;
/* fall-through */
case 2:
data[cursor+1] = value >> 8;
/* fall-through */
case 1:
data[cursor] = value;
/* fall-through */
case 0:
break;
default:
assert(0);
}
cursor += numBytes;
}
}
}
assert(cursor == int(data.size()));
if (writeC) {
for (auto &s : preText)
fprintf(fileOut, "%s\n", s.c_str());
fprintf(fileOut, "const char %s[%d] =\n\"", streams[0].name.c_str(), int(data.size())+1);
cursor = 1;
for (auto d : data) {
if (cursor > 70) {
fputc('\"', fileOut);
fputc('\n', fileOut);
cursor = 0;
}
if (cursor == 0) {
fputc('\"', fileOut);
cursor = 1;
}
if (d < 32 || d >= 128) {
fprintf(fileOut, "\\%03o", int(d));
cursor += 4;
} else
if (d == '\"' || d == '\'' || d == '\\') {
fputc('\\', fileOut);
fputc(d, fileOut);
cursor += 2;
} else {
fputc(d, fileOut);
cursor++;
}
}
fprintf(fileOut, "\";\n");
for (auto &s : postText)
fprintf(fileOut, "%s\n", s.c_str());
} else {
fwrite(data.data(), int(data.size()), 1, fileOut);
}
return 0; return 0;
} }

View File

@ -6,17 +6,11 @@ import textwrap
import argparse import argparse
parser = argparse.ArgumentParser(description="convert ICE40 chip database") 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("filename", type=str, help="chipdb input filename")
parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc") parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc")
parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h") parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h")
args = parser.parse_args() args = parser.parse_args()
endianness = "le"
nodebug = True
dev_name = None dev_name = None
dev_width = None dev_width = None
dev_height = None dev_height = None
@ -668,266 +662,56 @@ for ec in sorted(extra_cells.keys()):
add_bel_ec(ec) add_bel_ec(ec)
class BinaryBlobAssembler: 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): def l(self, name, ltype = None, export = False):
assert not self.finalized if ltype is None:
assert name not in self.labels print("label %s" % (name,))
assert len(self.data) not in self.labels_byaddr else:
self.labels[name] = len(self.data) print("label %s %s" % (name, ltype))
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 r(self, name, comment): def r(self, name, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 4 == 0 print("ref %s" % (name,))
assert len(self.data) not in self.refs else:
if self.nodebug: print("ref %s %s" % (name, comment))
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)"
def s(self, s, comment): def s(self, s, comment):
assert not self.finalized print("str %s" % s)
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)
def u8(self, v, comment): def u8(self, v, comment):
assert not self.finalized if comment is None:
if self.nodebug: print("u8 %d" % (v,))
comment = None else:
self.data.append(v) print("u8 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def u16(self, v, comment): def u16(self, v, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 2 == 0 print("u16 %d" % (v,))
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)
else: else:
assert 0 print("u16 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def u32(self, v, comment): def u32(self, v, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 4 == 0 print("u32 %d" % (v,))
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)
else: else:
assert 0 print("u32 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def finalize(self): def pre(self, s):
assert not self.finalized print("pre %s" % s)
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 write_verbose_c(self, f, ctype = "const unsigned char"): def post(self, s):
assert self.finalized print("post %s" % s)
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 write_compact_c(self, f, ctype = "const unsigned char"): def push(self, name):
assert self.finalized print("push %s" % name)
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"): def pop(self):
assert self.finalized print("pop")
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"): bba = BinaryBlobAssembler()
assert self.finalized bba.pre('#include "nextpnr.h"')
assert self.data[len(self.data)-1] == 0 bba.pre('NEXTPNR_NAMESPACE_BEGIN')
print("%s %s[%d] =" % (ctype, self.cname, len(self.data)), file=f) bba.post('NEXTPNR_NAMESPACE_END')
print(" \"", end="", file=f) bba.push("chipdb_blob_%s" % dev_name)
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.r("chip_info_%s" % dev_name, "chip_info") bba.r("chip_info_%s" % dev_name, "chip_info")
index = 0 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("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.r("package_info_%s" % dev_name, "packages_data")
bba.finalize() bba.pop()
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')

View File

@ -24,11 +24,11 @@ if (MSVC)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB} 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} DEPENDS ${DEV_TXT_DB} ${DB_PY}
) )
add_custom_command(OUTPUT ${DEV_CC_DB} 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} DEPENDS bbasm ${DEV_CC_BBA_DB}
) )
target_sources(ice40_chipdb PRIVATE ${DEV_CC_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_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB} 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} COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY} DEPENDS ${DEV_TXT_DB} ${DB_PY}
) )
add_custom_command(OUTPUT ${DEV_CC_DB} 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} COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB} DEPENDS bbasm ${DEV_CC_BBA_DB}
) )