mirror of https://github.com/YosysHQ/icestorm.git
Merge pull request #309 from smunaut/icebram
icebram: Complete rewrite to cleanup and make it work
This commit is contained in:
commit
5991092f51
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright (C) 2016 Clifford Wolf <clifford@clifford.at>
|
||||
// Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.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
|
||||
|
|
@ -14,54 +15,124 @@
|
|||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <valarray>
|
||||
#include <vector>
|
||||
|
||||
#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);
|
||||
|
||||
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
|
||||
{
|
||||
private:
|
||||
std::vector<std::vector<bool>> m_data;
|
||||
size_t m_word_size;
|
||||
|
||||
std::vector<bool> parse_digits(std::vector<int> &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::vector<bool>, std::pair<std::vector<bool>, 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");
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void push_back_bitvector(vector<vector<bool>> &hexfile, const vector<int> &digits)
|
||||
std::vector<bool>
|
||||
HexFile::parse_digits(std::vector<int> &digits) const
|
||||
{
|
||||
if (digits.empty())
|
||||
return;
|
||||
|
||||
hexfile.push_back(vector<bool>(digits.size() * 4));
|
||||
std::vector<bool> 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<vector<bool>> &hexfile, string &line)
|
||||
void
|
||||
HexFile::parse_line(std::string &line)
|
||||
{
|
||||
vector<int> digits;
|
||||
std::vector<int> digits;
|
||||
|
||||
for (char c : line) {
|
||||
if ('0' <= c && c <= '9')
|
||||
|
|
@ -76,21 +147,481 @@ void parse_hexfile_line(const char *filename, int linenr, vector<vector<bool>> &
|
|||
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);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
fprintf(stderr, "Can't parse line %d of %s: %s\n", linenr, filename, line.c_str());
|
||||
exit(1);
|
||||
if (digits.size())
|
||||
this->m_data.push_back(this->parse_digits(digits));
|
||||
}
|
||||
|
||||
void help(const char *cmd)
|
||||
void
|
||||
HexFile::pad_words_to(size_t size)
|
||||
{
|
||||
if (this->m_word_size > size)
|
||||
return;
|
||||
|
||||
for (auto &w : this->m_data)
|
||||
if (w.size() < size)
|
||||
w.resize(size, false);
|
||||
|
||||
this->m_word_size = size;
|
||||
}
|
||||
|
||||
void
|
||||
HexFile::pad_to(size_t size)
|
||||
{
|
||||
while (this->m_data.size() < size)
|
||||
this->m_data.push_back(std::vector<bool>(this->m_word_size));
|
||||
}
|
||||
|
||||
std::map<std::vector<bool>, std::pair<std::vector<bool>, int>>
|
||||
HexFile::generate_pattern(HexFile &to) const
|
||||
{
|
||||
std::map<std::vector<bool>, std::pair<std::vector<bool>, int>> pattern;
|
||||
|
||||
for (int i=0; i<int(this->m_word_size); i++)
|
||||
{
|
||||
std::vector<bool> pattern_from, pattern_to;
|
||||
|
||||
for (int j=0; j<int(this->m_data.size()); j++)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
|
||||
// Bitstream File
|
||||
// --------------
|
||||
|
||||
class EBRData
|
||||
{
|
||||
private:
|
||||
std::vector<bool> m_data;
|
||||
int m_read_mode;
|
||||
|
||||
int m_pos[2];
|
||||
int m_data_line;
|
||||
int m_config_line;
|
||||
std::vector<std::string> &m_lines;
|
||||
|
||||
friend class AscFile;
|
||||
|
||||
protected:
|
||||
void load_data ();
|
||||
void save_data ();
|
||||
void load_config ();
|
||||
|
||||
public:
|
||||
EBRData(std::vector<std::string> &lines, int pos[2]);
|
||||
virtual ~EBRData() { };
|
||||
|
||||
void apply_pattern(std::map<std::vector<bool>, std::pair<std::vector<bool>, int>> &pattern);
|
||||
};
|
||||
|
||||
class AscFile
|
||||
{
|
||||
private:
|
||||
std::vector<std::string> m_lines;
|
||||
std::map<int, EBRData> 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::vector<bool>, std::pair<std::vector<bool>, int>> &pattern);
|
||||
};
|
||||
|
||||
|
||||
EBRData::EBRData(std::vector<std::string> &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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Put new line
|
||||
*line = std::string(hex);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
EBRData::apply_pattern(std::map<std::vector<bool>, std::pair<std::vector<bool>, int>> &pattern)
|
||||
{
|
||||
const std::map<int, std::vector<int>> subidx_map = {
|
||||
{ 0, { 0 } },
|
||||
{ 1, { 0, 1 } },
|
||||
{ 2, { 0, 2, 1, 3 } },
|
||||
{ 3, { 0, 4, 2, 6, 1, 5, 3, 7 } },
|
||||
};
|
||||
|
||||
const std::vector<int> &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<bool> fbs(256);
|
||||
|
||||
// Create "From Bit Slice" from local memory
|
||||
for (int oaddr=0; oaddr<256/P; oaddr++)
|
||||
for (int iaddr=0; iaddr<P; iaddr++)
|
||||
fbs.at(oaddr*P+iaddr) = this->m_data.at(blk_base+bit_base+oaddr*16+subidx.at(iaddr));
|
||||
|
||||
// Perform substitution
|
||||
auto p = pattern.find(fbs);
|
||||
if (p == pattern.end())
|
||||
continue;
|
||||
|
||||
auto &tbs = p->second.first;
|
||||
p->second.second++;
|
||||
|
||||
// Map "To Bit Slice" back into local memory
|
||||
for (int oaddr=0; oaddr<256/P; oaddr++)
|
||||
for (int iaddr=0; iaddr<P; iaddr++)
|
||||
this->m_data.at(blk_base+bit_base+oaddr*16+subidx.at(iaddr)) = tbs.at(oaddr*P+iaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AscFile::save_config(std::ostream &os)
|
||||
{
|
||||
// Update all EBRs
|
||||
for (auto &ebr : this->m_ebr)
|
||||
ebr.second.save_data();
|
||||
|
||||
// Output new config
|
||||
for (auto &l: this->m_lines)
|
||||
os << l << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
AscFile::apply_pattern(std::map<std::vector<bool>, std::pair<std::vector<bool>, int>> &pattern)
|
||||
{
|
||||
for (auto &ebr : this->m_ebr)
|
||||
ebr.second.apply_pattern(pattern);
|
||||
}
|
||||
|
||||
|
||||
// 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 (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 ((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());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (hf_from.size() == 0 || hf_from.word_size() == 0) {
|
||||
fprintf(stderr, "Empty from/to hexfiles!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 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::vector<bool>, std::pair<std::vector<bool>, 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());
|
||||
|
||||
// Load FPGA config from stdin
|
||||
AscFile bitstream;
|
||||
bitstream.load_config(std::cin);
|
||||
|
||||
if (opts->verbose)
|
||||
fprintf(stderr, "Found %lu initialized bram cells in asc file.\n", bitstream.n_ebrs());
|
||||
|
||||
// Apply pattern
|
||||
bitstream.apply_pattern(pattern);
|
||||
|
||||
// Check pattern was applied uniformly
|
||||
int min_replace_cnt = INT_MAX;
|
||||
int max_replace_cnt = INT_MIN;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 (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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Generate mode
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static uint64_t
|
||||
xorshift64star(uint64_t *x)
|
||||
{
|
||||
*x ^= *x >> 12; // a
|
||||
*x ^= *x << 25; // b
|
||||
*x ^= *x >> 27; // c
|
||||
return *x * UINT64_C(2685821657736338717);
|
||||
}
|
||||
|
||||
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 (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);
|
||||
|
||||
|
||||
if (!opts->seed) {
|
||||
#if defined(__wasm)
|
||||
opts->seed_nr = 0;
|
||||
#else
|
||||
opts->seed_nr = getpid();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t x;
|
||||
|
||||
x = uint64_t(opts->seed_nr) << 32;
|
||||
x ^= uint64_t(depth) << 16;
|
||||
x ^= uint64_t(width) << 10;
|
||||
|
||||
xorshift64star(&x);
|
||||
xorshift64star(&x);
|
||||
xorshift64star(&x);
|
||||
|
||||
if (!opts->seed) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
x ^= uint64_t(tv.tv_sec) << 20;
|
||||
x ^= uint64_t(tv.tv_usec);
|
||||
}
|
||||
|
||||
xorshift64star(&x);
|
||||
xorshift64star(&x);
|
||||
xorshift64star(&x);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
help(const char *cmd)
|
||||
{
|
||||
printf("\n");
|
||||
printf("Usage: %s [options] <from_hexfile> <to_hexfile>\n", cmd);
|
||||
|
|
@ -114,8 +645,47 @@ void help(const char *cmd)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -128,283 +698,11 @@ int main(int argc, char **argv)
|
|||
);
|
||||
#endif
|
||||
|
||||
bool verbose = false;
|
||||
bool generate = false;
|
||||
bool seed = false;
|
||||
uint32_t seed_opt = 0;
|
||||
opts_defaults(&opts);
|
||||
opts_parse(&opts, argc, argv);
|
||||
|
||||
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_opt = 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_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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (max_replace_cnt == 0) {
|
||||
fprintf(stderr, "No memory instances were replaced.\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
// 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;
|
||||
if (opts.generate)
|
||||
return generate(&opts);
|
||||
else
|
||||
return update(&opts);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue