From 4fe3d7ccc1edf1056ce88e4be05a1a6b177afe18 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Sun, 21 Apr 2024 14:35:23 +0200 Subject: [PATCH] xilinx: added readback access to registers (stat, conf, ...) --- src/device.hpp | 2 + src/main.cpp | 21 ++++- src/xilinx.cpp | 222 ++++++++++++++++++++++++++++++++++++++++++++++++- src/xilinx.hpp | 17 ++++ 4 files changed, 257 insertions(+), 5 deletions(-) diff --git a/src/device.hpp b/src/device.hpp index 29f82fb43d..ded9130e3d 100644 --- a/src/device.hpp +++ b/src/device.hpp @@ -41,6 +41,8 @@ class Device { virtual void program(unsigned int offset, bool unprotect_flash) = 0; + virtual bool read_register(const std::string reg_name) { return false;} + /**********************/ /* flash access */ /**********************/ diff --git a/src/main.cpp b/src/main.cpp index af552a31b9..fe4ee0d8cd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -94,6 +94,7 @@ struct arguments { std::map user_misc_devs; bool read_dna; bool read_xadc; + string read_register; }; int run_xvc_server(const struct arguments &args, const cable_t &cable, @@ -123,7 +124,7 @@ int main(int argc, char **argv) /* xvc server */ false, 3721, "-", "", false, {}, // mcufw conmcu, user_misc_dev_list - false, false // read_dna, read_xadc + false, false, "" // read_dna, read_xadc, read_register }; /* parse arguments */ try { @@ -612,6 +613,11 @@ int main(int argc, char **argv) fpga->connectJtagToMCU(); } + /* read internal register */ + if (!args.read_register.empty()) { + fpga->read_register(args.read_register); + } + /* unprotect SPI flash */ if (args.unprotect_flash && args.bit_file.empty()) { fpga->unprotect_flash(); @@ -701,7 +707,7 @@ static int parse_eng(string arg, double *dst) { int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *pins_config) { - string freqo; + string freqo, rd_reg; vector pins, bus_dev_num; bool verbose, quiet; int8_t verbose_level = -2; @@ -831,6 +837,8 @@ int parse_opt(int argc, char **argv, struct arguments *args, cxxopts::value(args->read_dna)) ("X,read_xadc", "Read XADC (Xilinx FPGA only)", cxxopts::value(args->read_xadc)) + ("read-register", "Read Status Register(Xilinx FPGA only)", + cxxopts::value(rd_reg)) ("V,Version", "Print program version"); options.parse_positional({"bitstream"}); @@ -1017,6 +1025,12 @@ int parse_opt(int argc, char **argv, struct arguments *args, printf("pas empty\n"); } + if (result.count("read-register")) { + args->read_register = rd_reg; + printf("read_register"); + std::cout << args->read_register << std::endl; + } + if (args->bit_file.empty() && args->secondary_bit_file.empty() && args->file_type.empty() && @@ -1030,7 +1044,8 @@ int parse_opt(int argc, char **argv, struct arguments *args, !args->reset && !args->conmcu && !args->read_dna && - !args->read_xadc) { + !args->read_xadc && + args->read_register.empty()) { printError("Error: bitfile not specified"); cout << options.help() << endl; throw std::exception(); diff --git a/src/xilinx.cpp b/src/xilinx.cpp index 448969c2ef..e400a7d7ab 100644 --- a/src/xilinx.cpp +++ b/src/xilinx.cpp @@ -8,7 +8,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -131,6 +134,7 @@ static std::map>> { { "USER1", {0x02} }, { "USER2", {0x03} }, + { "CFG_OUT", {0x04} }, { "CFG_IN", {0x05} }, { "USERCODE", {0x08} }, { "IDCODE", {0x09} }, @@ -807,8 +811,222 @@ void Xilinx::program_mem(ConfigBitstreamParser *bitfile) * now functional. X 1 3 */ _jtag->go_test_logic_reset(); - /* Some xc7s50 does not detect correct connected flash w/o this shift*/ - _jtag->shiftIR(tx_buf, rx_buf, _irlen); + /* Some xc7s50 does not detect correct connected flash w/o this shift*/ + _jtag->shiftIR(tx_buf, rx_buf, _irlen); + uint8_t ir_c = rx_buf[0] & 0x03; + uint8_t isc_done = ((rx_buf[0] >> 2) & 0x01); + uint8_t isc_ena = ((rx_buf[0] >> 3) & 0x01); + uint8_t init = ((rx_buf[0] >> 4) & 0x01); + uint8_t done = ((rx_buf[0] >> 5) & 0x01); + printf("Shift IR %02x\n", rx_buf[0]); + printf("ir: %x isc_done %x isc_ena %x init %x done %x\n", ir_c, isc_done, isc_ena, + init, done); + + if (!done) { + read_register("STAT"); + } +} + +static const uint32_t reverseByte(uint32_t in) { + uint8_t out [4]; + for (int i = 0; i < 4; i++) { + uint8_t tmp = (in >> (i*8)) & 0xff; + out[i] = BitParser::reverseByte(tmp); + } + return ((((uint32_t)out[0]) << 24) | + (((uint32_t)out[1]) << 16) | + (((uint32_t)out[2]) << 8) | + (((uint32_t)out[3]) << 0)); +} + +static const uint32_t reverseWord(uint32_t in) { + uint32_t out = 0; + for (int i = 0; i < 32; i++) { + out <<= 1; + out |= (in >> i) & 0x01; + } + return out; +} + +static const uint32_t char_array_to_word(uint8_t *in) +{ + return (((uint32_t)in[3] << 24) | + ((uint32_t)in[2] << 16) | + ((uint32_t)in[1] << 8) | + ((uint32_t)in[0] << 0)); +} + +typedef struct { + std::string description; + uint8_t offset; + uint8_t size; + std::map reg_cnt; +} reg_struct_t; + +#define REG_ENTRY(_description, _offset, _size, ...) \ + {_description, _offset, _size, {__VA_ARGS__}} + +static const std::map> reg_content = { + {"CTRL0", std::list{ + REG_ENTRY("GTS USR B", 0, 1, + {0, "I/Os 3-stated"}, {1, "I/Os active"}), + REG_ENTRY("Reserved", 1, 2), + REG_ENTRY("PERSIST", 3, 1, + {0, "No"}, {1, "Yes"}), + REG_ENTRY("SBITS", 4, 2, + {0, "Read/Write OK"}, {1, "Readback disabled"}, + {2, "Both Writes and Reads disabled"}, + {3, "Both Writes and Reads disabled"}), + REG_ENTRY("DEC", 6, 1, + {0, "Disable"}, {1, "Enable"}), + REG_ENTRY("FARSRC", 7, 1), + REG_ENTRY("GLUMASK B", 8, 1), + REG_ENTRY("Reserved", 9, 1), + REG_ENTRY("ConfigFallback", 10, 1, + {0, "Disable"}, {1, "Enable"}), + REG_ENTRY("Reserved", 11, 1), + REG_ENTRY("OverTempPwrDown",12, 1, + {0, "Disable"}, {1, "Enable"}), + REG_ENTRY("Reserved", 13, 17), + REG_ENTRY("ICAP Select", 30, 1, + {0, "Top ICAPE2 Port Enabled"}, + {1, "Bottom ICAPE2 Port Enabled"}), + REG_ENTRY("EFUSE key", 31, 1, + {0, "Battery backed RAM"}, {1, "eFUSE"}), + }}, + {"STAT", std::list{ + REG_ENTRY("CRC Error", 0, 1, + {0, "No CRC error"}, {1, "CRC error"}), + REG_ENTRY("Part Secured", 1, 1), + REG_ENTRY("MMCM lock", 2, 1), + REG_ENTRY("DCI match", 3, 1), + REG_ENTRY("EOS", 4, 1), + REG_ENTRY("GTS CFG B", 5, 1), + REG_ENTRY("GWE", 6, 1), + REG_ENTRY("GHIGH B", 7, 1), + REG_ENTRY("MODE", 8, 3), + REG_ENTRY("INIT Complete", 11, 1), + REG_ENTRY("INIT B", 12, 1), + REG_ENTRY("Release Done", 13, 1), + REG_ENTRY("Done", 14, 1), + REG_ENTRY("ID Error", 15, 1, + {0, "No ID error"}, {1, "ID error"}), + REG_ENTRY("DEC Error", 16, 1), + REG_ENTRY("XADC Over temp", 17, 1), + REG_ENTRY("STARTUP State", 18, 3), + REG_ENTRY("Reserved", 21, 4), + REG_ENTRY("BUS Width", 25, 2, + {0, "x1"}, {1, "x8"}, {2, "x16"}, {3, "x32"}), + REG_ENTRY("Reserved", 27, 5) + }}, +}; + +/* UG470 table 5-23 */ +static const std::map reg_code = { + {"CTRL0", 0x05}, // Control register 0 + {"STAT", 0x07}, // Status register + {"CONF0", 0x09}, // Configuration Option 0 + {"CONF1", 0x0e}, // Configuration Option 1 + {"BHSTAT", 0x16}, // Boot history status register + {"CTRL1", 0x18}, // Control register 1 +}; + +uint32_t Xilinx::dumpRegister(std::string reg_name) +{ + uint8_t reg[4]; + const uint8_t dummy[] = {0x00, 0x00, 0x00, 0x04}; + /* opcode: + * 0x0: NOP + * 0x1: read + * 0x2: write + * 0x3: Reserved + */ + /* register code + * 0x05: Control register 0 + * 0x07: Status register + * 0x09: Configuration Option 0 + * 0x0e: Configuration Option 1 + * 0x18: Control Register 1 + * 0x16: Boot history Status register + */ + auto code = reg_code.find(reg_name); + if (code == reg_code.end()) { + printError("Unknown register " + reg_name); + printError("Known Register are:"); + for (auto reg :reg_code) + printError("\t" + reg.first); + return 0xdeadbeef; + } + + /* packet type 1 */ + const uint32_t regcode = static_cast(code->second); + uint32_t cfg_packets[] = { + 0xAA995566, // Sync Word + 0x20000000, // NOOP + ((0x01 & 0x0007) << 29) | // [31:29]: Header type + ((0x01 & 0x0003) << 27) | // [28:27]: opcode + ((regcode & 0x3fff) << 13) | // [26:13]: register code + ((0x00 & 0x0003) << 11) | // [12:11]: Reserved must be set to 0 + ((0x01 & 0x07ff) << 0), // [10:0 ]: word count + 0x20000000, // NOOP + 0x20000000, // NOOP + }; + + _jtag->go_test_logic_reset(); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->shiftIR(get_ircode(_ircode_map, "CFG_IN"), NULL, _irlen, Jtag::SELECT_DR_SCAN); + + Jtag::tapState_t next_state = Jtag::SHIFT_DR; + for (int i = 0; i < 5; i++) { + if (i == 4) + next_state = Jtag::SELECT_IR_SCAN; + const uint32_t tmp = reverseWord(cfg_packets[i]); + const uint8_t cfg[] = { + static_cast((tmp >> 0) & 0xff), + static_cast((tmp >> 8) & 0xff), + static_cast((tmp >> 16) & 0xff), + static_cast((tmp >> 24) & 0xff) + }; + + _jtag->shiftDR(cfg, NULL, 32, next_state); + } + + _jtag->shiftIR(get_ircode(_ircode_map, "CFG_OUT"), NULL, _irlen, Jtag::SELECT_DR_SCAN); + _jtag->shiftDR(dummy, reg, 32); + _jtag->go_test_logic_reset(); + + uint32_t reg_word = char_array_to_word(reg); + reg_word = reverseWord(reg_word); + return reg_word; +} + +void Xilinx::displayRegister(const std::string reg_name, const uint32_t reg_val) { + auto reg = reg_content.find(reg_name); + if (reg == reg_content.end()) { + printError("Unknown register " + reg_name); + return; + } + + std::stringstream raw_val; + raw_val << "0x" << std::hex << std::to_string(reg_val); + printSuccess("Register raw value: " + raw_val.str()); + + const std::list regs = reg->second; + for (reg_struct_t r: regs) { + uint8_t offset = r.offset; + uint8_t size = r.size; + uint32_t mask = (1 << size) - 1; + uint32_t val = (reg_val >> offset) & mask; + std::stringstream ss, desc; + desc << r.description; + ss << std::setw(15) << std::left << r.description; + if (r.reg_cnt.size() != 0) + ss << r.reg_cnt[val]; + else + ss << std::to_string(val); + + printInfo(ss.str()); + } } bool Xilinx::dumpFlash(uint32_t base_addr, uint32_t len) diff --git a/src/xilinx.hpp b/src/xilinx.hpp index b2426127be..ec0ce2a59c 100644 --- a/src/xilinx.hpp +++ b/src/xilinx.hpp @@ -36,6 +36,23 @@ class Xilinx: public Device, SPIInterface { void program_mem(ConfigBitstreamParser *bitfile); bool dumpFlash(uint32_t base_addr, uint32_t len) override; + bool read_register(const std::string reg_name) override { + displayRegister(reg_name, dumpRegister(reg_name)); + return true; + } + /*! + * \brief display register content (bits/slices) + * \in reg_name: register name + * \in reg_val: register value + */ + void displayRegister(const std::string reg_name, const uint32_t reg_val); + /*! + * \brief read register value from the devices + * \in reg_name: register name + * \return register value + */ + uint32_t dumpRegister(const std::string reg_name); + /*! * \brief protect SPI flash blocks */