aboutsummaryrefslogtreecommitdiffstats
path: root/icepack/icepack.cc
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2015-07-18 13:05:51 +0200
committerClifford Wolf <clifford@clifford.at>2015-07-18 13:05:51 +0200
commit0347c37d56374447d170e9a8f59e56f84cbefcce (patch)
treeada78ec11a4c109168dca3cc7aff1af2c3cda093 /icepack/icepack.cc
parentdfeb92a46b2228b4f3886a9c06502ccd0dde5562 (diff)
downloadicestorm-0347c37d56374447d170e9a8f59e56f84cbefcce.tar.gz
icestorm-0347c37d56374447d170e9a8f59e56f84cbefcce.tar.bz2
icestorm-0347c37d56374447d170e9a8f59e56f84cbefcce.zip
Import of icestorm-snapshot-150401.zip
Diffstat (limited to 'icepack/icepack.cc')
-rw-r--r--icepack/icepack.cc1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/icepack/icepack.cc b/icepack/icepack.cc
new file mode 100644
index 0000000..1614c5e
--- /dev/null
+++ b/icepack/icepack.cc
@@ -0,0 +1,1031 @@
+//
+// Copyright (C) 2015 Clifford Wolf <clifford@clifford.at>
+//
+// Based on a reference implementation provided by Mathias Lasser
+//
+// 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 <set>
+#include <tuple>
+#include <vector>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <cstdint>
+
+#include <stdio.h>
+#include <stdarg.h>
+
+using std::vector;
+using std::string;
+
+int log_level = 0;
+#define log(...) fprintf(stderr, __VA_ARGS__);
+#define info(...) do { if (log_level > 0) fprintf(stderr, __VA_ARGS__); } while (0)
+#define debug(...) do { if (log_level > 1) fprintf(stderr, __VA_ARGS__); } while (0)
+#define error(...) do { fprintf(stderr, "Error: " __VA_ARGS__); exit(1); } while (0)
+#define panic(fmt, ...) do { fprintf(stderr, "Internal Error at %s:%d: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); abort(); } while (0)
+
+string vstringf(const char *fmt, va_list ap)
+{
+ string string;
+ char *str = NULL;
+
+#ifdef _WIN32
+ int sz = 64, rc;
+ while (1) {
+ va_list apc;
+ va_copy(apc, ap);
+ str = (char*)realloc(str, sz);
+ rc = vsnprintf(str, sz, fmt, apc);
+ va_end(apc);
+ if (rc >= 0 && rc < sz)
+ break;
+ sz *= 2;
+ }
+#else
+ if (vasprintf(&str, fmt, ap) < 0)
+ str = NULL;
+#endif
+
+ if (str != NULL) {
+ string = str;
+ free(str);
+ }
+
+ return string;
+}
+
+string stringf(const char *fmt, ...)
+{
+ string string;
+ va_list ap;
+
+ va_start(ap, fmt);
+ string = vstringf(fmt, ap);
+ va_end(ap);
+
+ return string;
+}
+
+// ==================================================================
+// FpgaConfig stuff
+
+struct FpgaConfig
+{
+ string device;
+ string freqrange;
+ string warmboot;
+
+ // cram[BANK][X][Y]
+ int cram_width, cram_height;
+ vector<vector<vector<bool>>> cram;
+
+ // bram[BANK][X][Y]
+ int bram_width, bram_height;
+ vector<vector<vector<bool>>> bram;
+
+ // data before preamble
+ vector<uint8_t> initblop;
+
+ // bitstream i/o
+ void read_bits(std::istream &ifs);
+ void write_bits(std::ostream &ofs) const;
+
+ // icebox i/o
+ void read_ascii(std::istream &ifs);
+ void write_ascii(std::ostream &ofs) const;
+
+ // netpbm i/o
+ void write_cram_pbm(std::ostream &ofs, int bank_num = -1) const;
+
+ // query chip type metadata
+ int chip_width() const;
+ int chip_height() const;
+ vector<int> chip_cols() const;
+
+ // query tile metadata
+ string tile_type(int x, int y) const;
+ int tile_width(const string &type) const;
+
+ // cram bit manipulation
+ void cram_clear();
+ void cram_fill_tiles();
+ void cram_checkerboard(int m = 0);
+};
+
+struct CramIndexConverter
+{
+ const FpgaConfig *fpga;
+ int tile_x, tile_y;
+
+ string tile_type;
+ int tile_width;
+ int column_width;
+
+ bool left_right_io;
+ bool right_half;
+ bool top_half;
+
+ int bank_num;
+ int bank_tx;
+ int bank_ty;
+ int bank_xoff;
+ int bank_yoff;
+
+ CramIndexConverter(const FpgaConfig *fpga, int tile_x, int tile_y);
+ void get_cram_index(int bit_x, int bit_y, int &cram_bank, int &cram_x, int &cram_y) const;
+};
+
+static void update_crc16(uint16_t &crc, uint8_t byte)
+{
+ // CRC-16-CCITT, Initialize to 0xFFFF, No zero padding
+ for (int i = 7; i >= 0; i--) {
+ uint16_t xor_value = ((crc >> 15) ^ (byte >> i) & 1) ? 0x1021 : 0;
+ crc = (crc << 1) ^ xor_value;
+ }
+}
+
+static uint8_t read_byte(std::istream &ifs, uint16_t &crc_value, int &file_offset)
+{
+ int byte = ifs.get();
+
+ if (byte < 0)
+ error("Unexpected end of file.\n");
+
+ file_offset++;
+ update_crc16(crc_value, byte);
+
+ return byte;
+}
+
+static void write_byte(std::ostream &ofs, uint16_t &crc_value, int &file_offset, uint8_t byte)
+{
+ ofs << byte;
+ file_offset++;
+ update_crc16(crc_value, byte);
+}
+
+void FpgaConfig::read_bits(std::istream &ifs)
+{
+ int file_offset = 0;
+ uint16_t crc_value = 0;
+
+ debug("## %s\n", __PRETTY_FUNCTION__);
+ info("Parsing bitstream file..\n");
+
+ // skip initial comments until preamble is found
+
+ uint32_t preamble = 0;
+
+ while (1)
+ {
+ uint8_t byte = read_byte(ifs, crc_value, file_offset);
+ preamble = (preamble << 8) | byte;
+ if (preamble == 0xffffffff)
+ error("No preamble found in bitstream.\n");
+ if (preamble == 0x7EAA997E) {
+ info("Found preamble at offset %d.\n", file_offset-4);
+ break;
+ }
+ initblop.push_back(byte);
+ }
+
+ initblop.pop_back();
+ initblop.pop_back();
+ initblop.pop_back();
+
+ // main parser loop
+
+ int current_bank = 0;
+ int current_width = 0;
+ int current_height = 0;
+ int current_offset = 0;
+ bool wakeup = false;
+
+ this->cram_width = 0;
+ this->cram_height = 0;
+
+ this->bram_width = 0;
+ this->bram_height = 0;
+
+ while (!wakeup)
+ {
+ // one command byte. the lower 4 bits of the command byte specify
+ // the length of the command payload.
+
+ uint8_t command = read_byte(ifs, crc_value, file_offset);
+ uint32_t payload = 0;
+
+ for (int i = 0; i < (command & 0x0f); i++)
+ payload = (payload << 8) | read_byte(ifs, crc_value, file_offset);
+
+ debug("Next command at offset %d: 0x%02x 0x%0*x\n", file_offset - 1 - (command & 0x0f),
+ command, 2*(command & 0x0f), payload);
+
+ uint16_t end_token;
+
+ switch (command & 0xf0)
+ {
+ case 0x00:
+ switch (payload)
+ {
+ case 0x01:
+ info("CRAM Data [%d]: %d x %d bits = %d bits = %d bytes\n",
+ current_bank, current_width, current_height,
+ current_height*current_width, (current_height*current_width)/8);
+
+ this->cram_width = std::max(this->cram_width, current_width);
+ this->cram_height = std::max(this->cram_height, current_offset + current_height);
+
+ this->cram.resize(4);
+ this->cram[current_bank].resize(this->cram_width);
+ for (int x = 0; x < current_width; x++)
+ this->cram[current_bank][x].resize(this->cram_height);
+
+ for (int i = 0; i < (current_height*current_width)/8; i++) {
+ uint8_t byte = read_byte(ifs, crc_value, file_offset);
+ for (int j = 0; j < 8; j++) {
+ int x = (i*8 + j) % current_width;
+ int y = (i*8 + j) / current_width + current_offset;
+ this->cram[current_bank][x][y] = ((byte << j) & 0x80) != 0;
+ }
+ }
+
+ end_token = read_byte(ifs, crc_value, file_offset);
+ end_token = (end_token << 8) | read_byte(ifs, crc_value, file_offset);
+ if (end_token)
+ error("Expeded 0x0000 after CRAM data, got 0x%04x\n", end_token);
+ break;
+
+ case 0x03:
+ info("BRAM Data [%d]: %d x %d bits = %d bits = %d bytes\n",
+ current_bank, current_width, current_height,
+ current_height*current_width, (current_height*current_width)/8);
+
+ this->bram_width = std::max(this->bram_width, current_width);
+ this->bram_height = std::max(this->bram_height, current_offset + current_height);
+
+ this->bram.resize(4);
+ this->bram[current_bank].resize(this->bram_width);
+ for (int x = 0; x < current_width; x++)
+ this->bram[current_bank][x].resize(this->bram_height);
+
+ for (int i = 0; i < (current_height*current_width)/8; i++) {
+ uint8_t byte = read_byte(ifs, crc_value, file_offset);
+ for (int j = 0; j < 8; j++) {
+ int x = (i*8 + j) % current_width;
+ int y = (i*8 + j) / current_width + current_offset;
+ this->bram[current_bank][x][y] = ((byte << j) & 0x80) != 0;
+ }
+ }
+
+ end_token = read_byte(ifs, crc_value, file_offset);
+ end_token = (end_token << 8) | read_byte(ifs, crc_value, file_offset);
+ if (end_token)
+ error("Expeded 0x0000 after BRAM data, got 0x%04x\n", end_token);
+ break;
+
+ case 0x05:
+ debug("Resetting CRC.\n");
+ crc_value = 0xffff;
+ break;
+
+ case 0x06:
+ info("Wakeup.\n");
+ wakeup = true;
+ break;
+
+ default:
+ error("Unkown command: 0x%02x 0x%02x\n", command, payload);
+ }
+ break;
+
+ case 0x10:
+ current_bank = payload;
+ debug("Set bank to %d.\n", current_bank);
+ break;
+
+ case 0x20:
+ if (crc_value != 0)
+ error("CRC Check FAILED.\n");
+ info("CRC Check OK.\n");
+ break;
+
+ case 0x50:
+ if (payload == 0)
+ this->freqrange = "low";
+ else if (payload == 1)
+ this->freqrange = "medium";
+ else if (payload == 2)
+ this->freqrange = "high";
+ else
+ error("Unknown freqrange payload 0x%02x\n", payload);
+ info("Setting freqrange to '%s'.\n", this->freqrange.c_str());
+ break;
+
+ case 0x60:
+ current_width = payload + 1;
+ debug("Setting bank width to %d.\n", current_width);
+ break;
+
+ case 0x70:
+ current_height = payload;
+ debug("Setting bank height to %d.\n", current_height);
+ break;
+
+ case 0x80:
+ current_offset = payload;
+ debug("Setting bank offset to %d.\n", current_offset);
+ break;
+
+ case 0x90:
+ if (payload == 0)
+ this->warmboot = "disabled";
+ else if (payload == 32)
+ this->warmboot = "enabled";
+ else
+ error("Unknown warmboot payload 0x%02x\n", payload);
+ info("Setting warmboot to '%s'.\n", this->warmboot.c_str());
+ break;
+
+ default:
+ error("Unkown command: 0x%02x 0x%02x\n", command, payload);
+ }
+ }
+
+ if (this->cram_width == 182 && this->cram_height == 80)
+ this->device = "384";
+ else if (this->cram_width == 332 && this->cram_height == 144)
+ this->device = "1k";
+ else if (this->cram_width == 872 && this->cram_height == 272)
+ this->device = "4k8k";
+ else
+ error("Failed to detect chip type.\n");
+
+ info("Chip type is '%s'.\n", this->device.c_str());
+}
+
+void FpgaConfig::write_bits(std::ostream &ofs) const
+{
+ int file_offset = 0;
+ uint16_t crc_value = 0;
+
+ debug("## %s\n", __PRETTY_FUNCTION__);
+ info("Writing bitstream file..\n");
+
+ for (auto byte : this->initblop)
+ ofs << byte;
+
+ debug("Writing preamble.\n");
+ write_byte(ofs, crc_value, file_offset, 0x7E);
+ write_byte(ofs, crc_value, file_offset, 0xAA);
+ write_byte(ofs, crc_value, file_offset, 0x99);
+ write_byte(ofs, crc_value, file_offset, 0x7E);
+
+ debug("Setting freqrange to '%s'.\n", this->freqrange.c_str());
+ write_byte(ofs, crc_value, file_offset, 0x51);
+ if (this->freqrange == "low")
+ write_byte(ofs, crc_value, file_offset, 0x00);
+ else if (this->freqrange == "medium")
+ write_byte(ofs, crc_value, file_offset, 0x01);
+ else if (this->freqrange == "high")
+ write_byte(ofs, crc_value, file_offset, 0x02);
+ else
+ error("Unknown freqrange '%s'.\n", this->freqrange.c_str());
+
+ debug("Resetting CRC.\n");
+ write_byte(ofs, crc_value, file_offset, 0x01);
+ write_byte(ofs, crc_value, file_offset, 0x05);
+ crc_value = 0xffff;
+
+ debug("Setting warmboot to '%s'.\n", this->warmboot.c_str());
+ write_byte(ofs, crc_value, file_offset, 0x92);
+ write_byte(ofs, crc_value, file_offset, 0x00);
+ if (this->warmboot == "disabled")
+ write_byte(ofs, crc_value, file_offset, 0x00);
+ else if (this->warmboot == "enabled")
+ write_byte(ofs, crc_value, file_offset, 0x20);
+ else
+ error("Unknown warmboot setting '%s'.\n", this->warmboot.c_str());
+
+ debug("CRAM: Setting bank width to %d.\n", this->cram_width);
+ write_byte(ofs, crc_value, file_offset, 0x62);
+ write_byte(ofs, crc_value, file_offset, (this->cram_width-1) >> 8);
+ write_byte(ofs, crc_value, file_offset, (this->cram_width-1));
+
+ debug("CRAM: Setting bank height to %d.\n", this->cram_height);
+ write_byte(ofs, crc_value, file_offset, 0x72);
+ write_byte(ofs, crc_value, file_offset, this->cram_height >> 8);
+ write_byte(ofs, crc_value, file_offset, this->cram_height);
+
+ debug("CRAM: Setting bank offset to 0.\n");
+ write_byte(ofs, crc_value, file_offset, 0x82);
+ write_byte(ofs, crc_value, file_offset, 0x00);
+ write_byte(ofs, crc_value, file_offset, 0x00);
+
+ for (int cram_bank = 0; cram_bank < 4; cram_bank++)
+ {
+ vector<bool> cram_bits;
+ for (int cram_y = 0; cram_y < this->cram_height; cram_y++)
+ for (int cram_x = 0; cram_x < this->cram_width; cram_x++)
+ cram_bits.push_back(this->cram[cram_bank][cram_x][cram_y]);
+
+ debug("CRAM: Setting bank %d.\n", cram_bank);
+ write_byte(ofs, crc_value, file_offset, 0x11);
+ write_byte(ofs, crc_value, file_offset, cram_bank);
+
+ debug("CRAM: Writing bank %d data.\n", cram_bank);
+ write_byte(ofs, crc_value, file_offset, 0x01);
+ write_byte(ofs, crc_value, file_offset, 0x01);
+ for (int i = 0; i < cram_bits.size(); i += 8) {
+ uint8_t byte = 0;
+ for (int j = 0; j < 8; j++)
+ byte = (byte << 1) | (cram_bits[i+j] ? 1 : 0);
+ write_byte(ofs, crc_value, file_offset, byte);
+ }
+
+ write_byte(ofs, crc_value, file_offset, 0x00);
+ write_byte(ofs, crc_value, file_offset, 0x00);
+ }
+
+ int bram_chunk_size = 128;
+
+ debug("BRAM: Setting bank width to %d.\n", this->bram_width);
+ write_byte(ofs, crc_value, file_offset, 0x62);
+ write_byte(ofs, crc_value, file_offset, (this->bram_width-1) >> 8);
+ write_byte(ofs, crc_value, file_offset, (this->bram_width-1));
+
+ debug("BRAM: Setting bank height to %d.\n", this->bram_height);
+ write_byte(ofs, crc_value, file_offset, 0x72);
+ write_byte(ofs, crc_value, file_offset, bram_chunk_size >> 8);
+ write_byte(ofs, crc_value, file_offset, bram_chunk_size);
+
+ for (int bram_bank = 0; bram_bank < 4; bram_bank++)
+ {
+ debug("BRAM: Setting bank %d.\n", bram_bank);
+ write_byte(ofs, crc_value, file_offset, 0x11);
+ write_byte(ofs, crc_value, file_offset, bram_bank);
+
+ for (int offset = 0; offset < this->bram_height; offset += bram_chunk_size)
+ {
+ vector<bool> bram_bits;
+ for (int bram_y = 0; bram_y < bram_chunk_size; bram_y++)
+ for (int bram_x = 0; bram_x < this->bram_width; bram_x++)
+ bram_bits.push_back(this->bram[bram_bank][bram_x][bram_y+offset]);
+
+ debug("BRAM: Setting bank offset to %d.\n", offset);
+ write_byte(ofs, crc_value, file_offset, 0x82);
+ write_byte(ofs, crc_value, file_offset, offset >> 8);
+ write_byte(ofs, crc_value, file_offset, offset);
+
+ debug("BRAM: Writing bank %d data.\n", bram_bank);
+ write_byte(ofs, crc_value, file_offset, 0x01);
+ write_byte(ofs, crc_value, file_offset, 0x03);
+ for (int i = 0; i < bram_bits.size(); i += 8) {
+ uint8_t byte = 0;
+ for (int j = 0; j < 8; j++)
+ byte = (byte << 1) | (bram_bits[i+j] ? 1 : 0);
+ write_byte(ofs, crc_value, file_offset, byte);
+ }
+
+ write_byte(ofs, crc_value, file_offset, 0x00);
+ write_byte(ofs, crc_value, file_offset, 0x00);
+ }
+ }
+
+ debug("Writing CRC value.\n");
+ write_byte(ofs, crc_value, file_offset, 0x22);
+ uint8_t crc_hi = crc_value >> 8, crc_lo = crc_value;
+ write_byte(ofs, crc_value, file_offset, crc_hi);
+ write_byte(ofs, crc_value, file_offset, crc_lo);
+
+ debug("Wakeup.\n");
+ write_byte(ofs, crc_value, file_offset, 0x01);
+ write_byte(ofs, crc_value, file_offset, 0x06);
+
+ debug("Padding byte.\n");
+ write_byte(ofs, crc_value, file_offset, 0x00);
+}
+
+void FpgaConfig::read_ascii(std::istream &ifs)
+{
+ debug("## %s\n", __PRETTY_FUNCTION__);
+ info("Parsing ascii file..\n");
+
+ bool got_device = false;
+ this->cram.clear();
+ this->bram.clear();
+ this->freqrange = "low";
+ this->warmboot = "enabled";
+
+ bool reuse_line = true;
+ string line, command;
+
+ while (reuse_line || getline(ifs, line))
+ {
+ reuse_line = false;
+
+ std::istringstream is(line);
+ is >> command;
+
+ if (command.empty())
+ continue;
+
+ debug("Next command: %s\n", line.c_str());
+
+ if (command == ".comment")
+ {
+ this->initblop.clear();
+ this->initblop.push_back(0xff);
+ this->initblop.push_back(0x00);
+
+ while (getline(ifs, line))
+ {
+ if (line.substr(0, 1) == ".") {
+ reuse_line = true;
+ break;
+ }
+
+ for (auto ch : line)
+ this->initblop.push_back(ch);
+ this->initblop.push_back(0);
+ }
+
+ this->initblop.push_back(0x00);
+ this->initblop.push_back(0xff);
+ continue;
+ }
+
+ if (command == ".device")
+ {
+ is >> this->device;
+
+ if (this->device == "1k") {
+ this->cram_width = 332;
+ this->cram_height = 144;
+ this->bram_width = 64;
+ this->bram_height = 2 * 128;
+ } else
+ error("Unsupported chip type '%s'.\n", this->device.c_str());
+
+ this->cram.resize(4);
+ for (int i = 0; i < 4; i++) {
+ this->cram[i].resize(this->cram_width);
+ for (int x = 0; x < this->cram_width; x++)
+ this->cram[i][x].resize(this->cram_height);
+ }
+
+ this->bram.resize(4);
+ for (int i = 0; i < 4; i++) {
+ this->bram[i].resize(this->bram_width);
+ for (int x = 0; x < this->bram_width; x++)
+ this->bram[i][x].resize(this->bram_height);
+ }
+
+ got_device = true;
+ continue;
+ }
+
+ if (command == ".io_tile" || command == ".logic_tile" || command == ".ram_tile")
+ {
+ if (!got_device)
+ error("Missing .device statement before %s.\n", command.c_str());
+
+ int tile_x, tile_y;
+ is >> tile_x >> tile_y;
+
+ CramIndexConverter cic(this, tile_x, tile_y);
+
+ if (("." + cic.tile_type + "_tile") != command)
+ error("Got %s statement for %s tile %d %d.\n",
+ command.c_str(), cic.tile_type.c_str(), tile_x, tile_y);
+
+ for (int bit_y = 0; bit_y < 16 && getline(ifs, line); bit_y++)
+ {
+ if (line.substr(0, 1) == ".") {
+ reuse_line = true;
+ break;
+ }
+
+ for (int bit_x = 0; bit_x < line.size() && bit_x < cic.tile_width; bit_x++)
+ if (line[bit_x] == '1') {
+ int cram_bank, cram_x, cram_y;
+ cic.get_cram_index(bit_x, bit_y, cram_bank, cram_x, cram_y);
+ this->cram[cram_bank][cram_x][cram_y] = true;
+ }
+ }
+
+ continue;
+ }
+
+ if (command == ".extra_bit")
+ {
+ if (!got_device)
+ error("Missing .device statement before %s.\n", command.c_str());
+
+ int cram_bank, cram_x, cram_y;
+ is >> cram_bank >> cram_x >> cram_y;
+ this->cram[cram_bank][cram_x][cram_y] = true;
+
+ continue;
+ }
+
+ if (command.substr(0, 1) == ".")
+ error("Unknown statement: %s\n", command.c_str());
+ error("Unexpected data line: %s\n", line.c_str());
+ }
+}
+
+void FpgaConfig::write_ascii(std::ostream &ofs) const
+{
+ debug("## %s\n", __PRETTY_FUNCTION__);
+ info("Writing ascii file..\n");
+
+ ofs << ".comment";
+ bool insert_newline = true;
+ for (auto ch : this->initblop) {
+ if (ch == 0) {
+ insert_newline = true;
+ } else if (ch == 0xff) {
+ insert_newline = false;
+ } else {
+ if (insert_newline)
+ ofs << '\n';
+ ofs << ch;
+ insert_newline = false;
+ }
+ }
+
+ ofs << stringf("\n.device %s\n", this->device.c_str());
+
+ typedef std::tuple<int, int, int> tile_bit_t;
+ std::set<tile_bit_t> tile_bits;
+
+ for (int y = 0; y <= this->chip_height()+1; y++)
+ for (int x = 0; x <= this->chip_width()+1; x++)
+ {
+ CramIndexConverter cic(this, x, y);
+
+ if (cic.tile_type == "corner")
+ continue;
+
+ ofs << stringf(".%s_tile %d %d\n", cic.tile_type.c_str(), x, y);
+
+ for (int bit_y = 0; bit_y < 16; bit_y++) {
+ for (int bit_x = 0; bit_x < cic.tile_width; bit_x++) {
+ int cram_bank, cram_x, cram_y;
+ cic.get_cram_index(bit_x, bit_y, cram_bank, cram_x, cram_y);
+ tile_bits.insert(tile_bit_t(cram_bank, cram_x, cram_y));
+ ofs << (this->cram[cram_bank][cram_x][cram_y] ? '1' : '0');
+ }
+ ofs << '\n';
+ }
+ }
+
+ for (int i = 0; i < 4; i++)
+ for (int x = 0; x < this->cram_width; x++)
+ for (int y = 0; y < this->cram_height; y++)
+ if (this->cram[i][x][y] && tile_bits.count(tile_bit_t(i, x, y)) == 0)
+ ofs << stringf(".extra_bit %d %d %d\n", i, x, y);
+
+#if 0
+ for (int i = 0; i < 4; i++) {
+ ofs << stringf(".bram_bank %d\n", i);
+ for (int x = 0; x < this->bram_width; x++) {
+ for (int y = 0; y < this->bram_height; y += 4)
+ ofs << "0123456789abcdef"[(this->bram[i][x][y] ? 1 : 0) + (this->bram[i][x][y+1] ? 2 : 0) +
+ (this->bram[i][x][y+2] ? 4 : 0) + (this->bram[i][x][y+3] ? 8 : 0)];
+ ofs << '\n';
+ }
+ }
+#endif
+}
+
+void FpgaConfig::write_cram_pbm(std::ostream &ofs, int bank_num) const
+{
+ debug("## %s\n", __PRETTY_FUNCTION__);
+ info("Writing pbm file..\n");
+
+ ofs << "P1\n";
+ ofs << stringf("%d %d\n", 2*this->cram_width, 2*this->cram_height);
+ for (int y = 2*this->cram_height-1; y >= 0; y--) {
+ for (int x = 0; x < 2*this->cram_width; x++) {
+ int bank = 0, bank_x = x, bank_y = y;
+ if (bank_x >= this->cram_width)
+ bank |= 1, bank_x = 2*this->cram_width - bank_x - 1;
+ if (bank_y >= this->cram_height)
+ bank |= 2, bank_y = 2*this->cram_height - bank_y - 1;
+ if (bank_num >= 0 && bank != bank_num)
+ ofs << " 0";
+ else
+ ofs << (this->cram[bank][bank_x][bank_y] ? " 1" : " 0");
+ }
+ ofs << '\n';
+ }
+}
+
+int FpgaConfig::chip_width() const
+{
+ if (this->device == "384") return 6;
+ if (this->device == "1k") return 12;
+ if (this->device == "4k8k") return 32;
+ panic("Unkown chip type '%s'.\n", this->device.c_str());
+}
+
+int FpgaConfig::chip_height() const
+{
+ if (this->device == "384") return 8;
+ if (this->device == "1k") return 16;
+ if (this->device == "4k8k") return 32;
+ panic("Unkown chip type '%s'.\n", this->device.c_str());
+}
+
+vector<int> FpgaConfig::chip_cols() const
+{
+ if (this->device == "384") return vector<int>({18, 54, 54, 54});
+ if (this->device == "1k") return vector<int>({18, 54, 54, 42, 54, 54, 54});
+ if (this->device == "4k8k") return vector<int>({18, 2, 54, 54, 54, 54, 54, 54, 54, 42, 54, 54, 54, 54, 54, 54, 54, 54});
+ panic("Unkown chip type '%s'.\n", this->device.c_str());
+}
+
+string FpgaConfig::tile_type(int x, int y) const
+{
+ if (this->device == "1k") {
+ if ((x == 0 || x == this->chip_width()+1) && (y == 0 || y == this->chip_height()+1)) return "corner";
+ if ((x == 0 || x == this->chip_width()+1) || (y == 0 || y == this->chip_height()+1)) return "io";
+ if (x == 3 || x == 10) return "ram";
+ return "logic";
+ }
+ panic("Unkown chip type '%s'.\n", this->device.c_str());
+}
+
+int FpgaConfig::tile_width(const string &type) const
+{
+ if (type == "corner") return 0;
+ if (type == "logic") return 54;
+ if (type == "ram") return 42;
+ if (type == "io") return 18;
+ panic("Unkown tile type '%s'.\n", type.c_str());
+}
+
+void FpgaConfig::cram_clear()
+{
+ for (int i = 0; i < 4; i++)
+ for (int x = 0; x < this->cram_width; x++)
+ for (int y = 0; y < this->cram_height; y++)
+ this->cram[i][x][y] = false;
+}
+
+void FpgaConfig::cram_fill_tiles()
+{
+ for (int y = 0; y <= this->chip_height()+1; y++)
+ for (int x = 0; x <= this->chip_width()+1; x++)
+ {
+ CramIndexConverter cic(this, x, y);
+
+ for (int bit_y = 0; bit_y < 16; bit_y++)
+ for (int bit_x = 0; bit_x < cic.tile_width; bit_x++) {
+ int cram_bank, cram_x, cram_y;
+ cic.get_cram_index(bit_x, bit_y, cram_bank, cram_x, cram_y);
+ this->cram[cram_bank][cram_x][cram_y] = true;
+ }
+ }
+}
+
+void FpgaConfig::cram_checkerboard(int m)
+{
+ for (int y = 0; y <= this->chip_height()+1; y++)
+ for (int x = 0; x <= this->chip_width()+1; x++)
+ {
+ if ((x+y) % 2 == m)
+ continue;
+
+ CramIndexConverter cic(this, x, y);
+
+ for (int bit_y = 0; bit_y < 16; bit_y++)
+ for (int bit_x = 0; bit_x < cic.tile_width; bit_x++) {
+ int cram_bank, cram_x, cram_y;
+ cic.get_cram_index(bit_x, bit_y, cram_bank, cram_x, cram_y);
+ this->cram[cram_bank][cram_x][cram_y] = true;
+ }
+ }
+}
+
+CramIndexConverter::CramIndexConverter(const FpgaConfig *fpga, int tile_x, int tile_y)
+{
+ this->fpga = fpga;
+ this->tile_x = tile_x;
+ this->tile_y = tile_y;
+
+
+ this->tile_type = fpga->tile_type(this->tile_x, this->tile_y);
+ this->tile_width = fpga->tile_width(this->tile_type);
+
+ auto chip_width = fpga->chip_width();
+ auto chip_height = fpga->chip_height();
+ auto chip_cols = fpga->chip_cols();
+
+ this->left_right_io = this->tile_x == 0 || this->tile_x == chip_width+1;
+ this->right_half = this->tile_x > chip_width / 2;
+ this->top_half = this->tile_y > chip_height / 2;
+
+ this->bank_num = 0;
+ if (this->top_half) this->bank_num |= 1;
+ if (this->right_half) this->bank_num |= 2;
+
+ this->bank_tx = this->right_half ? chip_width + 1 - this->tile_x : this->tile_x;
+ this->bank_ty = this->top_half ? chip_height + 1 - this->tile_y : this->tile_y;
+
+ this->bank_xoff = 0;
+ for (int i = 0; i < this->bank_tx; i++)
+ this->bank_xoff += chip_cols.at(i);
+
+ this->bank_yoff = 16 * this->bank_ty;
+
+ this->column_width = chip_cols.at(this->bank_tx);
+}
+
+void CramIndexConverter::get_cram_index(int bit_x, int bit_y, int &cram_bank, int &cram_x, int &cram_y) const
+{
+ static const int io_top_bottom_permx[18] = {23, 25, 26, 27, 16, 17, 18, 19, 20, 14, 32, 33, 34, 35, 36, 37, 4, 5};
+ static const int io_top_bottom_permy[16] = {0, 1, 3, 2, 4, 5, 7, 6, 8, 9, 11, 10, 12, 13, 15, 14};
+
+ cram_bank = bank_num;
+
+ if (tile_type == "io")
+ {
+ if (left_right_io)
+ {
+ cram_x = bank_xoff + column_width - 1 - bit_x;
+
+ if (top_half)
+ cram_y = bank_yoff + 15 - bit_y;
+ else
+ cram_y = bank_yoff + bit_y;
+ }
+ else
+ {
+ cram_y = bank_yoff + 15 - io_top_bottom_permy[bit_y];
+
+ if (right_half)
+ cram_x = bank_xoff + column_width - 1 - io_top_bottom_permx[bit_x];
+ else
+ cram_x = bank_xoff + io_top_bottom_permx[bit_x];
+ }
+ }
+ else
+ {
+ if (right_half)
+ cram_x = bank_xoff + column_width - 1 - bit_x;
+ else
+ cram_x = bank_xoff + bit_x;
+
+ if (top_half)
+ cram_y = bank_yoff + (15 - bit_y);
+ else
+ cram_y = bank_yoff + bit_y;
+ }
+}
+
+
+// ==================================================================
+// Main program
+
+void usage()
+{
+ log("\n");
+ log("Usage: icepack [options] [input-file [output-file]]\n");
+ log("\n");
+ log(" -u\n");
+ log(" unpack mode (implied when called as 'iceunpack')\n");
+ log("\n");
+ log(" -v\n");
+ log(" verbose (repeat to increase verbosity)\n");
+ log("\n");
+ log(" -b\n");
+ log(" write cram bitmap as netpbm file\n");
+ log("\n");
+ log(" -f\n");
+ log(" write cram bitmap (fill tiles) as netpbm file\n");
+ log("\n");
+ log(" -c\n");
+ log(" write cram bitmap (checkerboard) as netpbm file\n");
+ log(" repeat to flip the selection of tiles\n");
+ log("\n");
+ log(" -B0, -B1, -B2, -B3\n");
+ log(" only include the specified bank in the netpbm file\n");
+ log("\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ vector<string> parameters;
+ bool unpack_mode = false;
+ bool netpbm_mode = false;
+ bool netpbm_fill_tiles = false;
+ bool netpbm_checkerboard = false;
+ int netpbm_banknum = -1;
+ int checkerboard_m = 1;
+
+ for (int i = 0; argv[0][i]; i++)
+ if (string(argv[0]+i) == "iceunpack")
+ unpack_mode = true;
+
+ for (int i = 1; i < argc; i++)
+ {
+ string arg(argv[i]);
+
+ if (arg[0] == '-' && arg.size() > 1) {
+ for (int i = 1; i < arg.size(); i++)
+ if (arg[i] == 'u') {
+ unpack_mode = true;
+ } else if (arg[i] == 'b') {
+ netpbm_mode = true;
+ } else if (arg[i] == 'f') {
+ netpbm_mode = true;
+ netpbm_fill_tiles = true;
+ } else if (arg[i] == 'c') {
+ netpbm_mode = true;
+ netpbm_checkerboard = true;
+ checkerboard_m = !checkerboard_m;
+ } else if (arg[i] == 'B') {
+ netpbm_mode = true;
+ netpbm_banknum = arg[++i] - '0';
+ } else if (arg[i] == 'v') {
+ log_level++;
+ } else
+ usage();
+ continue;
+ }
+
+ parameters.push_back(arg);
+ }
+
+ std::ifstream ifs;
+ std::ofstream ofs;
+
+ std::istream *isp;
+ std::ostream *osp;
+
+ if (parameters.size() >= 1 && parameters[0] != "-") {
+ ifs.open(parameters[0]);
+ if (!ifs.is_open())
+ error("Failed to open input file.\n");
+ isp = &ifs;
+ } else {
+ isp = &std::cin;
+ }
+
+ if (parameters.size() >= 2 && parameters[1] != "-") {
+ ofs.open(parameters[1]);
+ if (!ofs.is_open())
+ error("Failed to open output file.\n");
+ osp = &ofs;
+ } else {
+ osp = &std::cout;
+ }
+
+ if (parameters.size() > 2)
+ usage();
+
+ FpgaConfig fpga_config;
+
+ if (unpack_mode) {
+ fpga_config.read_bits(*isp);
+ if (!netpbm_mode)
+ fpga_config.write_ascii(*osp);
+ } else {
+ fpga_config.read_ascii(*isp);
+ if (!netpbm_mode)
+ fpga_config.write_bits(*osp);
+ }
+
+ if (netpbm_checkerboard) {
+ fpga_config.cram_clear();
+ fpga_config.cram_checkerboard(checkerboard_m);
+ }
+
+ if (netpbm_fill_tiles)
+ fpga_config.cram_fill_tiles();
+
+ if (netpbm_mode)
+ fpga_config.write_cram_pbm(*osp, netpbm_banknum);
+
+ info("Done.\n");
+ return 0;
+}
+