2015-07-18 13:05:51 +02:00
|
|
|
//
|
2025-05-20 14:14:56 +02:00
|
|
|
// Copyright (C) 2015 Claire Xenia Wolf <claire@clairexen.net>
|
2015-07-18 13:05:51 +02:00
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
|
2016-07-26 10:28:28 +02:00
|
|
|
#if !defined(_WIN32) && !defined(_GNU_SOURCE)
|
|
|
|
|
// for vasprintf()
|
2016-07-26 10:05:56 +02:00
|
|
|
#define _GNU_SOURCE
|
2016-07-26 10:28:28 +02:00
|
|
|
#endif
|
2016-07-26 10:05:56 +02:00
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
#include <set>
|
|
|
|
|
#include <tuple>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <iostream>
|
2015-08-21 19:50:52 +02:00
|
|
|
#include <algorithm>
|
2015-07-18 13:05:51 +02:00
|
|
|
#include <sstream>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
2017-11-15 12:14:14 +01:00
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
|
|
#include <emscripten.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-08-21 16:53:24 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
#define __PRETTY_FUNCTION__ __FUNCTION__
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
using std::vector;
|
|
|
|
|
using std::string;
|
|
|
|
|
|
2017-03-06 01:24:20 +01:00
|
|
|
int log_level = 0;
|
2015-07-18 13:05:51 +02:00
|
|
|
#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;
|
2017-12-31 07:34:36 +01:00
|
|
|
string nosleep;
|
2015-07-18 13:05:51 +02:00
|
|
|
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;
|
2019-05-15 16:57:17 +02:00
|
|
|
bool skip_bram_initialization;
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
// 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
|
2017-12-31 07:52:59 +01:00
|
|
|
void read_ascii(std::istream &ifs, bool nosleep);
|
2015-07-18 13:05:51 +02:00
|
|
|
void write_ascii(std::ostream &ofs) const;
|
|
|
|
|
|
|
|
|
|
// netpbm i/o
|
|
|
|
|
void write_cram_pbm(std::ostream &ofs, int bank_num = -1) const;
|
2015-07-18 13:07:39 +02:00
|
|
|
void write_bram_pbm(std::ostream &ofs, int bank_num = -1) const;
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-18 13:07:39 +02:00
|
|
|
struct BramIndexConverter
|
|
|
|
|
{
|
|
|
|
|
const FpgaConfig *fpga;
|
|
|
|
|
int tile_x, tile_y;
|
|
|
|
|
|
|
|
|
|
int bank_num;
|
|
|
|
|
int bank_off;
|
|
|
|
|
|
|
|
|
|
BramIndexConverter(const FpgaConfig *fpga, int tile_x, int tile_y);
|
2017-06-23 02:38:38 +02:00
|
|
|
void get_bram_index(int bit_x, int bit_y, int &bram_bank, int &bram_x, int &bram_y) const;
|
2015-07-18 13:07:39 +02:00
|
|
|
};
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
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--) {
|
2015-07-18 13:07:39 +02:00
|
|
|
uint16_t xor_value = ((crc >> 15) ^ ((byte >> i) & 1)) ? 0x1021 : 0;
|
2015-07-18 13:05:51 +02:00
|
|
|
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.
|
2017-06-21 01:50:52 +02:00
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
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)
|
2019-09-13 12:55:48 +02:00
|
|
|
error("Expected 0x0000 after BRAM data, got 0x%04x\n", end_token);
|
2015-07-18 13:05:51 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x05:
|
|
|
|
|
debug("Resetting CRC.\n");
|
|
|
|
|
crc_value = 0xffff;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x06:
|
|
|
|
|
info("Wakeup.\n");
|
|
|
|
|
wakeup = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2016-02-27 17:36:55 +01:00
|
|
|
error("Unknown command: 0x%02x 0x%02x\n", command, payload);
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
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:
|
2017-12-31 07:34:36 +01:00
|
|
|
switch(payload)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
this->warmboot = "disabled";
|
|
|
|
|
this->nosleep = "disabled";
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
this->warmboot = "disabled";
|
|
|
|
|
this->nosleep = "enabled";
|
|
|
|
|
break;
|
|
|
|
|
case 32:
|
|
|
|
|
this->warmboot = "enabled";
|
|
|
|
|
this->nosleep = "disabled";
|
|
|
|
|
break;
|
|
|
|
|
case 33:
|
|
|
|
|
this->warmboot = "enabled";
|
|
|
|
|
this->nosleep = "enabled";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
error("Unknown warmboot/nosleep payload 0x%02x\n", payload);
|
|
|
|
|
}
|
|
|
|
|
info("Setting warmboot to '%s', nosleep to '%s'.\n", this->warmboot.c_str(), this->nosleep.c_str());
|
2015-07-18 13:05:51 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2016-02-27 17:36:55 +01:00
|
|
|
error("Unknown command: 0x%02x 0x%02x\n", command, payload);
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-05 22:25:47 +01:00
|
|
|
if (this->cram_width == 182 && this->cram_height == 80)
|
|
|
|
|
this->device = "384";
|
|
|
|
|
else if (this->cram_width == 332 && this->cram_height == 144)
|
2015-07-18 13:05:51 +02:00
|
|
|
this->device = "1k";
|
|
|
|
|
else if (this->cram_width == 872 && this->cram_height == 272)
|
2015-07-18 13:10:40 +02:00
|
|
|
this->device = "8k";
|
2017-06-21 01:50:52 +02:00
|
|
|
else if (this->cram_width == 692 && this->cram_height == 336)
|
|
|
|
|
this->device = "5k";
|
2018-02-13 21:22:05 +01:00
|
|
|
else if (this->cram_width == 692 && this->cram_height == 176)
|
|
|
|
|
this->device = "u4k";
|
2018-05-06 20:10:36 +02:00
|
|
|
else if (this->cram_width == 656 && this->cram_height == 176)
|
|
|
|
|
this->device = "lm4k";
|
2015-07-18 13:05:51 +02:00
|
|
|
else
|
|
|
|
|
error("Failed to detect chip type.\n");
|
2017-06-21 01:50:52 +02:00
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
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;
|
|
|
|
|
|
2017-07-31 15:53:59 +02:00
|
|
|
debug("Writing preamble.\n");
|
2015-07-18 13:05:51 +02:00
|
|
|
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;
|
|
|
|
|
|
2017-12-31 07:52:59 +01:00
|
|
|
{
|
|
|
|
|
uint8_t nosleep_flag;
|
|
|
|
|
debug("Setting warmboot to '%s', nosleep to '%s'.\n", this->warmboot.c_str(), this->nosleep.c_str());
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, 0x92);
|
2015-07-18 13:05:51 +02:00
|
|
|
write_byte(ofs, crc_value, file_offset, 0x00);
|
2017-12-31 07:52:59 +01:00
|
|
|
|
|
|
|
|
if (this->nosleep == "disabled")
|
|
|
|
|
nosleep_flag = 0;
|
|
|
|
|
else if (this->nosleep == "enabled")
|
|
|
|
|
nosleep_flag = 1;
|
|
|
|
|
else
|
|
|
|
|
error("Unknown nosleep setting '%s'.\n", this->nosleep.c_str());
|
|
|
|
|
|
|
|
|
|
if (this->warmboot == "disabled")
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, 0x00 | nosleep_flag);
|
|
|
|
|
else if (this->warmboot == "enabled")
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, 0x20 | nosleep_flag);
|
|
|
|
|
else
|
|
|
|
|
error("Unknown warmboot setting '%s'.\n", this->warmboot.c_str());
|
|
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
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));
|
2017-11-05 17:17:57 +01:00
|
|
|
if(this->device != "5k") {
|
|
|
|
|
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);
|
|
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
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;
|
2017-11-05 17:17:57 +01:00
|
|
|
int height = this->cram_height;
|
|
|
|
|
if(this->device == "5k" && ((cram_bank % 2) == 1))
|
|
|
|
|
height = height / 2 + 8;
|
|
|
|
|
for (int cram_y = 0; cram_y < height; cram_y++)
|
2015-07-18 13:05:51 +02:00
|
|
|
for (int cram_x = 0; cram_x < this->cram_width; cram_x++)
|
|
|
|
|
cram_bits.push_back(this->cram[cram_bank][cram_x][cram_y]);
|
2017-12-31 07:34:36 +01:00
|
|
|
|
2017-11-05 17:17:57 +01:00
|
|
|
if(this->device == "5k") {
|
|
|
|
|
debug("CRAM: Setting bank height to %d.\n", height);
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, 0x72);
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, height >> 8);
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, height);
|
|
|
|
|
}
|
2017-12-31 07:34:36 +01:00
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
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);
|
2015-07-18 13:07:39 +02:00
|
|
|
for (int i = 0; i < int(cram_bits.size()); i += 8) {
|
2015-07-18 13:05:51 +02:00
|
|
|
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;
|
|
|
|
|
|
2017-03-07 08:08:10 +01:00
|
|
|
if (this->bram_width && this->bram_height)
|
2015-07-18 13:05:51 +02:00
|
|
|
{
|
2017-11-05 17:17:57 +01:00
|
|
|
if(this->device != "5k") {
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-06 16:56:32 +01:00
|
|
|
|
|
|
|
|
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);
|
2015-07-18 13:05:51 +02:00
|
|
|
|
2017-03-06 16:56:32 +01:00
|
|
|
for (int bram_bank = 0; bram_bank < 4; bram_bank++)
|
2015-07-18 13:05:51 +02:00
|
|
|
{
|
2017-03-06 16:56:32 +01:00
|
|
|
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);
|
2015-07-18 13:05:51 +02:00
|
|
|
|
2017-03-06 16:56:32 +01:00
|
|
|
for (int offset = 0; offset < this->bram_height; offset += bram_chunk_size)
|
|
|
|
|
{
|
|
|
|
|
vector<bool> bram_bits;
|
2017-11-05 17:17:57 +01:00
|
|
|
int width = this->bram_width;
|
|
|
|
|
if(this->device == "5k" && ((bram_bank % 2) == 1))
|
|
|
|
|
width = width / 2;
|
2017-03-06 16:56:32 +01:00
|
|
|
for (int bram_y = 0; bram_y < bram_chunk_size; bram_y++)
|
2017-11-05 17:17:57 +01:00
|
|
|
for (int bram_x = 0; bram_x < width; bram_x++)
|
2017-03-06 16:56:32 +01:00
|
|
|
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);
|
|
|
|
|
|
2017-11-05 17:17:57 +01:00
|
|
|
if(this->device == "5k") {
|
|
|
|
|
debug("BRAM: Setting bank width to %d.\n", width);
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, 0x62);
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, (width-1) >> 8);
|
|
|
|
|
write_byte(ofs, crc_value, file_offset, (width-1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-05-15 16:57:17 +02:00
|
|
|
if (!this->skip_bram_initialization) {
|
|
|
|
|
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 < int(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);
|
|
|
|
|
}
|
2017-03-06 16:56:32 +01:00
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-31 07:52:59 +01:00
|
|
|
void FpgaConfig::read_ascii(std::istream &ifs, bool nosleep)
|
2015-07-18 13:05:51 +02:00
|
|
|
{
|
|
|
|
|
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")
|
|
|
|
|
{
|
2015-07-18 13:07:39 +02:00
|
|
|
if (got_device)
|
|
|
|
|
error("More than one .device statement.\n");
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
is >> this->device;
|
|
|
|
|
|
2017-03-05 22:25:47 +01:00
|
|
|
if (this->device == "384") {
|
|
|
|
|
this->cram_width = 182;
|
|
|
|
|
this->cram_height = 80;
|
|
|
|
|
this->bram_width = 0;
|
|
|
|
|
this->bram_height = 0;
|
|
|
|
|
} else
|
2015-07-18 13:05:51 +02:00
|
|
|
if (this->device == "1k") {
|
|
|
|
|
this->cram_width = 332;
|
|
|
|
|
this->cram_height = 144;
|
|
|
|
|
this->bram_width = 64;
|
|
|
|
|
this->bram_height = 2 * 128;
|
2015-07-18 13:10:40 +02:00
|
|
|
} else
|
|
|
|
|
if (this->device == "8k") {
|
|
|
|
|
this->cram_width = 872;
|
|
|
|
|
this->cram_height = 272;
|
|
|
|
|
this->bram_width = 128;
|
|
|
|
|
this->bram_height = 2 * 128;
|
2017-06-21 01:50:52 +02:00
|
|
|
} else
|
|
|
|
|
if (this->device == "5k") {
|
|
|
|
|
this->cram_width = 692;
|
|
|
|
|
this->cram_height = 336;
|
|
|
|
|
this->bram_width = 160;
|
|
|
|
|
this->bram_height = 2 * 128;
|
2018-02-13 21:22:05 +01:00
|
|
|
} else
|
|
|
|
|
if (this->device == "u4k") {
|
|
|
|
|
this->cram_width = 692;
|
|
|
|
|
this->cram_height = 176;
|
|
|
|
|
this->bram_width = 80;
|
|
|
|
|
this->bram_height = 2 * 128;
|
2018-05-06 20:10:36 +02:00
|
|
|
} else
|
|
|
|
|
if (this->device == "lm4k") {
|
|
|
|
|
this->cram_width = 656;
|
|
|
|
|
this->cram_height = 176;
|
|
|
|
|
this->bram_width = 80;
|
|
|
|
|
this->bram_height = 2 * 128;
|
2015-07-18 13:05:51 +02:00
|
|
|
} else
|
|
|
|
|
error("Unsupported chip type '%s'.\n", this->device.c_str());
|
|
|
|
|
|
|
|
|
|
this->cram.resize(4);
|
2017-11-05 17:17:57 +01:00
|
|
|
if(this->device == "5k") {
|
|
|
|
|
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(((i % 2) == 1) ? (this->cram_height / 2 + 8) : this->cram_height);
|
|
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
|
2017-11-05 17:17:57 +01:00
|
|
|
this->bram.resize(4);
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
int width = ((i % 2) == 1) ? (this->bram_width / 2) : this->bram_width;
|
|
|
|
|
this->bram[i].resize(width);
|
|
|
|
|
for (int x = 0; x < width; x++)
|
|
|
|
|
this->bram[i][x].resize(this->bram_height);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
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);
|
|
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-05 17:17:57 +01:00
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
got_device = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-31 19:49:59 +02:00
|
|
|
if (command == ".warmboot")
|
|
|
|
|
{
|
|
|
|
|
is >> this->warmboot;
|
|
|
|
|
|
|
|
|
|
if (this->warmboot != "disabled" &&
|
|
|
|
|
this->warmboot != "enabled")
|
|
|
|
|
error("Unknown warmboot setting '%s'.\n",
|
|
|
|
|
this->warmboot.c_str());
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-31 07:52:59 +01:00
|
|
|
// No ".nosleep" section despite sharing the same byte as .warmboot.
|
|
|
|
|
// ".nosleep" is specified when icepack is invoked, which is too late.
|
|
|
|
|
// So we inject the section based on command line argument.
|
|
|
|
|
if (nosleep)
|
|
|
|
|
this->nosleep = "enabled";
|
|
|
|
|
else
|
|
|
|
|
this->nosleep = "disabled";
|
|
|
|
|
|
2017-11-08 17:05:42 +01:00
|
|
|
if (command == ".io_tile" || command == ".logic_tile" || command == ".ramb_tile" || command == ".ramt_tile" || command.substr(0, 4) == ".dsp" || command == ".ipcon_tile")
|
2015-07-18 13:05:51 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:07:39 +02:00
|
|
|
for (int bit_x = 0; bit_x < int(line.size()) && bit_x < cic.tile_width; bit_x++)
|
2015-07-18 13:05:51 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:07:39 +02:00
|
|
|
if (command == ".ram_data")
|
|
|
|
|
{
|
|
|
|
|
if (!got_device)
|
|
|
|
|
error("Missing .device statement before %s.\n", command.c_str());
|
|
|
|
|
|
|
|
|
|
int tile_x, tile_y;
|
|
|
|
|
is >> tile_x >> tile_y;
|
|
|
|
|
|
|
|
|
|
BramIndexConverter bic(this, 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 = 256-4, ch_idx = 0; ch_idx < int(line.size()) && bit_x >= 0; bit_x -= 4, ch_idx++)
|
|
|
|
|
{
|
|
|
|
|
int value = -1;
|
|
|
|
|
if ('0' <= line[ch_idx] && line[ch_idx] <= '9')
|
|
|
|
|
value = line[ch_idx] - '0';
|
|
|
|
|
if ('a' <= line[ch_idx] && line[ch_idx] <= 'f')
|
|
|
|
|
value = line[ch_idx] - 'a' + 10;
|
|
|
|
|
if ('A' <= line[ch_idx] && line[ch_idx] <= 'F')
|
|
|
|
|
value = line[ch_idx] - 'A' + 10;
|
|
|
|
|
if (value < 0)
|
|
|
|
|
error("Not a hex character: '%c' (in line '%s')\n", line[ch_idx], line.c_str());
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
if ((value & (1 << i)) != 0) {
|
2017-06-23 02:38:38 +02:00
|
|
|
int bram_bank, bram_x, bram_y;
|
2015-07-18 13:07:39 +02:00
|
|
|
bic.get_bram_index(bit_x+i, bit_y, bram_bank, bram_x, bram_y);
|
|
|
|
|
this->bram[bram_bank][bram_x][bram_y] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2017-06-21 01:50:52 +02:00
|
|
|
|
2015-07-18 13:07:39 +02:00
|
|
|
if (command == ".sym")
|
|
|
|
|
continue;
|
2017-06-21 01:50:52 +02:00
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
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());
|
2017-08-31 19:49:59 +02:00
|
|
|
if (this->warmboot != "enabled")
|
|
|
|
|
ofs << stringf(".warmboot %s\n", this->warmboot.c_str());
|
2015-07-18 13:05:51 +02:00
|
|
|
|
2017-12-31 07:34:36 +01:00
|
|
|
// As "nosleep" is an icepack command, we do not write out a ".nosleep"
|
|
|
|
|
// section. However, we parse it in read_bits() and notify the user in
|
|
|
|
|
// info.
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
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);
|
|
|
|
|
|
2017-06-24 07:53:54 +02:00
|
|
|
if (cic.tile_type == "corner" || cic.tile_type == "unsupported")
|
2015-07-18 13:05:51 +02:00
|
|
|
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));
|
2017-06-24 07:53:54 +02:00
|
|
|
if (cram_x > int(this->cram[cram_bank].size())) {
|
2024-06-21 23:36:34 +02:00
|
|
|
error("cram_x %d (bit %d, %d) larger than bank size %zu\n", cram_x, bit_x, bit_y, this->cram[cram_bank].size());
|
2017-06-24 07:53:54 +02:00
|
|
|
}
|
|
|
|
|
if (cram_y > int(this->cram[cram_bank][cram_x].size())) {
|
2024-06-21 23:36:34 +02:00
|
|
|
error("cram_y %d (bit %d, %d) larger than bank %d size %zu\n", cram_y, bit_x, bit_y, cram_bank, this->cram[cram_bank][cram_x].size());
|
2017-06-24 07:53:54 +02:00
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
ofs << (this->cram[cram_bank][cram_x][cram_y] ? '1' : '0');
|
|
|
|
|
}
|
|
|
|
|
ofs << '\n';
|
|
|
|
|
}
|
2015-07-18 13:07:39 +02:00
|
|
|
|
2019-08-08 17:02:44 +02:00
|
|
|
if (cic.tile_type == "ramb" && !this->bram.empty())
|
2015-07-18 13:07:39 +02:00
|
|
|
{
|
|
|
|
|
BramIndexConverter bic(this, x, y);
|
|
|
|
|
ofs << stringf(".ram_data %d %d\n", x, y);
|
|
|
|
|
|
2017-06-23 02:38:38 +02:00
|
|
|
for (int bit_y = 0; bit_y < 16; bit_y++) {
|
|
|
|
|
for (int bit_x = 256-4; bit_x >= 0; bit_x -= 4) {
|
2015-07-18 13:07:39 +02:00
|
|
|
int value = 0;
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
2017-06-23 02:38:38 +02:00
|
|
|
int bram_bank, bram_x, bram_y;
|
2015-07-18 13:07:39 +02:00
|
|
|
bic.get_bram_index(bit_x+i, bit_y, bram_bank, bram_x, bram_y);
|
2017-06-23 02:38:38 +02:00
|
|
|
if (bram_x >= int(this->bram[bram_bank].size())) {
|
2024-06-21 23:36:34 +02:00
|
|
|
error("%d %d bram_x %d higher than loaded bram size %zu\n",bit_x+i, bit_y, bram_x, this->bram[bram_bank].size());
|
2017-06-21 01:50:52 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2017-06-23 02:38:38 +02:00
|
|
|
if (bram_y >= int(this->bram[bram_bank][bram_x].size())) {
|
2024-06-21 23:36:34 +02:00
|
|
|
error("bram_y %d higher than loaded bram size %zu\n", bram_y, this->bram[bram_bank][bram_x].size());
|
2017-06-21 01:50:52 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2015-07-18 13:07:39 +02:00
|
|
|
if (this->bram[bram_bank][bram_x][bram_y])
|
|
|
|
|
value += 1 << i;
|
|
|
|
|
}
|
|
|
|
|
ofs << "0123456789abcdef"[value];
|
|
|
|
|
}
|
|
|
|
|
ofs << '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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__);
|
2015-07-18 13:07:39 +02:00
|
|
|
info("Writing cram pbm file..\n");
|
2015-07-18 13:05:51 +02:00
|
|
|
|
2017-07-03 00:39:14 +02:00
|
|
|
ofs << "P3\n";
|
2015-07-18 13:05:51 +02:00
|
|
|
ofs << stringf("%d %d\n", 2*this->cram_width, 2*this->cram_height);
|
2017-07-03 00:39:14 +02:00
|
|
|
ofs << "255\n";
|
2019-12-08 14:27:46 +01:00
|
|
|
|
|
|
|
|
vector<vector<uint32_t>> tile_type[4];
|
|
|
|
|
|
|
|
|
|
// We require random access to tile_type, so ensure that each column of each
|
|
|
|
|
// bank is initialised so that all possible indices are valid
|
|
|
|
|
for (int bank = 0; bank < 4; bank++) {
|
2020-04-22 18:05:12 +02:00
|
|
|
tile_type[bank].resize(this->cram_width);
|
|
|
|
|
for (int x = 0; x < this->cram_width; x++)
|
|
|
|
|
tile_type[bank][x].resize(this->cram_height);
|
2019-12-08 14:27:46 +01:00
|
|
|
}
|
|
|
|
|
|
2017-07-03 00:39:14 +02:00
|
|
|
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);
|
2017-07-04 12:24:28 +02:00
|
|
|
|
|
|
|
|
uint32_t color = 0x000000;
|
|
|
|
|
if (cic.tile_type == "io") {
|
|
|
|
|
color = 0x00aa00;
|
|
|
|
|
} else if (cic.tile_type == "logic") {
|
|
|
|
|
if ((x + y) % 2 == 0) {
|
|
|
|
|
color = 0x0000ff;
|
|
|
|
|
} else {
|
|
|
|
|
color = 0x0000aa;
|
|
|
|
|
}
|
|
|
|
|
if (x == 12 && y == 25) {
|
|
|
|
|
color = 0xaa00aa;
|
|
|
|
|
}
|
|
|
|
|
if (x == 12 && y == 24) {
|
|
|
|
|
color = 0x888888;
|
|
|
|
|
}
|
|
|
|
|
} else if (cic.tile_type == "ramt") {
|
|
|
|
|
color = 0xff0000;
|
|
|
|
|
} else if (cic.tile_type == "ramb") {
|
|
|
|
|
color = 0xaa0000;
|
|
|
|
|
} else if (cic.tile_type == "unsupported") {
|
|
|
|
|
color = 0x333333;
|
|
|
|
|
} else {
|
|
|
|
|
info("%s\n", cic.tile_type.c_str());
|
|
|
|
|
}
|
2017-07-03 00:39:14 +02:00
|
|
|
|
|
|
|
|
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_type[cram_bank][cram_x][cram_y] = color;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
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)
|
2017-07-03 00:39:14 +02:00
|
|
|
ofs << " 255 255 255";
|
|
|
|
|
else if (this->cram[bank][bank_x][bank_y]) {
|
2017-07-04 12:24:28 +02:00
|
|
|
ofs << " 255 255 255";
|
|
|
|
|
} else {
|
|
|
|
|
uint32_t color = tile_type[bank][bank_x][bank_y];
|
|
|
|
|
uint8_t r = color >> 16;
|
|
|
|
|
uint8_t g = color >> 8;
|
|
|
|
|
uint8_t b = color & 0xff;
|
2017-07-03 00:39:14 +02:00
|
|
|
ofs << stringf(" %d %d %d", r, g, b);
|
2017-07-04 12:24:28 +02:00
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
ofs << '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:07:39 +02:00
|
|
|
void FpgaConfig::write_bram_pbm(std::ostream &ofs, int bank_num) const
|
|
|
|
|
{
|
|
|
|
|
debug("## %s\n", __PRETTY_FUNCTION__);
|
|
|
|
|
info("Writing bram pbm file..\n");
|
|
|
|
|
|
|
|
|
|
ofs << "P1\n";
|
|
|
|
|
ofs << stringf("%d %d\n", 2*this->bram_width, 2*this->bram_height);
|
|
|
|
|
for (int y = 2*this->bram_height-1; y >= 0; y--) {
|
|
|
|
|
for (int x = 0; x < 2*this->bram_width; x++) {
|
|
|
|
|
int bank = 0, bank_x = x, bank_y = y;
|
|
|
|
|
if (bank_x >= this->bram_width)
|
|
|
|
|
bank |= 1, bank_x = 2*this->bram_width - bank_x - 1;
|
|
|
|
|
if (bank_y >= this->bram_height)
|
|
|
|
|
bank |= 2, bank_y = 2*this->bram_height - bank_y - 1;
|
2017-07-04 12:24:28 +02:00
|
|
|
info("%d %d %d\n", bank, bank_x, bank_y);
|
2015-07-18 13:07:39 +02:00
|
|
|
if (bank_num >= 0 && bank != bank_num)
|
|
|
|
|
ofs << " 0";
|
|
|
|
|
else
|
|
|
|
|
ofs << (this->bram[bank][bank_x][bank_y] ? " 1" : " 0");
|
|
|
|
|
}
|
|
|
|
|
ofs << '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
int FpgaConfig::chip_width() const
|
|
|
|
|
{
|
2017-03-05 22:25:47 +01:00
|
|
|
if (this->device == "384") return 6;
|
2015-07-18 13:10:40 +02:00
|
|
|
if (this->device == "1k") return 12;
|
2017-06-21 01:50:52 +02:00
|
|
|
if (this->device == "5k") return 24;
|
2018-02-13 21:22:05 +01:00
|
|
|
if (this->device == "u4k") return 24;
|
2018-05-06 20:10:36 +02:00
|
|
|
if (this->device == "lm4k") return 24;
|
2015-07-18 13:10:40 +02:00
|
|
|
if (this->device == "8k") return 32;
|
2016-02-27 17:36:55 +01:00
|
|
|
panic("Unknown chip type '%s'.\n", this->device.c_str());
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int FpgaConfig::chip_height() const
|
|
|
|
|
{
|
2017-03-05 22:25:47 +01:00
|
|
|
if (this->device == "384") return 8;
|
2015-07-18 13:10:40 +02:00
|
|
|
if (this->device == "1k") return 16;
|
2017-06-21 01:50:52 +02:00
|
|
|
if (this->device == "5k") return 30;
|
2018-02-13 21:22:05 +01:00
|
|
|
if (this->device == "u4k") return 20;
|
2018-05-06 20:10:36 +02:00
|
|
|
if (this->device == "lm4k") return 20;
|
2015-07-18 13:10:40 +02:00
|
|
|
if (this->device == "8k") return 32;
|
2016-02-27 17:36:55 +01:00
|
|
|
panic("Unknown chip type '%s'.\n", this->device.c_str());
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<int> FpgaConfig::chip_cols() const
|
|
|
|
|
{
|
2017-03-05 22:25:47 +01:00
|
|
|
if (this->device == "384") return vector<int>({18, 54, 54, 54, 54});
|
2015-07-18 13:10:40 +02:00
|
|
|
if (this->device == "1k") return vector<int>({18, 54, 54, 42, 54, 54, 54});
|
2018-02-13 21:22:05 +01:00
|
|
|
if (this->device == "u4k") return vector<int>({54, 54, 54, 54, 54, 54, 42, 54, 54, 54, 54, 54, 54});
|
2018-05-06 20:10:36 +02:00
|
|
|
if (this->device == "lm4k") return vector<int>({18, 54, 54, 54, 54, 54, 42, 54, 54, 54, 54, 54, 54});
|
2017-11-20 10:43:54 +01:00
|
|
|
// Its IPConnect or Mutiplier block, five logic, ram, six logic.
|
2017-06-24 07:53:54 +02:00
|
|
|
if (this->device == "5k") return vector<int>({54, 54, 54, 54, 54, 54, 42, 54, 54, 54, 54, 54, 54});
|
2015-07-18 13:10:40 +02:00
|
|
|
if (this->device == "8k") return vector<int>({18, 54, 54, 54, 54, 54, 54, 54, 42, 54, 54, 54, 54, 54, 54, 54, 54});
|
2016-02-27 17:36:55 +01:00
|
|
|
panic("Unknown chip type '%s'.\n", this->device.c_str());
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string FpgaConfig::tile_type(int x, int y) const
|
|
|
|
|
{
|
2015-07-18 13:10:40 +02:00
|
|
|
if ((x == 0 || x == this->chip_width()+1) && (y == 0 || y == this->chip_height()+1)) return "corner";
|
2017-11-20 10:43:54 +01:00
|
|
|
// The sides on the 5k devices are IPConnect or DSP tiles
|
2017-10-21 15:59:13 +02:00
|
|
|
if (this->device == "5k" && (x == 0 || x == this->chip_width()+1)) {
|
2018-02-13 21:22:05 +01:00
|
|
|
if( (y == 5) || (y == 10) || (y == 15) || (y == 23))
|
2017-10-21 15:59:13 +02:00
|
|
|
return "dsp0";
|
|
|
|
|
if( (y == 6) || (y == 11) || (y == 16) || (y == 24))
|
|
|
|
|
return "dsp1";
|
|
|
|
|
if( (y == 7) || (y == 12) || (y == 17) || (y == 25))
|
|
|
|
|
return "dsp2";
|
|
|
|
|
if( (y == 8) || (y == 13) || (y == 18) || (y == 26))
|
|
|
|
|
return "dsp3";
|
2017-11-08 17:05:42 +01:00
|
|
|
return "ipcon";
|
2017-10-21 15:59:13 +02:00
|
|
|
}
|
2018-02-13 21:22:05 +01:00
|
|
|
|
|
|
|
|
if (this->device == "u4k" && (x == 0 || x == this->chip_width()+1)) {
|
|
|
|
|
if( (y == 5) || (y == 13))
|
|
|
|
|
return "dsp0";
|
|
|
|
|
if( (y == 6) || (y == 14))
|
|
|
|
|
return "dsp1";
|
|
|
|
|
if( (y == 7) || (y == 15))
|
|
|
|
|
return "dsp2";
|
|
|
|
|
if( (y == 8) || (y == 16))
|
|
|
|
|
return "dsp3";
|
|
|
|
|
return "ipcon";
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:10:40 +02:00
|
|
|
if ((x == 0 || x == this->chip_width()+1) || (y == 0 || y == this->chip_height()+1)) return "io";
|
|
|
|
|
|
2017-03-05 22:25:47 +01:00
|
|
|
if (this->device == "384") return "logic";
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
if (this->device == "1k") {
|
2015-07-18 13:06:48 +02:00
|
|
|
if (x == 3 || x == 10) return y % 2 == 1 ? "ramb" : "ramt";
|
2015-07-18 13:05:51 +02:00
|
|
|
return "logic";
|
|
|
|
|
}
|
2015-07-18 13:10:40 +02:00
|
|
|
|
2018-05-06 20:10:36 +02:00
|
|
|
if (this->device == "5k" || this->device == "u4k" || this->device == "lm4k") {
|
2017-07-03 00:39:14 +02:00
|
|
|
if (x == 6 || x == 19) return y % 2 == 1 ? "ramb" : "ramt";
|
2017-06-21 01:50:52 +02:00
|
|
|
return "logic";
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:10:40 +02:00
|
|
|
if (this->device == "8k") {
|
|
|
|
|
if (x == 8 || x == 25) return y % 2 == 1 ? "ramb" : "ramt";
|
|
|
|
|
return "logic";
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 17:36:55 +01:00
|
|
|
panic("Unknown chip type '%s'.\n", this->device.c_str());
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int FpgaConfig::tile_width(const string &type) const
|
|
|
|
|
{
|
2017-07-04 12:24:28 +02:00
|
|
|
if (type == "corner") return 0;
|
|
|
|
|
if (type == "logic") return 54;
|
|
|
|
|
if (type == "ramb") return 42;
|
|
|
|
|
if (type == "ramt") return 42;
|
|
|
|
|
if (type == "io") return 18;
|
2017-10-21 15:59:13 +02:00
|
|
|
if (type.substr(0, 3) == "dsp") return 54;
|
2017-11-08 17:05:42 +01:00
|
|
|
if (type == "ipcon") return 54;
|
2017-10-21 15:59:13 +02:00
|
|
|
|
2016-02-27 17:36:55 +01:00
|
|
|
panic("Unknown tile type '%s'.\n", type.c_str());
|
2015-07-18 13:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2017-06-21 01:50:52 +02:00
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
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;
|
2017-06-24 07:53:54 +02:00
|
|
|
if (this->fpga->device == "5k") {
|
2017-07-03 00:39:14 +02:00
|
|
|
this->top_half = this->tile_y > (chip_height * 2 / 3);
|
2017-06-24 07:53:54 +02:00
|
|
|
} else {
|
|
|
|
|
this->top_half = this->tile_y > chip_height / 2;
|
|
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
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;
|
2017-06-21 01:50:52 +02:00
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
if (top_half)
|
|
|
|
|
cram_y = bank_yoff + (15 - bit_y);
|
|
|
|
|
else
|
|
|
|
|
cram_y = bank_yoff + bit_y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:07:39 +02:00
|
|
|
BramIndexConverter::BramIndexConverter(const FpgaConfig *fpga, int tile_x, int tile_y)
|
|
|
|
|
{
|
|
|
|
|
this->fpga = fpga;
|
|
|
|
|
this->tile_x = tile_x;
|
|
|
|
|
this->tile_y = tile_y;
|
|
|
|
|
|
|
|
|
|
auto chip_width = fpga->chip_width();
|
|
|
|
|
auto chip_height = fpga->chip_height();
|
|
|
|
|
|
|
|
|
|
bool right_half = this->tile_x > chip_width / 2;
|
|
|
|
|
bool top_half = this->tile_y > chip_height / 2;
|
2017-07-03 00:39:14 +02:00
|
|
|
// The UltraPlus 5k line is special because the top quarter of the chip is
|
|
|
|
|
// used for SRAM instead of logic. Therefore the bitstream for the top two
|
|
|
|
|
// quadrants are half the height of the bottom.
|
2017-06-21 01:50:52 +02:00
|
|
|
if (this->fpga->device == "5k") {
|
2017-11-01 13:02:44 +01:00
|
|
|
top_half = this->tile_y > (2 * chip_height / 3);
|
2017-06-21 01:50:52 +02:00
|
|
|
}
|
2015-07-18 13:07:39 +02:00
|
|
|
|
|
|
|
|
this->bank_num = 0;
|
2017-06-21 01:50:52 +02:00
|
|
|
int y_offset = this->tile_y - 1;
|
2017-07-08 02:00:20 +02:00
|
|
|
if (this->fpga->device == "5k") {
|
2017-11-01 13:02:44 +01:00
|
|
|
if (top_half) {
|
2017-07-08 02:00:20 +02:00
|
|
|
this->bank_num |= 1;
|
2017-11-01 13:02:44 +01:00
|
|
|
y_offset = this->tile_y - (2 * chip_height / 3);
|
2017-07-08 02:00:20 +02:00
|
|
|
} else {
|
2017-11-01 13:02:44 +01:00
|
|
|
//y_offset = this->tile_y - (2 * chip_height / 3);
|
2017-07-08 02:00:20 +02:00
|
|
|
}
|
|
|
|
|
} else if (top_half) {
|
2017-06-21 01:50:52 +02:00
|
|
|
this->bank_num |= 1;
|
2017-06-24 07:53:54 +02:00
|
|
|
y_offset = this->tile_y - chip_height / 2;
|
2017-06-21 01:50:52 +02:00
|
|
|
}
|
2015-07-18 13:07:39 +02:00
|
|
|
if (right_half) this->bank_num |= 2;
|
|
|
|
|
|
2017-06-21 01:50:52 +02:00
|
|
|
this->bank_off = 16 * (y_offset / 2);
|
2015-07-18 13:07:39 +02:00
|
|
|
}
|
|
|
|
|
|
2017-06-23 02:38:38 +02:00
|
|
|
void BramIndexConverter::get_bram_index(int bit_x, int bit_y, int &bram_bank, int &bram_x, int &bram_y) const
|
2015-07-18 13:07:39 +02:00
|
|
|
{
|
|
|
|
|
int index = 256 * bit_y + (16*(bit_x/16) + 15 - bit_x%16);
|
|
|
|
|
bram_bank = bank_num;
|
|
|
|
|
bram_x = bank_off + index % 16;
|
|
|
|
|
bram_y = index / 16;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
|
// Main program
|
|
|
|
|
|
2020-06-24 21:57:22 +02:00
|
|
|
void usage(const char *cmd)
|
2015-07-18 13:05:51 +02:00
|
|
|
{
|
|
|
|
|
log("\n");
|
2020-06-24 21:57:22 +02:00
|
|
|
log("Usage: %s [options] [input-file [output-file]]\n", cmd);
|
2015-07-18 13:05:51 +02:00
|
|
|
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");
|
2017-12-31 07:34:36 +01:00
|
|
|
log(" -s\n");
|
|
|
|
|
log(" disable final deep-sleep SPI flash command after bitstream is loaded\n");
|
|
|
|
|
log("\n");
|
2015-07-18 13:05:51 +02:00
|
|
|
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");
|
2015-07-18 13:07:39 +02:00
|
|
|
log(" -r\n");
|
|
|
|
|
log(" write bram data, not cram, to the netpbm file\n");
|
|
|
|
|
log("\n");
|
2015-07-18 13:05:51 +02:00
|
|
|
log(" -B0, -B1, -B2, -B3\n");
|
|
|
|
|
log(" only include the specified bank in the netpbm file\n");
|
|
|
|
|
log("\n");
|
2019-05-15 16:57:17 +02:00
|
|
|
log(" -n\n");
|
|
|
|
|
log(" skip initializing BRAM\n");
|
|
|
|
|
log("\n");
|
2015-07-18 13:05:51 +02:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
|
{
|
2017-11-15 12:14:14 +01:00
|
|
|
#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
|
|
|
|
|
|
2015-07-18 13:05:51 +02:00
|
|
|
vector<string> parameters;
|
|
|
|
|
bool unpack_mode = false;
|
2017-12-31 07:34:36 +01:00
|
|
|
bool nosleep_mode = false;
|
2015-07-18 13:05:51 +02:00
|
|
|
bool netpbm_mode = false;
|
2015-07-18 13:07:39 +02:00
|
|
|
bool netpbm_bram = false;
|
2015-07-18 13:05:51 +02:00
|
|
|
bool netpbm_fill_tiles = false;
|
|
|
|
|
bool netpbm_checkerboard = false;
|
2019-05-15 16:57:17 +02:00
|
|
|
bool skip_bram_initialization = false;
|
2015-07-18 13:05:51 +02:00
|
|
|
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) {
|
2015-07-18 13:07:39 +02:00
|
|
|
for (int i = 1; i < int(arg.size()); i++)
|
2015-07-18 13:05:51 +02:00
|
|
|
if (arg[i] == 'u') {
|
|
|
|
|
unpack_mode = true;
|
|
|
|
|
} else if (arg[i] == 'b') {
|
|
|
|
|
netpbm_mode = true;
|
2015-07-18 13:07:39 +02:00
|
|
|
} else if (arg[i] == 'r') {
|
|
|
|
|
netpbm_mode = true;
|
|
|
|
|
netpbm_bram = true;
|
2015-07-18 13:05:51 +02:00
|
|
|
} 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';
|
2017-12-31 07:34:36 +01:00
|
|
|
} else if (arg[i] == 's') {
|
|
|
|
|
nosleep_mode = true;
|
2015-07-18 13:05:51 +02:00
|
|
|
} else if (arg[i] == 'v') {
|
|
|
|
|
log_level++;
|
2019-05-15 16:57:17 +02:00
|
|
|
} else if (arg[i] == 'n') {
|
|
|
|
|
skip_bram_initialization = true;
|
2015-07-18 13:05:51 +02:00
|
|
|
} else
|
2020-06-24 21:57:22 +02:00
|
|
|
usage(argv[0]);
|
2015-07-18 13:05:51 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parameters.push_back(arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::ifstream ifs;
|
|
|
|
|
std::ofstream ofs;
|
|
|
|
|
|
|
|
|
|
std::istream *isp;
|
|
|
|
|
std::ostream *osp;
|
|
|
|
|
|
|
|
|
|
if (parameters.size() >= 1 && parameters[0] != "-") {
|
2015-08-21 16:53:24 +02:00
|
|
|
ifs.open(parameters[0], std::ios::binary);
|
2015-07-18 13:05:51 +02:00
|
|
|
if (!ifs.is_open())
|
|
|
|
|
error("Failed to open input file.\n");
|
|
|
|
|
isp = &ifs;
|
|
|
|
|
} else {
|
|
|
|
|
isp = &std::cin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (parameters.size() >= 2 && parameters[1] != "-") {
|
2015-08-21 19:50:52 +02:00
|
|
|
ofs.open(parameters[1], std::ios::binary);
|
2015-07-18 13:05:51 +02:00
|
|
|
if (!ofs.is_open())
|
|
|
|
|
error("Failed to open output file.\n");
|
|
|
|
|
osp = &ofs;
|
|
|
|
|
} else {
|
|
|
|
|
osp = &std::cout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (parameters.size() > 2)
|
2020-06-24 21:57:22 +02:00
|
|
|
usage(argv[0]);
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
FpgaConfig fpga_config;
|
2019-05-15 16:57:17 +02:00
|
|
|
|
|
|
|
|
fpga_config.skip_bram_initialization = skip_bram_initialization;
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
if (unpack_mode) {
|
|
|
|
|
fpga_config.read_bits(*isp);
|
|
|
|
|
if (!netpbm_mode)
|
|
|
|
|
fpga_config.write_ascii(*osp);
|
|
|
|
|
} else {
|
2017-12-31 07:52:59 +01:00
|
|
|
fpga_config.read_ascii(*isp, nosleep_mode);
|
2015-07-18 13:05:51 +02:00
|
|
|
if (!netpbm_mode)
|
|
|
|
|
fpga_config.write_bits(*osp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (netpbm_checkerboard) {
|
|
|
|
|
fpga_config.cram_clear();
|
|
|
|
|
fpga_config.cram_checkerboard(checkerboard_m);
|
|
|
|
|
}
|
2017-07-04 12:24:28 +02:00
|
|
|
|
2017-07-03 00:39:14 +02:00
|
|
|
info("netpbm\n");
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
if (netpbm_fill_tiles)
|
|
|
|
|
fpga_config.cram_fill_tiles();
|
2017-07-04 12:24:28 +02:00
|
|
|
|
|
|
|
|
info("fill done\n");
|
2015-07-18 13:05:51 +02:00
|
|
|
|
2015-07-18 13:07:39 +02:00
|
|
|
if (netpbm_mode) {
|
|
|
|
|
if (netpbm_bram)
|
|
|
|
|
fpga_config.write_bram_pbm(*osp, netpbm_banknum);
|
|
|
|
|
else
|
|
|
|
|
fpga_config.write_cram_pbm(*osp, netpbm_banknum);
|
|
|
|
|
}
|
2015-07-18 13:05:51 +02:00
|
|
|
|
|
|
|
|
info("Done.\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|