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>
|
|
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
2020-03-06 09:05:57 +01:00
|
|
|
#include "jtag.hpp"
|
2019-09-26 18:29:20 +02:00
|
|
|
#include "device.hpp"
|
2019-09-28 15:31:43 +02:00
|
|
|
#include "epcq.hpp"
|
2020-10-17 18:40:16 +02:00
|
|
|
#include "progressBar.hpp"
|
|
|
|
|
#include "rawParser.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
|
2020-02-16 13:33:38 +01:00
|
|
|
// DATA_DIR is defined at compile time.
|
|
|
|
|
#define BIT_FOR_FLASH (DATA_DIR "/openFPGALoader/test_sfl.svf")
|
2019-09-28 15:31:43 +02:00
|
|
|
|
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,
|
|
|
|
|
const std::string &device_package, bool verify, int8_t verbose):
|
|
|
|
|
Device(jtag, filename, file_type, verify, verbose), _svf(_jtag, _verbose),
|
|
|
|
|
_device_package(device_package),
|
|
|
|
|
_vir_addr(0x1000), _vir_length(14)
|
2019-09-28 15:31:43 +02:00
|
|
|
{
|
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;
|
2021-11-06 08:37:14 +01:00
|
|
|
} 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");
|
|
|
|
|
std::runtime_error("Error: wrong file");
|
|
|
|
|
} 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;
|
|
|
|
|
uint8_t *data = _bit.getData();
|
|
|
|
|
|
2021-07-08 20:52:46 +02:00
|
|
|
uint32_t clk_period = 1e9/static_cast<float>(_jtag->getClkFreq());
|
2021-05-15 08:35:16 +02:00
|
|
|
|
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);
|
2021-05-15 08:35:16 +02:00
|
|
|
_jtag->toggleClk(1000000/clk_period);
|
2020-10-17 18:40:16 +02:00
|
|
|
/* write */
|
2021-01-30 07:57:49 +01:00
|
|
|
ProgressBar progress("Flash SRAM", byte_length, 50, _quiet);
|
2020-10-17 18:40:16 +02:00
|
|
|
|
|
|
|
|
int xfer_len = 512;
|
|
|
|
|
int tx_len;
|
|
|
|
|
int 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;
|
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);
|
2021-05-15 08:35:16 +02: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);
|
2021-05-15 08:35:16 +02: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);
|
2021-05-15 08:35:16 +02:00
|
|
|
_jtag->toggleClk(1000000/clk_period);
|
2020-10-17 18:40:16 +02:00
|
|
|
/* -> idle */
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 20:52:46 +02:00
|
|
|
bool Altera::load_bridge()
|
|
|
|
|
{
|
|
|
|
|
if (_device_package.empty()) {
|
|
|
|
|
printError("Can't program SPI flash: missing device-package information");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DATA_DIR is defined at compile time.
|
|
|
|
|
std::string bitname = DATA_DIR "/openFPGALoader/spiOverJtag_";
|
|
|
|
|
bitname += _device_package + ".rbf";
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-28 15:31:43 +02:00
|
|
|
void Altera::program(unsigned int offset)
|
|
|
|
|
{
|
|
|
|
|
if (_mode == Device::NONE_MODE)
|
|
|
|
|
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) {
|
2021-07-08 20:52:46 +02:00
|
|
|
if (_file_extension == "svf") {
|
2020-10-17 18:40:16 +02:00
|
|
|
_svf.parse(_filename);
|
2021-07-08 20:52:46 +02:00
|
|
|
} else {
|
|
|
|
|
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
|
|
|
/* try to load spiOverJtag bridge
|
|
|
|
|
* to have an access to SPI flash
|
|
|
|
|
*/
|
|
|
|
|
if (!load_bridge()) {
|
|
|
|
|
printError("Fail to load bridge");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reverse only bitstream raw binaries data no
|
|
|
|
|
bool reverseOrder = false;
|
|
|
|
|
if (_file_extension == "rbf" || _file_extension == "rpd")
|
|
|
|
|
reverseOrder = true;
|
|
|
|
|
|
|
|
|
|
/* prepare data to write */
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-28 15:31:43 +02:00
|
|
|
/* GGM: TODO: fix this issue */
|
2021-07-08 20:52:46 +02:00
|
|
|
EPCQ epcq(this, 0);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
epcq.reset();
|
|
|
|
|
epcq.read_id();
|
2021-10-19 07:06:46 +02:00
|
|
|
epcq.display_status_reg(epcq.read_status_reg());
|
2021-07-08 20:52:46 +02:00
|
|
|
epcq.erase_and_prog(offset, data, length);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError(e.what());
|
|
|
|
|
throw std::runtime_error(e.what());
|
|
|
|
|
}
|
2021-06-25 08:58:45 +02:00
|
|
|
|
|
|
|
|
if (_verify)
|
2021-07-11 11:32:35 +02:00
|
|
|
epcq.verify(offset, data, length, 256);
|
2019-09-28 15:31:43 +02:00
|
|
|
reset();
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-11 11:32:35 +02:00
|
|
|
|
|
|
|
|
bool Altera::dumpFlash(const std::string filename, uint32_t base_addr,
|
|
|
|
|
uint32_t len)
|
|
|
|
|
{
|
|
|
|
|
int ret = true;
|
|
|
|
|
/* try to load spiOverJtag bridge
|
|
|
|
|
* to have an access to SPI flash
|
|
|
|
|
*/
|
|
|
|
|
if (!load_bridge()) {
|
|
|
|
|
printError("Fail to load bridge");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EPCQ epcq(this, 0);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
epcq.reset();
|
|
|
|
|
ret = epcq.dump(filename, base_addr, len, 256);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError(e.what());
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reset();
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-26 18:29:20 +02:00
|
|
|
int 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
|
|
|
|
|
|
|
|
/* SPI interface */
|
|
|
|
|
|
|
|
|
|
int Altera::spi_put(uint8_t cmd, 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 len;
|
|
|
|
|
}
|
|
|
|
|
int Altera::spi_put(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,
|
|
|
|
|
int 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);
|
|
|
|
|
}
|