diff --git a/doc/FPGAs.yml b/doc/FPGAs.yml index 682a4c5..30508aa 100644 --- a/doc/FPGAs.yml +++ b/doc/FPGAs.yml @@ -229,6 +229,7 @@ Xilinx: Model: - xcku035 - xcku040 + - xcku060 - xcku115 URL: https://www.xilinx.com/products/silicon-devices/fpga/kintex-ultrascale.html#productTable Memory: OK diff --git a/src/main.cpp b/src/main.cpp index 4d401cb..0a69038 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -92,6 +92,8 @@ struct arguments { string mcufw; bool conmcu; std::map user_misc_devs; + bool read_dna; + bool read_xadc; }; int run_xvc_server(const struct arguments &args, const cable_t &cable, @@ -120,7 +122,8 @@ int main(int argc, char **argv) "127.0.0.1", 0, false, false, "", false, false, /* xvc server */ false, 3721, "-", - "", false, {} // mcufw conmcu, user_misc_dev_list + "", false, {}, // mcufw conmcu, user_misc_dev_list + false, false // read_dna, read_xadc }; /* parse arguments */ try { @@ -557,7 +560,8 @@ int main(int argc, char **argv) if (fab == "xilinx") { fpga = new Xilinx(jtag, args.bit_file, args.secondary_bit_file, args.file_type, args.prg_type, args.fpga_part, args.bridge_path, - args.target_flash, args.verify, args.verbose, args.skip_load_bridge, args.skip_reset); + args.target_flash, args.verify, args.verbose, args.skip_load_bridge, args.skip_reset, + args.read_dna, args.read_xadc); } else if (fab == "altera") { fpga = new Altera(jtag, args.bit_file, args.file_type, args.prg_type, args.fpga_part, args.bridge_path, args.verify, @@ -822,6 +826,10 @@ int parse_opt(int argc, char **argv, struct arguments *args, cxxopts::value(args->mcufw)) ("conmcu", "Connect JTAG to MCU", cxxopts::value(args->conmcu)) + ("D,read_dna", "Read DNA (Xilinx FPGA only)", + cxxopts::value(args->read_dna)) + ("X,read_xadc", "Read XADC (Xilinx FPGA only)", + cxxopts::value(args->read_xadc)) ("V,Version", "Print program version"); options.parse_positional({"bitstream"}); @@ -1012,7 +1020,9 @@ int parse_opt(int argc, char **argv, struct arguments *args, !args->bulk_erase_flash && !args->xvc && !args->reset && - !args->conmcu) { + !args->conmcu && + !args->read_dna && + !args->read_xadc) { printError("Error: bitfile not specified"); cout << options.help() << endl; throw std::exception(); diff --git a/src/part.hpp b/src/part.hpp index d0b5660..91f729b 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -86,8 +86,9 @@ static std::map fpga_list = { {0x03736093, {"xilinx", "zynq", "xc7z100", 6}}, /* Xilinx Ultrascale / Kintex */ - {0x13822093, {"xilinx", "kintexus", "xcku040", 6}}, {0x13823093, {"xilinx", "kintexus", "xcku035", 6}}, + {0x13822093, {"xilinx", "kintexus", "xcku040", 6}}, + {0x13919093, {"xilinx", "kintexus", "xcku060", 6}}, {0x1390d093, {"xilinx", "kintexus", "xcku115", 6}}, /* Xilinx Ultrascale+ / Artix */ diff --git a/src/xilinx.cpp b/src/xilinx.cpp index 9b5924f..4d1e0b4 100644 --- a/src/xilinx.cpp +++ b/src/xilinx.cpp @@ -53,6 +53,74 @@ #define XC95_ISC_PROGRAM 0xea #define XC95_ISC_READ 0xee +/* DRP instructions set */ +#define XADC_DRP 0x37 + +/* XADC Addresses */ +#define XADC_TEMP 0x00 +#define XADC_LOCK 0x00 +#define XADC_VCCINT 0x01 +#define XADC_VCCAUX 0x02 +#define XADC_VAUXEN 0x02 +#define XADC_VPVN 0x03 +#define XADC_RESET 0x03 +#define XADC_VREFP 0x04 +#define XADC_VREFN 0x05 +#define XADC_VCCBRAM 0x06 +#define XADC_SUPAOFFS 0x08 +#define XADC_ADCAOFFS 0x09 +#define XADC_ADCAGAIN 0x0a +#define XADC_VCCPINT 0x0d +#define XADC_VCCPAUX 0x0e +#define XADC_VCCODDR 0x0f +#define XADC_VAUX0 0x10 +#define XADC_VAUX1 0x11 +#define XADC_VAUX2 0x12 +#define XADC_VAUX3 0x13 +#define XADC_VAUX4 0x14 +#define XADC_VAUX5 0x15 +#define XADC_VAUX6 0x16 +#define XADC_VAUX7 0x17 +#define XADC_VAUX8 0x18 +#define XADC_VAUX9 0x19 +#define XADC_VAUX10 0x1a +#define XADC_VAUX11 0x1b +#define XADC_VAUX12 0x1c +#define XADC_VAUX13 0x1d +#define XADC_VAUX14 0x1e +#define XADC_VAUX15 0x1f +#define XADC_SUPBOFFS 0x30 +#define XADC_ADCBOFFS 0x31 +#define XADC_ADCBGAIN 0x32 +#define XADC_FLAG 0x3f +#define XADC_CFG0 0x40 +#define XADC_CFG1 0x41 +#define XADC_CFG2 0x42 +#define XADC_SEQ0 0x48 +#define XADC_SEQ1 0x49 +#define XADC_SEQ2 0x4a +#define XADC_SEQ3 0x4b +#define XADC_SEQ4 0x4c +#define XADC_SEQ5 0x4d +#define XADC_SEQ6 0x4e +#define XADC_SEQ7 0x4f +#define XADC_ALARM0 0x50 +#define XADC_ALARM1 0x51 +#define XADC_ALARM2 0x52 +#define XADC_ALARM3 0x53 +#define XADC_ALARM4 0x54 +#define XADC_ALARM5 0x55 +#define XADC_ALARM6 0x56 +#define XADC_ALARM7 0x57 +#define XADC_ALARM8 0x58 +#define XADC_ALARM9 0x59 +#define XADC_ALARM10 0x5a +#define XADC_ALARM11 0x5b +#define XADC_ALARM12 0x5c +#define XADC_ALARM13 0x5d +#define XADC_ALARM14 0x5e +#define XADC_ALARM15 0x5f + /* Boundary-scan instruction set based on the FPGA model */ static std::map>> ircode_mapping { @@ -125,6 +193,69 @@ static void open_bitfile( printSuccess("DONE"); } +#define FUSE_DNA 0x32 + +unsigned long long Xilinx::fuse_dna_read(void) +{ + unsigned char tx_data[8] = {0,0,0,0,0,0,0,0}; + unsigned char rx_data[8]; + + _jtag->go_test_logic_reset(); + _jtag->shiftIR(FUSE_DNA, 6); + _jtag->shiftDR((unsigned char *)&tx_data, (unsigned char *)&rx_data, 64); + + unsigned long long dna = 0; + + for(int i = 0; i < 8; i++) { + unsigned char rev = 0; + for (int j = 0; j < 8; j++) { + rev |= ((rx_data[i]>>j)&1)<<(7-j); + } + dna = (dna << 8ULL) | rev; + } + + return dna & 0x1ffffffffffffff; +} + +unsigned int Xilinx::xadc_read(unsigned short addr) +{ + unsigned int tx_data = (1 << 26) | (addr << 16); + unsigned int rx_data = 0; + + _jtag->go_test_logic_reset(); + _jtag->shiftIR(XADC_DRP, 6); + _jtag->shiftDR((unsigned char *)&tx_data, (unsigned char *)&rx_data, 32); + usleep(1000); + _jtag->shiftIR(XADC_DRP, 6); + _jtag->shiftDR((unsigned char *)&tx_data, (unsigned char *)&rx_data, 32); + + return rx_data; +} + +void Xilinx::xadc_write(unsigned short addr, unsigned short data) +{ + unsigned int tx_data = (1 << 26) | (addr << 16) | data; + unsigned int rx_data = 0; + + _jtag->go_test_logic_reset(); + _jtag->shiftIR(XADC_DRP, 6); + _jtag->shiftDR((unsigned char *)&tx_data, (unsigned char *)&rx_data, 32); +} + +unsigned int Xilinx::xadc_single(unsigned short ch) +{ + _jtag->go_test_logic_reset(); + // single channel, disable the sequencer + xadc_write(XADC_CFG1,0x3000); + // set channel, no averaging, additional settling time + xadc_write(XADC_CFG0,(1<<15) | (1<<8) | ch); + // leave some time (1ms) for the conversion + usleep(1000); + unsigned int ret = xadc_read(ch); + + return ret; +} + Xilinx::Xilinx(Jtag *jtag, const std::string &filename, const std::string &secondary_filename, const std::string &file_type, @@ -132,7 +263,7 @@ Xilinx::Xilinx(Jtag *jtag, const std::string &filename, const std::string &device_package, const std::string &spiOverJtagPath, const std::string &target_flash, bool verify, int8_t verbose, - bool skip_load_bridge, bool skip_reset): + bool skip_load_bridge, bool skip_reset, bool read_dna, bool read_xadc): Device(jtag, filename, file_type, verify, verbose), SPIInterface(filename, verbose, 256, verify, skip_load_bridge, skip_reset), @@ -251,6 +382,57 @@ Xilinx::Xilinx(Jtag *jtag, const std::string &filename, } else { _fpga_family = UNKNOWN_FAMILY; } + + if (read_dna) { + if (_fpga_family == ARTIX_FAMILY || _fpga_family == KINTEXUS_FAMILY) { + unsigned long long dna = Xilinx::fuse_dna_read(); + printf("{\"dna\": \"0x%016lx\"}\n", dna); + } else { + throw std::runtime_error("Error: read_xadc only supported for Artix 7"); + } + } + + if (read_xadc) { + if (_fpga_family == ARTIX_FAMILY || _fpga_family == KINTEXUS_FAMILY) { + // calibrate XADC + Xilinx::xadc_single(8); + + const int MAX_CHANNEL = 8; + const int TEMP_MEAS = 4; + + unsigned int v = 0; + for (int i = 0; i < TEMP_MEAS; i++) { + v += Xilinx::xadc_single(0); + } + double temp = ((v/(double)TEMP_MEAS) * 503.975)/(1 << 16) - 273.15; + + unsigned int channel_values[32]; + for (int ch = 0; ch < MAX_CHANNEL; ch++) { + if (ch < 7 || ch > 12) { + v = Xilinx::xadc_single(ch); + } else { + // 7 = Invalid channel selection + // 8 = Carry out XADC calibration + // 9...12 = Invalid channel selection + v = 0; + } + channel_values[ch] = v; + } + + /* output as JSON dict */ + std::cout << "{"; + std::cout << "\"temp\": " << temp << ", "; + std::cout << "\"raw\": {"; + for (int ch = 0; ch < MAX_CHANNEL; ch++) { + std::cout << "\"" << ch << "\": " << channel_values[ch] + << ((ch==MAX_CHANNEL-1)? "}" : ", "); + } + std::cout << "}" << std::endl; + + } else { + throw std::runtime_error("Error: read_xadc only supported for Artix 7"); + } + } } Xilinx::~Xilinx() {} diff --git a/src/xilinx.hpp b/src/xilinx.hpp index c8cf142..ae78c49 100644 --- a/src/xilinx.hpp +++ b/src/xilinx.hpp @@ -24,7 +24,8 @@ class Xilinx: public Device, SPIInterface { const std::string &spiOverJtagPath, const std::string &target_flash, bool verify, int8_t verbose, - bool skip_load_bridge, bool skip_reset); + bool skip_load_bridge, bool skip_reset, + bool read_dna, bool read_xadc); ~Xilinx(); void program(unsigned int offset, bool unprotect_flash) override; @@ -191,6 +192,14 @@ class Xilinx: public Device, SPIInterface { SECONDARY_FLASH = 0x2 }; + /* XADC */ + unsigned int xadc_read(unsigned short addr); + void xadc_write(unsigned short addr, unsigned short data); + unsigned int xadc_single(unsigned short ch); + + /* DNA */ + unsigned long long fuse_dna_read(void); + /*! * \brief Starting from UltraScale, Xilinx devices can support dual * QSPI flash configuration, with two different flash chips