| // 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); |
| } |