// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2019 Gwenhael Goavec-Merou */ #include "altera.hpp" #include #include #include #include "common.hpp" #include "device.hpp" #include "jtag.hpp" #include "progressBar.hpp" #include "rawParser.hpp" #if defined (_WIN64) || defined (_WIN32) #include "pathHelper.hpp" #endif #include "pofParser.hpp" #define IDCODE 6 #define USER0 0x0C #define USER1 0x0E #define BYPASS 0x3FF #define IRLENGTH 10 Altera::Altera(Jtag *jtag, const std::string &filename, const std::string &file_type, Device::prog_type_t prg_type, const std::string &device_package, const std::string &spiOverJtagPath, bool verify, int8_t verbose, const std::string &flash_sectors, bool skip_load_bridge, bool skip_reset): Device(jtag, filename, file_type, verify, verbose), FlashInterface(filename, verbose, 256, verify, skip_load_bridge, skip_reset), _device_package(device_package), _spiOverJtagPath(spiOverJtagPath), _vir_addr(0x1000), _vir_length(14), _clk_period(1), _flash_sectors(flash_sectors) { /* check device family */ _idcode = _jtag->get_target_device_id(); std::string family = fpga_list[_idcode].family; if (family == "MAX 10") { _fpga_family = MAX10_FAMILY; } else { _fpga_family = CYCLONE_MISC; // FIXME } if (prg_type == Device::RD_FLASH) { _mode = Device::READ_MODE; } else { if (!_file_extension.empty()) { if (_file_extension == "svf") { _mode = Device::MEM_MODE; } else if (_file_extension == "rpd" || _file_extension == "rbf") { if (prg_type == Device::WR_SRAM) _mode = Device::MEM_MODE; else _mode = Device::SPI_MODE; } else if (_file_extension == "pof") { // MAX10 _mode = Device::FLASH_MODE; } else if (_fpga_family == MAX10_FAMILY) { _mode = Device::FLASH_MODE; } else { // unknown type -> sanity check if (prg_type == Device::WR_SRAM) { printError("file has an unknown type:"); printError("\tplease use rbf or svf file"); printError("\tor use --write-flash with: ", false); printError("-b board_name or --fpga_part xxxx"); throw std::runtime_error("Error: wrong file"); } else { _mode = Device::SPI_MODE; } } } } } Altera::~Altera() {} void Altera::reset() { /* PULSE_NCONFIG */ unsigned char tx_buff[2] = {0x01, 0x00}; _jtag->set_state(Jtag::TEST_LOGIC_RESET); _jtag->shiftIR(tx_buff, NULL, IRLENGTH); _jtag->toggleClk(1); _jtag->set_state(Jtag::TEST_LOGIC_RESET); } void Altera::programMem(RawParser &_bit) { int byte_length = _bit.getLength()/8; const uint8_t *data = _bit.getData(); unsigned char cmd[2]; unsigned char tx[864/8], rx[864/8]; memset(tx, 0, 864/8); /* enddr idle * endir irpause * state idle */ /* ir 0x02 IRLENGTH */ *reinterpret_cast(cmd) = 0x02; _jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); /* RUNTEST IDLE 12000 TCK ENDSTATE IDLE; */ _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(1000000/_clk_period); /* write */ ProgressBar progress("Load SRAM", byte_length, 50, _quiet); int xfer_len = 512; int tx_len; Jtag::tapState_t tx_end; for (int i=0; i < byte_length; i+=xfer_len) { if (i + xfer_len > byte_length) { // last packet with some size tx_len = (byte_length - i) * 8; tx_end = Jtag::EXIT1_DR; } else { tx_len = xfer_len * 8; tx_end = Jtag::SHIFT_DR; } _jtag->shiftDR(data+i, NULL, tx_len, tx_end); progress.display(i); } progress.done(); /* reboot */ /* SIR 10 TDI (004); */ *reinterpret_cast(cmd) = 0x04; _jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); /* RUNTEST 60 TCK; */ _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(5000/_clk_period); /* * SDR 864 TDI * (000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) * TDO (00000000000000000000 * 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000) * MASK (00000000000000000000000000000000000000000000000000 * 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000); */ _jtag->shiftDR(tx, rx, 864, Jtag::RUN_TEST_IDLE); /* TBD -> something to check */ /* SIR 10 TDI (003); */ *reinterpret_cast(cmd) = 0x003; _jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); /* RUNTEST 49152 TCK; */ _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(4099645/_clk_period); /* RUNTEST 512 TCK; */ _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(512); /* SIR 10 TDI (3FF); */ *reinterpret_cast(cmd) = BYPASS; _jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); /* RUNTEST 12000 TCK; */ _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(1000000/_clk_period); /* -> idle */ _jtag->set_state(Jtag::RUN_TEST_IDLE); } bool Altera::post_flash_access() { if (_skip_reset) printInfo("Skip resetting device"); else reset(); return true; } bool Altera::prepare_flash_access() { if (_skip_load_bridge) { printInfo("Skip loading bridge for spiOverjtag"); return true; } return load_bridge(); } bool Altera::load_bridge() { std::string bitname; if (!_spiOverJtagPath.empty()) { bitname = _spiOverJtagPath; } else { if (_device_package.empty()) { printError("Can't program SPI flash: missing device-package information"); return false; } bitname = get_shell_env_var("OPENFPGALOADER_SOJ_DIR", DATA_DIR "/openFPGALoader"); #ifdef HAS_ZLIB bitname += "/spiOverJtag_" + _device_package + ".rbf.gz"; #else bitname += "/spiOverJtag_" + _device_package + ".rbf"; #endif } #if defined (_WIN64) || defined (_WIN32) /* Convert relative path embedded at compile time to an absolute path */ bitname = PathHelper::absolutePath(bitname); #endif std::cout << "use: " << bitname << std::endl; /* first: load spi over jtag */ try { RawParser bridge(bitname, false); bridge.parse(); programMem(bridge); } catch (std::exception &e) { printError(e.what()); throw std::runtime_error(e.what()); } return true; } void Altera::program(unsigned int offset, bool unprotect_flash) { if (_mode == Device::NONE_MODE) return; /* Access clk frequency to store clk_period required * for all operations. Can't be done at CTOR because * frequency be changed between these 2 methods. * */ _clk_period = 1e9/static_cast(_jtag->getClkFreq()); /* Specific case for MAX10 */ if (_fpga_family == MAX10_FAMILY) { max10_program(offset); return; } /* in all case we consider svf is mandatory * MEM_MODE : svf file provided for constructor * is the bitstream to use * SPI_MODE : svf file provided is bridge to have * access to the SPI flash */ /* mem mode -> svf */ if (_mode == Device::MEM_MODE) { RawParser _bit(_filename, false); _bit.parse(); programMem(_bit); } else if (_mode == Device::SPI_MODE) { // reverse only bitstream raw binaries data no bool reverseOrder = false; if (_file_extension == "rbf" || _file_extension == "rpd") reverseOrder = true; /* prepare data to write */ const uint8_t *data = NULL; int length = 0; RawParser bit(_filename, reverseOrder); try { bit.parse(); data = bit.getData(); length = bit.getLength() / 8; } catch (std::exception &e) { printError(e.what()); throw std::runtime_error(e.what()); } if (!FlashInterface::write(offset, data, length, unprotect_flash)) throw std::runtime_error("Fail to write data"); } } uint32_t Altera::idCode() { unsigned char tx_data[4] = {IDCODE}; unsigned char rx_data[4]; _jtag->go_test_logic_reset(); _jtag->shiftIR(tx_data, NULL, IRLENGTH); memset(tx_data, 0, 4); _jtag->shiftDR(tx_data, rx_data, 32); return ((rx_data[0] & 0x000000ff) | ((rx_data[1] << 8) & 0x0000ff00) | ((rx_data[2] << 16) & 0x00ff0000) | ((rx_data[3] << 24) & 0xff000000)); } /* MAX 10 specifics methods */ /* ------------------------ */ #define MAX10_ISC_ADDRESS_SHIFT {0x03, 0x02} #define MAX10_ISC_READ {0x05, 0x02} #define MAX10_ISC_ENABLE {0xcc, 0x02} #define MAX10_ISC_DISABLE {0x01, 0x02} #define MAX10_ISC_ADDRESS_SHIFT {0x03, 0x02} #define MAX10_ISC_ERASE {0xf2, 0x02} #define MAX10_ISC_PROGRAM {0xf4, 0x02} #define MAX10_DSM_ICB_PROGRAM {0xF4, 0x03} #define MAX10_DSM_VERIFY {0x07, 0x03} #define MAX10_DSM_CLEAR {0xf2, 0x03} #define MAX10_BYPASS {0xFF, 0x03} struct Altera::max10_mem_t { uint32_t check_addr0; // something to check before sequence uint32_t dsm_addr; uint32_t dsm_len; // 32bits uint32_t ufm_addr; // UFM1 addr uint32_t ufm_len[2]; // 32bits uint32_t cfm_addr; // CFM2 addr uint32_t cfm_len[3]; // 32bits uint32_t sectors_erase_addr[5]; // UFM1, UFM0, CFM2, CFM1, CFM0 uint32_t done_bit_addr; uint32_t pgm_success_addr; }; const std::map Altera::max10_memory_map = { {0x031820dd, { // 10M08SAU .check_addr0 = 0x80005, // check_addr0 .dsm_addr = 0x0000, .dsm_len = 512, // DSM .ufm_addr = 0x0200, .ufm_len = {4096, 4096}, // UFM .cfm_addr = 0x2200, .cfm_len = {35840, 14848, 20992}, // CFM .sectors_erase_addr = {0x17ffff, 0x27ffff, 0x37ffff, 0x47ffff, 0x57ffff}, // sectors erase address .done_bit_addr = 0x0009, // done bit .pgm_success_addr = 0x000b} // program success addr }, {0x031830dd, { // 10M16SA .check_addr0 = 0x80009, // check_addr0 .dsm_addr = 0x0000, .dsm_len = 1024, // DSM .ufm_addr = 0x0400, .ufm_len = {4096, 4096}, // UFM .cfm_addr = 0x2400, .cfm_len = {67584, 28672, 38912}, // CFM .sectors_erase_addr = {0x17ffff, 0x27ffff, 0x37ffff, 0x47ffff, 0x57ffff}, // sectors erase address .done_bit_addr = 0x0011, // done bit .pgm_success_addr = 0x0015} // program success addr }, {0x031840dd, { // 10M25SA .check_addr0 = 0x80009, // check_addr0 .dsm_addr = 0x0000, .dsm_len = 1024, // DSM .ufm_addr = 0x0400, .ufm_len = {4096, 4096}, // UFM .cfm_addr = 0x2400, .cfm_len = {94208, 40960, 53248}, // CFM .sectors_erase_addr = {0x17ffff, 0x27ffff, 0x37ffff, 0x47ffff, 0x57ffff}, // sectors erase address .done_bit_addr = 0x0015, // done bit // CHECKME: this line .pgm_success_addr = 0x0015} // program success addr }, {0x031030dd, { // 10M16D .check_addr0 = 0x80009, // check_addr0 .dsm_addr = 0x0000, .dsm_len = 1024, // DSM .ufm_addr = 0x0400, .ufm_len = {4096, 4096}, // UFM .cfm_addr = 0x2400, .cfm_len = {67584, 28672, 38912}, // CFM .sectors_erase_addr = {0x17ffff, 0x27ffff, 0x37ffff, 0x47ffff, 0x57ffff}, // sectors erase address .done_bit_addr = 0x0011, // done bit .pgm_success_addr = 0x0015} // program success addr }, }; /* Write an arbitrary file in UFM1, UFM0 by default and also CFM2 and CFM1 if * requested. */ bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, uint32_t offset, uint8_t update_sectors) { uint32_t start_addr = 0; // 32bit align uint32_t end_addr = 0; // 32bit align uint8_t erase_sectors_mask; /* check CFM0 is not mentionned */ if (update_sectors & (1 << 4)) std::runtime_error("Error: CFM0 cant't be used to store User Binary"); /* First task: search for the first and the last sector to use */ sectors_mask_start_end_addr(mem, update_sectors, &start_addr, &end_addr, &erase_sectors_mask); RawParser _bit(_filename, true); _bit.parse(); if (_verbose) _bit.displayHeader(); const uint8_t *data = _bit.getData(); const uint32_t length = _bit.getLength() / 8; const uint32_t base_addr = start_addr + offset / 4; // 32bit align const uint32_t flash_len = (end_addr - base_addr) * 4; // Byte align /* check */ if (base_addr > end_addr) { // wrong offset printError("Error: start offset is out of xFM region"); return false; } if (flash_len < length) { // too big file printError("Error: no enough space to write\n"); return false; } if (base_addr + (length / 4) > end_addr) { printError("Error: end address is out of xFM region"); return false; } uint8_t *buff = (uint8_t *)malloc(length); if (!buff) { printError("max10_program_ufm: Failed to allocate buffer"); return false; } /* data needs to be re-ordered */ for (uint32_t i = 0; i < length; i+=4) { for (int b = 0; b < 4; b++) { buff[i + b] = data[i + (3-b)]; } } printf("%x %x %x %x\n", update_sectors, erase_sectors_mask, base_addr, end_addr); // Start! max10_flow_enable(); /* Erase xFM sectors */ printInfo("Erase xFM ", false); max10_flow_erase(mem, erase_sectors_mask); printInfo("Done"); /* Program xFM */ // Simplify code: // UFM0 follows UFM1, // CFM2 follow UFM0, etc... // so we don't need to iterate printInfo("Write xFM"); writeXFM(buff, base_addr, 0, length / 4); /* Verify */ if (_verify) verifyxFM(buff, base_addr, 0, length / 4); max10_flow_disable(); free(buff); return true; } void Altera::max10_program(unsigned int offset) { uint32_t base_addr; uint8_t update_sectors; /* Needs to have some specifics informations about internal flash size/organisation * and some magics. */ auto mem_map = max10_memory_map.find(_idcode); if (mem_map == max10_memory_map.end()) { printError("Model not supported. Please update max10_memory_map."); throw std::runtime_error("Model not supported. Please update max10_memory_map."); } const Altera::max10_mem_t mem = mem_map->second; /* Check for a full update or only for a subset */ update_sectors = max10_flash_sectors_to_mask(_flash_sectors); if (_file_extension != "pof") { max10_program_ufm(&mem, offset, (_flash_sectors.size() == 0) ? 0 : update_sectors); return; } POFParser _bit(_filename, _verbose); _bit.parse(); _bit.displayHeader(); /* * MAX10 memory map differs according to internal configuration mode * - 1 dual compressed image: CFM0 is used for img0, CFM1 + CFM2 for img1 * - 2 single uncompressed image: CFM0 + CFM1 are used, CFM2 used to additional UFM * - 3/4 single (un)compressed image with mem init: CFM0 + CFM1 + CFM2 * - 5 single compressed image: CFM0 is used, CFM1&CFM2 used to additional UFM */ /* For Mode (POF content): * 1 : UFM: UFM1+UFM0 (in this order, this POF section size == memory section size), * CFM1: CFM2+CFM1 (in this order, this section == CFM2+CFM1 size), * CFM0: CFM0 (this section size == CFM0 size) * * 2 : UFM: UFM1+UFM0+CFM2 (in this order, this section size == full UFM section size + CFM2 size) * CFM0: CFM1+CFM0 (in this order, this section size == CFM1+CFM0) * * 3/4: UFM: UFM1+UFM0 (in this order, this section size == full UFM section size) * CFM0: CFM2+CFM1+CFM0 (in this order, this section size == full CFM section size) * * 5 : UFM: UFM1+UFM0+CFM2+CFM1 (in this order, this section size == full UFM section size + CFM2 size + CFM1 size) * CFM0: CFM0 (this section size == CFM0) */ /* OPTIONS: * ON_CHIP_BITSTREAM_DECOMPRESSION ON/OFF * Dual Compressed Images (256Kbits UFM): * set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "DUAL IMAGES" * Single Compressed Image (1376Kbits UFM): * set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "SINGLE COMP IMAGE" * Single Compressed Image with Memory Initialization (256Kbits UFM): * set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "SINGLE COMP IMAGE WITH ERAM" * Single Uncompressed Image (912Kbits UFM): * set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "SINGLE IMAGE" * Single Uncompressed Image with Memory Initialization (256Kbits UFM): * set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "SINGLE IMAGE WITH ERAM" */ /* * Memory organisation based on internal flash configuration mode is great but in fact * POF configuration data match MAX10 memory organisation: * its more easy to start with POF's CFM section and uses pointer based on prev ptr and section size */ uint8_t *ufm_data[2], *cfm_data[3]; // memory pointers (2 for UFM, 3 for CFM) // UFM Mapping ufm_data[1] = _bit.getData("UFM"); ufm_data[0] = &ufm_data[1][mem.ufm_len[1] * 4]; // Just after UFM1 (but size may differs // CFM Mapping cfm_data[2] = &ufm_data[0][mem.ufm_len[0] * 4]; // First CFM section in FPGA internal flash cfm_data[1] = &cfm_data[2][mem.cfm_len[2] * 4]; // Second CFM section but just after CFM2 cfm_data[0] = &cfm_data[1][mem.cfm_len[1] * 4]; // last CFM section but just after CFM1 // DSM Mapping const uint8_t *dsm_data = _bit.getData("ICB"); const int dsm_len = _bit.getLength("ICB") / 32; // getLength (bits) dsm_len in 32bits word // Start! max10_flow_enable(); max10_flow_erase(&mem, update_sectors); if (update_sectors == 0x1f) max10_dsm_verify(); /* Write */ // UFM 1 -> 0 base_addr = mem.ufm_addr; for (int i = 1; i >= 0; i--) { if (update_sectors & (1 << i)) { printInfo("Write UFM" + std::to_string(i)); writeXFM(ufm_data[i], base_addr, 0, mem.ufm_len[i]); } base_addr += mem.ufm_len[i]; } // CFM2 -> 0 base_addr = mem.cfm_addr; for (int i = 2; i >= 0; i--) { if (update_sectors & (1 << ((2 - i) + 2))) { printInfo("Write CFM" + std::to_string(i)); writeXFM(cfm_data[i], base_addr, 0, mem.cfm_len[i]); } base_addr += mem.cfm_len[i]; } /* Verify */ if (_verify) { // UFM 1 -> 0 base_addr = mem.ufm_addr; for (int i = 1; i >= 0; i--) { if (update_sectors & (1 << i)) { printInfo("Verify UFM" + std::to_string(i)); verifyxFM(ufm_data[i], base_addr, 0, mem.ufm_len[i]); } base_addr += mem.ufm_len[i]; } // CFM2->0 base_addr = mem.cfm_addr; for (int i = 2; i >= 0; i--) { if (update_sectors & (1 << ((2 - i) + 2))) { printInfo("Verify CFM" + std::to_string(i)); verifyxFM(cfm_data[i], base_addr, 0, mem.cfm_len[i]); } base_addr += mem.cfm_len[i]; } } // DSM if (update_sectors == 0x1F) { max10_dsm_program(dsm_data, dsm_len); max10_dsm_verify(); max10_flow_program_donebit(mem.done_bit_addr); max10_dsm_verify(); max10_dsm_program_success(mem.pgm_success_addr); max10_dsm_verify(); } /* disable ISC flow */ max10_flow_disable(); _jtag->set_state(Jtag::RUN_TEST_IDLE); } static void word_to_array(uint32_t in, uint8_t *out) { out[0] = (in >> 0) & 0xff; out[1] = (in >> 8) & 0xff; out[2] = (in >> 16) & 0xff; out[3] = (in >> 24) & 0xff; } /* Two possibilities: * - full internal flash must be erased (to write a POF file): DSM_CLEAR must be used to * perform a full erase * - only a sub-set of sectors must be erased (to update a CPU firmware): only specified * sectors have to be erased using MAX10_ISC_ERASE instruction after moving to a specific * erase address */ void Altera::max10_flow_erase(const Altera::max10_mem_t *mem, const uint8_t erase_sectors) { const uint32_t dsm_clear_delay = 350000120 / _clk_period; /* All sectors must be erased: DSM_CLEAR is better/faster */ if (erase_sectors == 0x1f) { const uint8_t dsm_clear[2] = MAX10_DSM_CLEAR; max10_addr_shift(0x000000); _jtag->shiftIR((unsigned char *)dsm_clear, NULL, IRLENGTH); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(dsm_clear_delay); } else { //const uint32_t isc_clear_delay = 5120 / _clk_period; const uint8_t isc_erase[2] = MAX10_ISC_ERASE; /* each bit is a sector to erase */ for (int sect = 0; sect < 5; sect++) { if ((erase_sectors >> sect) & 0x01) { max10_addr_shift(mem->sectors_erase_addr[sect]); _jtag->shiftIR((unsigned char *)isc_erase, NULL, IRLENGTH); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(dsm_clear_delay); } } } } void Altera::writeXFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset, uint32_t len) { uint8_t *ptr = (uint8_t *)cfg_data + offset; // FIXME: maybe adding offset here ? const uint8_t isc_program[2] = MAX10_ISC_PROGRAM; /* precompute some delays required during loop */ const uint32_t isc_program2_delay = 320000 / _clk_period; // ns must be 350us ProgressBar progress("Write Flash", len, 50, _quiet); for (uint32_t i = 0; i < len; i+=512) { bool must_send_sir = true; uint32_t max = (i + 512 <= len)? 512 : len - i; for (uint32_t ii = 0; ii < max; ii++) { const uint32_t data = ARRAY2INT32(ptr); progress.display(i); /* flash was erased before: to save time skip write when cfg_data * contains only bit set to high */ if (data == 0xffffffff) { must_send_sir = true; ptr += 4; continue; } /* TODO: match more or less svf but not bsdl */ if (must_send_sir) { /* Set base addr */ max10_addr_shift(base_addr + i + ii); /* set ISC_PROGRAM/DSM_PROGRAM */ _jtag->shiftIR((unsigned char *)isc_program, NULL, IRLENGTH, Jtag::PAUSE_IR); must_send_sir = false; } _jtag->shiftDR(ptr, NULL, 32, Jtag::RUN_TEST_IDLE); _jtag->toggleClk(isc_program2_delay); ptr += 4; } } progress.done(); } uint32_t Altera::verifyxFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset, uint32_t len) { uint8_t *ptr = (uint8_t *)cfg_data + offset; // avoid passing offset ? const uint8_t read_cmd[2] = MAX10_ISC_READ; uint32_t errors = 0; ProgressBar progress("Verify", len, 50, _quiet); for (uint32_t i = 0; i < len; i+=512) { const uint32_t max = (i + 512 <= len)? 512 : len - i; progress.display(i); /* send address */ max10_addr_shift(base_addr + i); /* send read command */ _jtag->shiftIR((unsigned char *)read_cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); for (uint32_t ii = 0; ii < max; ii++) { uint8_t data[4]; _jtag->shiftDR(NULL, data, 32, Jtag::RUN_TEST_IDLE); /* TODO: compare */ for (uint8_t pos = 0; pos < 4; pos++) { if (ptr[pos] != data[pos]) { printf("Error@%d: %02x %02x %02x %02x ", pos, data[0], data[1], data[2], data[3]); printf("%02x %02x %02x %02x\n", ptr[0], ptr[1], ptr[2], ptr[3]); errors++; break; } } ptr += 4; } } if (errors == 0) progress.done(); else progress.fail(); return errors; } void Altera::max10_flow_enable() { const int enable_delay = 350000120 / _clk_period; // must be 1 tck const uint8_t cmd[2] = MAX10_ISC_ENABLE; _jtag->shiftIR((unsigned char *)cmd, NULL, IRLENGTH); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(enable_delay); } void Altera::max10_flow_disable() { // ISC_DISABLE WAIT 100.0e-3) // BYPASS WAIT 305.0e-6 const int disable_len = (1e9 * 350e-3) / _clk_period; const int bypass_len = (3 + (1e9 * 1e-3) / _clk_period); const uint8_t cmd0[2] = MAX10_ISC_DISABLE; const uint8_t cmd1[2] = MAX10_BYPASS; _jtag->shiftIR((unsigned char *)cmd0, NULL, IRLENGTH); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(disable_len); _jtag->shiftIR((unsigned char *)cmd1, NULL, IRLENGTH); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(bypass_len); } void Altera::max10_dsm_program(const uint8_t *dsm_data, const uint32_t dsm_len) { const int program_del = 5120 / _clk_period; const int write_del = 320000 / _clk_period; uint32_t *icb_dat = (uint32_t *)dsm_data; const int len = dsm_len / 32; const uint8_t cmd[2] = MAX10_DSM_ICB_PROGRAM; uint8_t dat[4]; /* Instead of writing the full section * only write word with a value != 0xffffffff */ for (int i = 0; i < len; i++) { if (icb_dat[i] != 0xffffffff) { /* send addr */ max10_addr_shift(i); word_to_array(icb_dat[i], dat); _jtag->shiftIR((unsigned char *)cmd, NULL, IRLENGTH); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(program_del); _jtag->shiftDR(dat, NULL, 32, Jtag::RUN_TEST_IDLE); _jtag->toggleClk(write_del); // 305.0e-6 } } } bool Altera::max10_dsm_verify() { const uint32_t dsm_delay = 5120 / _clk_period; const uint8_t cmd[2] = MAX10_DSM_VERIFY; const uint8_t tx = 0x00; // 1 in bsdl, 0 in svf uint8_t rx = 0; _jtag->shiftIR((unsigned char *)cmd, NULL, IRLENGTH); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(dsm_delay); _jtag->shiftDR(&tx, &rx, 1, Jtag::RUN_TEST_IDLE); printf("BSM: %02x\n", rx); if ((rx & 0x01) == 0x01) { printInfo("DSM Verify: OK"); return true; } printError("DSM Verify: KO"); return false; } void Altera::max10_addr_shift(uint32_t addr) { const uint8_t cmd[2] = MAX10_ISC_ADDRESS_SHIFT; const uint32_t base_addr = POFParser::reverse_32(addr) >> 9; uint8_t addr_arr[4]; word_to_array(base_addr, addr_arr); /* FIXME/TODO: * 1. in bsdl file no delay between IR and DR * but 1TCK after DR * 2. PAUSE IR is a state where loop is possible -> required to move * to RUN_TEST_IDLE ? */ _jtag->shiftIR((unsigned char *)cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(5120 / _clk_period); // fine delay ? _jtag->shiftDR(addr_arr, NULL, 23, Jtag::RUN_TEST_IDLE); } void Altera::max10_dsm_program_success(const uint32_t pgm_success_addr) { const uint32_t prog_len = 5120 / _clk_period; // ?? const uint32_t prog2_len = 320000 / _clk_period; // ?? const uint8_t cmd[2] = MAX10_DSM_ICB_PROGRAM; uint8_t magic[4]; word_to_array(0x6C48A50F, magic); // FIXME: uses define instead max10_addr_shift(pgm_success_addr); /* Send 'Magic' code */ _jtag->shiftIR((unsigned char *)cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(prog_len); // fine delay ? _jtag->shiftDR(magic, NULL, 32, Jtag::RUN_TEST_IDLE); _jtag->toggleClk(prog2_len); // must wait 305.0e-6 } void Altera::max10_flow_program_donebit(const uint32_t done_bit_addr) { const uint32_t addr_shift_delay = 5120 / _clk_period; // ?? const uint32_t icb_program_delay = 320000 / _clk_period; // ?? uint8_t cmd[2] = MAX10_DSM_ICB_PROGRAM; uint8_t magic[4]; word_to_array(0x6C48A50F, magic); // FIXME: uses define instead /* Send target address */ max10_addr_shift(done_bit_addr); /* Send 'Magic' code */ _jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(addr_shift_delay); // fine delay ? _jtag->shiftDR(magic, NULL, 32, Jtag::RUN_TEST_IDLE); _jtag->toggleClk(icb_program_delay); // must wait 305.0e-6 } bool Altera::max10_read_section(FILE *fd, const uint32_t base_addr, const uint32_t len) { const uint8_t read_cmd[2] = MAX10_ISC_READ; ProgressBar progress("Dump", len, 50, _quiet); for (uint32_t i = 0; i < len; i += 512) { const uint32_t max = (i + 512 <= len)? 512 : len - i; progress.display(i); /* send address */ max10_addr_shift(base_addr + i); /* send read command */ _jtag->shiftIR((unsigned char *)read_cmd, NULL, IRLENGTH, Jtag::PAUSE_IR); for (uint32_t ii = 0; ii < max; ii++) { uint8_t data[4]; _jtag->shiftDR(NULL, data, 32, Jtag::RUN_TEST_IDLE); fwrite(data, sizeof(uint8_t), 4, fd); } } progress.done(); return true; } bool Altera::max10_dump() { uint32_t base_addr; auto mem_map = max10_memory_map.find(_idcode); if (mem_map == max10_memory_map.end()) { printError("Model not supported. Please update max10_memory_map."); return false; } const max10_mem_t mem = mem_map->second; /* Access clk frequency to store clk_period required * for all operations. Can't be done at CTOR because * frequency be changed between these 2 methods. */ _clk_period = 1e9/static_cast(_jtag->getClkFreq()); FILE *fd = fopen(_filename.c_str(), "w"); if (!fd) { printError("Failed to open dump file."); return false; } max10_flow_enable(); /* UFM 1 -> 0 */ base_addr = mem.ufm_addr; for (int i = 1; i >= 0; i--) { printInfo("Dump UFM" + std::to_string(i)); max10_read_section(fd, base_addr, mem.ufm_len[i]); base_addr += mem.ufm_len[i]; } /* CFM 2 -> 0 */ base_addr = mem.cfm_addr; for (int i = 2; i >= 0; i--) { printInfo("Dump CFM" + std::to_string(i)); max10_read_section(fd, base_addr, mem.cfm_len[i]); base_addr += mem.cfm_len[i]; } max10_flow_disable(); fclose(fd); return true; } uint8_t Altera::max10_flash_sectors_to_mask(std::string flash_sectors) { uint8_t mask = 0; if (flash_sectors.size() > 0) { const std::vector sectors = splitString(flash_sectors, ','); for (const auto §or: sectors) { if (sector == "UFM1") mask |= (1 << 0); else if (sector == "UFM0") mask |= (1 << 1); else if (sector == "CFM2") mask |= (1 << 2); else if (sector == "CFM1") mask |= (1 << 3); else if (sector == "CFM0") mask |= (1 << 4); else throw std::runtime_error("Unknown sector " + sector); } } else { // full update mask = 0x1F; } return mask; } bool Altera::sectors_mask_start_end_addr(const Altera::max10_mem_t *mem, const uint8_t update_sectors, uint32_t *start, uint32_t *end, uint8_t *sectors_mask) { uint32_t saddr = mem->ufm_addr; uint32_t eaddr = saddr; uint8_t start_bit = 0, end_bit = 0; /* For sake of simplicity: create an array with all length aligned * as it in MAX10 devices */ const uint32_t mem_map_length[] = { mem->ufm_len[1], mem->ufm_len[0], mem->cfm_len[2], mem->cfm_len[1] }; if (update_sectors == 0) { eaddr = mem->ufm_addr + (mem->ufm_len[0] + mem->ufm_len[1]); *sectors_mask = 0x3; } else { /* eaddr start with full memory size */ for (uint8_t i = 0; i < 4; i++) eaddr += mem_map_length[i]; /* search first bit == 1 and increment start address */ for (uint8_t i = 0; i < 4; i++) { if (update_sectors & (1 << i)) { start_bit = i; break; } saddr += mem_map_length[i]; } /* decrement eaddr until last bit == 1 found */ for (int8_t i = 3; i >= 0; i--) { if (update_sectors & (1 << i)) { end_bit = i + 1; break; } eaddr -= mem_map_length[i]; } *sectors_mask = ((1 << end_bit) - 1) - ((1 << start_bit) - 1); } *start = saddr; *end = eaddr; return true; } /* SPI interface */ int Altera::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len) { /* +1 because send first cmd + len byte + 1 for rx due to a delay of * one bit */ int xfer_len = len + 1 + ((rx == NULL) ? 0 : 1); uint8_t jtx[xfer_len]; uint8_t jrx[xfer_len]; if (tx != NULL) { for (uint32_t i = 0; i < len; i++) jtx[i] = RawParser::reverseByte(tx[i]); } shiftVIR(RawParser::reverseByte(cmd)); shiftVDR(jtx, (rx) ? jrx : NULL, 8 * xfer_len); if (rx) { for (uint32_t i = 0; i < len; i++) { rx[i] = RawParser::reverseByte(jrx[i+1] >> 1) | (jrx[i+2] & 0x01); } } return 0; } int Altera::spi_put(const uint8_t *tx, uint8_t *rx, uint32_t len) { return spi_put(tx[0], &tx[1], rx, len-1); } int Altera::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose) { uint8_t rx[3]; uint8_t tmp; uint32_t count = 0; bool first = true; shiftVIR(RawParser::reverseByte(cmd)); do { if (first) { first = false; shiftVDR(NULL, rx, 24, Jtag::SHIFT_DR); tmp = RawParser::reverseByte(rx[1] >> 1) | (rx[2] & 0x01); } else { _jtag->shiftDR(NULL, rx, 16, Jtag::SHIFT_DR); tmp = RawParser::reverseByte(rx[0] >> 1) | (rx[1] & 0x01); } count++; if (count == timeout){ printf("timeout: %x %x %x\n", tmp, rx[0], rx[1]); break; } if (verbose) { printf("%x %x %x %u\n", tmp, mask, cond, count); } } while ((tmp & mask) != cond); _jtag->set_state(Jtag::UPDATE_DR); if (count == timeout) { printf("%x\n", tmp); std::cout << "wait: Error" << std::endl; return -1; } return 0; } /* VIrtual Jtag Access */ void Altera::shiftVIR(uint32_t reg) { uint32_t len = _vir_length; uint32_t mask = (1 << len) - 1; uint32_t tmp = (reg & mask) | _vir_addr; uint8_t *tx = (uint8_t *) & tmp; uint8_t tx_ir[2] = {USER1, 0}; _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->shiftIR(tx_ir, NULL, IRLENGTH, Jtag::UPDATE_IR); /* len + 1 + 1 => IRLENGTH + Slave ID + 1 (ASMI/SFL) */ _jtag->shiftDR(tx, NULL, len/* + 2*/, Jtag::UPDATE_DR); } void Altera::shiftVDR(uint8_t * tx, uint8_t * rx, uint32_t len, Jtag::tapState_t end_state, bool debug) { (void) debug; uint8_t tx_ir[2] = {USER0, 0}; _jtag->shiftIR(tx_ir, NULL, IRLENGTH, Jtag::UPDATE_IR); _jtag->shiftDR(tx, rx, len, end_state); }