2021-06-26 15:24:07 +02:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
|
|
|
|
*/
|
|
|
|
|
|
2019-09-26 18:29:20 +02:00
|
|
|
#include "altera.hpp"
|
2020-10-17 18:40:16 +02:00
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
2025-02-01 08:28:17 +01:00
|
|
|
#include <map>
|
2020-10-17 18:40:16 +02:00
|
|
|
#include <string>
|
|
|
|
|
|
2023-04-17 20:55:10 +02:00
|
|
|
#include "common.hpp"
|
2019-09-26 18:29:20 +02:00
|
|
|
#include "device.hpp"
|
2019-09-28 15:31:43 +02:00
|
|
|
#include "epcq.hpp"
|
2025-02-01 08:28:17 +01:00
|
|
|
#include "jtag.hpp"
|
2020-10-17 18:40:16 +02:00
|
|
|
#include "progressBar.hpp"
|
|
|
|
|
#include "rawParser.hpp"
|
2022-10-23 10:22:47 +02:00
|
|
|
#if defined (_WIN64) || defined (_WIN32)
|
|
|
|
|
#include "pathHelper.hpp"
|
|
|
|
|
#endif
|
2025-02-01 08:28:17 +01:00
|
|
|
#include "pofParser.hpp"
|
2019-09-26 18:29:20 +02:00
|
|
|
|
2019-09-28 15:31:43 +02:00
|
|
|
#define IDCODE 6
|
2021-07-08 20:52:46 +02:00
|
|
|
#define USER0 0x0C
|
|
|
|
|
#define USER1 0x0E
|
2020-10-17 18:40:16 +02:00
|
|
|
#define BYPASS 0x3FF
|
2019-09-28 15:31:43 +02:00
|
|
|
#define IRLENGTH 10
|
|
|
|
|
|
2021-02-21 18:30:13 +01:00
|
|
|
Altera::Altera(Jtag *jtag, const std::string &filename,
|
2021-07-08 20:52:46 +02:00
|
|
|
const std::string &file_type, Device::prog_type_t prg_type,
|
2022-12-10 22:05:37 +01:00
|
|
|
const std::string &device_package,
|
|
|
|
|
const std::string &spiOverJtagPath, bool verify, int8_t verbose,
|
2022-05-24 07:29:35 +02:00
|
|
|
bool skip_load_bridge, bool skip_reset):
|
2021-12-22 19:11:35 +01:00
|
|
|
Device(jtag, filename, file_type, verify, verbose),
|
2022-05-24 07:29:35 +02:00
|
|
|
SPIInterface(filename, verbose, 256, verify, skip_load_bridge,
|
|
|
|
|
skip_reset),
|
2022-12-10 22:05:37 +01:00
|
|
|
_device_package(device_package), _spiOverJtagPath(spiOverJtagPath),
|
2024-12-31 18:42:52 +01:00
|
|
|
_vir_addr(0x1000), _vir_length(14), _clk_period(1)
|
2019-09-28 15:31:43 +02:00
|
|
|
{
|
2025-03-15 07:41:57 +01:00
|
|
|
/* check device family */
|
|
|
|
|
_idcode = _jtag->get_target_device_id();
|
|
|
|
|
string family = fpga_list[_idcode].family;
|
|
|
|
|
if (family == "MAX 10") {
|
|
|
|
|
_fpga_family = MAX10_FAMILY;
|
|
|
|
|
} else {
|
|
|
|
|
_fpga_family = CYCLONE_MISC; // FIXME
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 20:52:46 +02:00
|
|
|
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;
|
2025-02-01 08:28:17 +01:00
|
|
|
} else if (_file_extension == "pof") { // MAX10
|
2024-12-31 18:42:52 +01:00
|
|
|
_mode = Device::FLASH_MODE;
|
2025-03-15 07:41:57 +01:00
|
|
|
} else if (_fpga_family == MAX10_FAMILY) {
|
|
|
|
|
_mode = Device::FLASH_MODE;
|
2025-02-01 08:28:17 +01:00
|
|
|
} else { // unknown type -> sanity check
|
2021-11-06 08:37:14 +01:00
|
|
|
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");
|
2023-09-01 15:35:44 +02:00
|
|
|
throw std::runtime_error("Error: wrong file");
|
2021-11-06 08:37:14 +01:00
|
|
|
} else {
|
|
|
|
|
_mode = Device::SPI_MODE;
|
|
|
|
|
}
|
2021-07-08 20:52:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-09-28 15:31:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
2021-07-08 20:52:46 +02:00
|
|
|
|
2019-09-26 18:29:20 +02:00
|
|
|
Altera::~Altera()
|
|
|
|
|
{}
|
2019-09-28 15:31:43 +02:00
|
|
|
void Altera::reset()
|
|
|
|
|
{
|
|
|
|
|
/* PULSE_NCONFIG */
|
|
|
|
|
unsigned char tx_buff[2] = {0x01, 0x00};
|
2020-03-06 09:05:57 +01:00
|
|
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET);
|
2019-09-28 15:31:43 +02:00
|
|
|
_jtag->shiftIR(tx_buff, NULL, IRLENGTH);
|
|
|
|
|
_jtag->toggleClk(1);
|
2020-03-06 09:05:57 +01:00
|
|
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET);
|
2019-09-28 15:31:43 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-08 20:52:46 +02:00
|
|
|
void Altera::programMem(RawParser &_bit)
|
2020-10-17 18:40:16 +02:00
|
|
|
{
|
|
|
|
|
int byte_length = _bit.getLength()/8;
|
2023-08-08 15:54:27 +02:00
|
|
|
const uint8_t *data = _bit.getData();
|
2020-10-17 18:40:16 +02:00
|
|
|
|
|
|
|
|
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<uint16_t *>(cmd) = 0x02;
|
|
|
|
|
_jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR);
|
|
|
|
|
/* RUNTEST IDLE 12000 TCK ENDSTATE IDLE; */
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
2024-12-31 18:42:52 +01:00
|
|
|
_jtag->toggleClk(1000000/_clk_period);
|
2020-10-17 18:40:16 +02:00
|
|
|
/* write */
|
2023-10-09 14:53:57 +02:00
|
|
|
ProgressBar progress("Load SRAM", byte_length, 50, _quiet);
|
2020-10-17 18:40:16 +02:00
|
|
|
|
|
|
|
|
int xfer_len = 512;
|
|
|
|
|
int tx_len;
|
2023-08-10 16:36:18 +02:00
|
|
|
Jtag::tapState_t tx_end;
|
2020-10-17 18:40:16 +02:00
|
|
|
|
|
|
|
|
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;
|
2021-05-15 09:05:48 +02:00
|
|
|
tx_end = Jtag::EXIT1_DR;
|
2020-10-17 18:40:16 +02:00
|
|
|
} else {
|
|
|
|
|
tx_len = xfer_len * 8;
|
2021-05-15 09:05:48 +02:00
|
|
|
tx_end = Jtag::SHIFT_DR;
|
2020-10-17 18:40:16 +02:00
|
|
|
}
|
2021-05-15 09:05:48 +02:00
|
|
|
_jtag->shiftDR(data+i, NULL, tx_len, tx_end);
|
2020-10-17 18:40:16 +02:00
|
|
|
progress.display(i);
|
|
|
|
|
}
|
|
|
|
|
progress.done();
|
|
|
|
|
|
|
|
|
|
/* reboot */
|
|
|
|
|
/* SIR 10 TDI (004); */
|
|
|
|
|
*reinterpret_cast<uint16_t *>(cmd) = 0x04;
|
|
|
|
|
_jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR);
|
|
|
|
|
/* RUNTEST 60 TCK; */
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
2024-12-31 18:42:52 +01:00
|
|
|
_jtag->toggleClk(5000/_clk_period);
|
2020-10-17 18:40:16 +02:00
|
|
|
/*
|
|
|
|
|
* 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<uint16_t *>(cmd) = 0x003;
|
|
|
|
|
_jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR);
|
|
|
|
|
/* RUNTEST 49152 TCK; */
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
2024-12-31 18:42:52 +01:00
|
|
|
_jtag->toggleClk(4099645/_clk_period);
|
2020-10-17 18:40:16 +02:00
|
|
|
/* RUNTEST 512 TCK; */
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
_jtag->toggleClk(512);
|
|
|
|
|
/* SIR 10 TDI (3FF); */
|
|
|
|
|
*reinterpret_cast<uint16_t *>(cmd) = BYPASS;
|
|
|
|
|
_jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR);
|
|
|
|
|
|
|
|
|
|
/* RUNTEST 12000 TCK; */
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
2024-12-31 18:42:52 +01:00
|
|
|
_jtag->toggleClk(1000000/_clk_period);
|
2020-10-17 18:40:16 +02:00
|
|
|
/* -> idle */
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-24 07:29:35 +02:00
|
|
|
bool Altera::post_flash_access()
|
|
|
|
|
{
|
|
|
|
|
if (_skip_reset)
|
|
|
|
|
printInfo("Skip resetting device");
|
|
|
|
|
else
|
|
|
|
|
reset();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-05-11 14:29:08 +02:00
|
|
|
|
|
|
|
|
bool Altera::prepare_flash_access()
|
|
|
|
|
{
|
|
|
|
|
if (_skip_load_bridge) {
|
|
|
|
|
printInfo("Skip loading bridge for spiOverjtag");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return load_bridge();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 20:52:46 +02:00
|
|
|
bool Altera::load_bridge()
|
|
|
|
|
{
|
2022-12-10 22:05:37 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2021-07-08 20:52:46 +02:00
|
|
|
|
2023-04-17 20:55:10 +02:00
|
|
|
bitname = get_shell_env_var("OPENFPGALOADER_SOJ_DIR", DATA_DIR "/openFPGALoader");
|
2021-11-25 08:11:50 +01:00
|
|
|
#ifdef HAS_ZLIB
|
2023-04-17 20:55:10 +02:00
|
|
|
bitname += "/spiOverJtag_" + _device_package + ".rbf.gz";
|
2021-11-25 08:11:50 +01:00
|
|
|
#else
|
2023-04-17 20:55:10 +02:00
|
|
|
bitname += "/spiOverJtag_" + _device_package + ".rbf";
|
2021-11-25 08:11:50 +01:00
|
|
|
#endif
|
2022-12-10 22:05:37 +01:00
|
|
|
}
|
2022-10-22 10:53:23 +02:00
|
|
|
|
|
|
|
|
#if defined (_WIN64) || defined (_WIN32)
|
|
|
|
|
/* Convert relative path embedded at compile time to an absolute path */
|
|
|
|
|
bitname = PathHelper::absolutePath(bitname);
|
|
|
|
|
#endif
|
2021-07-08 20:52:46 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 19:11:35 +01:00
|
|
|
void Altera::program(unsigned int offset, bool unprotect_flash)
|
2019-09-28 15:31:43 +02:00
|
|
|
{
|
|
|
|
|
if (_mode == Device::NONE_MODE)
|
|
|
|
|
return;
|
2024-12-31 18:42:52 +01:00
|
|
|
|
|
|
|
|
/* 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<float>(_jtag->getClkFreq());
|
|
|
|
|
|
|
|
|
|
/* Specific case for MAX10 */
|
|
|
|
|
if (_fpga_family == MAX10_FAMILY) {
|
2025-03-15 07:41:57 +01:00
|
|
|
max10_program(offset);
|
2024-12-31 18:42:52 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-28 15:31:43 +02:00
|
|
|
/* 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) {
|
2022-11-11 00:01:21 +01:00
|
|
|
RawParser _bit(_filename, false);
|
|
|
|
|
_bit.parse();
|
|
|
|
|
programMem(_bit);
|
2019-09-28 15:31:43 +02:00
|
|
|
} else if (_mode == Device::SPI_MODE) {
|
2021-07-08 20:52:46 +02:00
|
|
|
// reverse only bitstream raw binaries data no
|
|
|
|
|
bool reverseOrder = false;
|
|
|
|
|
if (_file_extension == "rbf" || _file_extension == "rpd")
|
|
|
|
|
reverseOrder = true;
|
|
|
|
|
|
|
|
|
|
/* prepare data to write */
|
2023-08-08 15:54:27 +02:00
|
|
|
const uint8_t *data = NULL;
|
2021-07-08 20:52:46 +02:00
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 19:11:35 +01:00
|
|
|
if (!SPIInterface::write(offset, data, length, unprotect_flash))
|
|
|
|
|
throw std::runtime_error("Fail to write data");
|
2021-07-11 11:32:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-10 12:50:26 +02:00
|
|
|
uint32_t Altera::idCode()
|
2019-09-28 15:31:43 +02:00
|
|
|
{
|
2020-03-06 09:05:57 +01:00
|
|
|
unsigned char tx_data[4] = {IDCODE};
|
2019-09-28 15:31:43 +02:00
|
|
|
unsigned char rx_data[4];
|
|
|
|
|
_jtag->go_test_logic_reset();
|
2020-03-06 09:05:57 +01:00
|
|
|
_jtag->shiftIR(tx_data, NULL, IRLENGTH);
|
2020-07-25 04:55:41 +02:00
|
|
|
memset(tx_data, 0, 4);
|
2020-03-06 09:05:57 +01:00
|
|
|
_jtag->shiftDR(tx_data, rx_data, 32);
|
2019-09-28 15:31:43 +02:00
|
|
|
return ((rx_data[0] & 0x000000ff) |
|
|
|
|
|
((rx_data[1] << 8) & 0x0000ff00) |
|
|
|
|
|
((rx_data[2] << 16) & 0x00ff0000) |
|
|
|
|
|
((rx_data[3] << 24) & 0xff000000));
|
|
|
|
|
}
|
2021-07-08 20:52:46 +02:00
|
|
|
|
2024-12-31 18:42:52 +01:00
|
|
|
/* 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}
|
2025-03-15 07:41:57 +01:00
|
|
|
#define MAX10_ISC_ERASE {0xf2, 0x02}
|
2024-12-31 18:42:52 +01:00
|
|
|
#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}
|
|
|
|
|
|
2025-03-15 07:41:57 +01:00
|
|
|
struct Altera::max10_mem_t {
|
2025-02-01 08:28:17 +01:00
|
|
|
uint32_t check_addr0; // something to check before sequence
|
2024-12-31 18:42:52 +01:00
|
|
|
uint32_t dsm_addr;
|
|
|
|
|
uint32_t dsm_len; // 32bits
|
2025-02-01 08:28:17 +01:00
|
|
|
uint32_t ufm_addr; // UFM1 addr
|
2025-03-15 07:41:57 +01:00
|
|
|
uint32_t ufm_len[2]; // 32bits
|
2025-02-01 08:28:17 +01:00
|
|
|
uint32_t cfm_addr; // CFM2 addr
|
2025-03-15 07:41:57 +01:00
|
|
|
uint32_t cfm_len[3]; // 32bits
|
|
|
|
|
uint32_t sectors_erase_addr[5]; // UFM1, UFM0, CFM2, CFM1, CFM0
|
2025-02-01 08:12:04 +01:00
|
|
|
uint32_t done_bit_addr;
|
|
|
|
|
uint32_t pgm_success_addr;
|
2025-03-15 07:41:57 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const std::map<uint32_t, Altera::max10_mem_t> Altera::max10_memory_map = {
|
|
|
|
|
{0x031820dd, { // 10M08SAU
|
|
|
|
|
.check_addr0 = 0x80005, // check_addr0
|
|
|
|
|
.dsm_addr = 0x0000, 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
|
2024-12-31 18:42:52 +01:00
|
|
|
},
|
2025-03-16 08:50:13 +01:00
|
|
|
{0x031830dd, { // 10M16SA
|
|
|
|
|
.check_addr0 = 0x80009, // check_addr0
|
|
|
|
|
.dsm_addr = 0x0000, 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
|
|
|
|
|
},
|
2024-12-31 18:42:52 +01:00
|
|
|
};
|
|
|
|
|
|
2025-03-15 07:41:57 +01:00
|
|
|
/* Write an arbitrary file in UFM1 and UFM0
|
|
|
|
|
* FIXME: in some mode its also possible to uses CFM2 & CFM1
|
|
|
|
|
*/
|
|
|
|
|
bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, unsigned int offset)
|
2024-12-31 18:42:52 +01:00
|
|
|
{
|
2025-03-15 07:41:57 +01:00
|
|
|
RawParser _bit(_filename, true);
|
2024-12-31 18:42:52 +01:00
|
|
|
_bit.parse();
|
|
|
|
|
_bit.displayHeader();
|
2025-03-15 07:41:57 +01:00
|
|
|
const uint8_t *data = _bit.getData();
|
|
|
|
|
const uint32_t length = _bit.getLength() / 8;
|
|
|
|
|
const uint32_t base_addr = mem->ufm_addr + offset;
|
|
|
|
|
|
|
|
|
|
uint8_t *buff = (uint8_t *)malloc(length);
|
|
|
|
|
if (!buff) {
|
|
|
|
|
printError("max10_program_ufm: Failed to allocate buffer");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-03-15 07:41:57 +01:00
|
|
|
/* check */
|
|
|
|
|
const uint32_t ufmx_len = 4 * (mem->ufm_len[0] + mem->ufm_len[1]);
|
|
|
|
|
if (base_addr > length) {
|
|
|
|
|
printError("Error: start offset is out of UFM region");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (base_addr + length > ufmx_len) {
|
|
|
|
|
printError("Error: end address is out of UFM region");
|
|
|
|
|
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)];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start!
|
|
|
|
|
max10_flow_enable();
|
|
|
|
|
|
|
|
|
|
/* Erase UFM1 & UFM0 */
|
|
|
|
|
printInfo("Erase UFM ", false);
|
|
|
|
|
max10_flow_erase(mem, 0x3);
|
|
|
|
|
printInfo("Done");
|
|
|
|
|
|
|
|
|
|
/* Program UFM1 & UFM0 */
|
|
|
|
|
// Simplify code:
|
|
|
|
|
// UFM0 follows UFM1, so we don't need to iterate
|
|
|
|
|
printInfo("Write UFM");
|
|
|
|
|
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)
|
|
|
|
|
{
|
2025-02-01 08:14:07 +01:00
|
|
|
uint32_t base_addr;
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-02-01 08:14:07 +01:00
|
|
|
/* Needs to have some specifics informations about internal flash size/organisation
|
|
|
|
|
* and some magics.
|
|
|
|
|
*/
|
2024-12-31 18:42:52 +01:00
|
|
|
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.");
|
|
|
|
|
}
|
2025-03-15 07:41:57 +01:00
|
|
|
const Altera::max10_mem_t mem = mem_map->second;
|
|
|
|
|
|
|
|
|
|
if (_file_extension != "pof") {
|
|
|
|
|
max10_program_ufm(&mem, offset);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
POFParser _bit(_filename, _verbose);
|
|
|
|
|
_bit.parse();
|
|
|
|
|
_bit.displayHeader();
|
2025-02-01 08:14:07 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
|
2025-02-01 08:28:17 +01:00
|
|
|
uint8_t *ufm_data[2], *cfm_data[3]; // memory pointers (2 for UFM, 3 for CFM)
|
2025-02-01 08:14:07 +01:00
|
|
|
|
|
|
|
|
// UFM Mapping
|
2025-02-16 15:30:30 +01:00
|
|
|
ufm_data[1] = _bit.getData("UFM");
|
|
|
|
|
ufm_data[0] = &ufm_data[1][mem.ufm_len[0] * 4]; // Just after UFM1 (but size may differs
|
2025-02-01 08:14:07 +01:00
|
|
|
|
|
|
|
|
// CFM Mapping
|
2025-02-16 15:30:30 +01:00
|
|
|
cfm_data[2] = &ufm_data[0][mem.ufm_len[1] * 4]; // First CFM section in FPGA internal flash
|
2025-02-01 08:28:17 +01:00
|
|
|
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
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-02-01 08:14:07 +01:00
|
|
|
// DSM Mapping
|
2024-12-31 18:42:52 +01:00
|
|
|
const uint8_t *dsm_data = _bit.getData("ICB");
|
2025-02-01 08:28:17 +01:00
|
|
|
const int dsm_len = _bit.getLength("ICB") / 32; // getLength (bits) dsm_len in 32bits word
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-02-01 08:14:07 +01:00
|
|
|
// Start!
|
2025-02-02 09:11:14 +01:00
|
|
|
max10_flow_enable();
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-03-15 07:41:57 +01:00
|
|
|
max10_flow_erase(&mem, 0x1F);
|
2024-12-31 18:42:52 +01:00
|
|
|
max10_dsm_verify();
|
|
|
|
|
|
|
|
|
|
/* Write */
|
2025-02-01 08:14:07 +01:00
|
|
|
|
|
|
|
|
// UFM 1 -> 0
|
2024-12-31 18:42:52 +01:00
|
|
|
base_addr = mem.ufm_addr;
|
|
|
|
|
for (int i = 1; i >= 0; i--) {
|
|
|
|
|
printInfo("Write UFM" + std::to_string(i));
|
2025-02-01 08:14:07 +01:00
|
|
|
writeXFM(ufm_data[i], base_addr, 0, mem.ufm_len[i]);
|
2024-12-31 18:42:52 +01:00
|
|
|
base_addr += mem.ufm_len[i];
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-01 08:14:07 +01:00
|
|
|
// CFM2 -> 0
|
|
|
|
|
base_addr = mem.cfm_addr;
|
|
|
|
|
for (int i = 2; i >= 0; i--) {
|
|
|
|
|
printInfo("Write CFM" + std::to_string(i));
|
|
|
|
|
writeXFM(cfm_data[i], base_addr, 0, mem.cfm_len[i]);
|
|
|
|
|
base_addr += mem.cfm_len[i];
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-31 18:42:52 +01:00
|
|
|
/* Verify */
|
|
|
|
|
if (_verify) {
|
2025-02-01 08:14:07 +01:00
|
|
|
// UFM 1 -> 0
|
|
|
|
|
base_addr = mem.ufm_addr;
|
|
|
|
|
for (int i = 1; i >= 0; 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];
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-31 18:42:52 +01:00
|
|
|
// CFM2->0
|
|
|
|
|
base_addr = mem.cfm_addr;
|
|
|
|
|
for (int i = 2; i >= 0; i--) {
|
|
|
|
|
printInfo("Verify CFM" + std::to_string(i));
|
2025-02-01 08:14:07 +01:00
|
|
|
verifyxFM(cfm_data[i], base_addr, 0, mem.cfm_len[i]);
|
2024-12-31 18:42:52 +01:00
|
|
|
base_addr += mem.cfm_len[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DSM
|
|
|
|
|
|
|
|
|
|
max10_dsm_program(dsm_data, dsm_len);
|
|
|
|
|
max10_dsm_verify();
|
|
|
|
|
|
2025-02-01 08:12:04 +01:00
|
|
|
max10_flow_program_donebit(mem.done_bit_addr);
|
2024-12-31 18:42:52 +01:00
|
|
|
max10_dsm_verify();
|
2025-02-01 08:12:04 +01:00
|
|
|
max10_dsm_program_success(mem.pgm_success_addr);
|
2024-12-31 18:42:52 +01:00
|
|
|
max10_dsm_verify();
|
|
|
|
|
|
|
|
|
|
/* disable ISC flow */
|
2025-02-02 09:11:14 +01:00
|
|
|
max10_flow_disable();
|
2024-12-31 18:42:52 +01:00
|
|
|
_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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-15 07:41:57 +01:00
|
|
|
/* 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)
|
2024-12-31 18:42:52 +01:00
|
|
|
{
|
|
|
|
|
const uint32_t dsm_clear_delay = 350000120 / _clk_period;
|
2025-03-15 07:41:57 +01:00
|
|
|
/* All sectors must be erased: DSM_CLEAR is better/faster */
|
|
|
|
|
if (erase_sectors == 0x1f) {
|
|
|
|
|
const uint8_t dsm_clear[2] = MAX10_DSM_CLEAR;
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-03-15 07:41:57 +01:00
|
|
|
max10_addr_shift(0x000000);
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-03-15 07:41:57 +01:00
|
|
|
_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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-31 18:42:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Altera::writeXFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset, uint32_t len)
|
|
|
|
|
{
|
2025-02-01 08:28:17 +01:00
|
|
|
uint8_t *ptr = (uint8_t *)cfg_data + offset; // FIXME: maybe adding offset here ?
|
2024-12-31 18:42:52 +01:00
|
|
|
const uint8_t isc_program[2] = MAX10_ISC_PROGRAM;
|
|
|
|
|
|
|
|
|
|
/* precompute some delays required during loop */
|
2025-02-01 08:28:17 +01:00
|
|
|
const uint32_t isc_program2_delay = 320000 / _clk_period; // ns must be 350us
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-02-01 08:14:07 +01:00
|
|
|
ProgressBar progress("Write Flash", len, 50, _quiet);
|
2024-12-31 18:42:52 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2025-02-01 08:28:17 +01:00
|
|
|
uint8_t *ptr = (uint8_t *)cfg_data + offset; // avoid passing offset ?
|
2024-12-31 18:42:52 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-02 09:11:14 +01:00
|
|
|
void Altera::max10_flow_enable()
|
2024-12-31 18:42:52 +01:00
|
|
|
{
|
2025-02-01 08:28:17 +01:00
|
|
|
const int enable_delay = 350000120 / _clk_period; // must be 1 tck
|
2024-12-31 18:42:52 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-02 09:11:14 +01:00
|
|
|
void Altera::max10_flow_disable()
|
2024-12-31 18:42:52 +01:00
|
|
|
{
|
2025-02-01 08:28:17 +01:00
|
|
|
// ISC_DISABLE WAIT 100.0e-3)
|
|
|
|
|
// BYPASS WAIT 305.0e-6
|
2024-12-31 18:42:52 +01:00
|
|
|
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);
|
2025-02-01 08:28:17 +01:00
|
|
|
_jtag->toggleClk(write_del); // 305.0e-6
|
2024-12-31 18:42:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Altera::max10_dsm_verify()
|
|
|
|
|
{
|
|
|
|
|
const uint32_t dsm_delay = 5120 / _clk_period;
|
|
|
|
|
const uint8_t cmd[2] = MAX10_DSM_VERIFY;
|
|
|
|
|
|
2025-02-01 08:28:17 +01:00
|
|
|
const uint8_t tx = 0x00; // 1 in bsdl, 0 in svf
|
|
|
|
|
uint8_t rx = 0;
|
2024-12-31 18:42:52 +01:00
|
|
|
|
|
|
|
|
_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);
|
2025-02-01 08:28:17 +01:00
|
|
|
_jtag->toggleClk(5120 / _clk_period); // fine delay ?
|
2024-12-31 18:42:52 +01:00
|
|
|
_jtag->shiftDR(addr_arr, NULL, 23, Jtag::RUN_TEST_IDLE);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-01 08:12:04 +01:00
|
|
|
void Altera::max10_dsm_program_success(const uint32_t pgm_success_addr)
|
2024-12-31 18:42:52 +01:00
|
|
|
{
|
2025-02-01 08:28:17 +01:00
|
|
|
const uint32_t prog_len = 5120 / _clk_period; // ??
|
|
|
|
|
const uint32_t prog2_len = 320000 / _clk_period; // ??
|
|
|
|
|
|
2024-12-31 18:42:52 +01:00
|
|
|
const uint8_t cmd[2] = MAX10_DSM_ICB_PROGRAM;
|
|
|
|
|
|
|
|
|
|
uint8_t magic[4];
|
2025-02-01 08:28:17 +01:00
|
|
|
word_to_array(0x6C48A50F, magic); // FIXME: uses define instead
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2025-02-01 08:12:04 +01:00
|
|
|
max10_addr_shift(pgm_success_addr);
|
2024-12-31 18:42:52 +01:00
|
|
|
|
|
|
|
|
/* Send 'Magic' code */
|
|
|
|
|
_jtag->shiftIR((unsigned char *)cmd, NULL, IRLENGTH, Jtag::PAUSE_IR);
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
2025-02-01 08:28:17 +01:00
|
|
|
_jtag->toggleClk(prog_len); // fine delay ?
|
2024-12-31 18:42:52 +01:00
|
|
|
_jtag->shiftDR(magic, NULL, 32, Jtag::RUN_TEST_IDLE);
|
2025-02-01 08:28:17 +01:00
|
|
|
_jtag->toggleClk(prog2_len); // must wait 305.0e-6
|
2024-12-31 18:42:52 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-01 08:12:04 +01:00
|
|
|
void Altera::max10_flow_program_donebit(const uint32_t done_bit_addr)
|
2024-12-31 18:42:52 +01:00
|
|
|
{
|
2025-02-01 08:28:17 +01:00
|
|
|
const uint32_t addr_shift_delay = 5120 / _clk_period; // ??
|
|
|
|
|
const uint32_t icb_program_delay = 320000 / _clk_period; // ??
|
2024-12-31 18:42:52 +01:00
|
|
|
|
|
|
|
|
uint8_t cmd[2] = MAX10_DSM_ICB_PROGRAM;
|
|
|
|
|
|
|
|
|
|
uint8_t magic[4];
|
2025-02-01 08:28:17 +01:00
|
|
|
word_to_array(0x6C48A50F, magic); // FIXME: uses define instead
|
2024-12-31 18:42:52 +01:00
|
|
|
|
|
|
|
|
/* Send target address */
|
2025-02-01 08:12:04 +01:00
|
|
|
max10_addr_shift(done_bit_addr);
|
2024-12-31 18:42:52 +01:00
|
|
|
|
|
|
|
|
/* Send 'Magic' code */
|
|
|
|
|
_jtag->shiftIR(cmd, NULL, IRLENGTH, Jtag::PAUSE_IR);
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
2025-02-01 08:28:17 +01:00
|
|
|
_jtag->toggleClk(addr_shift_delay); // fine delay ?
|
2024-12-31 18:42:52 +01:00
|
|
|
_jtag->shiftDR(magic, NULL, 32, Jtag::RUN_TEST_IDLE);
|
2025-02-01 08:28:17 +01:00
|
|
|
_jtag->toggleClk(icb_program_delay); // must wait 305.0e-6
|
2024-12-31 18:42:52 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-02 20:50:06 +01:00
|
|
|
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<float>(_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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 20:52:46 +02:00
|
|
|
/* SPI interface */
|
|
|
|
|
|
2023-08-08 15:54:27 +02:00
|
|
|
int Altera::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len)
|
2021-07-08 20:52:46 +02:00
|
|
|
{
|
|
|
|
|
/* +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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 19:11:35 +01:00
|
|
|
return 0;
|
2021-07-08 20:52:46 +02:00
|
|
|
}
|
2024-12-31 18:42:52 +01:00
|
|
|
|
2023-08-08 15:54:27 +02:00
|
|
|
int Altera::spi_put(const uint8_t *tx, uint8_t *rx, uint32_t len)
|
2021-07-08 20:52:46 +02:00
|
|
|
{
|
|
|
|
|
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,
|
2023-08-10 16:36:18 +02:00
|
|
|
Jtag::tapState_t end_state, bool debug)
|
2021-07-08 20:52:46 +02:00
|
|
|
{
|
|
|
|
|
(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);
|
|
|
|
|
}
|