aboutsummaryrefslogtreecommitdiffstats
path: root/bba
diff options
context:
space:
mode:
authorClifford Wolf <cliffordvienna@gmail.com>2018-07-25 11:07:51 +0000
committerClifford Wolf <cliffordvienna@gmail.com>2018-07-25 11:07:51 +0000
commitf3dab003e7b9a417a490f341f7e4dd28d7417caf (patch)
tree0755eff0f628d039bbc47548bed959f6352c5556 /bba
parent9bcdf2000901a49d96b3ccd16fcb154ff7abc7b3 (diff)
parent1e71621f6517faca01f1351ec60dd51288e14ec8 (diff)
downloadnextpnr-f3dab003e7b9a417a490f341f7e4dd28d7417caf.tar.gz
nextpnr-f3dab003e7b9a417a490f341f7e4dd28d7417caf.tar.bz2
nextpnr-f3dab003e7b9a417a490f341f7e4dd28d7417caf.zip
Merge branch 'bba' into 'master'
bbasm See merge request SymbioticEDA/nextpnr!18
Diffstat (limited to 'bba')
-rw-r--r--bba/README.md70
-rw-r--r--bba/bba.cmake1
-rw-r--r--bba/main.cc357
3 files changed, 419 insertions, 9 deletions
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 <string>
+------------
+
+When a C file is generated as output, all the "pre" strings will be included
+before the binary blob.
+
+post <string>
+-------------
+
+When a C file is generated as output, all the "post" strings will be included
+after the binary blob.
+
+push <name>
+-----------
+
+All following commands up until the matching "pop" will be writen to stream
+<name>. Everything written to the same stream will end up in a continous
+region of the output.
+
+pop
+---
+
+End of a push..pop block.
+
+label <name> [<comment>]
+------------------------
+
+Add a label for the current position.
+
+ref <name> [<comment>]
+----------------------
+
+Add a 32-bit reference to the specified label. The reference will be a byte
+offset relative to the memory location of the reference itself.
+
+u8 <value> [<comment>]
+----------------------
+
+Add a 8-bit value to the binary blob.
+
+u16 <value> [<comment>]
+-----------------------
+
+Add a 16-bit value to the binary blob. Note that the input must be structured
+in a way that ensures that all u16 are aligned to 2-byte addresses.
+
+u32 <value> [<comment>]
+----------------------
+
+Add a 32-bit value to the binary blob. Note that the input must be structured
+in a way that ensures that all u32 are aligned to 4-byte addresses.
+
+str <string>
+------------
+
+Add a reference to a zero-terminated copy of the specified string.
diff --git a/bba/bba.cmake b/bba/bba.cmake
index 4bb9ea0e..3e094277 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_PROGRAM_OPTIONS_LIBRARY})
ENDIF(NOT CMAKE_CROSSCOMPILING)
IF(NOT CMAKE_CROSSCOMPILING)
diff --git a/bba/main.cc b/bba/main.cc
index 42b79504..cd09f804 100644
--- a/bba/main.cc
+++ b/bba/main.cc
@@ -1,15 +1,354 @@
-#include <unistd.h>
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
#include <assert.h>
-int main()
+#include <boost/program_options.hpp>
+#include <iostream>
+#include <map>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <vector>
+
+enum TokenType : int8_t
{
+ TOK_LABEL,
+ TOK_REF,
+ TOK_U8,
+ TOK_U16,
+ TOK_U32
+};
+
+struct Stream
+{
+ std::string name;
+ std::vector<TokenType> tokenTypes;
+ std::vector<uint32_t> tokenValues;
+ std::vector<std::string> tokenComments;
+};
+
+Stream stringStream;
+std::vector<Stream> streams;
+std::map<std::string, int> streamIndex;
+std::vector<int> streamStack;
+
+std::vector<int> labels;
+std::map<std::string, int> labelIndex;
+
+std::vector<std::string> preText, postText;
+
+const char *skipWhitespace(const char *p)
+{
+ if (p == nullptr)
+ return "";
+ while (*p == ' ' || *p == '\t')
+ p++;
+ return p;
+}
+
+int main(int argc, char **argv)
+{
+ bool verbose = false;
+ bool bigEndian = false;
+ bool writeC = false;
char buffer[512];
- int i, j;
- while (1) {
- i = read(0, buffer, 512);
- if (i == 0) break;
- assert(i > 0);
- j = write(1, buffer, i);
- assert(i == j);
+
+ namespace po = boost::program_options;
+ po::positional_options_description pos;
+ po::options_description options("Allowed options");
+ options.add_options()("v", "verbose output");
+ options.add_options()("b", "big endian");
+ options.add_options()("c", "write c strings");
+ options.add_options()("files", po::value<std::vector<std::string>>(), "file parameters");
+ pos.add("files", -1);
+
+ po::variables_map vm;
+ try {
+ po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run();
+
+ po::store(parsed, vm);
+
+ po::notify(vm);
+ } catch (std::exception &e) {
+ std::cout << e.what() << "\n";
+ return 1;
+ }
+ if (vm.count("v"))
+ verbose = true;
+ if (vm.count("b"))
+ bigEndian = true;
+ if (vm.count("c"))
+ writeC = true;
+
+ if (vm.count("files") == 0) {
+ printf("File parameters are mandatory\n");
+ exit(-1);
+ }
+ std::vector<std::string> files = vm["files"].as<std::vector<std::string>>();
+ if (files.size() != 2) {
+ printf("Input and output parameters must be set\n");
+ exit(-1);
+ }
+
+ FILE *fileIn = fopen(files.at(0).c_str(), "rt");
+ assert(fileIn != nullptr);
+
+ FILE *fileOut = fopen(files.at(1).c_str(), writeC ? "wt" : "wb");
+ assert(fileOut != nullptr);
+
+ while (fgets(buffer, 512, fileIn) != nullptr) {
+ std::string cmd = strtok(buffer, " \t\r\n");
+
+ if (cmd == "pre") {
+ const char *p = skipWhitespace(strtok(nullptr, "\r\n"));
+ preText.push_back(p);
+ continue;
+ }
+
+ if (cmd == "post") {
+ const char *p = skipWhitespace(strtok(nullptr, "\r\n"));
+ postText.push_back(p);
+ continue;
+ }
+
+ if (cmd == "push") {
+ const char *p = strtok(nullptr, " \t\r\n");
+ if (streamIndex.count(p) == 0) {
+ streamIndex[p] = streams.size();
+ streams.resize(streams.size() + 1);
+ streams.back().name = p;
+ }
+ streamStack.push_back(streamIndex.at(p));
+ continue;
+ }
+
+ if (cmd == "pop") {
+ streamStack.pop_back();
+ continue;
+ }
+
+ if (cmd == "label" || cmd == "ref") {
+ const char *label = strtok(nullptr, " \t\r\n");
+ const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
+ Stream &s = streams.at(streamStack.back());
+ if (labelIndex.count(label) == 0) {
+ labelIndex[label] = labels.size();
+ labels.push_back(-1);
+ }
+ s.tokenTypes.push_back(cmd == "label" ? TOK_LABEL : TOK_REF);
+ s.tokenValues.push_back(labelIndex.at(label));
+ if (verbose)
+ s.tokenComments.push_back(comment);
+ continue;
+ }
+
+ if (cmd == "u8" || cmd == "u16" || cmd == "u32") {
+ const char *value = strtok(nullptr, " \t\r\n");
+ const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
+ Stream &s = streams.at(streamStack.back());
+ s.tokenTypes.push_back(cmd == "u8" ? TOK_U8 : cmd == "u16" ? TOK_U16 : TOK_U32);
+ s.tokenValues.push_back(atoll(value));
+ if (verbose)
+ s.tokenComments.push_back(comment);
+ continue;
+ }
+
+ if (cmd == "str") {
+ const char *value = skipWhitespace(strtok(nullptr, "\r\n"));
+ std::string label = std::string("str:") + value;
+ Stream &s = streams.at(streamStack.back());
+ if (labelIndex.count(label) == 0) {
+ labelIndex[label] = labels.size();
+ labels.push_back(-1);
+ }
+ s.tokenTypes.push_back(TOK_REF);
+ s.tokenValues.push_back(labelIndex.at(label));
+ if (verbose)
+ s.tokenComments.push_back(value);
+ stringStream.tokenTypes.push_back(TOK_LABEL);
+ stringStream.tokenValues.push_back(labelIndex.at(label));
+ while (1) {
+ stringStream.tokenTypes.push_back(TOK_U8);
+ stringStream.tokenValues.push_back(*value);
+ if (*value == 0)
+ break;
+ value++;
+ }
+ continue;
+ }
+
+ assert(0);
+ }
+
+ if (verbose) {
+ printf("Constructed %d streams:\n", int(streams.size()));
+ for (auto &s : streams)
+ printf(" stream '%s' with %d tokens\n", s.name.c_str(), int(s.tokenTypes.size()));
+ }
+
+ assert(!streams.empty());
+ assert(streamStack.empty());
+ streams.push_back(Stream());
+ streams.back().tokenTypes.swap(stringStream.tokenTypes);
+ streams.back().tokenValues.swap(stringStream.tokenValues);
+
+ int cursor = 0;
+ for (auto &s : streams) {
+ for (int i = 0; i < int(s.tokenTypes.size()); i++) {
+ switch (s.tokenTypes[i]) {
+ case TOK_LABEL:
+ labels[s.tokenValues[i]] = cursor;
+ break;
+ case TOK_REF:
+ cursor += 4;
+ break;
+ case TOK_U8:
+ cursor += 1;
+ break;
+ case TOK_U16:
+ assert(cursor % 2 == 0);
+ cursor += 2;
+ break;
+ case TOK_U32:
+ assert(cursor % 4 == 0);
+ cursor += 4;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+
+ if (verbose) {
+ printf("resolved positions for %d labels.\n", int(labels.size()));
+ printf("total data (including strings): %.2f MB\n", double(cursor) / (1024 * 1024));
+ }
+
+ std::vector<uint8_t> data(cursor);
+
+ cursor = 0;
+ for (auto &s : streams) {
+ for (int i = 0; i < int(s.tokenTypes.size()); i++) {
+ uint32_t value = s.tokenValues[i];
+ int numBytes = 0;
+
+ switch (s.tokenTypes[i]) {
+ case TOK_LABEL:
+ break;
+ case TOK_REF:
+ value = labels[value] - cursor;
+ numBytes = 4;
+ break;
+ case TOK_U8:
+ numBytes = 1;
+ break;
+ case TOK_U16:
+ numBytes = 2;
+ break;
+ case TOK_U32:
+ numBytes = 4;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (bigEndian) {
+ switch (numBytes) {
+ case 4:
+ data[cursor++] = value >> 24;
+ data[cursor++] = value >> 16;
+ /* fall-through */
+ case 2:
+ data[cursor++] = value >> 8;
+ /* fall-through */
+ case 1:
+ data[cursor++] = value;
+ /* fall-through */
+ case 0:
+ break;
+ default:
+ assert(0);
+ }
+ } else {
+ switch (numBytes) {
+ case 4:
+ data[cursor + 3] = value >> 24;
+ data[cursor + 2] = value >> 16;
+ /* fall-through */
+ case 2:
+ data[cursor + 1] = value >> 8;
+ /* fall-through */
+ case 1:
+ data[cursor] = value;
+ /* fall-through */
+ case 0:
+ break;
+ default:
+ assert(0);
+ }
+ cursor += numBytes;
+ }
+ }
+ }
+
+ assert(cursor == int(data.size()));
+
+ if (writeC) {
+ for (auto &s : preText)
+ fprintf(fileOut, "%s\n", s.c_str());
+
+ fprintf(fileOut, "const char %s[%d] =\n\"", streams[0].name.c_str(), int(data.size()) + 1);
+
+ cursor = 1;
+ for (auto d : data) {
+ if (cursor > 70) {
+ fputc('\"', fileOut);
+ fputc('\n', fileOut);
+ cursor = 0;
+ }
+ if (cursor == 0) {
+ fputc('\"', fileOut);
+ cursor = 1;
+ }
+ if (d < 32 || d >= 128) {
+ fprintf(fileOut, "\\%03o", int(d));
+ cursor += 4;
+ } else if (d == '\"' || d == '\'' || d == '\\') {
+ fputc('\\', fileOut);
+ fputc(d, fileOut);
+ cursor += 2;
+ } else {
+ fputc(d, fileOut);
+ cursor++;
+ }
+ }
+
+ fprintf(fileOut, "\";\n");
+
+ for (auto &s : postText)
+ fprintf(fileOut, "%s\n", s.c_str());
+ } else {
+ fwrite(data.data(), int(data.size()), 1, fileOut);
}
+
return 0;
}