xilinx: added readback access to registers (stat, conf, ...)
This commit is contained in:
parent
99147efa27
commit
2dd2f45e61
|
|
@ -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 */
|
||||
/**********************/
|
||||
|
|
|
|||
21
src/main.cpp
21
src/main.cpp
|
|
@ -94,6 +94,7 @@ struct arguments {
|
|||
std::map<uint32_t, misc_device> 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<string> 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<bool>(args->read_dna))
|
||||
("X,read_xadc", "Read XADC (Xilinx FPGA only)",
|
||||
cxxopts::value<bool>(args->read_xadc))
|
||||
("read-register", "Read Status Register(Xilinx FPGA only)",
|
||||
cxxopts::value<string>(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();
|
||||
|
|
|
|||
222
src/xilinx.cpp
222
src/xilinx.cpp
|
|
@ -8,7 +8,10 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
|
@ -131,6 +134,7 @@ static std::map<std::string, std::map<std::string, std::vector<uint8_t>>>
|
|||
{
|
||||
{ "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<int, std::string> reg_cnt;
|
||||
} reg_struct_t;
|
||||
|
||||
#define REG_ENTRY(_description, _offset, _size, ...) \
|
||||
{_description, _offset, _size, {__VA_ARGS__}}
|
||||
|
||||
static const std::map<std::string, std::list<reg_struct_t>> reg_content = {
|
||||
{"CTRL0", std::list<reg_struct_t>{
|
||||
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_struct_t>{
|
||||
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<std::string, uint8_t> 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<uint32_t>(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<uint8_t>((tmp >> 0) & 0xff),
|
||||
static_cast<uint8_t>((tmp >> 8) & 0xff),
|
||||
static_cast<uint8_t>((tmp >> 16) & 0xff),
|
||||
static_cast<uint8_t>((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<reg_struct_t> 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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue