From cd9add540d19be75467865fa02be74534814c315 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Tue, 14 Feb 2023 14:02:58 +0100 Subject: icebram: Complete rewrite to cleanup and make it work Signed-off-by: Sylvain Munaut --- icebram/icebram.cc | 842 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 570 insertions(+), 272 deletions(-) diff --git a/icebram/icebram.cc b/icebram/icebram.cc index 9d27ca6..86ca11e 100644 --- a/icebram/icebram.cc +++ b/icebram/icebram.cc @@ -1,5 +1,6 @@ // // Copyright (C) 2016 Clifford Wolf +// Copyright (C) 2023 Sylvain Munaut // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -14,54 +15,124 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // + #include +#include #include #include -#include -#include -#include #include +#include -#include -#include -#include +#include #include #include +#include +#include +#include +#include #ifdef __EMSCRIPTEN__ #include #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> &hexfile, const vector &digits) + +struct app_opts { + char *prog; + + int extra_argc; + char **extra_argv; + + bool generate; + bool verbose; + uint32_t seed_nr; + bool seed; +}; + +static void help(const char *cmd); + + +// --------------------------------------------------------------------------- +// Update mode +// --------------------------------------------------------------------------- + + +// Hex Data File +// ------------- + +class HexFile { - if (digits.empty()) - return; +private: + std::vector> m_data; + size_t m_word_size; + + std::vector parse_digits(std::vector &digits) const; + void parse_line(std::string &line); +public: + HexFile(const char *filename, bool pad_words); + virtual ~HexFile() { }; + + void pad_words_to(size_t size); + void pad_to(size_t size); + + size_t size() const { return this->m_data.size(); }; + size_t word_size() const { return this->m_word_size; }; + + std::map, std::pair, int>> generate_pattern(HexFile &to) const; +}; + +HexFile::HexFile(const char *filename, bool pad_words=false) +{ + std::ifstream stream(filename); + + if (!stream.is_open()) { + fprintf(stderr, "Failed to open file %s\n", filename); + throw std::runtime_error("Unable to open input file"); + } + + // Parse file + std::string line; + for (int i=1; std::getline(stream, line); i++) + try { + this->parse_line(line); + } catch (std::exception &e) { + fprintf(stderr, "Can't parse line %d of %s: %s\n", i, filename, line.c_str()); + throw std::runtime_error("Invalid input file"); + } - hexfile.push_back(vector(digits.size() * 4)); + // Check word size + this->m_word_size = this->m_data.at(0).size(); + + for (auto &w : this->m_data) + { + if ((w.size() != this->m_word_size) && !pad_words) { + fprintf(stderr, "Inconsistent word sizes in %s\n", filename); + throw std::runtime_error("Invalid input file"); + } + if (w.size() > this->m_word_size) + this->m_word_size = w.size(); + } + + // If requested, pad them + this->pad_words_to(this->m_word_size); +} + +std::vector +HexFile::parse_digits(std::vector &digits) const +{ + std::vector line_data(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; + line_data.at(i) = true; + + return line_data; } -void parse_hexfile_line(const char *filename, int linenr, vector> &hexfile, string &line) +void +HexFile::parse_line(std::string &line) { - vector digits; + std::vector digits; for (char c : line) { if ('0' <= c && c <= '9') @@ -76,335 +147,562 @@ void parse_hexfile_line(const char *filename, int linenr, vector> & else if ('_' == c) ; else if (' ' == c || '\t' == c || '\r' == c) { - push_back_bitvector(hexfile, digits); - digits.clear(); - } else goto error; + if (digits.size()) { + this->m_data.push_back(this->parse_digits(digits)); + digits.clear(); + } + } else { + throw std::runtime_error("Invalid char"); + } } - push_back_bitvector(hexfile, digits); + if (digits.size()) + this->m_data.push_back(this->parse_digits(digits)); +} + +void +HexFile::pad_words_to(size_t size) +{ + if (this->m_word_size > size) + return; - return; + for (auto &w : this->m_data) + if (w.size() < size) + w.resize(size, false); -error: - fprintf(stderr, "Can't parse line %d of %s: %s\n", linenr, filename, line.c_str()); - exit(1); + this->m_word_size = size; } -void help(const char *cmd) +void +HexFile::pad_to(size_t size) { - printf("\n"); - printf("Usage: %s [options] \n", cmd); - printf(" %s [options] -g [-s ] \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 later.\n"); - printf("\n"); - printf(" -s \n"); - printf(" seed random generator with fixed value.\n"); - printf("\n"); - printf(" -v\n"); - printf(" verbose output\n"); - printf("\n"); - exit(1); + while (this->m_data.size() < size) + this->m_data.push_back(std::vector(this->m_word_size)); } -int main(int argc, char **argv) +std::map, std::pair, int>> +HexFile::generate_pattern(HexFile &to) const { -#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_opt = 0; + std::map, std::pair, int>> pattern; - int opt; - while ((opt = getopt(argc, argv, "vgs:")) != -1) + for (int i=0; im_word_size); i++) { - switch (opt) + std::vector pattern_from, pattern_to; + + for (int j=0; jm_data.size()); j++) { - case 'v': - verbose = true; - break; - case 'g': - generate = true; - break; - case 's': - seed = true; - seed_opt = atoi(optarg); - break; - default: - help(argv[0]); + pattern_from.push_back(this->m_data.at(j).at(i)); + pattern_to.push_back(to.m_data.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); + throw std::runtime_error("Non-unique source pattern"); + } + pattern[pattern_from] = std::make_pair(pattern_to, 0); + pattern_from.clear(), pattern_to.clear(); + } } } - if (generate) - { - if (optind+2 != argc) - help(argv[0]); + return pattern; +} - 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); - } +// Bitstream File +// -------------- + +class EBRData +{ +private: + std::vector m_data; + int m_read_mode; - if (depth <= 0 || depth % 256 != 0) { - fprintf(stderr, "Hexfile number of words (%d) is not divisible by 256 or nonpositive!\n", depth); - exit(1); + int m_pos[2]; + int m_data_line; + int m_config_line; + std::vector &m_lines; + + friend class AscFile; + +protected: + void load_data (); + void save_data (); + void load_config (); + +public: + EBRData(std::vector &lines, int pos[2]); + virtual ~EBRData() { }; + + void apply_pattern(std::map, std::pair, int>> &pattern); +}; + +class AscFile +{ +private: + std::vector m_lines; + std::map m_ebr; + + EBRData &get_ebr(int pos[2]); + +public: + AscFile(); + virtual ~AscFile() { }; + + void load_config(std::istream &is); + void save_config(std::ostream &os); + + size_t n_ebrs() const { return this->m_ebr.size(); }; + + void apply_pattern(std::map, std::pair, int>> &pattern); +}; + + +EBRData::EBRData(std::vector &lines, int pos[2]) : + m_data(4096), + m_pos{pos[0], pos[1]}, + m_data_line(-1), m_config_line(-1), m_lines(lines) +{ + +} + +void +EBRData::load_data() +{ + auto si = this->m_lines.begin() + this->m_data_line + 16; + auto ei = this->m_lines.begin() + this->m_data_line; + int idx = 4096; + + for (auto line=si; line!=ei; line--) { + for (char c : *line) { + int digit; + + if ('0' <= c && c <= '9') + digit = c - '0'; + else if ('a' <= c && c <= 'f') + digit = 10 + c - 'a'; + else if ('A' <= c && c <= 'F') + digit = 10 + c - 'A'; + else + throw std::runtime_error("Invalid char"); + + idx -= 4; + + for (int subidx=3; subidx>=0; subidx--) + if (digit & (1 << subidx)) + this->m_data.at(idx+subidx) = true; } + } +} - if (verbose && seed) - fprintf(stderr, "Seed: %d\n", seed_opt); - - // If -s is provided: seed with the given value. - // If -s is not provided: seed with the PID and current time, which are unlikely - // to repeat simultaneously. - uint32_t seed_nr; - if (!seed) { -#if defined(__wasm) - seed_nr = 0; -#else - seed_nr = getpid(); -#endif - } else { - seed_nr = seed_opt; +void +EBRData::save_data() +{ + auto si = this->m_lines.begin() + this->m_data_line + 16; + auto ei = this->m_lines.begin() + this->m_data_line; + int idx = 4096; + + for (auto line=si; line!=ei; line--) { + // Hex String + char hex[65]; + idx -= 256; + for (int bit=0; bit<256; bit+=4) { + int digit = (this->m_data[idx+bit+3] ? 8 : 0) | + (this->m_data[idx+bit+2] ? 4 : 0) | + (this->m_data[idx+bit+1] ? 2 : 0) | + (this->m_data[idx+bit+0] ? 1 : 0); + hex[63-(bit>>2)] = "0123456789abcdef"[digit]; } + hex[64] = 0; - x = uint64_t(seed_nr) << 32; - x ^= uint64_t(depth) << 16; - x ^= uint64_t(width) << 10; + // Put new line + *line = std::string(hex); + } +} - xorshift64star(); - xorshift64star(); - xorshift64star(); +void +EBRData::load_config() +{ + this->m_read_mode = ( + ((this->m_lines.at(this->m_config_line+3).at(7) == '1') ? 2 : 0) | // RamConfig.CBIT_2 + ((this->m_lines.at(this->m_config_line+4).at(7) == '1') ? 1 : 0) // RamConfig.CBIT_3 + ); +} - if (!seed) { - struct timeval tv; - gettimeofday(&tv, NULL); - x ^= uint64_t(tv.tv_sec) << 20; - x ^= uint64_t(tv.tv_usec); - } +void +EBRData::apply_pattern(std::map, std::pair, int>> &pattern) +{ + const std::map> subidx_map = { + { 0, { 0 } }, + { 1, { 0, 1 } }, + { 2, { 0, 2, 1, 3 } }, + { 3, { 0, 4, 2, 6, 1, 5, 3, 7 } }, + }; + + const std::vector &subidx = subidx_map.at(this->m_read_mode); + int W = 16 >> this->m_read_mode; + int P = 16 / W; + + for (int blk_base=0; blk_base<4096; blk_base+=4096/P) + { + for (int bit_base=0; bit_base<16; bit_base+=P) + { + std::vector fbs(256); - xorshift64star(); - xorshift64star(); - xorshift64star(); + // Create "From Bit Slice" from local memory + for (int oaddr=0; oaddr<256/P; oaddr++) + for (int iaddr=0; iaddrm_data.at(blk_base+bit_base+oaddr*16+subidx.at(iaddr)); - 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; - } + // Perform substitution + auto p = pattern.find(fbs); + if (p == pattern.end()) + continue; + + auto &tbs = p->second.first; + p->second.second++; - exit(0); + // Map "To Bit Slice" back into local memory + for (int oaddr=0; oaddr<256/P; oaddr++) + for (int iaddr=0; iaddrm_data.at(blk_base+bit_base+oaddr*16+subidx.at(iaddr)) = tbs.at(oaddr*P+iaddr); + } } +} - if (optind+2 != argc) - help(argv[0]); +AscFile::AscFile() +{ + // Nothing to do for now +} + +EBRData & +AscFile::get_ebr(int pos[2]) +{ + int p = pos[0] | (pos[1] << 8); + return (*this->m_ebr.emplace(p, EBRData{this->m_lines, pos}).first).second; +} - // ------------------------------------------------------- - // Load from_hexfile and to_hexfile +void +AscFile::load_config(std::istream &is) +{ + std::string line; + int pos[2]; + + // Load data and track where each EBR is configured and initialized + for (int l=0; std::getline(is, line); l++) { + // Save line + this->m_lines.push_back(line); + + // Keep position of RAM infos + if (line.substr(0, 9) == ".ram_data") { + sscanf(line.substr(10).c_str(), "%d %d", &pos[0], &pos[1]); + this->get_ebr(pos).m_data_line = l; + } else if (line.substr(0, 10) == ".ramt_tile") { + sscanf(line.substr(11).c_str(), "%d %d", &pos[0], &pos[1]); + pos[1] -= 1; + this->get_ebr(pos).m_config_line = l; + } + } - const char *from_hexfile_n = argv[optind]; - ifstream from_hexfile_f(from_hexfile_n); - vector> from_hexfile; + // Only keep EBR that are initialized + for (auto it = this->m_ebr.begin(); it != this->m_ebr.end(); ) + if (it->second.m_data_line < 0) + it = this->m_ebr.erase(it); + else + ++it; + + // Load data config for those + for (auto &ebr : this->m_ebr) { + ebr.second.load_data(); + ebr.second.load_config(); + } +} - const char *to_hexfile_n = argv[optind+1]; - ifstream to_hexfile_f(to_hexfile_n); - vector> to_hexfile; +void +AscFile::save_config(std::ostream &os) +{ + // Update all EBRs + for (auto &ebr : this->m_ebr) + ebr.second.save_data(); - string line; + // Output new config + for (auto &l: this->m_lines) + os << l << std::endl; +} - for (int i = 1; getline(from_hexfile_f, line); i++) - parse_hexfile_line(from_hexfile_n, i, from_hexfile, line); +void +AscFile::apply_pattern(std::map, std::pair, int>> &pattern) +{ + for (auto &ebr : this->m_ebr) + ebr.second.apply_pattern(pattern); +} - 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(to_hexfile.at(0).size())); - while (from_hexfile.size() > to_hexfile.size()); +// Update process +// --------------- + +static int +update(struct app_opts *opts) +{ + if (opts->extra_argc != 2) + help(opts->prog); + + // Parse two source files + HexFile hf_from (opts->extra_argv[0]); + HexFile hf_to (opts->extra_argv[1], true); + + // Perform checks + if ((hf_to.word_size() > 0) && (hf_from.word_size() > hf_to.word_size())) { + if (opts->verbose) + fprintf(stderr, "Padding to_hexfile words from %lu bits to %lu bits\n", + hf_to.word_size(), hf_from.word_size()); + hf_to.pad_words_to(hf_from.word_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 (hf_to.word_size() != hf_from.word_size()) { + fprintf(stderr, "Hexfiles have different word sizes! (%lu bits vs. %lu bits)\n", + hf_from.word_size(), hf_to.word_size()); + return 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); + if ((hf_to.size() > 0) && (hf_from.size() > hf_to.size())) { + if (opts->verbose) + fprintf(stderr, "Padding to_hexfile from %lu words to %lu\n", + hf_to.size(), hf_from.size()); + hf_to.pad_to(hf_from.size()); } - 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); - } + if (hf_to.size() != hf_from.size()) { + fprintf(stderr, "Hexfiles have different number of words! (%lu vs. %lu)\n", + hf_from.size(), hf_to.size()); + return 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 (hf_from.size() % 256 != 0) { + fprintf(stderr, "Hexfile number of words (%lu) is not divisible by 256!\n", + hf_from.size()); + return 1; } - if (from_hexfile.size() == 0 || from_hexfile.at(0).size() == 0) { + if (hf_from.size() == 0 || hf_from.word_size() == 0) { fprintf(stderr, "Empty from/to hexfiles!\n"); - exit(1); + return 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())); + // Debug + if (opts->verbose) + fprintf(stderr, "Loaded pattern for %lu bits wide and %lu words deep memory.\n", + hf_from.word_size(), hf_from.size()); + // Generate mapping for slices + std::map, std::pair, int>> pattern = hf_from.generate_pattern(hf_to); + if (opts->verbose) + fprintf(stderr, "Extracted %lu bit slices from from/to hexfile data.\n", pattern.size()); - // ------------------------------------------------------- - // Create bitslices from pattern data + // Load FPGA config from stdin + AscFile bitstream; + bitstream.load_config(std::cin); - map, pair, int>> pattern; + if (opts->verbose) + fprintf(stderr, "Found %lu initialized bram cells in asc file.\n", bitstream.n_ebrs()); - for (int i = 0; i < int(from_hexfile.at(0).size()); i++) - { - vector pattern_from, pattern_to; + // Apply pattern + bitstream.apply_pattern(pattern); - 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)); + // Check pattern was applied uniformly + int min_replace_cnt = INT_MAX; + int max_replace_cnt = INT_MIN; - 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(); - } - } + for (auto &it : pattern) { + max_replace_cnt = std::max(max_replace_cnt, it.second.second); + min_replace_cnt = std::min(min_replace_cnt, it.second.second); + } - assert(pattern_from.empty()); - assert(pattern_to.empty()); + 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); + return 1; + } + + if (max_replace_cnt == 0) { + fprintf(stderr, "No memory instances were replaced.\n"); + return 1; } - if (verbose) - fprintf(stderr, "Extracted %d bit slices from from/to hexfile data.\n", int(pattern.size())); + if (opts->verbose) + fprintf(stderr, "Found and replaced %d instances of the memory.\n", max_replace_cnt); + // Save new FPGA config to stdout + bitstream.save_config(std::cout); - // ------------------------------------------------------- - // Read ascfile from stdin + return 0; +} - vector ascfile_lines; - map>> ascfile_hexdata; - for (int i = 1; getline(std::cin, line); i++) - { - next_asc_stmt: - ascfile_lines.push_back(line); +// --------------------------------------------------------------------------- +// Generate mode +// --------------------------------------------------------------------------- - if (line.substr(0, 9) == ".ram_data") - { - auto &hexdata = ascfile_hexdata[line]; +static uint64_t +xorshift64star(uint64_t *x) +{ + *x ^= *x >> 12; // a + *x ^= *x << 25; // b + *x ^= *x >> 27; // c + return *x * UINT64_C(2685821657736338717); +} - for (; getline(std::cin, line); i++) { - if (line.substr(0, 1) == ".") - goto next_asc_stmt; - parse_hexfile_line("stdin", i, hexdata, line); - } - } +static int +generate(struct app_opts *opts) +{ + if (opts->extra_argc != 2) + help(opts->prog); + + int width = atoi(opts->extra_argv[0]); + int depth = atoi(opts->extra_argv[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 (verbose) - fprintf(stderr, "Found %d initialized bram cells in asc file.\n", int(ascfile_hexdata.size())); + 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 (opts->verbose && opts->seed) + fprintf(stderr, "Seed: %d\n", opts->seed_nr); - // ------------------------------------------------------- - // Replace bram data - int max_replace_cnt = 0; + if (!opts->seed) { +#if defined(__wasm) + opts->seed_nr = 0; +#else + opts->seed_nr = getpid(); +#endif + } - for (auto &bram_it : ascfile_hexdata) - { - auto &bram_data = bram_it.second; + uint64_t x; - for (int i = 0; i < 16; i++) - { - vector from_bitslice; + x = uint64_t(opts->seed_nr) << 32; + x ^= uint64_t(depth) << 16; + x ^= uint64_t(width) << 10; - for (int j = 0; j < 256; j++) - from_bitslice.push_back(bram_data.at(j / 16).at(16 * (j % 16) + i)); + xorshift64star(&x); + xorshift64star(&x); + xorshift64star(&x); - auto p = pattern.find(from_bitslice); - if (p != pattern.end()) - { - auto &to_bitslice = p->second.first; + if (!opts->seed) { + struct timeval tv; + gettimeofday(&tv, NULL); + x ^= uint64_t(tv.tv_sec) << 20; + x ^= uint64_t(tv.tv_usec); + } - for (int j = 0; j < 256; j++) - bram_data.at(j / 16).at(16 * (j % 16) + i) = to_bitslice.at(j); + xorshift64star(&x); + xorshift64star(&x); + xorshift64star(&x); - max_replace_cnt = std::max(++p->second.second, max_replace_cnt); - } + for (int i = 0; i < depth; i++) { + for (int j = 0; j < width / 4; j++) { + int digit = xorshift64star(&x) & 15; + std::cout << "0123456789abcdef"[digit]; } + std::cout << std::endl; } - int min_replace_cnt = max_replace_cnt; - for (auto &it : pattern) - min_replace_cnt = std::min(min_replace_cnt, it.second.second); + return 0; +} - 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); +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- - if (max_replace_cnt == 0) { - fprintf(stderr, "No memory instances were replaced.\n"); - exit(2); - } +static void +help(const char *cmd) +{ + printf("\n"); + printf("Usage: %s [options] \n", cmd); + printf(" %s [options] -g [-s ] \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 later.\n"); + printf("\n"); + printf(" -s \n"); + printf(" seed random generator with fixed value.\n"); + printf("\n"); + printf(" -v\n"); + printf(" verbose output\n"); + printf("\n"); + exit(1); +} - // ------------------------------------------------------- - // 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; - } +static void +opts_defaults(struct app_opts *opts) +{ + // Clear + memset(opts, 0x00, sizeof(*opts)); +} + +static void +opts_parse(struct app_opts *opts, int argc, char *argv[]) +{ + int opt; + + opts->prog = argv[0]; + + while ((opt = getopt(argc, argv, "vgs:")) != -1) + { + switch (opt) + { + case 'v': + opts->verbose = true; + break; + case 'g': + opts->generate = true; + break; + case 's': + opts->seed = true; + opts->seed_nr = atoi(optarg); + break; + default: + help(argv[0]); } } - return 0; + opts->extra_argc = argc - optind; + opts->extra_argv = &argv[optind]; +} + +int main(int argc, char **argv) +{ + struct app_opts opts; + +#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 + + opts_defaults(&opts); + opts_parse(&opts, argc, argv); + + if (opts.generate) + return generate(&opts); + else + return update(&opts); } -- cgit v1.2.3