blob: f86e4943a7acabd3797951825ce5051bf1ced167 [file] [log] [blame]
// Copyright 2020 Project U-Ray Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <map>
#include <set>
#include <cassert>
#include <stdexcept>
#include <stdarg.h>
#include "common.h"
enum BitstreamRegister : uint16_t {
#define X(a, b) a = b,
#include "registers.inc"
#undef X
};
ChipData chip;
std::unordered_set<uint32_t> roi_frames;
std::map<uint32_t, std::vector<bool>> frames;
std::map<uint32_t, uint32_t> next_frame;
std::string db_root;
void set_tile_bit(const TileInstance &tile, int bit) {
int pos = 0;
for (auto &bm : tile.bits) {
if (bit >= pos && bit < (pos + bm.size)) {
frames.at(bm.frame_offset).at(bm.bit_offset + (bit - pos)) = true;
return;
}
pos += bm.size;
}
throw std::runtime_error("bad bit " + tile.name + "." + std::to_string(bit));
}
void set_feature(const std::string &tile, const std::string &feature) {
auto &db = chip.load_tile_database(tile);
if (!db.features.count(feature)) {
throw std::runtime_error("unknown feature " + feature + " in tile " + tile);
}
const auto &fbits = db.features.at(feature);
auto &t = chip.tiles[tile];
for (auto bit : fbits)
set_tile_bit(t, bit);
}
void read_quasi_fasm(const std::string &filename) {
LineReader rd(filename);
std::vector<std::string> split;
for (auto &line : rd) {
split_str(line, split, ".", true, 1);
if (split.size() < 2)
continue;
set_feature(split.at(0), split.at(1));
}
}
// Add ECCs to frames
void add_frame_ecc() {
auto get_ecc_value = [](int word, int bit) {
int nib = bit / 4;
int nibbit = bit % 4;
// ECC offset is expanded to 1 bit per nibble,
// and then shifted based on the bit index in nibble
// e.g. word 3, bit 9
// offset: 0b10100110010 - concatenate (3 + (255 - 92)) [frame offset] and 9/4 [nibble offset]
// becomes: 0x10100110010
// shifted by bit in nibble (9%4): 0x20200220020
uint32_t offset = (word + (255 - 92)) << 3 | nib;
uint64_t exp_offset = 0;
// Odd parity
offset ^= (1 << 11);
for (int i = 0; i < 11; i++)
if (offset & (1 << i)) offset ^= (1 << 11);
// Expansion
for (int i = 0; i < 12; i++)
if (offset & (1 << i)) exp_offset |= (1ULL << (4 * i));
return exp_offset << nibbit;
};
for (auto &fr : frames) {
auto &bits = fr.second;
uint64_t ecc = 0;
for (int word = 0; word < 93; word++) {
for (int i = 0; i < 32; i++) {
if (!bits.at(word * 32 + i))
continue;
if ((word == 45) || ((word == 46 && (i < 16)))) {
bits.at(word * 32 + i) = false;
continue;
}
ecc ^= get_ecc_value(word, i);
}
}
for (int i = 0; i < 48; i++)
if (ecc & (1ULL << i))
bits.at(45*32 + i) = true;
}
}
struct ByteStreamWriter {
std::vector<uint8_t> data;
uint32_t curr_crc = 0;
uint32_t curr_addr = 0;
void write_byte(uint8_t byte) {
data.push_back(byte);
}
void write_bytes(const std::vector<uint8_t> &bytes) {
data.insert(data.end(), bytes.begin(), bytes.end());
}
void write_string(const std::string &str) {
int len = int(str.length()) + 1;
data.push_back((len >> 8) & 0xFF);
data.push_back(len & 0xFF);
for (char c : str) data.push_back(uint8_t(c));
data.push_back(0x00);
}
void write_u32(uint32_t word, bool update_crc = false) {
data.push_back((word >> 24) & 0xFF);
data.push_back((word >> 16) & 0xFF);
data.push_back((word >> 8) & 0xFF);
data.push_back((word >> 0) & 0xFF);
if (update_crc)
curr_crc = icap_crc(curr_addr, word, curr_crc);
}
void write_short_packet(BitstreamOp op, BitstreamRegister reg = BitstreamRegister(0x00), const std::vector<uint32_t> &payload = {}) {
curr_addr = uint32_t(reg);
write_u32((0b001UL << 29) | ((uint32_t(op) & 0x03) << 27) | ((uint32_t(reg) & 0x3FFF) << 13) | (uint32_t(payload.size()) & 0x3FFF));
for (uint32_t x : payload)
write_u32(x, true);
}
void write_long_packet(const std::vector<uint32_t> &payload) {
write_u32((0b010UL << 29) | (uint32_t(OP_READ) << 27) | (uint32_t(payload.size()) & 0x3FFFFFF));
for (uint32_t x : payload)
write_u32(x, true);
}
void write_crc() {
write_short_packet(OP_WRITE, CRC, {curr_crc});
curr_crc = 0;
}
};
void write_bitstream(std::ofstream &f) {
#if 0
// FIXME - write an actual binary bitstream
for (auto &frame : frames) {
for (size_t i = 0; i < frame.second.size(); i++) {
if (frame.second.at(i))
f << stringf("F%08xW%03dB%02d", frame.first, int(i / 32), int(i % 32)) << std::endl;
}
}
#else
add_frame_ecc();
ByteStreamWriter bsw;
// Header
bsw.write_bytes({0x00, 0x09, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x00, 0x00, 0x01, 0x61});
bsw.write_string("top;UserID=0XFFFFFFFF;Version=2019.1");
bsw.write_byte(0x62);
bsw.write_string("xczu7ev-ffvc1156-2-e");
bsw.write_byte(0x63);
bsw.write_string("2019/09/08");
bsw.write_byte(0x64);
bsw.write_string("00:00:00");
bsw.write_bytes({0x65, 0x01, 0x36, 0x6F, 0xB0});
for (int i = 0; i < 64; i++)
bsw.write_byte(0xFF);
bsw.write_bytes({0x00, 0x00, 0x00, 0xBB, 0x11, 0x22, 0x00, 0x44});
for (int i = 0; i < 8; i++)
bsw.write_byte(0xFF);
// Preamble
bsw.write_u32(0xAA995566);
// Initial commands
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, TIMER, {0x00000000});
bsw.write_short_packet(OP_WRITE, WBSTAR, {0x00000000});
bsw.write_short_packet(OP_WRITE, CMD, {0x00000000});
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, CMD, {0x00000007});
bsw.curr_crc = 0;
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, FAR, {0x00000000});
bsw.write_short_packet(OP_WRITE, BitstreamRegister(0x13), {0x00000000});
bsw.write_short_packet(OP_WRITE, COR0, {0x38003fe5});
bsw.write_short_packet(OP_WRITE, COR1, {0x00400000});
bsw.write_short_packet(OP_WRITE, IDCODE, {0x04a5a093});
bsw.write_short_packet(OP_WRITE, CMD, {0x00000009});
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, MASK, {0x00000001});
bsw.write_short_packet(OP_WRITE, CTL0, {0x00000101});
bsw.write_short_packet(OP_WRITE, MASK, {0x00200000});
bsw.write_short_packet(OP_WRITE, CTL1, {0x00200000});
for (int i = 0; i < 8; i++)
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, CMD, {0x00000001});
bsw.write_short_packet(OP_NOP);
// Frame data
std::vector<uint32_t> fdata(93, 0x00000000);
bsw.write_short_packet(OP_WRITE, FAR, {frames.begin()->first});
for (auto &fr : frames) {
for (int i = 0; i < 93; i++) {
fdata[i] = 0x00000000;
for (int j = 0; j < 32; j++)
if (fr.second.at(i * 32 + j))
fdata[i] |= (1 << j);
}
bsw.write_short_packet(OP_WRITE, FDRI, fdata);
bsw.write_short_packet(OP_WRITE, FAR, {fr.first});
bsw.write_crc();
if (!next_frame.count(fr.first) || ((next_frame.at(fr.first) ^ fr.first) & 0xFFFC0000)) {
// Duplicate last frame in a row, but empty??
for (int i = 0; i < 93; i++)
fdata[i] = 0;
bsw.write_short_packet(OP_WRITE, FDRI, fdata);
bsw.write_short_packet(OP_WRITE, FAR, {fr.first});
bsw.write_crc();
if (next_frame.count(fr.first)) {
// CMD 1 (WCFG)
bsw.write_short_packet(OP_WRITE, CMD, {0x00000001});
bsw.write_short_packet(OP_NOP);
// Next frame address
bsw.write_short_packet(OP_WRITE, FAR, {next_frame.at(fr.first)});
}
}
//bsw.write_long_packet(fdata);
}
// End of bitstream
bsw.write_short_packet(OP_WRITE, CMD, {0x00000000});
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, MASK, {0x00200000});
bsw.write_short_packet(OP_WRITE, CTL1, {0x00000000});
bsw.write_crc();
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, CMD, {0x0000000a});
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, CMD, {0x00000003});
for (int i = 0; i < 20; i++)
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, CMD, {0x00000005});
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, FAR, {0x07FC0000});
bsw.write_short_packet(OP_WRITE, MASK, {0x00000101});
bsw.write_short_packet(OP_WRITE, CTL0, {0x00000101});
bsw.write_crc();
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_NOP);
bsw.write_short_packet(OP_WRITE, CMD, {0x0000000d});
for (int i = 0; i < 400; i++)
bsw.write_short_packet(OP_NOP);
f.write(reinterpret_cast<const char *>(&(bsw.data[0])), bsw.data.size());
#endif
}
void parse_harness(const std::string &filename) {
LineReader rd(filename);
for (auto &line : rd) {
assert(line.at(0) == 'F');
const char *curr = line.c_str() + 1;
char *next = nullptr;
uint32_t frame = std::strtoul(curr, &next, 16);
if (roi_frames.count(frame))
continue;
assert(*next == 'W');
curr = next + 1;
int word = std::strtol(curr, &next, 10);
curr = next + 1;
assert(*next == 'B');
int bit = std::strtol(curr, &next, 10);
frames[frame].at(word * 32 + bit) = true;
}
}
void parse_frames(const std::string &filename, std::unordered_set<uint32_t> &dest_set) {
LineReader rd(filename);
for (auto &line : rd) {
const char *start = line.c_str();
char *end = nullptr;
uint32_t addr = std::strtol(start, &end, 16);
if ((end == nullptr) || (end == start))
continue;
dest_set.insert(addr);
frames[addr].clear();
frames[addr].resize(93*32, false);
}
}
void setup_base_frames() {
uint32_t last_frame = 0xFFFFFFFF;
for (auto addr : chip.all_frames) {
if (addr == 0x07FC0000)
continue;
frames[addr].clear();
frames[addr].resize(93*32, false);
if (last_frame != 0xFFFFFFFF)
next_frame[last_frame] = addr;
last_frame = addr;
}
}
int main(int argc, char *argv[]) {
if (argc < 6) {
std::cerr << "Usage: ./assemble dbdir roi_frames.txt harness.txt input.fasm out.bit" << std::endl;
return 2;
}
db_root = argv[1];
chip.open(db_root);
setup_base_frames();
parse_frames(argv[2], roi_frames);
parse_harness(argv[3]);
read_quasi_fasm(argv[4]);
std::ofstream obf(argv[5], std::ios::binary);
if (!obf) {
std::cerr << "failed to open " << argv[5] << std::endl;
return 1;
}
write_bitstream(obf);
}