| // |
| // Copyright (C) 2016 Clifford Wolf <clifford@clifford.at> |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| // |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <stdint.h> |
| #include <sys/time.h> |
| |
| #include <map> |
| #include <vector> |
| #include <string> |
| #include <fstream> |
| #include <iostream> |
| |
| #ifdef __EMSCRIPTEN__ |
| #include <emscripten.h> |
| #endif |
| |
| using std::map; |
| using std::pair; |
| using std::vector; |
| using std::string; |
| using std::ifstream; |
| using std::getline; |
| |
| uint64_t x; |
| uint64_t xorshift64star(void) { |
| x ^= x >> 12; // a |
| x ^= x << 25; // b |
| x ^= x >> 27; // c |
| return x * UINT64_C(2685821657736338717); |
| } |
| |
| void push_back_bitvector(vector<vector<bool>> &hexfile, const vector<int> &digits) |
| { |
| if (digits.empty()) |
| return; |
| |
| hexfile.push_back(vector<bool>(digits.size() * 4)); |
| |
| for (int i = 0; i < int(digits.size()) * 4; i++) |
| if ((digits.at(digits.size() - i/4 -1) & (1 << (i%4))) != 0) |
| hexfile.back().at(i) = true; |
| } |
| |
| void parse_hexfile_line(const char *filename, int linenr, vector<vector<bool>> &hexfile, string &line) |
| { |
| vector<int> digits; |
| |
| for (char c : line) { |
| if ('0' <= c && c <= '9') |
| digits.push_back(c - '0'); |
| else if ('a' <= c && c <= 'f') |
| digits.push_back(10 + c - 'a'); |
| else if ('A' <= c && c <= 'F') |
| digits.push_back(10 + c - 'A'); |
| else if ('x' == c || 'X' == c || |
| 'z' == c || 'Z' == c) |
| digits.push_back(0); |
| else if ('_' == c) |
| ; |
| else if (' ' == c || '\t' == c || '\r' == c) { |
| push_back_bitvector(hexfile, digits); |
| digits.clear(); |
| } else goto error; |
| } |
| |
| push_back_bitvector(hexfile, digits); |
| |
| return; |
| |
| error: |
| fprintf(stderr, "Can't parse line %d of %s: %s\n", linenr, filename, line.c_str()); |
| exit(1); |
| } |
| |
| void help(const char *cmd) |
| { |
| printf("\n"); |
| printf("Usage: %s [options] <from_hexfile> <to_hexfile>\n", cmd); |
| printf(" %s [options] -g [-s <seed>] <width> <depth>\n", cmd); |
| printf("\n"); |
| printf("Replace BRAM initialization data in a .asc file. This can be used\n"); |
| printf("for example to replace firmware images without re-running synthesis\n"); |
| printf("and place&route.\n"); |
| printf("\n"); |
| printf(" -g\n"); |
| printf(" generate a hex file with random contents.\n"); |
| printf(" use this to generate the hex file used during synthesis, then\n"); |
| printf(" use the same file as <from_hexfile> later.\n"); |
| printf("\n"); |
| printf(" -s <seed>\n"); |
| printf(" seed random generator with fixed value.\n"); |
| printf("\n"); |
| printf(" -v\n"); |
| printf(" verbose output\n"); |
| printf("\n"); |
| exit(1); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| #ifdef __EMSCRIPTEN__ |
| EM_ASM( |
| if (ENVIRONMENT_IS_NODE) |
| { |
| FS.mkdir('/hostcwd'); |
| FS.mount(NODEFS, { root: '.' }, '/hostcwd'); |
| FS.mkdir('/hostfs'); |
| FS.mount(NODEFS, { root: '/' }, '/hostfs'); |
| } |
| ); |
| #endif |
| |
| bool verbose = false; |
| bool generate = false; |
| bool seed = false; |
| uint32_t seed_nr = getpid(); |
| |
| int opt; |
| while ((opt = getopt(argc, argv, "vgs:")) != -1) |
| { |
| switch (opt) |
| { |
| case 'v': |
| verbose = true; |
| break; |
| case 'g': |
| generate = true; |
| break; |
| case 's': |
| seed = true; |
| seed_nr = atoi(optarg); |
| break; |
| default: |
| help(argv[0]); |
| } |
| } |
| |
| if (generate) |
| { |
| if (optind+2 != argc) |
| help(argv[0]); |
| |
| int width = atoi(argv[optind]); |
| int depth = atoi(argv[optind+1]); |
| |
| if (width <= 0 || width % 4 != 0) { |
| fprintf(stderr, "Hexfile width (%d bits) is not divisible by 4 or nonpositive!\n", width); |
| exit(1); |
| } |
| |
| if (depth <= 0 || depth % 256 != 0) { |
| fprintf(stderr, "Hexfile number of words (%d) is not divisible by 256 or nonpositive!\n", depth); |
| exit(1); |
| } |
| |
| if (verbose && seed) |
| fprintf(stderr, "Seed: %d\n", seed_nr); |
| |
| x = uint64_t(seed_nr) << 32; |
| x ^= uint64_t(depth) << 16; |
| x ^= uint64_t(width) << 10; |
| |
| xorshift64star(); |
| xorshift64star(); |
| xorshift64star(); |
| |
| if (!seed){ |
| struct timeval tv; |
| gettimeofday(&tv, NULL); |
| x ^= uint64_t(tv.tv_sec) << 20; |
| x ^= uint64_t(tv.tv_usec); |
| } |
| else { |
| x ^= uint64_t(seed) << 20; |
| x ^= uint64_t(seed); |
| } |
| |
| xorshift64star(); |
| xorshift64star(); |
| xorshift64star(); |
| |
| for (int i = 0; i < depth; i++) { |
| for (int j = 0; j < width / 4; j++) { |
| int digit = xorshift64star() & 15; |
| std::cout << "0123456789abcdef"[digit]; |
| } |
| std::cout << std::endl; |
| } |
| |
| exit(0); |
| } |
| |
| if (optind+2 != argc) |
| help(argv[0]); |
| |
| |
| // ------------------------------------------------------- |
| // Load from_hexfile and to_hexfile |
| |
| const char *from_hexfile_n = argv[optind]; |
| ifstream from_hexfile_f(from_hexfile_n); |
| vector<vector<bool>> from_hexfile; |
| |
| const char *to_hexfile_n = argv[optind+1]; |
| ifstream to_hexfile_f(to_hexfile_n); |
| vector<vector<bool>> to_hexfile; |
| |
| string line; |
| |
| for (int i = 1; getline(from_hexfile_f, line); i++) |
| parse_hexfile_line(from_hexfile_n, i, from_hexfile, line); |
| |
| for (int i = 1; getline(to_hexfile_f, line); i++) |
| parse_hexfile_line(to_hexfile_n, i, to_hexfile, line); |
| |
| if (to_hexfile.size() > 0 && from_hexfile.size() > to_hexfile.size()) { |
| if (verbose) |
| fprintf(stderr, "Padding to_hexfile from %d words to %d\n", |
| int(to_hexfile.size()), int(from_hexfile.size())); |
| do |
| to_hexfile.push_back(vector<bool>(to_hexfile.at(0).size())); |
| while (from_hexfile.size() > to_hexfile.size()); |
| } |
| |
| if (from_hexfile.size() != to_hexfile.size()) { |
| fprintf(stderr, "Hexfiles have different number of words! (%d vs. %d)\n", int(from_hexfile.size()), int(to_hexfile.size())); |
| exit(1); |
| } |
| |
| if (from_hexfile.size() % 256 != 0) { |
| fprintf(stderr, "Hexfile number of words (%d) is not divisible by 256!\n", int(from_hexfile.size())); |
| exit(1); |
| } |
| |
| for (size_t i = 1; i < from_hexfile.size(); i++) |
| if (from_hexfile.at(i-1).size() != from_hexfile.at(i).size()) { |
| fprintf(stderr, "Inconsistent word width at line %d of %s!\n", int(i), from_hexfile_n); |
| exit(1); |
| } |
| |
| for (size_t i = 1; i < to_hexfile.size(); i++) { |
| while (to_hexfile.at(i-1).size() > to_hexfile.at(i).size()) |
| to_hexfile.at(i).push_back(false); |
| if (to_hexfile.at(i-1).size() != to_hexfile.at(i).size()) { |
| fprintf(stderr, "Inconsistent word width at line %d of %s!\n", int(i+1), to_hexfile_n); |
| exit(1); |
| } |
| } |
| |
| if (from_hexfile.size() == 0 || from_hexfile.at(0).size() == 0) { |
| fprintf(stderr, "Empty from/to hexfiles!\n"); |
| exit(1); |
| } |
| |
| if (verbose) |
| fprintf(stderr, "Loaded pattern for %d bits wide and %d words deep memory.\n", int(from_hexfile.at(0).size()), int(from_hexfile.size())); |
| |
| |
| // ------------------------------------------------------- |
| // Create bitslices from pattern data |
| |
| map<vector<bool>, pair<vector<bool>, int>> pattern; |
| |
| for (int i = 0; i < int(from_hexfile.at(0).size()); i++) |
| { |
| vector<bool> pattern_from, pattern_to; |
| |
| for (int j = 0; j < int(from_hexfile.size()); j++) |
| { |
| pattern_from.push_back(from_hexfile.at(j).at(i)); |
| pattern_to.push_back(to_hexfile.at(j).at(i)); |
| |
| if (pattern_from.size() == 256) { |
| if (pattern.count(pattern_from)) { |
| fprintf(stderr, "Conflicting from pattern for bit slice from_hexfile[%d:%d][%d]!\n", j, j-255, i); |
| exit(1); |
| } |
| pattern[pattern_from] = std::make_pair(pattern_to, 0); |
| pattern_from.clear(), pattern_to.clear(); |
| } |
| } |
| |
| assert(pattern_from.empty()); |
| assert(pattern_to.empty()); |
| } |
| |
| if (verbose) |
| fprintf(stderr, "Extracted %d bit slices from from/to hexfile data.\n", int(pattern.size())); |
| |
| |
| // ------------------------------------------------------- |
| // Read ascfile from stdin |
| |
| vector<string> ascfile_lines; |
| map<string, vector<vector<bool>>> ascfile_hexdata; |
| |
| for (int i = 1; getline(std::cin, line); i++) |
| { |
| next_asc_stmt: |
| ascfile_lines.push_back(line); |
| |
| if (line.substr(0, 9) == ".ram_data") |
| { |
| auto &hexdata = ascfile_hexdata[line]; |
| |
| for (; getline(std::cin, line); i++) { |
| if (line.substr(0, 1) == ".") |
| goto next_asc_stmt; |
| parse_hexfile_line("stdin", i, hexdata, line); |
| } |
| } |
| } |
| |
| if (verbose) |
| fprintf(stderr, "Found %d initialized bram cells in asc file.\n", int(ascfile_hexdata.size())); |
| |
| |
| // ------------------------------------------------------- |
| // Replace bram data |
| |
| int max_replace_cnt = 0; |
| |
| for (auto &bram_it : ascfile_hexdata) |
| { |
| auto &bram_data = bram_it.second; |
| |
| for (int i = 0; i < 16; i++) |
| { |
| vector<bool> from_bitslice; |
| |
| for (int j = 0; j < 256; j++) |
| from_bitslice.push_back(bram_data.at(j / 16).at(16 * (j % 16) + i)); |
| |
| auto p = pattern.find(from_bitslice); |
| if (p != pattern.end()) |
| { |
| auto &to_bitslice = p->second.first; |
| |
| for (int j = 0; j < 256; j++) |
| bram_data.at(j / 16).at(16 * (j % 16) + i) = to_bitslice.at(j); |
| |
| max_replace_cnt = std::max(++p->second.second, max_replace_cnt); |
| } |
| } |
| } |
| |
| int min_replace_cnt = max_replace_cnt; |
| for (auto &it : pattern) |
| min_replace_cnt = std::min(min_replace_cnt, it.second.second); |
| |
| if (min_replace_cnt != max_replace_cnt) { |
| fprintf(stderr, "Found some bitslices up to %d times, others only %d times!\n", max_replace_cnt, min_replace_cnt); |
| exit(1); |
| } |
| |
| if (verbose) |
| fprintf(stderr, "Found and replaced %d instances of the memory.\n", max_replace_cnt); |
| |
| |
| // ------------------------------------------------------- |
| // Write ascfile to stdout |
| |
| for (size_t i = 0; i < ascfile_lines.size(); i++) { |
| auto &line = ascfile_lines.at(i); |
| std::cout << line << std::endl; |
| if (ascfile_hexdata.count(line)) { |
| for (auto &word : ascfile_hexdata.at(line)) { |
| for (int k = word.size()-4; k >= 0; k -= 4) { |
| int digit = (word[k+3] ? 8 : 0) + (word[k+2] ? 4 : 0) + (word[k+1] ? 2 : 0) + (word[k] ? 1 : 0); |
| std::cout << "0123456789abcdef"[digit]; |
| } |
| std::cout << std::endl; |
| } |
| } |
| } |
| |
| return 0; |
| } |