[Pkg-electronics-devel] Bug#1055214: bookworm-pu: package fpga-icestorm/0~20220915gita545498-3
Daniel Gröber
dxld at darkboxed.org
Thu Nov 2 10:36:23 GMT 2023
Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian.org at packages.debian.org
Usertags: pu
X-Debbugs-Cc: fpga-icestorm at packages.debian.org, dxld at darkboxed.org
Control: affects -1 + src:fpga-icestorm
[ Reason ]
Andras Pal reported fpga-icestorm's "icebram" utility being broken in
stable (#1055171) due to incompatible changes to yosys's output.
[ Impact ]
Users will not be able to easily embed ROM images into their FPGA
designs during or after netlist build.
[ Tests ]
I've tested the updated version against Andras' reproducer
and confirmed it fixes the issue.
[ Risks ]
The risk of breakage is low, icebram is already broken in stable so a
rewrite wont hurt.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in stable
[x] the issue is verified as fixed in unstable
[ Changes ]
Upstream fixed this by rewriting the icebram utility as a major
refactoring was needed to fix it.
The version in unstable differs from the stable version in this
respect and some minor hardware enablement additions in icebox. I
belive simply updating the version in stable is warrented here.
[ Other info ]
Upstream discussion:
https://github.com/YosysHQ/icestorm/issues/301
https://github.com/YosysHQ/icestorm/pull/309
--Daniel
-------------- next part --------------
diff -Nru fpga-icestorm-0~20220915gita545498/debian/changelog fpga-icestorm-0~20230218gitd20a5e9/debian/changelog
--- fpga-icestorm-0~20220915gita545498/debian/changelog 2022-11-16 23:51:42.000000000 +0100
+++ fpga-icestorm-0~20230218gitd20a5e9/debian/changelog 2023-11-02 11:10:26.000000000 +0100
@@ -1,3 +1,23 @@
+fpga-icestorm (0~20230218gitd20a5e9-1~deb12u1) bookworm; urgency=medium
+
+ * Fix yosys incompatibility (Closes: #1055171)
+
+ -- Daniel Gr?ber <dxld at darkboxed.org> Thu, 02 Nov 2023 11:10:26 +0100
+
+fpga-icestorm (0~20230218gitd20a5e9-1) unstable; urgency=medium
+
+ * New upstream version 0~20230218gitd20a5e9
+
+ -- Daniel Gr?ber <dxld at darkboxed.org> Tue, 13 Jun 2023 14:52:48 +0200
+
+fpga-icestorm (0~20220915gita545498-4) unstable; urgency=medium
+
+ * autopkgtest: Fix missing icetime command
+ * Refresh patches
+ * Add patch fixing up5k_rgb icetime failure
+
+ -- Daniel Gr?ber <dxld at darkboxed.org> Tue, 13 Jun 2023 11:56:01 +0200
+
fpga-icestorm (0~20220915gita545498-3) unstable; urgency=medium
* Fix autopkgtest, examples-compile now needs nextpnr
diff -Nru fpga-icestorm-0~20220915gita545498/debian/patches/0003-Fix-examples-icemulti-all-target.patch fpga-icestorm-0~20230218gitd20a5e9/debian/patches/0003-Fix-examples-icemulti-all-target.patch
--- fpga-icestorm-0~20220915gita545498/debian/patches/0003-Fix-examples-icemulti-all-target.patch 2022-10-29 20:42:04.000000000 +0200
+++ fpga-icestorm-0~20230218gitd20a5e9/debian/patches/0003-Fix-examples-icemulti-all-target.patch 2023-11-02 11:10:08.000000000 +0100
@@ -7,7 +7,7 @@
1 file changed, 2 insertions(+)
diff --git a/examples/icemulti/Makefile b/examples/icemulti/Makefile
-index d8a8320..1e38f9c 100644
+index a7ce692..96b31e1 100644
--- a/examples/icemulti/Makefile
+++ b/examples/icemulti/Makefile
@@ -1,3 +1,5 @@
diff -Nru fpga-icestorm-0~20220915gita545498/debian/patches/0004-Fix-up5k_rgb-failing-timing-analysis.patch fpga-icestorm-0~20230218gitd20a5e9/debian/patches/0004-Fix-up5k_rgb-failing-timing-analysis.patch
--- fpga-icestorm-0~20220915gita545498/debian/patches/0004-Fix-up5k_rgb-failing-timing-analysis.patch 1970-01-01 01:00:00.000000000 +0100
+++ fpga-icestorm-0~20230218gitd20a5e9/debian/patches/0004-Fix-up5k_rgb-failing-timing-analysis.patch 2023-11-02 11:10:08.000000000 +0100
@@ -0,0 +1,18 @@
+From: =?utf-8?q?Daniel_Gr=C3=B6ber?= <dxld at darkboxed.org>
+Date: Wed, 17 May 2023 21:09:31 +0200
+Subject: Fix up5k_rgb failing timing analysis
+
+---
+ examples/up5k_rgb/rgb.pcf | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/examples/up5k_rgb/rgb.pcf b/examples/up5k_rgb/rgb.pcf
+index 0954260..19a93d1 100644
+--- a/examples/up5k_rgb/rgb.pcf
++++ b/examples/up5k_rgb/rgb.pcf
+@@ -1,3 +1,4 @@
+ set_io RGB0 39
+ set_io RGB1 40
+ set_io RGB2 41
++set_frequency clk 32
+\ No newline at end of file
diff -Nru fpga-icestorm-0~20220915gita545498/debian/patches/0004-Remove-hard-coded-path-in-icebox_vlog.py.patch fpga-icestorm-0~20230218gitd20a5e9/debian/patches/0004-Remove-hard-coded-path-in-icebox_vlog.py.patch
--- fpga-icestorm-0~20220915gita545498/debian/patches/0004-Remove-hard-coded-path-in-icebox_vlog.py.patch 2022-03-27 16:45:36.000000000 +0200
+++ fpga-icestorm-0~20230218gitd20a5e9/debian/patches/0004-Remove-hard-coded-path-in-icebox_vlog.py.patch 2023-11-02 11:10:08.000000000 +0100
@@ -6,6 +6,8 @@
icebox/icebox_vlog.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
+diff --git a/icebox/icebox_vlog.py b/icebox/icebox_vlog.py
+index 74ac3d3..9ba2e30 100755
--- a/icebox/icebox_vlog.py
+++ b/icebox/icebox_vlog.py
@@ -384,7 +384,7 @@ def seg_to_net(seg, default=None):
diff -Nru fpga-icestorm-0~20220915gita545498/debian/patches/0005-Fix-GCC-10-build-with-missing-include.patch fpga-icestorm-0~20230218gitd20a5e9/debian/patches/0005-Fix-GCC-10-build-with-missing-include.patch
--- fpga-icestorm-0~20220915gita545498/debian/patches/0005-Fix-GCC-10-build-with-missing-include.patch 2022-03-27 16:45:36.000000000 +0200
+++ fpga-icestorm-0~20230218gitd20a5e9/debian/patches/0005-Fix-GCC-10-build-with-missing-include.patch 2023-11-02 11:10:08.000000000 +0100
@@ -4,8 +4,10 @@
---
icetime/icetime.cc | 1 +
- 2 files changed, 3 insertions(+), 1 deletion(-)
+ 1 file changed, 1 insertion(+)
+diff --git a/icetime/icetime.cc b/icetime/icetime.cc
+index fef65d2..2bace03 100644
--- a/icetime/icetime.cc
+++ b/icetime/icetime.cc
@@ -34,6 +34,7 @@
diff -Nru fpga-icestorm-0~20220915gita545498/debian/patches/series fpga-icestorm-0~20230218gitd20a5e9/debian/patches/series
--- fpga-icestorm-0~20220915gita545498/debian/patches/series 2022-03-27 16:45:22.000000000 +0200
+++ fpga-icestorm-0~20230218gitd20a5e9/debian/patches/series 2023-11-02 11:10:08.000000000 +0100
@@ -1,3 +1,4 @@
0004-Remove-hard-coded-path-in-icebox_vlog.py.patch
0005-Fix-GCC-10-build-with-missing-include.patch
0003-Fix-examples-icemulti-all-target.patch
+0004-Fix-up5k_rgb-failing-timing-analysis.patch
diff -Nru fpga-icestorm-0~20220915gita545498/debian/tests/control fpga-icestorm-0~20230218gitd20a5e9/debian/tests/control
--- fpga-icestorm-0~20220915gita545498/debian/tests/control 2022-11-16 23:50:15.000000000 +0100
+++ fpga-icestorm-0~20230218gitd20a5e9/debian/tests/control 2023-11-02 11:10:08.000000000 +0100
@@ -3,6 +3,6 @@
Restrictions: superficial
Tests: examples-compile
-Depends: fpga-icestorm-chipdb, yosys, nextpnr-ice40
+Depends: @, yosys, nextpnr-ice40
Restrictions: rw-build-tree allow-stderr
Architecture: !s390x
diff -Nru fpga-icestorm-0~20220915gita545498/docs/io_tile.html fpga-icestorm-0~20230218gitd20a5e9/docs/io_tile.html
--- fpga-icestorm-0~20220915gita545498/docs/io_tile.html 2022-09-15 12:37:29.000000000 +0200
+++ fpga-icestorm-0~20230218gitd20a5e9/docs/io_tile.html 2023-02-18 16:37:53.000000000 +0100
@@ -428,6 +428,9 @@
<tr><td>0 3</td><td><span style="font-family:monospace">PLLCONFIG_8</span></td><td rowspan="1"><span style="font-family:monospace">TEST_MODE</span></td></tr>
+<tr><td>0 5</td><td><span style="font-family:monospace">PLLCONFIG_2</span></td><td rowspan="1">Enable ICEGATE for <span style="font-family:monospace">PLLOUTGLOBALA</span></td></tr>
+<tr><td>0 5</td><td><span style="font-family:monospace">PLLCONFIG_4</span></td><td rowspan="1">Enable ICEGATE for <span style="font-family:monospace">PLLOUTGLOBALB</span></td></tr>
+
</table></td><td>
<table class="ctab">
@@ -502,4 +505,12 @@
are being used.
</p>
+<p>
+The input path that are stolen are also used to implement the ICEGATE function.
+If the input pin type of the input path being stolen is set to
+<span style="font-family:monospace">PIN_INPUT_LATCH</span>, then the ICEGATE
+function is enabled for the corresponding <span style="font-family:monospace">CORE</span>
+output of the PLL.
+</p>
+
</body></html>
diff -Nru fpga-icestorm-0~20220915gita545498/icebox/icebox.py fpga-icestorm-0~20230218gitd20a5e9/icebox/icebox.py
--- fpga-icestorm-0~20220915gita545498/icebox/icebox.py 2022-09-15 12:37:29.000000000 +0200
+++ fpga-icestorm-0~20230218gitd20a5e9/icebox/icebox.py 2023-02-18 16:37:53.000000000 +0100
@@ -1795,6 +1795,8 @@
"FILTER_RANGE_1": ( 0, 2, "PLLCONFIG_7"),
"FILTER_RANGE_2": ( 0, 2, "PLLCONFIG_8"),
"TEST_MODE": ( 0, 3, "PLLCONFIG_8"),
+ "ENABLE_ICEGATE_PORTA": ( 0, 5, "PLLCONFIG_2"), # Controls global output only !
+ "ENABLE_ICEGATE_PORTB": ( 0, 5, "PLLCONFIG_4"), # Controls global output only !
# PLL Ports
"PLLOUT_A": ( 6, 0, 1),
@@ -1887,6 +1889,8 @@
"FILTER_RANGE_1": (11, 0, "PLLCONFIG_7"),
"FILTER_RANGE_2": (11, 0, "PLLCONFIG_8"),
"TEST_MODE": (12, 0, "PLLCONFIG_8"),
+ "ENABLE_ICEGATE_PORTA": (14, 0, "PLLCONFIG_2"), # Controls global output only !
+ "ENABLE_ICEGATE_PORTB": (14, 0, "PLLCONFIG_4"), # Controls global output only !
# PLL Ports
# TODO(awygle) confirm these
@@ -1981,6 +1985,8 @@
"FILTER_RANGE_1": (11, 31, "PLLCONFIG_7"),
"FILTER_RANGE_2": (11, 31, "PLLCONFIG_8"),
"TEST_MODE": (12, 31, "PLLCONFIG_8"),
+ "ENABLE_ICEGATE_PORTA": (14, 31, "PLLCONFIG_2"), # Controls global output only !
+ "ENABLE_ICEGATE_PORTB": (14, 31, "PLLCONFIG_4"), # Controls global output only !
# PLL Ports
"PLLOUT_A": ( 12, 31, 1),
@@ -2045,6 +2051,8 @@
"TEST_MODE": (12, 21, "PLLCONFIG_8"),
"DELAY_ADJMODE_FB": (13, 21, "PLLCONFIG_4"),
"DELAY_ADJMODE_REL": (13, 21, "PLLCONFIG_9"),
+ "ENABLE_ICEGATE_PORTA": (14, 21, "PLLCONFIG_2"), # Controls global output only !
+ "ENABLE_ICEGATE_PORTB": (14, 21, "PLLCONFIG_4"), # Controls global output only !
# PLL Ports
"PLLOUT_A": ( 12, 21, 1),
@@ -2138,6 +2146,8 @@
"FILTER_RANGE_1": ( 15, 0, "PLLCONFIG_7"),
"FILTER_RANGE_2": ( 15, 0, "PLLCONFIG_8"),
"TEST_MODE": ( 16, 0, "PLLCONFIG_8"),
+ "ENABLE_ICEGATE_PORTA": ( 18, 0, "PLLCONFIG_2"), # Controls global output only !
+ "ENABLE_ICEGATE_PORTB": ( 18, 0, "PLLCONFIG_4"), # Controls global output only !
# PLL Ports
"PLLOUT_A": ( 16, 0, 1),
@@ -2231,6 +2241,8 @@
"FILTER_RANGE_1": ( 15, 33, "PLLCONFIG_7"),
"FILTER_RANGE_2": ( 15, 33, "PLLCONFIG_8"),
"TEST_MODE": ( 16, 33, "PLLCONFIG_8"),
+ "ENABLE_ICEGATE_PORTA": ( 18, 33, "PLLCONFIG_2"), # Controls global output only !
+ "ENABLE_ICEGATE_PORTB": ( 18, 33, "PLLCONFIG_4"), # Controls global output only !
# PLL Ports
"PLLOUT_A": ( 16, 33, 1),
diff -Nru fpga-icestorm-0~20220915gita545498/icebram/icebram.cc fpga-icestorm-0~20230218gitd20a5e9/icebram/icebram.cc
--- fpga-icestorm-0~20220915gita545498/icebram/icebram.cc 2022-09-15 12:37:29.000000000 +0200
+++ fpga-icestorm-0~20230218gitd20a5e9/icebram/icebram.cc 2023-02-18 16:37:53.000000000 +0100
@@ -1,5 +1,6 @@
//
// Copyright (C) 2016 Clifford Wolf <clifford at clifford.at>
+// Copyright (C) 2023 Sylvain Munaut <tnt at 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,122 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
+
#include <stdio.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <limits.h>
#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-#include <stdint.h>
#include <sys/time.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);
-}
-void push_back_bitvector(vector<vector<bool>> &hexfile, const vector<int> &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<std::vector<bool>> m_data;
+ size_t m_word_size;
+
+ std::vector<bool> parse_digits(std::vector<int> &digits) const;
+ bool 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);
+ exit(1);
+ }
- hexfile.push_back(vector<bool>(digits.size() * 4));
+ // Parse file
+ std::string line;
+ for (int i=1; std::getline(stream, line); i++)
+ if (!this->parse_line(line)) {
+ fprintf(stderr, "Can't parse line %d of %s: %s\n", i, filename, line.c_str());
+ exit(1);
+ }
+
+ // 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);
+ exit(1);
+ }
+ 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<bool>
+HexFile::parse_digits(std::vector<int> &digits) const
+{
+ 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)
+bool
+HexFile::parse_line(std::string &line)
{
- vector<int> digits;
+ std::vector<int> digits;
for (char c : line) {
if ('0' <= c && c <= '9')
@@ -76,331 +145,566 @@
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 {
+ return false;
+ }
}
- push_back_bitvector(hexfile, digits);
+ if (digits.size())
+ this->m_data.push_back(this->parse_digits(digits));
+
+ return true;
+}
+
+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] <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);
+ while (this->m_data.size() < size)
+ this->m_data.push_back(std::vector<bool>(this->m_word_size));
}
-int main(int argc, char **argv)
+std::map<std::vector<bool>, std::pair<std::vector<bool>, 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
+ std::map<std::vector<bool>, std::pair<std::vector<bool>, int>> pattern;
- bool verbose = false;
- bool generate = false;
- bool seed = false;
- uint32_t seed_opt = 0;
-
- int opt;
- while ((opt = getopt(argc, argv, "vgs:")) != -1)
+ for (int i=0; i<int(this->m_word_size); i++)
{
- switch (opt)
+ std::vector<bool> pattern_from, pattern_to;
+
+ for (int j=0; j<int(this->m_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);
+ exit(1);
+ }
+ 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
+// --------------
- if (depth <= 0 || depth % 256 != 0) {
- fprintf(stderr, "Hexfile number of words (%d) is not divisible by 256 or nonpositive!\n", depth);
- exit(1);
- }
+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() { };
- 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 apply_pattern(std::map<std::vector<bool>, std::pair<std::vector<bool>, int>> &pattern);
+};
- x = uint64_t(seed_nr) << 32;
- x ^= uint64_t(depth) << 16;
- x ^= uint64_t(width) << 10;
+class AscFile
+{
+private:
+ std::vector<std::string> m_lines;
+ std::map<int, EBRData> m_ebr;
- xorshift64star();
- xorshift64star();
- xorshift64star();
+ EBRData &get_ebr(int pos[2]);
- if (!seed) {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- x ^= uint64_t(tv.tv_sec) << 20;
- x ^= uint64_t(tv.tv_usec);
- }
+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);
+};
- 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];
+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 {
+ fprintf(stderr, "Invalid char in BRAM data\n");
+ exit(1);
}
- std::cout << std::endl;
+
+ idx -= 4;
+
+ for (int subidx=3; subidx>=0; subidx--)
+ if (digit & (1 << subidx))
+ this->m_data.at(idx+subidx) = true;
}
+ }
+}
- exit(0);
+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);
}
+}
- if (optind+2 != argc)
- help(argv[0]);
+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;
- // -------------------------------------------------------
- // Load from_hexfile and to_hexfile
+ 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);
- const char *from_hexfile_n = argv[optind];
- ifstream from_hexfile_f(from_hexfile_n);
- vector<vector<bool>> from_hexfile;
+ // 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);
+ }
+ }
+}
- const char *to_hexfile_n = argv[optind+1];
- ifstream to_hexfile_f(to_hexfile_n);
- vector<vector<bool>> to_hexfile;
- string line;
+AscFile::AscFile()
+{
+ // Nothing to do for now
+}
- for (int i = 1; getline(from_hexfile_f, line); i++)
- parse_hexfile_line(from_hexfile_n, i, from_hexfile, line);
+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;
+}
- for (int i = 1; getline(to_hexfile_f, line); i++)
- parse_hexfile_line(to_hexfile_n, i, to_hexfile, line);
+void
+AscFile::load_config(std::istream &is)
+{
+ std::string line;
+ int pos[2];
- 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());
+ // 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;
+ }
}
- 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);
+ // 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();
}
+}
- 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);
+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());
}
- 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.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;
+ }
- 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_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 (from_hexfile.size() == 0 || from_hexfile.at(0).size() == 0) {
+ 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");
- 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::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());
- // -------------------------------------------------------
- // Create bitslices from pattern data
+ // Load FPGA config from stdin
+ AscFile bitstream;
+ bitstream.load_config(std::cin);
- map<vector<bool>, pair<vector<bool>, 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<bool> 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 (verbose)
- fprintf(stderr, "Extracted %d bit slices from from/to hexfile data.\n", int(pattern.size()));
+ 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);
- // -------------------------------------------------------
- // Read ascfile from stdin
+ // Save new FPGA config to stdout
+ bitstream.save_config(std::cout);
- vector<string> ascfile_lines;
- map<string, vector<vector<bool>>> ascfile_hexdata;
+ return 0;
+}
- 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];
+// ---------------------------------------------------------------------------
+// Generate mode
+// ---------------------------------------------------------------------------
- for (; getline(std::cin, line); i++) {
- if (line.substr(0, 1) == ".")
- goto next_asc_stmt;
- parse_hexfile_line("stdin", i, 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);
+}
+
+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<bool> 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
+// ---------------------------------------------------------------------------
+static 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);
+}
- // -------------------------------------------------------
- // Write ascfile to stdout
+static void
+opts_defaults(struct app_opts *opts)
+{
+ // Clear
+ memset(opts, 0x00, sizeof(*opts));
+}
- 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_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);
}
More information about the Pkg-electronics-devel
mailing list