Add prjuray tools Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..ec121a9 --- /dev/null +++ b/tools/.gitignore
@@ -0,0 +1,4 @@ +CMakeCache.txt +CMakeFiles +Makefile +cmake_install.cmake
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 2ebadf1..612bef1 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt
@@ -33,3 +33,19 @@ gflags libprjxray ) + +add_library(common common.cpp) + +add_executable(dump_bitstream dump_bitstream.cpp) +target_compile_options(dump_bitstream PRIVATE -Wno-error) +target_link_libraries(dump_bitstream common) + +add_executable(correlate correlate.cpp) +add_executable(stripdb stripdb.cpp) +set_target_properties(correlate stripdb PROPERTIES CXX_STANDARD 17) + +add_executable(explain explain.cpp) +target_link_libraries(explain common) + +add_executable(assemble assemble.cpp) +target_link_libraries(assemble common)
diff --git a/tools/assemble.cpp b/tools/assemble.cpp new file mode 100644 index 0000000..f86e494 --- /dev/null +++ b/tools/assemble.cpp
@@ -0,0 +1,347 @@ +// 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); +} \ No newline at end of file
diff --git a/tools/bits.py b/tools/bits.py new file mode 100644 index 0000000..e493cc4 --- /dev/null +++ b/tools/bits.py
@@ -0,0 +1,81 @@ +# 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. + +import re +import sys +import json +# Usage: frames.txt tiles.txt tilegrid.json + +frame_line_re = re.compile(r'0x([0-9A-Fa-f]+).*') + +frame_rc_height = {} + +with open(sys.argv[1], 'r') as f: + for line in f: + m = frame_line_re.match(line) + if not m: + continue + frame = int(m.group(1), 16) + bus = (frame >> 24) & 0x7 + half = (frame >> 23) & 0x1 + row = (frame >> 18) & 0x1F + col = (frame >> 8) & 0x3FF + minor = frame & 0xFF + if bus != 0 or half != 0: + continue + if (row, col) not in frame_rc_height: + frame_rc_height[(row, col, frame & ~0xFF)] = minor + 1 + else: + frame_rc_height[(row, col, frame & ~0xFF)] = max(frame_rc_height[(row, col)], minor + 1) + +tiles_to_xy = {} +with open(sys.argv[2], 'r') as tilef: + for line in tilef: + sl = line.strip().split(",") + if len(sl) < 4: + continue + x = int(sl[0]) + y = int(sl[1]) + name = sl[2] + tiles_to_xy[name] = (x, y) + +with open(sys.argv[3]) as tb_f: + tbj = json.load(tb_f) + +frames_to_tiles = {} +for tilename, tiledata in tbj.items(): + tile_offset = 0 + for chunk in tiledata: + frame, start, size = chunk + if frame not in frames_to_tiles: + frames_to_tiles[frame] = [] + name = tilename.split(":")[0] + frames_to_tiles[frame].append((start, tiles_to_xy[name][1], tiles_to_xy[name][0], name)) + tile_offset += size + +for frame, tiles in frames_to_tiles.items(): + tiles.sort() + +for rc, height in sorted(frame_rc_height.items()): + row, col, frame = rc + line = "%08x %6d %6d %6d" % (frame, row, col, height) + print(line) + frame = (row << 18) | (col << 8) + last_start = 0; + if frame in frames_to_tiles and len(frames_to_tiles[frame]) > 0: + for tile in frames_to_tiles[frame]: + start, ty, tx, tname = tile + print(" %6d (%4d) %6d %6d %s" % (start, start - last_start, tx, ty, tname)) + last_start = start +
diff --git a/tools/bits_to_tiles.py b/tools/bits_to_tiles.py new file mode 100644 index 0000000..42a5928 --- /dev/null +++ b/tools/bits_to_tiles.py
@@ -0,0 +1,61 @@ +# 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. + +import re +import sys +import json + +line_re = re.compile(r'F(0x[0-9A-Fa-f]+)W(\d+)B(\d+)') +frames_to_tiles = {} # (start, size, tile, tile offset) + +with open(sys.argv[1]) as tb_f: + tbj = json.load(tb_f) + +for tilename, tiledata in tbj.items(): + tile_offset = 0 + for chunk in tiledata: + frame, start, size = chunk + if frame not in frames_to_tiles: + frames_to_tiles[frame] = [] + frames_to_tiles[frame].append((start, size, tilename, tile_offset)) + tile_offset += size + +tile_bits = {} + +# Always write these tiles to avoid correlation issues +# (distinguishing between all/no bits) +for tilename, tiledata in tbj.items(): + if "INT_INTF_L_IO" in tilename: + tile_bits[tilename] = set() + +with open(sys.argv[2]) as df: + for line in df: + m = line_re.match(line) + if not m: + continue + frame = int(m[1], 16) + if frame not in frames_to_tiles: + continue + framebit = int(m[2]) * 32 + int(m[3]) + for fb in frames_to_tiles[frame]: + start, size, tile, toff = fb + if framebit >= start and framebit < (start + size): + if tile not in tile_bits: + tile_bits[tile] = set() + tile_bits[tile].add(toff + (framebit - start)) + +for tile, bits in sorted(tile_bits.items()): + print(".tile %s" % tile) + for b in sorted(bits): + print(b)
diff --git a/tools/columns.py b/tools/columns.py new file mode 100644 index 0000000..826cc04 --- /dev/null +++ b/tools/columns.py
@@ -0,0 +1,80 @@ +# 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. + +import re +import sys +import json +# Usage: frames.txt tiles.txt tilegrid.json + +frame_line_re = re.compile(r'0x([0-9A-Fa-f]+).*') + +frame_rc_height = {} + +with open(sys.argv[1], 'r') as f: + for line in f: + m = frame_line_re.match(line) + if not m: + continue + frame = int(m.group(1), 16) + bus = (frame >> 24) & 0x7 + half = (frame >> 23) & 0x1 + row = (frame >> 18) & 0x1F + col = (frame >> 8) & 0x3FF + minor = frame & 0xFF + if bus != 0 or half != 0: + continue + if (row, col) not in frame_rc_height: + frame_rc_height[(row, col)] = minor + 1 + else: + frame_rc_height[(row, col)] = max(frame_rc_height[(row, col)], minor + 1) + +tiles_to_xy = {} +with open(sys.argv[2], 'r') as tilef: + for line in tilef: + sl = line.strip().split(",") + if len(sl) < 4: + continue + x = int(sl[0]) + y = int(sl[1]) + name = sl[2] + tiles_to_xy[name] = (x, y) + +with open(sys.argv[3]) as tb_f: + tbj = json.load(tb_f) + +frames_to_tiles = {} +for tilename, tiledata in tbj.items(): + tile_offset = 0 + for chunk in tiledata: + frame, start, size = chunk + if frame not in frames_to_tiles: + frames_to_tiles[frame] = [] + name = tilename.split(":")[0] + frames_to_tiles[frame].append((tiles_to_xy[name][1], tiles_to_xy[name][0], name)) + tile_offset += size + +for frame, tiles in frames_to_tiles.items(): + tiles.sort() + +print("row col height tx ty tname") + +for rc, height in sorted(frame_rc_height.items()): + row, col = rc + line = "%6d %6d %6d" % (row, col, height) + frame = (row << 18) | (col << 8) + if frame in frames_to_tiles and len(frames_to_tiles[frame]) > 0: + ty, tx, tname = frames_to_tiles[frame][0] + line += " %6d %6d %s" % (tx, ty, tname) + print(line) +
diff --git a/tools/common.cpp b/tools/common.cpp new file mode 100644 index 0000000..01d80b0 --- /dev/null +++ b/tools/common.cpp
@@ -0,0 +1,80 @@ +// 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 "common.h" + + +void ChipData::open(const std::string &root) { + this->root = root; + load_frames(); + load_tiles(); +} + +void ChipData::load_frames() { + LineReader rd(root + "/frames.txt"); + for (const std::string &line : rd) { + all_frames.insert(std::strtoul(line.c_str(), nullptr, 16)); + } +} + +void ChipData::load_tiletype_database(int type) { + auto &tt = tiletypes.at(type); + if (tt.loaded_db) + return; + LineReader rd(root + "/" + tt.type + ".bits"); + std::vector<std::string> split; + for (const std::string &line : rd) { + split_str(line, split); + if (split.size() < 1) + continue; + auto &feat = tt.features[split.at(0)]; + for (size_t i = 1; i < split.size(); i++) + feat.push_back(std::stoi(split.at(i))); + } + tt.loaded_db = true; +} + +void ChipData::load_tiles() { + LineReader rd(root + "/tiles.txt"); + std::vector<std::string> split; + TileInstance *curr = nullptr; + + for (const std::string &line : rd) { + split_str(line, split); + if (split.size() < 1) + continue; + if (split.at(0) == ".tile") { + curr = &(tiles[split.at(1)]); + curr->name = split.at(1); + if (!tiletype_by_name.count(split.at(2))) { + tiletype_by_name[split.at(2)] = int(tiletypes.size()); + curr->type = int(tiletypes.size()); + tiletypes.emplace_back(); + tiletypes.back().type = split.at(2); + } else { + curr->type = tiletype_by_name.at(split.at(2)); + } + curr->x = std::stoi(split.at(3)); + curr->y = std::stoi(split.at(4)); + } else if (split.at(0) == "frame") { + TileInstance::TileBitMapping tbm; + tbm.frame_offset = std::strtoul(split.at(1).c_str(), nullptr, 16); + tbm.bit_offset = std::stoi(split.at(3)); + tbm.size = std::stoi(split.at(5)); + curr->bits.push_back(tbm); + } + } +} + +
diff --git a/tools/common.h b/tools/common.h new file mode 100644 index 0000000..481e842 --- /dev/null +++ b/tools/common.h
@@ -0,0 +1,211 @@ +#ifndef COMMON_H +#define COMMON_H + +#include <stdint.h> +#include <string> +#include <vector> +#include <iostream> +#include <fstream> +#include <stdexcept> +#include <map> +#include <set> +#include <unordered_map> +#include <stdarg.h> +constexpr uint32_t kCrc32CastagnoliPolynomial = 0x82F63B78; + +// From prjxray +// The CRC is calculated from each written data word and the current +// register address the data is written to. + +// Extend the current CRC value with one register address (5bit) and +// frame data (32bit) pair and return the newly computed CRC value. + +inline uint32_t icap_crc(uint32_t addr, uint32_t data, uint32_t prev) { + constexpr int kAddressBitWidth = 5; + constexpr int kDataBitWidth = 32; + + uint64_t poly = static_cast<uint64_t>(kCrc32CastagnoliPolynomial) << 1; + uint64_t val = (static_cast<uint64_t>(addr) << 32) | data; + uint64_t crc = prev; + + for (int i = 0; i < kAddressBitWidth + kDataBitWidth; i++) { + if ((val & 1) != (crc & 1)) + crc ^= poly; + + val >>= 1; + crc >>= 1; + } + return crc; +} + +// From Yosys +inline std::string vstringf(const char *fmt, va_list ap) +{ + std::string string; + char *str = NULL; + +#if defined(_WIN32 )|| defined(__CYGWIN__) + 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; +} + +inline std::string stringf(const char *fmt, ...) +{ + std::string string; + va_list ap; + + va_start(ap, fmt); + string = vstringf(fmt, ap); + va_end(ap); + + return string; +} + +// Bitstream definitions + +enum BitstreamOp : uint8_t { + OP_NOP = 0, + OP_READ = 1, + OP_WRITE = 2 +}; + +// File and database convenience functions +// Line-by-line reader, skipping over blank lines and comments +struct LineReader { + LineReader(const std::string &filename) { + in.open(filename); + if (!in) { + throw std::runtime_error("failed to open " + filename); + } + } + std::ifstream in; + std::string linebuf; + bool at_sof = true; + + struct iterator { + LineReader *parent = nullptr; + bool at_end = false; + inline bool operator!=(const iterator &other) const { + return at_end != other.at_end; + }; + inline const std::string& operator*() const { + return parent->linebuf; + } + inline iterator &operator++() { + parent->next(); + at_end = parent->linebuf.empty(); + return *this; + } + }; + + void next() { + while (std::getline(in, linebuf)) { + auto cpos = linebuf.find('#'); + if (cpos != std::string::npos) + linebuf = linebuf.substr(0, cpos); + if (linebuf.empty()) + continue; + linebuf = linebuf.substr(linebuf.find_first_not_of(" \t")); + if (linebuf.empty()) + continue; + break; + } + at_sof = false; + } + + iterator begin() { + if (at_sof) + next(); + return iterator{this, linebuf.empty()}; + } + + iterator end() { + return iterator{this, true}; + } +}; + +struct TileInstance { + std::string name; + int type; + int x, y; + struct TileBitMapping { + int frame_offset; + int bit_offset; + int size; + }; + std::vector<TileBitMapping> bits; +}; + +struct TileType { + std::string type; + bool loaded_db = false; + std::unordered_map<std::string, std::vector<int>> features; +}; + +struct ChipData { + + std::string root; + void open(const std::string &root); + void load_frames(); + void load_tiles(); + void load_tiletype_database(int type); + + std::unordered_map<std::string, TileInstance> tiles; + inline TileInstance &get_tile_by_name(const std::string &name) { + return tiles.at(name); + } + + std::vector<TileType> tiletypes; + std::unordered_map<std::string, int> tiletype_by_name; + TileType &get_tiletype_by_name(const std::string &name) { + return tiletypes.at(tiletype_by_name.at(name)); + } + TileType &load_tile_database(const std::string &tile) { + int type = tiles.at(tile).type; + load_tiletype_database(type); + return tiletypes.at(type); + } + + std::set<uint32_t> all_frames; + +}; + +inline void split_str(const std::string &s, std::vector<std::string> &dest, const std::string &delim = " ", bool skip_empty = true, int lim = -1) { + dest.clear(); + std::string buf; + + for (char c : s) { + if (delim.find(c) != std::string::npos && (lim == -1 || int(dest.size()) < lim)) { + if (!buf.empty() || !skip_empty) + dest.push_back(buf); + buf.clear(); + } else { + buf += c; + } + } + + if (!buf.empty()) + dest.push_back(buf); +} + +#endif \ No newline at end of file
diff --git a/tools/correlate.cpp b/tools/correlate.cpp new file mode 100644 index 0000000..2c4676d --- /dev/null +++ b/tools/correlate.cpp
@@ -0,0 +1,254 @@ +// 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 <vector> +#include <iostream> +#include <map> +#include <string> +#include <fstream> +#include <stdexcept> +#include <iterator> +#include <stdarg.h> +#include <unordered_set> +#include <unordered_map> +#include <set> +#include <sstream> +#include <algorithm> +#include <filesystem> +namespace fs = std::filesystem; + +struct FeatureData { + std::unordered_set<int> always_set_with_feature; + std::unordered_set<int> never_set_with_feature; + std::unordered_set<int> always_set_without_feature; + + std::vector<std::pair<int, bool>> featbits; + std::set<std::string> deps; + int count; +}; + +struct TileTypeData { + std::unordered_map<std::string, std::unordered_set<std::string>> inst_features; + std::unordered_map<std::string, std::unordered_set<int>> inst_bits; + std::unordered_set<int> settable_bits; + std::set<std::string> extant_features; + std::unordered_map<std::string, FeatureData> features; +}; + +std::map<std::string, TileTypeData> tiletypes; +std::set<std::string> include_tts; + +std::pair<std::string, std::string> split_tilename(const std::string &name) { + size_t idx = name.find(':'); + return std::make_pair(name.substr(0, idx), name.substr(idx+1)); +} + +void parse_bits(const std::string &prefix, std::istream &in) { + std::string line; + std::unordered_set<int> *bits = nullptr; + std::unordered_set<int> *settable_bits = nullptr; + bool skip = false; + + while (std::getline(in, line)) { + if (line.empty()) + continue; + std::istringstream iss(line); + if (line.front() == '.') { + std::string t, tn; + iss >> t >> tn; + auto spn = split_tilename(tn); + if (!include_tts.empty() && !include_tts.count(spn.second)) { + skip = true; + continue; + } else { + skip = false; + } + settable_bits = &(tiletypes[spn.second].settable_bits); + bits = &(tiletypes[spn.second].inst_bits[prefix + spn.first]); + } else { + int bit = -1; + iss >> bit; + if (!skip && bit != -1) { + bits->insert(bit); + settable_bits->insert(bit); + } + } + } +} + +void parse_features(const std::string &prefix, std::istream &in) { + std::string line; + std::unordered_set<std::string> *feats = nullptr; + std::set<std::string> *ext_feats = nullptr; + bool skip = false; + + while (std::getline(in, line)) { + if (line.empty()) + continue; + std::istringstream iss(line); + if (line.front() == '.') { + std::string t, tn; + iss >> t >> tn; + auto spn = split_tilename(tn); + if (!include_tts.empty() && !include_tts.count(spn.second)) { + skip = true; + continue; + } else { + skip = false; + } + ext_feats = &(tiletypes[spn.second].extant_features); + feats = &(tiletypes[spn.second].inst_features[prefix + spn.first]); + } else { + std::string feat; + iss >> feat; + if (!feat.empty() && !skip) { + feats->insert(feat); + ext_feats->insert(feat); + } + } + } +} + +template <typename Tc, typename Tv, typename Tf> void set_erase_if(Tc &target, std::vector<Tv> &temp, Tf pred) { + temp.clear(); + for (auto &entry : target) + if (pred(entry)) + temp.push_back(entry); + for (auto &toerase : temp) + target.erase(toerase); +} + +void find_feature_deps(TileTypeData &tt) { + for (auto &f : tt.extant_features) { + auto &fd = tt.features[f]; + fd.deps = tt.extant_features; + fd.deps.erase(f); + std::vector<std::string> temp; + for (auto &inst : tt.inst_features) { + if (!inst.second.count(f)) + continue; + set_erase_if(fd.deps, temp, [&](const std::string &ef){ return !inst.second.count(ef); }); + } + } +} + +void process_feature(TileTypeData &tt, const std::string &feature) { + + FeatureData &fd = tt.features[feature]; + + fd.always_set_with_feature = tt.settable_bits; + //fd.never_set_with_feature = tt.settable_bits; + fd.always_set_without_feature = tt.settable_bits; + + std::vector<int> temp; + fd.count = 0; + bool always_have_feature = true; + for (auto &inst : tt.inst_bits) { + if (!tt.inst_features.count(inst.first)) + continue; + auto &ib = inst.second; + bool has_feature = tt.inst_features.at(inst.first).count(feature); + if (!has_feature) + always_have_feature = false; + + if (has_feature) + ++fd.count; + + if (has_feature) + set_erase_if(fd.always_set_with_feature, temp, [&](int bit){ return !ib.count(bit); }); + //if (has_feature) + // set_erase_if(fd.never_set_with_feature, temp, [&](int bit){ return ib.count(bit); }); + if (!has_feature) + set_erase_if(fd.always_set_without_feature, temp, [&](int bit){ return !ib.count(bit); }); + } + for (int as : fd.always_set_with_feature) + if (always_have_feature || !fd.always_set_without_feature.count(as)) + if (std::all_of(fd.deps.begin(), fd.deps.end(), [&](const std::string &dep) { + auto &dd = tt.features[dep]; + return std::find(dd.featbits.begin(), dd.featbits.end(), std::make_pair(as, false)) == dd.featbits.end(); + })) + fd.featbits.emplace_back(as, false); + /*for (int nv : fd.never_set_with_feature) + if (fd.always_set_without_feature.count(nv)) + fd.featbits.emplace_back(nv, true);*/ + // FIXME: inverted feature bits? + std::sort(fd.featbits.begin(), fd.featbits.end()); +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + std::cerr << "usage: correlate specfolder tiledata" << std::endl; + return 2; + } + + if (argc > 3) { + for (int i = 3; i < argc; i++) + include_tts.insert(argv[i]); + } + + for (const auto &entry : fs::directory_iterator(argv[1])) { + auto p = entry.path(); + if (p.extension() != ".features") + continue; + std::ifstream tilebits(p.parent_path().string() + "/" + p.stem().string() + ".tbits"); + if (!tilebits) { + std::cerr << "Failed to open " << (p.parent_path().string() + "/" + p.stem().string() + ".tbits") << std::endl; + return 1; + } + parse_bits(p.stem().string(), tilebits); + std::ifstream features(p.string()); + if (!features) { + std::cerr << "Failed to open " << p.string() << std::endl; + return 1; + } + parse_features(p.stem().string(), features); + } + + + + for (auto &tiletype : tiletypes) { + auto &t = tiletype.second; + std::ofstream td(std::string(argv[2]) + "/" + tiletype.first + ".bits"); + find_feature_deps(tiletype.second); + std::vector<std::string> ord_feats(t.extant_features.begin(), + t.extant_features.end()); + std::stable_sort(ord_feats.begin(), ord_feats.end(), [&](const std::string &a, const std::string &b) { + return t.features[a].deps.size() < t.features[b].deps.size(); + }); + + for (auto &feat : ord_feats) { + std::cerr << "Processing " << tiletype.first << "." << feat << std::endl; + FeatureData fd; + process_feature(t, feat); + } + + for (auto &feat : t.extant_features) { + auto &fd = t.features[feat]; + if (fd.count < 2) + continue; + td << feat; + for (auto &fb : fd.featbits) + td << " " << (fb.second ? "!" : "") << fb.first; + td << " # count: " << fd.count; + if (fd.deps.size() > 0) { + td << ", deps: "; + for (auto &d : fd.deps) + td << " " << d; + } + td << std::endl; + } + } + + return 0; +} \ No newline at end of file
diff --git a/tools/dump_bitstream.cpp b/tools/dump_bitstream.cpp new file mode 100644 index 0000000..c1b3689 --- /dev/null +++ b/tools/dump_bitstream.cpp
@@ -0,0 +1,278 @@ +// 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 <vector> +#include <iostream> +#include <map> +#include <string> +#include <fstream> +#include <stdexcept> +#include <iterator> +#include <stdarg.h> +#include <iomanip> +#include "common.h" + +const uint32_t preamble = 0xAA995566; + +#define SKIP_CRC +#define SKIP_CHECKSUM +#define SKIP_COMMENT + +class dummy_ostream : public std::ostream { + +}; + +dummy_ostream dummy_out; + +bool verbose_flag = false; + +#define COMMENT(x) (verbose_flag ? std::cout : dummy_out) << x << std::endl + + +struct ByteStreamReader { + std::vector<uint8_t> data; + std::size_t ptr = 0; + void reset() { + ptr = 0; + } + bool done() { + return ptr >= (data.size() - 3); + } + uint32_t curr_crc = 0; + uint32_t curr_addr = 0; + + uint32_t next_u32(bool skip_crc = false) { + if (done()) + throw std::runtime_error("at end of bitstream"); + uint32_t val = data[ptr] << 24UL | data[ptr+1] << 16UL | data[ptr+2] << 8UL | data[ptr+3]; + ptr += 4; + if (!skip_crc) + curr_crc = icap_crc(curr_addr, val, curr_crc); + return val; + } + + uint32_t peek_u32() { + if (done()) + throw std::runtime_error("at end of bitstream"); + uint32_t val = data[ptr] << 24UL | data[ptr+1] << 16UL | data[ptr+2] << 8UL | data[ptr+3]; + return val; + } + void skip_till_preamble() { + while (peek_u32() != preamble) + ++ptr; + COMMENT("# found preamble at offset " << ptr); + ptr += 4; + } +}; + + + +enum BitstreamRegister : uint16_t { + #define X(a, b) a = b, + #include "registers.inc" + #undef X +}; + + + +std::string get_register_name(uint16_t val) { + BitstreamRegister r = (BitstreamRegister)val; + #define X(a, b) if (val == a) return #a; + #include "registers.inc" + #undef X + return stringf("reg%04x", val); +} + +std::map<uint32_t, uint32_t> next_frame; + +void parse_bitstream(ByteStreamReader &rd) { + rd.reset(); + rd.skip_till_preamble(); + uint32_t frame = 0; + uint16_t last_reg = 0; + uint64_t checksum = 0, exp_checksum = 0; + int word = 0; + auto is_checksum = [] (int w, int b) { + return ((w == 45 && b <= 31) || (w == 46 && b <= 15)); + }; + + 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; + }; + + auto process_word = [&](uint32_t data) { + + + for (int i = 0; i < 32; i++) + if (data & (1 << i)) { + if (is_checksum(word, i)) { + checksum |= 1ULL << ((word - 45) * 32 + i); +#ifdef SKIP_CHECKSUM + continue; +#endif + } else { + exp_checksum ^= get_ecc_value(word, i); + } + std::cout << stringf("F0x%08xW%03dB%02d", frame, word, i) << std::endl; + } + ++word; + if (word >= 93 && next_frame.count(frame)) { + // 4 parity bits for each bit in nibbles +#if 0 + for (int i = 0; i < 4; i++) + for (int j = 0; j < 11; j++) + if (exp_checksum & (1ULL << (4 * j + i))) + exp_checksum ^= (1ULL << (44 + i)); +#endif + COMMENT(stringf("# checksum: 0x%012llX calc: 0x%012llX %s", checksum, exp_checksum, (checksum == exp_checksum) ? "" : "~~~~~")); + checksum = 0; + exp_checksum = 0; + frame = next_frame.at(frame); + } + }; + + while (!rd.done()) { + uint32_t hdr = rd.next_u32(true); + if (hdr == 0xFFFFFFFF) { + COMMENT("# desync"); + rd.skip_till_preamble(); + continue; + } + uint8_t type = (hdr >> 29) & 0x07; + if (type == 0b001) { + // Type 1 (short) packet + uint8_t op = (hdr >> 27) & 0x03; + switch(op) { + case 0x00: + COMMENT("# NOP "); + // NOP + break; + case 0x01: + // READ + break; + case 0x02: { + // WRITE + uint16_t reg = (hdr >> 13) & 0x3FFF; + rd.curr_addr = reg; + last_reg = reg; + int count = hdr & 0x3FF; + COMMENT("# write " << get_register_name(reg)); + if (reg == FAR) { + if (count != 1) + COMMENT("# bad FAR length " << count); + exp_checksum = 0; + checksum = 0; + frame = rd.next_u32(); + word = 0; + COMMENT(stringf("# frame 0x%08x", frame)); + } else if (reg == CRC) { + uint32_t crc = 0; + for(int i = 0; i < count; i++) + crc = rd.next_u32(true); + COMMENT(stringf("# CRC written=%08x calc=%08x %s", crc, rd.curr_crc, (crc == rd.curr_crc) ? "" : "*****")); + rd.curr_crc = 0; + } else if (reg == FDRI) { + for(int i = 0; i < count; i++) + process_word(rd.next_u32()); + } else if (reg == CMD) { + uint32_t cmd = 0; + for(int i = 0; i < count; i++) + cmd = rd.next_u32(); + COMMENT(stringf("# CMD %08x", cmd)); + if (cmd == 0x7) + rd.curr_crc = 0; + } else { + for(int i = 0; i < count; i++) + COMMENT(stringf("# data %08x", rd.next_u32())); + } + + if (reg == CRC && next_frame.count(frame)) { + exp_checksum = 0; + checksum = 0; + frame = next_frame.at(frame); + } + + } break; + } + } else if (type == 0b010) { + // Type 2 (long) packet + int count = hdr & 0x3FFFFFF; + if (last_reg == FDRI) { + for(int i = 0; i < count; i++) + process_word(rd.next_u32()); + } else { + for(int i = 0; i < count; i++) + COMMENT(stringf("# data %08x", rd.next_u32())); + } + } else { + std::cout << stringf("# unknown packet type %01x (header: %08x)", type, hdr) << std::endl; + return; + } + } +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + std::cerr << "Usage: dump_bitstream file.bit [frames.txt] [verbose]" << std::endl; + return 2; + } + + if (argc > 2) { + std::ifstream frame_db(argv[2]); + bool had_last = false; + uint32_t last; + uint32_t val; + frame_db.unsetf(std::ios::dec); + frame_db.unsetf(std::ios::hex); + frame_db.unsetf(std::ios::oct); + while (frame_db >> val) { + if (had_last) + next_frame[last] = val; + last = val; + had_last = true; + } + } + + if (argc > 3) { + if (std::string(argv[3]) == "verbose") + verbose_flag = true; + } + + ByteStreamReader rd; + std::ifstream file(argv[1], std::ios::binary); + file.unsetf(std::ios::skipws); + if (!file) { + std::cerr << "Failed to open input file" << std::endl; + return 2; + } + rd.data.insert(rd.data.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>()); + parse_bitstream(rd); +} \ No newline at end of file
diff --git a/tools/explain.cpp b/tools/explain.cpp new file mode 100644 index 0000000..acc1d07 --- /dev/null +++ b/tools/explain.cpp
@@ -0,0 +1,146 @@ +// 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 <vector> +#include <iostream> +#include <map> +#include <string> +#include <fstream> +#include <stdexcept> +#include <iterator> +#include <stdarg.h> +#include <iomanip> +#include <filesystem> +#include <map> +#include <set> +#include <cassert> +#include "common.h" + +ChipData chip; + +struct Tile { + std::string name, type; + TileInstance *data; + std::set<int> set_bits; + std::set<int> unknown_bits; + std::set<std::string> matched_features; +}; + +std::vector<Tile> tiles; +std::unordered_map<std::string, int> tile_by_name; + +struct InverseTileBitMap { + int tile; + int offset_in_frame; + int offset_in_tile; + int size; +}; + +std::unordered_map<uint32_t, std::vector<InverseTileBitMap>> tiles_by_frame; + +void parse_bits(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); + 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); + if (tiles_by_frame.count(frame)) { + int fb = word * 32 + bit; + for (auto &t : tiles_by_frame.at(frame)) { + if (fb >= t.offset_in_frame && fb < (t.offset_in_frame + t.size)) { + int tilebit = (fb - t.offset_in_frame) + t.offset_in_tile; + tiles[t.tile].set_bits.insert(tilebit); + tiles[t.tile].unknown_bits.insert(tilebit); + } + } + } + } +} + +// Currently have poor quality DBs for these tiles, +// skip outputting them +std::set<std::string> skip_tiles = { + "CLEL_L", "CLEM_R", "RCLK_INT_R", +}; + + +void setup_tiles() { + for (auto &tile : chip.tiles) { + auto &ti = tile.second; + if (skip_tiles.count(chip.tiletypes[ti.type].type)) + continue; + Tile t; + t.name = ti.name; + t.type = chip.tiletypes[ti.type].type; + t.data = &ti; + tile_by_name[ti.name] = int(tiles.size()); + + int off = 0; + for (auto &b : ti.bits) { + tiles_by_frame[b.frame_offset].push_back(InverseTileBitMap{int(tiles.size()), b.bit_offset, off, b.size}); + off += b.size; + } + + tiles.push_back(t); + + } +} + + +void process_tile(Tile &t) { + if (t.set_bits.empty()) + return; + auto &td = chip.load_tile_database(t.name); + for (const auto &feat : td.features) { + if (feat.second.empty()) + continue; + bool matched = true; + for (auto bit : feat.second) + if (!t.set_bits.count(bit)) { + matched = false; + break; + } + if (!matched) + continue; + t.matched_features.insert(feat.first); + for (auto bit : feat.second) + t.unknown_bits.erase(bit); + } +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + std::cerr << "usage: explain dbdir/ bitstream.dump" << std::endl; + return 2; + } + chip.open(argv[1]); + setup_tiles(); + parse_bits(argv[2]); + for (auto &t : tiles) { + process_tile(t); + for (auto &f : t.matched_features) + std::cout << t.name << "." << f << std::endl; + for (auto b : t.unknown_bits) + std::cout << t.name << ".?" << b << std::endl; + if (!t.matched_features.empty() || !t.unknown_bits.empty()) + std::cout << std::endl; + } +} \ No newline at end of file
diff --git a/tools/filter.py b/tools/filter.py new file mode 100644 index 0000000..8776311 --- /dev/null +++ b/tools/filter.py
@@ -0,0 +1,30 @@ +# 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. + +import re +import sys +import json + +line_re = re.compile(r'F(0x[0-9A-Fa-f]+)W(\d+)B(\d+)') +frames_to_tiles = {} # (start, size, tile, tile offset) + +active = False + +with open(sys.argv[1]) as f: + for line in f: + sl = line.strip() + if sl[0] == '.': + active = sys.argv[2] in sl + if active: + print(sl) \ No newline at end of file
diff --git a/tools/frames.py b/tools/frames.py new file mode 100644 index 0000000..8265cb3 --- /dev/null +++ b/tools/frames.py
@@ -0,0 +1,37 @@ +# 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. + +import re +import sys + +line_re = re.compile(r'F(0x[0-9A-Fa-f]+).*') + +outlines = set() + +with open(sys.argv[1], 'r') as f: + for line in f: + m = line_re.match(line) + if not m: + continue + frame = int(m.group(1), 16) + bus = (frame >> 24) & 0x7 + half = (frame >> 23) & 0x1 + row = (frame >> 18) & 0x1F + col = (frame >> 8) & 0x3FF + minor = frame & 0xFF + outlines.add("F=%08x B=%d H=%d R=%03d C=%04d M=%03d" + % (frame, bus, half, row, col, minor)) + +for o in sorted(outlines): + print(o)
diff --git a/tools/frames_2.py b/tools/frames_2.py new file mode 100644 index 0000000..0fe5890 --- /dev/null +++ b/tools/frames_2.py
@@ -0,0 +1,37 @@ +# 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. + +import re +import sys + +line_re = re.compile(r'0x([0-9A-Fa-f]+).*') + +outlines = set() + +with open(sys.argv[1], 'r') as f: + for line in f: + m = line_re.match(line) + if not m: + continue + frame = int(m.group(1), 16) + bus = (frame >> 24) & 0x7 + half = (frame >> 23) & 0x1 + row = (frame >> 18) & 0x1F + col = (frame >> 8) & 0x3FF + minor = frame & 0xFF + outlines.add("F=%08x B=%d H=%d R=%03d C=%04d M=%03d" + % (frame, bus, half, row, col, minor)) + +for o in sorted(outlines): + print(o)
diff --git a/tools/ll.py b/tools/ll.py new file mode 100644 index 0000000..f65beb9 --- /dev/null +++ b/tools/ll.py
@@ -0,0 +1,38 @@ +# 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. + +import re +import sys + +line_re = re.compile(r'Bit\s+\d+\s+(0x[0-9A-Fa-f]+)\s+\d+\s+SLR\d\s+\d+\s+Block=([A-Za-z0-9_]+).*') + +outlines = set() + +with open(sys.argv[1], 'r') as f: + for line in f: + m = line_re.match(line) + if not m: + continue + frame = int(m.group(1), 16) + site = m.group(2) + bus = (frame >> 24) & 0x7 + half = (frame >> 23) & 0x1 + row = (frame >> 18) & 0x1F + col = (frame >> 8) & 0x3FF + minor = frame & 0xFF + outlines.add("F=%08x B=%d H=%d R=%03d C=%04d M=%03d %s" + % (frame, bus, half, row, col, minor, site)) + +for o in sorted(outlines): + print(o)
diff --git a/tools/oddtiles.py b/tools/oddtiles.py new file mode 100644 index 0000000..82451cf --- /dev/null +++ b/tools/oddtiles.py
@@ -0,0 +1,58 @@ +# 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. + +import re +import sys +import json + +line_re = re.compile(r'F(0x[0-9A-Fa-f]+)W(\d+)B(\d+)') +frames_to_tiles = {} # (start, size, tile, tile offset) + +with open(sys.argv[1]) as tb_f: + tbj = json.load(tb_f) + +for tilename, tiledata in tbj.items(): + tile_offset = 0 + for chunk in tiledata: + frame, start, size = chunk + if frame not in frames_to_tiles: + frames_to_tiles[frame] = [] + frames_to_tiles[frame].append((start, size, tilename, tile_offset)) + tile_offset += size + +tile_bits = {} + +with open(sys.argv[2]) as df: + for line in df: + m = line_re.match(line) + if not m: + continue + frame = int(m[1], 16) + if frame not in frames_to_tiles: + continue + framebit = int(m[2]) * 32 + int(m[3]) + for fb in frames_to_tiles[frame]: + start, size, tile, toff = fb + if framebit > start and framebit < (start + size): + if tile not in tile_bits: + tile_bits[tile] = set() + tile_bits[tile].add(toff + (framebit - start)) + +for tile, bits in sorted(tile_bits.items()): + if "CLE" in tile: + if 152 not in bits: + print(tile) + if "INT" in tile: + if 3640 not in bits: + print(tile) \ No newline at end of file
diff --git a/tools/registers.inc b/tools/registers.inc new file mode 100644 index 0000000..0f0c8d8 --- /dev/null +++ b/tools/registers.inc
@@ -0,0 +1,20 @@ +X(CRC , 0b00000) +X(FAR , 0b00001) +X(FDRI , 0b00010) +X(FDRO , 0b00011) +X(CMD , 0b00100) +X(CTL0 , 0b00101) +X(MASK , 0b00110) +X(STAT , 0b00111) +X(LOUT , 0b01000) +X(COR0 , 0b01001) +X(MFWR , 0b01010) +X(CBC , 0b01011) +X(IDCODE , 0b01100) +X(AXSS , 0b01101) +X(COR1 , 0b01110) +X(WBSTAR , 0b10000) +X(TIMER , 0b10001) +X(BOOTSTS, 0b10110) +X(CTL1 , 0b11000) +X(BSPI , 0b11111) \ No newline at end of file
diff --git a/tools/roi.py b/tools/roi.py new file mode 100644 index 0000000..e1b217a --- /dev/null +++ b/tools/roi.py
@@ -0,0 +1,78 @@ +# 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. + +import re +import sys +import json +# Usage: tilegrid.json +with open(sys.argv[1]) as tb_f: + tbj = json.load(tb_f) +tile_to_frames = {} +frame_to_tiles = {} +for tilename, tiledata in tbj.items(): + tn = tilename.split(":")[0] + tile_offset = 0 + tile_to_frames[tn] = [] + for chunk in tiledata: + frame, start, size = chunk + tile_to_frames[tn].append(frame) + if frame not in frame_to_tiles: + frame_to_tiles[frame] = [] + frame_to_tiles[frame].append(tn) +basis_tiles = [ + "CLEM_X41Y120", + "INT_X41Y120", + "CLEL_R_X41Y120", + "BRAM_X42Y120", + "INT_INTF_L_X42Y120", + "INT_X42Y120", + "CLEL_R_X42Y120", + "CLEM_X43Y120", + "INT_X43Y120", + "INT_INTF_R_X43Y120", + "DSP_X43Y120", + "CLEM_X44Y120", + "INT_X44Y120", + "CLEL_R_X44Y120", + "CLEM_X45Y120", + "INT_X45Y120", + "INT_INTF_R_X45Y120", + "DSP_X45Y120", + "CLEM_X46Y120", + "INT_X46Y120", + "CLEL_R_X46Y120", + "INT_X47Y120", + "CLEL_R_X47Y120" +] + +roi_frames = set() + +for tile in basis_tiles: + if tile not in tile_to_frames: + continue + for frame in tile_to_frames[tile]: + roi_frames.add(frame) + +roi_tiles = set() +for frame in roi_frames: + for tile in frame_to_tiles[frame]: + roi_tiles.add(tile) + +with open(sys.argv[2], "w") as frames_f: + for frame in sorted(roi_frames): + print("0x%08x" % frame, file=frames_f) + +with open(sys.argv[3], "w") as tiles_f: + for tile in sorted(roi_tiles): + print("tile %s" % tile, file=tiles_f) \ No newline at end of file
diff --git a/tools/stripdb.cpp b/tools/stripdb.cpp new file mode 100644 index 0000000..01ff1ab --- /dev/null +++ b/tools/stripdb.cpp
@@ -0,0 +1,104 @@ +// 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 <vector> +#include <iostream> +#include <map> +#include <string> +#include <fstream> +#include <stdexcept> +#include <iterator> +#include <stdarg.h> +#include <iomanip> +#include <filesystem> +#include <map> +#include <set> + +namespace fs = std::filesystem; + + +struct TileType { + std::map<std::string, std::vector<int>> features; +}; + +std::map<std::string, TileType> tiletypes; + + +void parse_database(const std::string &name, std::istream &in) { + std::string line; + while (std::getline(in, line)) { + auto cpos = line.find('#'); + if (cpos != std::string::npos) + line = line.substr(0, cpos); + if (line.empty()) + continue; + std::istringstream iss(line); + std::string featname; + iss >> featname; + if (featname.empty()) + continue; + tiletypes[name].features[featname]; + int bit = -1; + iss >> bit; + while (bit != -1) { + tiletypes[name].features[featname].push_back(bit); + bit = -1; + iss >> bit; + } + } +} + + +int main(int argc, char *argv[]) { + if (argc < 3) { + std::cerr << "usage: stripdb in/ out/" << std::endl; + return 2; + } + + // Currently have poor quality DBs for these tiles, + // skip outputting them + std::set<std::string> skip_tiles = { + "CLEL_L", "CLEM_R", "RCLK_INT_R", + }; + + for (const auto &entry : fs::directory_iterator(argv[1])) { + auto p = entry.path(); + if (p.extension() != ".bits") + continue; + std::ifstream tiledata(p.string()); + if (skip_tiles.count(p.stem())) + continue; + parse_database(p.stem(), tiledata); + } + + // Misc cleanups + for (auto &f : tiletypes["INT"].features) + if (f.first.find(".VCC_WIRE") != std::string::npos) + f.second.clear(); + + for (const auto &tt : tiletypes) { + if(tt.second.features.empty()) + continue; + std::string dbname = std::string(argv[2]) + "/" + tt.first + ".bits"; + std::ofstream out(dbname); + if (!out) + std::cerr << "failed to open " << dbname << " for writing." << std::endl; + for (auto &f : tt.second.features) { + out << f.first; + for (int bit : f.second) + out << " " << bit; + out << std::endl; + } + } +} \ No newline at end of file
diff --git a/tools/tilebits.py b/tools/tilebits.py new file mode 100644 index 0000000..57fbe2b --- /dev/null +++ b/tools/tilebits.py
@@ -0,0 +1,151 @@ +# 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. + +import re +import sys +import json + +tiles = {} +site_to_tile = {} +tile_to_bits = {} # (frame, bit start, bit size) + +with open(sys.argv[1], 'r') as tilef: + for line in tilef: + sl = line.strip().split(",") + if len(sl) < 4: + continue + x = int(sl[0]) + y = int(sl[1]) + name = sl[2] + ttype = sl[3] + tiles[(x, y)] = (name + ":" + ttype, ttype, []) + for site in sl[4:]: + sitename, sitetype = site.split(":") + tiles[(x, y)][2].append((sitename, sitetype)) + site_to_tile[sitename] = (x, y) + +ll_line_re = re.compile(r'Bit\s+\d+\s+(0x[0-9A-Fa-f]+)\s+(\d+)\s+SLR\d\s+\d+\s+Block=([A-Za-z0-9_]+).*') +site_re = re.compile(r'SLICE_X(\d+)Y(\d+)') +with open(sys.argv[2], 'r') as llf: + for line in llf: + m = ll_line_re.match(line) + if not m: + continue + frame = int(m.group(1), 16) + bit = int(m.group(2)) + start_bit = bit - 2 + site = m.group(3) + bus = (frame >> 24) & 0x7 + half = (frame >> 23) & 0x1 + row = (frame >> 18) & 0x1F + col = (frame >> 8) & 0x3FF + m = frame & 0xFF + + + sm = site_re.match(site) + site_x = int(sm.group(1)) + site_y = int(sm.group(2)) + frame_upper = frame & ~0xFF + + + if site not in site_to_tile: + continue + tx, ty = site_to_tile[site] + tiledata = tiles[tx, ty] + tile_to_bits[tiledata[0]] = [] + for m in range(16): + tile_to_bits[tiledata[0]].append((frame_upper | m, start_bit, 48)) + + def process_nonlogic(x, y, icol): + if (x, y) not in tiles: + return + itiledata = tiles[x, y] + if itiledata[1] == "INT": + if (x + 1, y) not in tiles: + return + + int_frame_base = (frame_upper & ~0x3FFFF) | (icol << 8) + tile_to_bits[itiledata[0]] = [] + for m in range(76): + tile_to_bits[itiledata[0]].append((int_frame_base | m, start_bit, 48)) + process_clock(x, y-1, int_frame_base, start_bit + 48, 76) + process_cmt(x-2, y, icol-2) + process_int_intf(x-1, y, icol-1) + elif itiledata[1] == "BRAM": + bram_frame_base = (frame_upper & ~0x3FFFF) | (icol << 8) + tile_to_bits[itiledata[0]] = [] + for m in range(6): + tile_to_bits[itiledata[0]].append((bram_frame_base | m, start_bit, 5 * 48)) + process_clock(x, y-5, bram_frame_base, start_bit + 5*48, 6) + elif itiledata[1] == "DSP": + dsp_frame_base = (frame_upper & ~0x3FFFF) | (icol << 8) + tile_to_bits[itiledata[0]] = [] + for m in range(8): + tile_to_bits[itiledata[0]].append((dsp_frame_base | m, start_bit, 5 * 48)) + process_clock(x, y-5, dsp_frame_base, start_bit + 5*48, 8) + + if (x - 1, y) in tiles and "INT_INTF" in tiles[x - 1, y][1]: + process_clock(x-1, y-5, dsp_frame_base, start_bit + 5*48, 8) + + def process_clock(cx, cy, frame_base, end_bit, height): + if (cx, cy) not in tiles: + return + if end_bit != (1392 + 48): + return + ctiledata = tiles[cx, cy] + if not ctiledata[1].startswith("RCLK"): + return + tile_to_bits[ctiledata[0]] = [] + for m in range(height): + tile_to_bits[ctiledata[0]].append((frame_base | m, end_bit + 48, 48)) + + def process_cmt(x, y, icol): + if (x, y) not in tiles: + return + ctiledata = tiles[x, y] + if not ctiledata[1] in ("CMT_L", "CMT_RIGHT"): + return + cmt_frame_base = (frame_upper & ~0x3FFFF) | (icol << 8) + tile_to_bits[ctiledata[0]] = [] + for m in range(12): + tile_to_bits[ctiledata[0]].append((cmt_frame_base | m, start_bit, 60 * 48)) + + def process_int_intf(x, y, icol): + if (x, y) not in tiles: + return + itiledata = tiles[x, y] + if not itiledata[1] in ("INT_INTF_L_IO", "INT_INTF_R_IO"): + return + int_frame_base = (frame_upper & ~0x3FFFF) | (icol << 8) + tile_to_bits[itiledata[0]] = [] + for m in range(4): + tile_to_bits[itiledata[0]].append((int_frame_base | m, start_bit, 48)) + + + process_nonlogic(tx-1, ty, col-1) + process_nonlogic(tx+1, ty, col+1) + process_clock(tx, ty-1, frame_upper, start_bit + 48, 16) +# Original JSON +with open(sys.argv[3], 'w') as tj: + tj.write(json.dumps(tile_to_bits, sort_keys=True, indent=4, separators=(',', ': '))) + tj.write("\n") +# New simplified text format +with open(sys.argv[4], 'w') as tf: + for loc, tiledata in sorted(tiles.items()): + print(".tile %s %s %d %d" % (tiledata[0].split(":")[0], tiledata[1], loc[0], loc[1]), file=tf) + for site in tiledata[2]: + print("site %s %s" % site, file=tf) + if tiledata[0] in tile_to_bits: + for frame, offset, size in tile_to_bits[tiledata[0]]: + print("frame 0x%08x bits %d +: %d" % (frame, offset, size), file=tf) \ No newline at end of file