From 0347c37d56374447d170e9a8f59e56f84cbefcce Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 18 Jul 2015 13:05:51 +0200 Subject: Import of icestorm-snapshot-150401.zip --- icepack/icepack.cc | 1031 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1031 insertions(+) create mode 100644 icepack/icepack.cc (limited to 'icepack/icepack.cc') 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 +// +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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>> cram; + + // bram[BANK][X][Y] + int bram_width, bram_height; + vector>> bram; + + // data before preamble + vector 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 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 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 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 tile_bit_t; + std::set 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 FpgaConfig::chip_cols() const +{ + if (this->device == "384") return vector({18, 54, 54, 54}); + if (this->device == "1k") return vector({18, 54, 54, 42, 54, 54, 54}); + if (this->device == "4k8k") return vector({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 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; +} + -- cgit v1.2.3