From 66e35e36dcebd3d0e68fea0b3148a50d7f966a63 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 11 May 2016 13:37:52 +0200 Subject: Added icebram test bench --- icebram/.gitignore | 10 ++++++++ icebram/makedemo.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ icebram/rundemo.sh | 17 +++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 icebram/.gitignore create mode 100644 icebram/makedemo.py create mode 100644 icebram/rundemo.sh diff --git a/icebram/.gitignore b/icebram/.gitignore new file mode 100644 index 0000000..3569a03 --- /dev/null +++ b/icebram/.gitignore @@ -0,0 +1,10 @@ +demo.asc +demo.blif +demo.pcf +demo.v +demo.vvp +demo_dat0.hex +demo_dat1.hex +demo_new.asc +demo_new.v +demo_tb.v diff --git a/icebram/makedemo.py b/icebram/makedemo.py new file mode 100644 index 0000000..bcec93f --- /dev/null +++ b/icebram/makedemo.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 + +import numpy as np + +while True: + bram_width = 4 * np.random.randint(1, 9) + bram_depth = 256 * np.random.randint(1, 9) + numrports = np.random.randint(1, 5) + if bram_width * bram_depth * numrports < 16*4096: break + +with open("demo.v", "wt") as f: + print("module demo (", file=f) + for i in range(numrports): + print(" input [%d:0] raddr%d," % (np.ceil(np.log2(bram_depth))-1, i), file=f) + print(" output reg [%d:0] rdata%d," % (bram_width-1, i), file=f) + print(" input [%d:0] waddr," % (np.ceil(np.log2(bram_depth))-1), file=f) + print(" input [%d:0] wdata," % (bram_width-1), file=f) + print(" input wen, clk", file=f) + print(");", file=f) + print(" reg [%d:0] memory [0:%d];" % (bram_width-1, bram_depth-1), file=f) + print(" initial $readmemh(\"demo_dat0.hex\", memory);", file=f) + for i in range(numrports): + print(" always @(posedge clk) rdata%d <= memory[raddr%d];" % (i, i), file=f) + print(" always @(posedge clk) if (wen) memory[waddr] <= wdata;", file=f) + print("endmodule", file=f) + +with open("demo_tb.v", "wt") as f: + print("module demo_tb;", file=f) + print(" reg clk = 0;", file=f) + print(" always #5 clk = ~clk;", file=f) + print(" integer i, errcnt = 0;", file=f) + print(" reg [%d:0] addr;" % (np.ceil(np.log2(bram_depth))-1), file=f) + for i in range(numrports): + print(" wire [%d:0] rdata%d;" % (bram_width-1, i), file=f) + print(" reg [%d:0] refmem [0:%d];" % (bram_width-1, bram_depth-1), file=f) + print(" initial $readmemh(\"demo_dat1.hex\", refmem);", file=f) + print(" demo uut (", file=f) + for i in range(numrports): + print(" .raddr%d(addr+%d'd%d)," % (i, np.ceil(np.log2(bram_depth)), i), file=f) + print(" .rdata%d(rdata%d)," % (i, i), file=f) + print(" .wen(1'b0),", file=f) + print(" .clk(clk)", file=f) + print(" );", file=f) + print(" initial begin", file=f) + print(" repeat (10) @(negedge clk);", file=f) + print(" for (i = 0; i < %d; i = i + %d) begin" % (bram_depth, numrports), file=f) + print(" addr <= i;", file=f) + print(" @(posedge clk);", file=f) + print(" @(negedge clk);", file=f) + for i in range(numrports): + print(" if (i+%d < %d && refmem[i+%d] !== rdata%d) begin errcnt = errcnt+1; " % (i, bram_depth, i, i) + + "$display(\"ERROR @%%x: %%0%dx != %%0%dx\", i+%d, refmem[i+%d], rdata%d); end" % (bram_width/4, bram_width/4, i, i, i), file=f) + print(" end", file=f) + print(" if (errcnt == 0)", file=f) + print(" $display(\"All tests OK.\");", file=f) + print(" else", file=f) + print(" $display(\"Found %1d ERROR(s).\", errcnt);", file=f) + print(" $finish;", file=f) + print(" end", file=f) + print("endmodule", file=f) + +with open("demo_dat0.hex", "wt") as f: + for i in range(bram_depth): + print("%0*x" % (bram_width//4, np.random.randint(1 << bram_width)), file=f) + +with open("demo_dat1.hex", "wt") as f: + for i in range(bram_depth): + print("%0*x" % (bram_width//4, np.random.randint(1 << bram_width)), file=f) + diff --git a/icebram/rundemo.sh b/icebram/rundemo.sh new file mode 100644 index 0000000..9358447 --- /dev/null +++ b/icebram/rundemo.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -ex + +python3 makedemo.py +yosys -p 'synth_ice40 -blif demo.blif' demo.v +arachne-pnr -d 8k -w demo.pcf -o demo.asc demo.blif + +if true; then + cp demo.asc demo_new.asc + cp demo_dat0.hex demo_dat1.hex +else + ./icebram -o demo_new.asc demo.asc demo_dat0.hex demo_dat1.hex +fi + +icebox_vlog -n demo -p demo.pcf -c demo_new.asc > demo_new.v +iverilog -o demo.vvp demo_tb.v demo_new.v $( yosys-config --datdir/ice40/cells_sim.v ) +vvp -N demo.vvp -- cgit v1.2.3 From 0e1417249c832afabb3c81eb707d39efe94955df Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 12 May 2016 00:48:37 +0200 Subject: Added icebram skeleton --- icebram/.gitignore | 3 + icebram/Makefile | 33 +++++++++++ icebram/icebram.cc | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ icebram/rundemo.sh | 7 +-- 4 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 icebram/Makefile create mode 100644 icebram/icebram.cc diff --git a/icebram/.gitignore b/icebram/.gitignore index 3569a03..10fbc8a 100644 --- a/icebram/.gitignore +++ b/icebram/.gitignore @@ -8,3 +8,6 @@ demo_dat1.hex demo_new.asc demo_new.v demo_tb.v +icebram +icebram.d +icebram.o diff --git a/icebram/Makefile b/icebram/Makefile new file mode 100644 index 0000000..827fe7c --- /dev/null +++ b/icebram/Makefile @@ -0,0 +1,33 @@ +include ../config.mk +LDLIBS = -lm -lstdc++ +CXXFLAGS = -MD -O0 -ggdb -Wall -std=c++11 -I/usr/local/include + +ifeq ($(STATIC),1) +LDFLAGS += -static +endif + +all: icebram$(EXE) + +icebram$(EXE): icebram.o + $(CC) -o $@ $(LDFLAGS) $^ $(LDLIBS) + +test: icebram + bash rundemo.sh + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp icebram $(DESTDIR)$(PREFIX)/bin/icebram + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/icebram + +clean: + rm -f icebram + rm -f icebram.exe + rm -f demo.* demo_*.* + rm -f *.o *.d + +-include *.d + +.PHONY: all test install uninstall clean + diff --git a/icebram/icebram.cc b/icebram/icebram.cc new file mode 100644 index 0000000..a4cb415 --- /dev/null +++ b/icebram/icebram.cc @@ -0,0 +1,165 @@ +// +// Copyright (C) 2016 Clifford Wolf +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using std::map; +using std::vector; +using std::string; +using std::ifstream; +using std::getline; + +void parse_hexfile_line(const char *filename, int linenr, vector> &hexfile, string &line) +{ + vector digits; + + for (char c : line) { + if ('0' <= c && c <= '9') + digits.push_back(c - '0'); + else if ('a' <= c && c <= 'f') + digits.push_back(10 + c - 'a'); + else if ('A' <= c && c <= 'F') + digits.push_back(10 + c - 'A'); + else goto error; + } + + hexfile.push_back(vector(digits.size() * 4)); + + for (int i = 0; i < digits.size() * 4; i++) + if ((digits.at(digits.size() - i/4 -1) & (1 << (i%4))) != 0) + hexfile.back().at(i) = true; + + return; + +error: + fprintf(stderr, "Can't parse line %d of %s: %s\n", linenr, filename, line.c_str()); + exit(1); +} + +void help(const char *cmd) +{ + printf("\n"); + printf("Usage: %s [options] from_hexfile to_hexfile \n", cmd); + printf("\n"); + // printf(" -S\n"); + // printf(" Disable SIMPLE feedback path mode\n"); + // printf("\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int opt; + while ((opt = getopt(argc, argv, "")) != -1) + { + switch (opt) + { + // case 'i': + // f_pllin = atof(optarg); + // break; + default: + help(argv[0]); + } + } + + if (optind+2 != argc) + help(argv[0]); + + const char *from_hexfile_n = argv[optind]; + ifstream from_hexfile_f(from_hexfile_n); + vector> from_hexfile; + + const char *to_hexfile_n = argv[optind+1]; + ifstream to_hexfile_f(to_hexfile_n); + vector> 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 (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++) + 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); + } + + vector ascfile_lines; + map>> 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); + } + } + } + + // FIXME: Do the actual thing + + 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; +} diff --git a/icebram/rundemo.sh b/icebram/rundemo.sh index 9358447..b2e7ecd 100644 --- a/icebram/rundemo.sh +++ b/icebram/rundemo.sh @@ -5,12 +5,7 @@ python3 makedemo.py yosys -p 'synth_ice40 -blif demo.blif' demo.v arachne-pnr -d 8k -w demo.pcf -o demo.asc demo.blif -if true; then - cp demo.asc demo_new.asc - cp demo_dat0.hex demo_dat1.hex -else - ./icebram -o demo_new.asc demo.asc demo_dat0.hex demo_dat1.hex -fi +./icebram demo_dat0.hex demo_dat1.hex < demo.asc > demo_new.asc icebox_vlog -n demo -p demo.pcf -c demo_new.asc > demo_new.v iverilog -o demo.vvp demo_tb.v demo_new.v $( yosys-config --datdir/ice40/cells_sim.v ) -- cgit v1.2.3 From 2c553baeac92026177dcf3be95cb793f1a5c973d Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 12 May 2016 18:32:07 +0200 Subject: Added icebram bitslice replacer --- icebram/icebram.cc | 115 ++++++++++++++++++++++++++++++++++++++++++++++++---- icebram/makedemo.py | 4 ++ icebram/rundemo.sh | 2 +- 3 files changed, 112 insertions(+), 9 deletions(-) diff --git a/icebram/icebram.cc b/icebram/icebram.cc index a4cb415..cea30f5 100644 --- a/icebram/icebram.cc +++ b/icebram/icebram.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ #include using std::map; +using std::pair; using std::vector; using std::string; using std::ifstream; @@ -63,22 +65,24 @@ void help(const char *cmd) printf("\n"); printf("Usage: %s [options] from_hexfile to_hexfile \n", cmd); printf("\n"); - // printf(" -S\n"); - // printf(" Disable SIMPLE feedback path mode\n"); - // printf("\n"); + printf(" -v\n"); + printf(" verbose output\n"); + printf("\n"); exit(1); } int main(int argc, char **argv) { + bool verbose = false; + int opt; - while ((opt = getopt(argc, argv, "")) != -1) + while ((opt = getopt(argc, argv, "v")) != -1) { switch (opt) { - // case 'i': - // f_pllin = atof(optarg); - // break; + case 'v': + verbose = true; + break; default: help(argv[0]); } @@ -87,6 +91,10 @@ int main(int argc, char **argv) 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> from_hexfile; @@ -125,6 +133,50 @@ int main(int argc, char **argv) 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, pair, int>> pattern; + + for (int i = 0; i < int(from_hexfile.at(0).size()); i++) + { + vector 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 ascfile_lines; map>> ascfile_hexdata; @@ -145,7 +197,54 @@ int main(int argc, char **argv) } } - // FIXME: Do the actual thing + 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 from_bitslice; + + for (int j = 0; j < 256; j++) + from_bitslice.push_back(bram_data.at(j / 16).at(16 * (j % 16) + i)); + + auto p = pattern.find(from_bitslice); + if (p != pattern.end()) + { + auto &to_bitslice = p->second.first; + + for (int j = 0; j < 256; j++) + bram_data.at(j / 16).at(16 * (j % 16) + i) = to_bitslice.at(j); + + max_replace_cnt = std::max(++p->second.second, max_replace_cnt); + } + } + } + + int min_replace_cnt = max_replace_cnt; + for (auto &it : pattern) + min_replace_cnt = std::min(min_replace_cnt, it.second.second); + + if (min_replace_cnt != max_replace_cnt) { + fprintf(stderr, "Found some bitslices up to %d times, others only %d times!\n", max_replace_cnt, min_replace_cnt); + exit(1); + } + + if (verbose) + fprintf(stderr, "Found and replaced %d instances of the memory.\n", max_replace_cnt); + + + // ------------------------------------------------------- + // Write ascfile to stdout for (size_t i = 0; i < ascfile_lines.size(); i++) { auto &line = ascfile_lines.at(i); diff --git a/icebram/makedemo.py b/icebram/makedemo.py index bcec93f..35d3653 100644 --- a/icebram/makedemo.py +++ b/icebram/makedemo.py @@ -8,6 +8,10 @@ while True: numrports = np.random.randint(1, 5) if bram_width * bram_depth * numrports < 16*4096: break +bram_width = 16 +bram_depth = 256 +numrports = 1 + with open("demo.v", "wt") as f: print("module demo (", file=f) for i in range(numrports): diff --git a/icebram/rundemo.sh b/icebram/rundemo.sh index b2e7ecd..4e90ea2 100644 --- a/icebram/rundemo.sh +++ b/icebram/rundemo.sh @@ -5,7 +5,7 @@ python3 makedemo.py yosys -p 'synth_ice40 -blif demo.blif' demo.v arachne-pnr -d 8k -w demo.pcf -o demo.asc demo.blif -./icebram demo_dat0.hex demo_dat1.hex < demo.asc > demo_new.asc +./icebram -v demo_dat0.hex demo_dat1.hex < demo.asc > demo_new.asc icebox_vlog -n demo -p demo.pcf -c demo_new.asc > demo_new.v iverilog -o demo.vvp demo_tb.v demo_new.v $( yosys-config --datdir/ice40/cells_sim.v ) -- cgit v1.2.3 From 8503d3edd12f62c78e1de4ecc91d5ca89250e13c Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 15 May 2016 00:37:45 +0200 Subject: Finished icebram --- icebram/icebram.cc | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++-- icebram/makedemo.py | 7 +++-- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/icebram/icebram.cc b/icebram/icebram.cc index cea30f5..c0bea32 100644 --- a/icebram/icebram.cc +++ b/icebram/icebram.cc @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -33,6 +35,14 @@ 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 parse_hexfile_line(const char *filename, int linenr, vector> &hexfile, string &line) { vector digits; @@ -63,7 +73,17 @@ error: void help(const char *cmd) { printf("\n"); - printf("Usage: %s [options] from_hexfile to_hexfile \n", cmd); + printf("Usage: %s [options] \n", cmd); + printf(" %s [options] -g \n", cmd); + printf("\n"); + printf("Replace BRAM initialization data in a .asc file. This can be used\n"); + printf("for example to replace firmware images without re-running synthesis\n"); + printf("and place&route.\n"); + printf("\n"); + printf(" -g\n"); + printf(" generate a hex file with random contents.\n"); + printf(" use this to generate the hex file used during synthesis, then\n"); + printf(" use the same file as later.\n"); printf("\n"); printf(" -v\n"); printf(" verbose output\n"); @@ -74,20 +94,70 @@ void help(const char *cmd) int main(int argc, char **argv) { bool verbose = false; + bool generate = false; int opt; - while ((opt = getopt(argc, argv, "v")) != -1) + while ((opt = getopt(argc, argv, "vg")) != -1) { switch (opt) { case 'v': verbose = true; break; + case 'g': + generate = true; + 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); + } + + x = uint64_t(getpid()) << 32; + x ^= uint64_t(depth) << 16; + x ^= uint64_t(width) << 10; + + xorshift64star(); + xorshift64star(); + xorshift64star(); + + 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(1); + } + if (optind+2 != argc) help(argv[0]); diff --git a/icebram/makedemo.py b/icebram/makedemo.py index 35d3653..a014484 100644 --- a/icebram/makedemo.py +++ b/icebram/makedemo.py @@ -8,11 +8,10 @@ while True: numrports = np.random.randint(1, 5) if bram_width * bram_depth * numrports < 16*4096: break -bram_width = 16 -bram_depth = 256 -numrports = 1 - with open("demo.v", "wt") as f: + print("// bram_width = %d" % bram_width, file=f) + print("// bram_depth = %d" % bram_depth, file=f) + print("// numrports = %d" % numrports, file=f) print("module demo (", file=f) for i in range(numrports): print(" input [%d:0] raddr%d," % (np.ceil(np.log2(bram_depth))-1, i), file=f) -- cgit v1.2.3