Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian....@packages.debian.org
Usertags: pu
X-Debbugs-Cc: fpga-icest...@packages.debian.org, d...@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
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 <d...@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 <d...@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 <d...@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?= <d...@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 <cliff...@clifford.at>
+//  Copyright (C) 2023  Sylvain Munaut <t...@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);
 }

Reply via email to