2021-06-26 15:24:07 +02:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
2019-12-06 07:27:08 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
|
|
|
|
*/
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
#include <stdint.h>
|
2019-12-06 07:27:08 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2023-09-01 19:24:33 +02:00
|
|
|
#include <strings.h>
|
2019-12-06 07:27:08 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include <iostream>
|
2020-08-24 08:16:48 +02:00
|
|
|
#include <stdexcept>
|
2019-12-06 07:27:08 +01:00
|
|
|
|
|
|
|
|
#include "display.hpp"
|
|
|
|
|
#include "fsparser.hpp"
|
2023-09-01 19:24:33 +02:00
|
|
|
#include "gowin.hpp"
|
|
|
|
|
#include "jtag.hpp"
|
|
|
|
|
#include "progressBar.hpp"
|
2021-09-15 20:18:49 +02:00
|
|
|
#include "rawParser.hpp"
|
|
|
|
|
#include "spiFlash.hpp"
|
2019-12-06 07:27:08 +01:00
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
2020-07-25 08:21:27 +02:00
|
|
|
#ifdef STATUS_TIMEOUT
|
|
|
|
|
// defined in the Windows headers included by libftdi.h
|
|
|
|
|
#undef STATUS_TIMEOUT
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-12-06 07:27:08 +01:00
|
|
|
#define NOOP 0x02
|
|
|
|
|
#define ERASE_SRAM 0x05
|
|
|
|
|
#define READ_SRAM 0x03
|
|
|
|
|
#define XFER_DONE 0x09
|
|
|
|
|
#define READ_IDCODE 0x11
|
|
|
|
|
#define INIT_ADDR 0x12
|
|
|
|
|
#define READ_USERCODE 0x13
|
|
|
|
|
#define CONFIG_ENABLE 0x15
|
|
|
|
|
#define XFER_WRITE 0x17
|
|
|
|
|
#define CONFIG_DISABLE 0x3A
|
|
|
|
|
#define RELOAD 0x3C
|
|
|
|
|
#define STATUS_REGISTER 0x41
|
|
|
|
|
# define STATUS_CRC_ERROR (1 << 0)
|
|
|
|
|
# define STATUS_BAD_COMMAND (1 << 1)
|
|
|
|
|
# define STATUS_ID_VERIFY_FAILED (1 << 2)
|
|
|
|
|
# define STATUS_TIMEOUT (1 << 3)
|
|
|
|
|
# define STATUS_MEMORY_ERASE (1 << 5)
|
|
|
|
|
# define STATUS_PREAMBLE (1 << 6)
|
|
|
|
|
# define STATUS_SYSTEM_EDIT_MODE (1 << 7)
|
|
|
|
|
# define STATUS_PRG_SPIFLASH_DIRECT (1 << 8)
|
|
|
|
|
# define STATUS_NON_JTAG_CNF_ACTIVE (1 << 10)
|
|
|
|
|
# define STATUS_BYPASS (1 << 11)
|
|
|
|
|
# define STATUS_GOWIN_VLD (1 << 12)
|
|
|
|
|
# define STATUS_DONE_FINAL (1 << 13)
|
|
|
|
|
# define STATUS_SECURITY_FINAL (1 << 14)
|
|
|
|
|
# define STATUS_READY (1 << 15)
|
|
|
|
|
# define STATUS_POR (1 << 16)
|
|
|
|
|
# define STATUS_FLASH_LOCK (1 << 17)
|
2023-09-01 19:24:33 +02:00
|
|
|
|
2020-01-04 17:37:16 +01:00
|
|
|
#define EF_PROGRAM 0x71
|
2019-12-06 07:27:08 +01:00
|
|
|
#define EFLASH_ERASE 0x75
|
2022-07-20 23:27:32 +02:00
|
|
|
#define SWITCH_TO_MCU_JTAG 0x7a
|
2019-12-06 07:27:08 +01:00
|
|
|
|
2021-09-15 20:18:49 +02:00
|
|
|
/* BSCAN spi (external flash) (see below for details) */
|
|
|
|
|
/* most common pins def */
|
|
|
|
|
#define BSCAN_SPI_SCK (1 << 1)
|
|
|
|
|
#define BSCAN_SPI_CS (1 << 3)
|
|
|
|
|
#define BSCAN_SPI_DI (1 << 5)
|
|
|
|
|
#define BSCAN_SPI_DO (1 << 7)
|
2023-09-01 19:24:33 +02:00
|
|
|
#define BSCAN_SPI_MSK (1 << 6)
|
2021-09-15 20:18:49 +02:00
|
|
|
/* GW1NSR-4C pins def */
|
|
|
|
|
#define BSCAN_GW1NSR_4C_SPI_SCK (1 << 7)
|
|
|
|
|
#define BSCAN_GW1NSR_4C_SPI_CS (1 << 5)
|
|
|
|
|
#define BSCAN_GW1NSR_4C_SPI_DI (1 << 3)
|
|
|
|
|
#define BSCAN_GW1NSR_4C_SPI_DO (1 << 1)
|
2023-09-01 19:24:33 +02:00
|
|
|
#define BSCAN_GW1NSR_4C_SPI_MSK (1 << 0)
|
2021-09-15 20:18:49 +02:00
|
|
|
|
2022-07-20 23:25:53 +02:00
|
|
|
Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::string mcufw,
|
2021-09-15 20:18:49 +02:00
|
|
|
Device::prog_type_t prg_type, bool external_flash,
|
2021-06-25 08:58:45 +02:00
|
|
|
bool verify, int8_t verbose): Device(jtag, filename, file_type,
|
2023-11-19 10:18:45 +01:00
|
|
|
verify, verbose),
|
2023-11-19 13:29:15 +01:00
|
|
|
SPIInterface(filename, verbose, 0, verify, false, false),
|
2023-11-19 10:18:45 +01:00
|
|
|
_fs(NULL), _idcode(0), is_gw1n1(false), is_gw2a(false),
|
|
|
|
|
is_gw1n4(false), is_gw5a(false), _external_flash(external_flash),
|
2021-09-15 20:18:49 +02:00
|
|
|
_spi_sck(BSCAN_SPI_SCK), _spi_cs(BSCAN_SPI_CS),
|
|
|
|
|
_spi_di(BSCAN_SPI_DI), _spi_do(BSCAN_SPI_DO),
|
2023-11-19 10:18:45 +01:00
|
|
|
_spi_msk(BSCAN_SPI_MSK),
|
|
|
|
|
_mcufw(NULL)
|
2019-12-06 07:27:08 +01:00
|
|
|
{
|
2023-11-19 10:18:45 +01:00
|
|
|
detectFamily();
|
2021-06-24 08:57:18 +02:00
|
|
|
|
2023-11-19 13:29:15 +01:00
|
|
|
_prev_wr_edge = _jtag->getWriteEdge();
|
|
|
|
|
_prev_rd_edge = _jtag->getReadEdge();
|
|
|
|
|
|
2021-09-15 20:18:49 +02:00
|
|
|
if (prg_type == Device::WR_FLASH)
|
|
|
|
|
_mode = Device::FLASH_MODE;
|
|
|
|
|
else
|
|
|
|
|
_mode = Device::MEM_MODE;
|
|
|
|
|
|
2023-11-19 10:18:45 +01:00
|
|
|
if (!_file_extension.empty() && prg_type != Device::RD_FLASH) {
|
2019-12-06 07:27:08 +01:00
|
|
|
if (_file_extension == "fs") {
|
2021-02-24 06:36:48 +01:00
|
|
|
try {
|
|
|
|
|
_fs = new FsParser(_filename, _mode == Device::MEM_MODE, _verbose);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
throw std::runtime_error(e.what());
|
|
|
|
|
}
|
2021-09-15 20:18:49 +02:00
|
|
|
} else {
|
|
|
|
|
/* non fs file is only allowed with external flash */
|
2023-11-19 10:18:45 +01:00
|
|
|
if (!_external_flash)
|
2021-09-15 20:18:49 +02:00
|
|
|
throw std::runtime_error("incompatible file format");
|
|
|
|
|
try {
|
|
|
|
|
_fs = new RawParser(_filename, false);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
throw std::runtime_error(e.what());
|
2021-02-24 06:36:48 +01:00
|
|
|
}
|
2021-09-15 20:18:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printInfo("Parse file ", false);
|
|
|
|
|
if (_fs->parse() == EXIT_FAILURE) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
delete _fs;
|
|
|
|
|
throw std::runtime_error("can't parse file");
|
|
|
|
|
} else {
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_verbose)
|
|
|
|
|
_fs->displayHeader();
|
2021-02-24 06:36:48 +01:00
|
|
|
|
2021-09-15 20:18:49 +02:00
|
|
|
/* for fs file check match with targeted device */
|
|
|
|
|
if (_file_extension == "fs") {
|
2021-06-24 08:57:18 +02:00
|
|
|
string idcode_str = _fs->getHeaderVal("idcode");
|
|
|
|
|
uint32_t fs_idcode = std::stoul(idcode_str.c_str(), NULL, 16);
|
2023-11-19 10:18:45 +01:00
|
|
|
if ((fs_idcode & 0x0fffffff) != _idcode) {
|
2022-09-04 14:16:02 +02:00
|
|
|
char mess[256];
|
2023-09-01 19:24:33 +02:00
|
|
|
snprintf(mess, 256, "mismatch between target's idcode and bitstream idcode\n"
|
2023-11-19 10:18:45 +01:00
|
|
|
"\tbitstream has 0x%08X hardware requires 0x%08x", fs_idcode, _idcode);
|
2022-09-04 14:16:02 +02:00
|
|
|
throw std::runtime_error(mess);
|
2021-06-24 08:57:18 +02:00
|
|
|
}
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 14:51:48 +02:00
|
|
|
|
2023-11-19 10:18:45 +01:00
|
|
|
if (mcufw.size() > 0) {
|
|
|
|
|
if (_idcode != 0x0100981b)
|
|
|
|
|
throw std::runtime_error("Microcontroller firmware flashing only supported on GW1NSR-4C");
|
|
|
|
|
|
|
|
|
|
_mcufw = new RawParser(mcufw, false);
|
|
|
|
|
|
|
|
|
|
if (_mcufw->parse() == EXIT_FAILURE) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
delete _mcufw;
|
|
|
|
|
throw std::runtime_error("can't parse file");
|
|
|
|
|
} else {
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-19 13:29:15 +01:00
|
|
|
|
|
|
|
|
if (is_gw5a && _mode == Device::FLASH_MODE) {
|
|
|
|
|
_jtag->setClkFreq(2500000);
|
|
|
|
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET);
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("Before disable SPI mode", readStatusReg());
|
|
|
|
|
disableCfg();
|
|
|
|
|
send_command(0); // BYPASS ?
|
|
|
|
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET);
|
|
|
|
|
gw5a_disable_spi();
|
|
|
|
|
}
|
2023-11-19 10:18:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Gowin::~Gowin()
|
|
|
|
|
{
|
|
|
|
|
if (_fs)
|
|
|
|
|
delete _fs;
|
|
|
|
|
if (_mcufw)
|
|
|
|
|
delete _mcufw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Gowin::detectFamily()
|
|
|
|
|
{
|
|
|
|
|
_idcode = _jtag->get_target_device_id();
|
|
|
|
|
|
2020-05-23 14:51:48 +02:00
|
|
|
/* erase and program flash differ for GW1N1 */
|
2023-11-19 10:18:45 +01:00
|
|
|
if (_idcode == 0x0900281B)
|
2020-05-23 14:51:48 +02:00
|
|
|
is_gw1n1 = true;
|
2023-09-01 19:24:33 +02:00
|
|
|
/* erase and program flash differ for GW1N4, GW1N1Z-1 */
|
2023-11-19 10:18:45 +01:00
|
|
|
if (_idcode == 0x0100381B || _idcode == 0x100681b)
|
2023-09-01 19:24:33 +02:00
|
|
|
is_gw1n4 = true;
|
|
|
|
|
|
2021-09-15 20:18:49 +02:00
|
|
|
/* bscan spi external flash differ for GW1NSR-4C */
|
2023-11-19 10:18:45 +01:00
|
|
|
if (_idcode == 0x0100981b) {
|
2021-09-15 20:18:49 +02:00
|
|
|
_spi_sck = BSCAN_GW1NSR_4C_SPI_SCK;
|
|
|
|
|
_spi_cs = BSCAN_GW1NSR_4C_SPI_CS;
|
|
|
|
|
_spi_di = BSCAN_GW1NSR_4C_SPI_DI;
|
|
|
|
|
_spi_do = BSCAN_GW1NSR_4C_SPI_DO;
|
|
|
|
|
_spi_msk = BSCAN_GW1NSR_4C_SPI_MSK;
|
|
|
|
|
}
|
2022-06-16 03:52:44 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* GW2 series has no internal flash and uses new bitstream checksum
|
|
|
|
|
* algorithm that is not yet supported.
|
|
|
|
|
*/
|
2023-11-19 10:18:45 +01:00
|
|
|
switch (_idcode) {
|
2022-06-16 03:52:44 +02:00
|
|
|
case 0x0000081b: /* GW2A(R)-18(C) */
|
|
|
|
|
case 0x0000281b: /* GW2A(R)-55(C) */
|
|
|
|
|
_external_flash = true;
|
|
|
|
|
/* FIXME: implement GW2 checksum calculation */
|
|
|
|
|
skip_checksum = true;
|
2022-09-06 21:24:40 +02:00
|
|
|
is_gw2a = true;
|
2023-08-09 16:12:46 +02:00
|
|
|
break;
|
|
|
|
|
case 0x0001081b: /* GW5AST-138 */
|
|
|
|
|
case 0x0001181b: /* GW5AT-138 */
|
|
|
|
|
case 0x0001281b: /* GW5A-25 */
|
|
|
|
|
_external_flash = true;
|
|
|
|
|
/* FIXME: implement GW5 checksum calculation */
|
|
|
|
|
skip_checksum = true;
|
|
|
|
|
is_gw5a = true;
|
|
|
|
|
break;
|
2023-09-01 19:24:33 +02:00
|
|
|
}
|
2022-07-20 23:25:53 +02:00
|
|
|
|
2023-11-19 10:18:45 +01:00
|
|
|
return true;
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
bool Gowin::send_command(uint8_t cmd)
|
|
|
|
|
{
|
|
|
|
|
_jtag->shiftIR(&cmd, nullptr, 8);
|
|
|
|
|
_jtag->toggleClk(5);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
|
#include <libkern/OSByteOrder.h>
|
|
|
|
|
#define le32toh(x) OSSwapLittleToHostInt32(x)
|
|
|
|
|
#define htole32(x) OSSwapHostToLittleInt32(x)
|
|
|
|
|
#elif (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) || defined(__WINDOWS__)
|
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#define htole32(x) (x)
|
|
|
|
|
#define le32toh(x) (x)
|
|
|
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
|
|
|
#define htole32(x) (x)
|
|
|
|
|
#define le32toh(x) (x)
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
uint32_t Gowin::readReg32(uint8_t cmd)
|
|
|
|
|
{
|
|
|
|
|
uint32_t reg = 0, tmp = 0xffffffffU;
|
|
|
|
|
send_command(cmd);
|
|
|
|
|
_jtag->shiftDR((uint8_t *)&tmp, (uint8_t *)®, 32);
|
|
|
|
|
return le32toh(reg);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-15 09:49:09 +01:00
|
|
|
void Gowin::reset()
|
|
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(RELOAD);
|
|
|
|
|
send_command(NOOP);
|
2020-01-15 09:49:09 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-04 17:37:16 +01:00
|
|
|
void Gowin::programFlash()
|
|
|
|
|
{
|
2023-08-08 15:54:27 +02:00
|
|
|
const uint8_t *data = _fs->getData();
|
2022-07-24 17:31:36 +02:00
|
|
|
int length = _fs->getLength();
|
2023-08-08 15:54:27 +02:00
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
_jtag->setClkFreq(2500000); // default for GOWIN, should use LoadingRate from file header
|
2020-01-04 17:37:16 +01:00
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(CONFIG_DISABLE);
|
|
|
|
|
send_command(0);
|
|
|
|
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET);
|
|
|
|
|
uint32_t state = readStatusReg();
|
|
|
|
|
if ((state & (STATUS_GOWIN_VLD | STATUS_POR)) == 0) {
|
|
|
|
|
displayReadReg("Either GOWIN_VLD or POR should be set, aborting", state);
|
2020-01-04 17:37:16 +01:00
|
|
|
return;
|
2023-09-01 19:24:33 +02:00
|
|
|
}
|
2020-01-04 17:37:16 +01:00
|
|
|
|
|
|
|
|
if (!eraseFLASH())
|
|
|
|
|
return;
|
|
|
|
|
/* test status a faire */
|
2023-09-01 19:24:33 +02:00
|
|
|
if (!writeFLASH(0, data, length))
|
2020-01-04 17:37:16 +01:00
|
|
|
return;
|
2023-09-01 19:24:33 +02:00
|
|
|
if (_mcufw) {
|
|
|
|
|
const uint8_t *mcu_data = _mcufw->getData();
|
|
|
|
|
int mcu_length = _mcufw->getLength();
|
|
|
|
|
if (!writeFLASH(0x380, mcu_data, mcu_length))
|
2022-07-20 23:25:53 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2021-06-25 08:58:45 +02:00
|
|
|
if (_verify)
|
|
|
|
|
printWarn("writing verification not supported");
|
2020-05-23 14:51:48 +02:00
|
|
|
|
|
|
|
|
/* check if file checksum == checksum in FPGA */
|
2023-09-01 19:24:33 +02:00
|
|
|
if (!skip_checksum)
|
|
|
|
|
checkCRC();
|
2020-05-23 14:51:48 +02:00
|
|
|
|
2020-01-18 17:07:31 +01:00
|
|
|
if (_verbose)
|
2023-09-01 19:24:33 +02:00
|
|
|
displayReadReg("after program flash", readStatusReg());
|
2020-01-04 17:37:16 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
void Gowin::programExtFlash(unsigned int offset, bool unprotect_flash)
|
2019-12-06 07:27:08 +01:00
|
|
|
{
|
2023-11-19 13:29:15 +01:00
|
|
|
displayReadReg("after program flash", readStatusReg());
|
2021-09-15 20:18:49 +02:00
|
|
|
|
2023-11-19 13:29:15 +01:00
|
|
|
if (!prepare_flash_access()) {
|
|
|
|
|
throw std::runtime_error("Error: fail to prepare flash access");
|
2023-09-01 19:24:33 +02:00
|
|
|
}
|
2021-09-15 20:18:49 +02:00
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
SPIFlash spiFlash(this, unprotect_flash,
|
|
|
|
|
(_verbose ? 1 : (_quiet ? -1 : 0)));
|
|
|
|
|
spiFlash.reset();
|
|
|
|
|
spiFlash.read_id();
|
|
|
|
|
spiFlash.display_status_reg(spiFlash.read_status_reg());
|
|
|
|
|
const uint8_t *data = _fs->getData();
|
|
|
|
|
int length = _fs->getLength();
|
2021-09-15 20:18:49 +02:00
|
|
|
|
2023-11-19 13:29:15 +01:00
|
|
|
char mess[256];
|
|
|
|
|
bool ret = true;
|
|
|
|
|
|
|
|
|
|
if (spiFlash.erase_and_prog(offset, data, length / 8) != 0) {
|
|
|
|
|
snprintf(mess, 256, "Error: write to flash failed");
|
|
|
|
|
printError(mess);
|
|
|
|
|
ret = false;
|
2020-01-04 17:37:16 +01:00
|
|
|
}
|
2023-11-19 13:29:15 +01:00
|
|
|
if (ret && _verify)
|
|
|
|
|
if (!spiFlash.verify(offset, data, length / 8, 256)) {
|
|
|
|
|
snprintf(mess, 256, "Error: flash vefication failed");
|
|
|
|
|
printError(mess);
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
2020-01-04 17:37:16 +01:00
|
|
|
|
2023-11-19 13:29:15 +01:00
|
|
|
if (!post_flash_access()) {
|
|
|
|
|
snprintf(mess, 256, "Error: fail to disable flash access");
|
|
|
|
|
printError(mess);
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
|
throw std::runtime_error(mess);
|
|
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Gowin::programSRAM()
|
|
|
|
|
{
|
2019-12-06 07:27:08 +01:00
|
|
|
if (_verbose) {
|
2023-09-01 19:24:33 +02:00
|
|
|
displayReadReg("before program sram", readStatusReg());
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
2023-08-09 16:12:46 +02:00
|
|
|
/* Work around FPGA stuck in Bad Command status */
|
|
|
|
|
if (is_gw5a) {
|
|
|
|
|
reset();
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
_jtag->toggleClk(1000000);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
if (!eraseSRAM())
|
2019-12-06 07:27:08 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* load bitstream in SRAM */
|
2023-09-01 19:24:33 +02:00
|
|
|
if (!writeSRAM(_fs->getData(), _fs->getLength()))
|
2019-12-06 07:27:08 +01:00
|
|
|
return;
|
|
|
|
|
|
2023-02-16 20:02:36 +01:00
|
|
|
/* ocheck if file checksum == checksum in FPGA */
|
2023-09-01 19:24:33 +02:00
|
|
|
if (!skip_checksum)
|
|
|
|
|
checkCRC();
|
2023-02-16 20:02:36 +01:00
|
|
|
if (_verbose)
|
2023-09-01 19:24:33 +02:00
|
|
|
displayReadReg("after program sram", readStatusReg());
|
2023-02-16 20:02:36 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
void Gowin::program(unsigned int offset, bool unprotect_flash)
|
2023-02-16 20:02:36 +01:00
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
if (!_fs)
|
2023-02-16 20:02:36 +01:00
|
|
|
return;
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
if (_mode == FLASH_MODE) {
|
|
|
|
|
if (_external_flash)
|
|
|
|
|
programExtFlash(offset, unprotect_flash);
|
|
|
|
|
else
|
|
|
|
|
programFlash();
|
|
|
|
|
} else if (_mode == MEM_MODE) {
|
|
|
|
|
programSRAM();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Gowin::checkCRC()
|
|
|
|
|
{
|
|
|
|
|
uint32_t ucode = readUserCode();
|
2023-02-16 20:02:36 +01:00
|
|
|
uint16_t checksum = static_cast<FsParser *>(_fs)->checksum();
|
2023-09-01 19:24:33 +02:00
|
|
|
if (static_cast<uint16_t>(0xffff & ucode) == checksum)
|
|
|
|
|
goto success;
|
|
|
|
|
/* no match:
|
|
|
|
|
* user code register contains checksum or
|
|
|
|
|
* user_code when set_option -user_code
|
|
|
|
|
* is used, try to compare with this value
|
|
|
|
|
*/
|
2023-02-16 20:02:36 +01:00
|
|
|
try {
|
2023-09-01 19:24:33 +02:00
|
|
|
string hdr = _fs->getHeaderVal("checkSum");
|
|
|
|
|
if (!hdr.empty()) {
|
|
|
|
|
if (ucode == strtol(hdr.c_str(), NULL, 16))
|
|
|
|
|
goto success;
|
2022-06-16 03:52:44 +02:00
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
} catch (std::exception &e) {}
|
|
|
|
|
char mess[256];
|
|
|
|
|
snprintf(mess, 256, "Read: 0x%08x checksum: 0x%04x\n", ucode, checksum);
|
|
|
|
|
printError("CRC check : FAIL");
|
|
|
|
|
printError(mess);
|
|
|
|
|
return;
|
|
|
|
|
success:
|
|
|
|
|
printSuccess("CRC check: Success");
|
|
|
|
|
return;
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
bool Gowin::enableCfg()
|
2019-12-06 07:27:08 +01:00
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(CONFIG_ENABLE);
|
2019-12-06 07:27:08 +01:00
|
|
|
return pollFlag(STATUS_SYSTEM_EDIT_MODE, STATUS_SYSTEM_EDIT_MODE);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
bool Gowin::disableCfg()
|
2019-12-06 07:27:08 +01:00
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(CONFIG_DISABLE);
|
|
|
|
|
send_command(NOOP);
|
2019-12-06 07:27:08 +01:00
|
|
|
return pollFlag(STATUS_SYSTEM_EDIT_MODE, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-10 12:50:26 +02:00
|
|
|
uint32_t Gowin::idCode()
|
2019-12-06 07:27:08 +01:00
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
return readReg32(READ_IDCODE);
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t Gowin::readStatusReg()
|
|
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
return readReg32(STATUS_REGISTER);
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t Gowin::readUserCode()
|
|
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
return readReg32(READ_USERCODE);
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
void Gowin::displayReadReg(const char *prefix, uint32_t reg)
|
2019-12-06 07:27:08 +01:00
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
static const char *desc[19] = {
|
|
|
|
|
"CRC Error", "Bad Command", "ID Verify Failed", "Timeout",
|
|
|
|
|
"Reserved4", "Memory Erase", "Preamble", "System Edit Mode",
|
2023-11-19 10:25:06 +01:00
|
|
|
"Program SPI FLASH directly", "Reserved9",
|
|
|
|
|
"Non-JTAG configuration is active", "Bypass",
|
2023-09-01 19:24:33 +02:00
|
|
|
"Gowin VLD", "Done Final", "Security Final", "Ready",
|
|
|
|
|
"POR", "FLASH lock", "FLASH2 lock",
|
|
|
|
|
};
|
2023-11-19 10:25:06 +01:00
|
|
|
|
|
|
|
|
static const char *gw5a_desc[32] = {
|
|
|
|
|
"CRC Error", "Bad Command", "ID Verify Failed", "Timeout",
|
|
|
|
|
"auto_boot_2nd_fail", "Memory Erase", "Preamble", "System Edit Mode",
|
|
|
|
|
"Program SPI FLASH directly", "auto_boot_1st_fail",
|
|
|
|
|
"Non-JTAG configuration is active", "Bypass", "i2c_sram_f",
|
|
|
|
|
"Done Final", "Security Final", "encrypted_format",
|
|
|
|
|
"key_right", "sspi_mode", "CRC Comparison Done", "CRC Error",
|
|
|
|
|
"ECC Error", "ECC Error Uncorrectable", "CMSER IDLE",
|
|
|
|
|
"CPU Bus Width", "", "Retry time sync pattern detect", "",
|
|
|
|
|
"Decompression Failed", "OTP Reading Done", "Init Done",
|
|
|
|
|
"Wakeup Done", "Auto Erase",
|
|
|
|
|
};
|
2023-12-04 07:01:56 +01:00
|
|
|
/* 20-22 differ */
|
|
|
|
|
static const char *gw5ast_desc[3] = {
|
|
|
|
|
"Ser_Ecc_Corr", "Ser_Ecc_Uncorr", "Ser_Ecc_Runing",
|
|
|
|
|
};
|
2023-11-19 10:25:06 +01:00
|
|
|
|
|
|
|
|
/* Bits 26:25 */
|
|
|
|
|
static const char *gw5a_sync_det_retry[4] = {
|
|
|
|
|
"no retry",
|
|
|
|
|
"retry one time",
|
|
|
|
|
"retry two times",
|
|
|
|
|
"no \"sync pattern\" is found after three times detection",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Bits 24:23 */
|
|
|
|
|
static const char *gw5a_cpu_bus_width[4] = {
|
|
|
|
|
"no BWD pattern is detected",
|
|
|
|
|
"8-bit mode",
|
|
|
|
|
"16-bit mode",
|
|
|
|
|
"32-bit mode",
|
|
|
|
|
};
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
printf("%s: displayReadReg %08x\n", prefix, reg);
|
2023-11-19 10:25:06 +01:00
|
|
|
|
2023-12-04 07:01:56 +01:00
|
|
|
if (is_gw5a) {
|
|
|
|
|
uint8_t max_shift = (_idcode == 0x1081b) ? 24 : 32;
|
|
|
|
|
for (unsigned i = 0, bm = 1; i < max_shift; ++i, bm <<= 1) {
|
2023-11-19 10:25:06 +01:00
|
|
|
switch (i) {
|
|
|
|
|
case 23:
|
|
|
|
|
printf("\t[%d:%d] %s: %s\n", i + 1, i, gw5a_desc[i],
|
|
|
|
|
gw5a_cpu_bus_width[(reg >> i)&0x3]);
|
|
|
|
|
bm <<= 1;
|
|
|
|
|
i++;
|
|
|
|
|
break;
|
|
|
|
|
case 25:
|
|
|
|
|
printf("\t[%d:%d] %s: %s\n", i + 1, i, gw5a_desc[i],
|
|
|
|
|
gw5a_sync_det_retry[(reg >> i)&0x3]);
|
|
|
|
|
bm <<= 1;
|
|
|
|
|
i++;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2023-12-04 07:01:56 +01:00
|
|
|
if (reg & bm) {
|
|
|
|
|
if (_idcode == 0x1081b && i >= 20)
|
|
|
|
|
printf("\t [%2d] %s\n", i, gw5ast_desc[i-20]);
|
|
|
|
|
else
|
|
|
|
|
printf("\t [%2d] %s\n", i, gw5a_desc[i]);
|
|
|
|
|
}
|
2023-11-19 10:25:06 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (unsigned i = 0, bm = 1; i < 19; ++i, bm <<= 1) {
|
|
|
|
|
if (reg & bm)
|
|
|
|
|
printf("\t%s\n", desc[i]);
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Gowin::pollFlag(uint32_t mask, uint32_t value)
|
|
|
|
|
{
|
|
|
|
|
uint32_t status;
|
|
|
|
|
int timeout = 0;
|
|
|
|
|
do {
|
|
|
|
|
status = readStatusReg();
|
|
|
|
|
if (_verbose)
|
2023-09-01 19:24:33 +02:00
|
|
|
printf("pollFlag: %x (%x)\n", status, status & mask);
|
2019-12-06 07:27:08 +01:00
|
|
|
if (timeout == 100000000){
|
|
|
|
|
printError("timeout");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
timeout++;
|
|
|
|
|
} while ((status & mask) != value);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
inline uint32_t bswap_32(uint32_t x)
|
|
|
|
|
{
|
|
|
|
|
return ((x << 24) & 0xff000000) |
|
|
|
|
|
((x << 8) & 0x00ff0000) |
|
|
|
|
|
((x >> 8) & 0x0000ff00) |
|
|
|
|
|
((x >> 24) & 0x000000ff);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-04 17:37:16 +01:00
|
|
|
/* TN653 p. 17-21 */
|
2023-09-01 19:24:33 +02:00
|
|
|
bool Gowin::writeFLASH(uint32_t page, const uint8_t *data, int length)
|
2020-01-04 17:37:16 +01:00
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
printInfo("Write FLASH ", false);
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("before write flash", readStatusReg());
|
|
|
|
|
|
|
|
|
|
uint8_t xpage[256];
|
|
|
|
|
length /= 8;
|
|
|
|
|
ProgressBar progress("Writing to FLASH", length, 50, _quiet);
|
|
|
|
|
for (int off = 0; off < length; off += 256) {
|
|
|
|
|
int l = 256;
|
|
|
|
|
if (length - off < l) {
|
|
|
|
|
memset(xpage, 0xff, sizeof(xpage));
|
|
|
|
|
l = length - off;
|
|
|
|
|
}
|
|
|
|
|
memcpy(xpage, &data[off], l);
|
|
|
|
|
unsigned addr = off / 4 + page;
|
|
|
|
|
if (addr) {
|
|
|
|
|
sendClkUs(16);
|
|
|
|
|
} else {
|
|
|
|
|
// autoboot pattern
|
|
|
|
|
static const uint8_t pat[4] = {'G', 'W', '1', 'N'};
|
|
|
|
|
memcpy(xpage, pat, 4);
|
2022-07-20 23:25:53 +02:00
|
|
|
}
|
2020-05-23 14:51:48 +02:00
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(CONFIG_ENABLE);
|
|
|
|
|
send_command(NOOP);
|
|
|
|
|
send_command(EF_PROGRAM);
|
|
|
|
|
|
|
|
|
|
unsigned w = htole32(addr);
|
|
|
|
|
_jtag->shiftDR((uint8_t *)&w, nullptr, 32);
|
|
|
|
|
sendClkUs(16);
|
|
|
|
|
for (int y = 0; y < 64; ++y) {
|
|
|
|
|
memcpy(&w, &xpage[y * 4], 4);
|
|
|
|
|
w = bswap_32(w);
|
|
|
|
|
_jtag->shiftDR((uint8_t *)&w, nullptr, 32);
|
|
|
|
|
sendClkUs((is_gw1n1) ? 32 : 16);
|
2022-07-20 23:25:53 +02:00
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
sendClkUs((is_gw1n1) ? 2400 : 6);
|
|
|
|
|
progress.display(off);
|
2022-07-20 23:25:53 +02:00
|
|
|
}
|
2020-05-23 14:51:48 +02:00
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
if (!disableCfg()) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
return false;
|
2020-01-04 17:37:16 +01:00
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("after write flash #1", readStatusReg());
|
|
|
|
|
send_command(RELOAD);
|
|
|
|
|
send_command(NOOP);
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("after write flash #2", readStatusReg());
|
|
|
|
|
_jtag->flush();
|
|
|
|
|
usleep(500*1000);
|
2020-01-04 17:37:16 +01:00
|
|
|
|
|
|
|
|
progress.done();
|
2023-09-01 19:24:33 +02:00
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("after write flash", readStatusReg());
|
|
|
|
|
if (readStatusReg() & STATUS_DONE_FINAL) {
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
printSuccess("FAIL");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-01-04 17:37:16 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-20 23:27:32 +02:00
|
|
|
bool Gowin::connectJtagToMCU()
|
|
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(SWITCH_TO_MCU_JTAG);
|
2022-07-20 23:27:32 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 07:27:08 +01:00
|
|
|
/* TN653 p. 9 */
|
2023-09-01 19:24:33 +02:00
|
|
|
bool Gowin::writeSRAM(const uint8_t *data, int length)
|
2019-12-06 07:27:08 +01:00
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
printInfo("Load SRAM ", false);
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("before write sram", readStatusReg());
|
|
|
|
|
ProgressBar progress("Load SRAM", length, 50, _quiet);
|
|
|
|
|
send_command(CONFIG_ENABLE); // config enable
|
2019-12-06 07:27:08 +01:00
|
|
|
|
2023-08-09 16:12:46 +02:00
|
|
|
/* UG704 3.4.3 */
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(INIT_ADDR); // address initialize
|
2023-08-09 16:12:46 +02:00
|
|
|
|
2019-12-06 07:27:08 +01:00
|
|
|
/* 2.2.6.4 */
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(XFER_WRITE); // transfer configuration data
|
|
|
|
|
int remains = length;
|
|
|
|
|
const uint8_t *ptr = data;
|
|
|
|
|
static const unsigned pstep = 524288; // 0x80000, about 0.2 sec of bitstream at 2.5MHz
|
|
|
|
|
while (remains) {
|
|
|
|
|
int chunk = pstep;
|
|
|
|
|
/* 2.2.6.5 */
|
|
|
|
|
Jtag::tapState_t next = Jtag::SHIFT_DR;
|
|
|
|
|
if (remains < chunk) {
|
|
|
|
|
chunk = remains;
|
|
|
|
|
/* 2.2.6.6 */
|
|
|
|
|
next = Jtag::RUN_TEST_IDLE;
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
_jtag->shiftDR(ptr, NULL, chunk, next);
|
|
|
|
|
ptr += chunk >> 3; // in bytes
|
|
|
|
|
remains -= chunk;
|
|
|
|
|
progress.display(length - remains);
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
progress.done();
|
|
|
|
|
send_command(0x0a);
|
|
|
|
|
uint32_t checksum = static_cast<FsParser *>(_fs)->checksum();
|
|
|
|
|
checksum = htole32(checksum);
|
|
|
|
|
_jtag->shiftDR((uint8_t *)&checksum, NULL, 32);
|
|
|
|
|
send_command(0x08);
|
2019-12-06 07:27:08 +01:00
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
send_command(CONFIG_DISABLE); // config disable
|
|
|
|
|
send_command(NOOP); // noop
|
2020-03-06 09:05:57 +01:00
|
|
|
|
2023-09-01 19:24:33 +02:00
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("after write sram", readStatusReg());
|
|
|
|
|
if (readStatusReg() & STATUS_DONE_FINAL) {
|
|
|
|
|
printSuccess("DONE");
|
2019-12-06 07:27:08 +01:00
|
|
|
return true;
|
|
|
|
|
} else {
|
2023-09-01 19:24:33 +02:00
|
|
|
printSuccess("FAIL");
|
2019-12-06 07:27:08 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-04 17:37:16 +01:00
|
|
|
/* Erase SRAM:
|
|
|
|
|
* TN653 p.14-17
|
2023-09-01 19:24:33 +02:00
|
|
|
* UG290-2.7.1E p.53
|
2020-01-04 17:37:16 +01:00
|
|
|
*/
|
|
|
|
|
bool Gowin::eraseFLASH()
|
|
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
if (readStatusReg() & STATUS_GOWIN_VLD) {
|
|
|
|
|
if (!eraseSRAM()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-05-23 14:51:48 +02:00
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
|
|
|
|
|
printInfo("Erase FLASH ", false);
|
|
|
|
|
ProgressBar progress("Erasing FLASH", 100, 50, _quiet);
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < 100; ++i) { // 100 attempts?
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("before erase flash", readStatusReg());
|
|
|
|
|
if (!enableCfg()) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
progress.fail();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
send_command(EFLASH_ERASE);
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
|
|
|
|
|
/* GW1N1 need 65 x 32bits
|
|
|
|
|
* others 1 x 32bits
|
|
|
|
|
*/
|
|
|
|
|
int nb_iter = (is_gw1n1)?65:1;
|
|
|
|
|
for (int i = 0; i < nb_iter; ++i) {
|
|
|
|
|
// keep following sequence as-is. it is _not_ _jtag->shiftDR().
|
|
|
|
|
_jtag->set_state(Jtag::SHIFT_DR);
|
|
|
|
|
_jtag->toggleClk(32);
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TN653 specifies to wait for 160ms with
|
|
|
|
|
* there are no bit in status register to specify
|
|
|
|
|
* when this operation is done so we need to wait
|
|
|
|
|
*/
|
|
|
|
|
sendClkUs(150 * 1000);
|
|
|
|
|
if (!disableCfg()) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
progress.fail();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
_jtag->flush();
|
|
|
|
|
usleep(500 * 1000);
|
|
|
|
|
uint32_t state = readStatusReg();
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("after erase flash", state);
|
|
|
|
|
progress.display(i);
|
|
|
|
|
if (!(state & STATUS_DONE_FINAL)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (readStatusReg() & STATUS_DONE_FINAL) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
progress.fail();
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
progress.done();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Gowin::sendClkUs(unsigned us)
|
|
|
|
|
{
|
|
|
|
|
uint64_t clocks = _jtag->getClkFreq();
|
|
|
|
|
clocks *= us;
|
|
|
|
|
clocks /= 1000000;
|
|
|
|
|
_jtag->toggleClk(clocks);
|
2020-01-04 17:37:16 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-06 07:27:08 +01:00
|
|
|
/* Erase SRAM:
|
|
|
|
|
* TN653 p.9-10, 14 and 31
|
|
|
|
|
*/
|
|
|
|
|
bool Gowin::eraseSRAM()
|
|
|
|
|
{
|
2023-09-01 19:24:33 +02:00
|
|
|
printInfo("Erase SRAM ", false);
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("before erase sram", readStatusReg());
|
|
|
|
|
|
|
|
|
|
if (!enableCfg()) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
send_command(ERASE_SRAM);
|
|
|
|
|
send_command(NOOP);
|
2019-12-06 07:27:08 +01:00
|
|
|
|
|
|
|
|
/* TN653 specifies to wait for 4ms with
|
|
|
|
|
* clock generated but
|
|
|
|
|
* status register bit MEMORY_ERASE goes low when ERASE_SRAM
|
|
|
|
|
* is send and goes high after erase
|
|
|
|
|
* this check seems enough
|
|
|
|
|
*/
|
|
|
|
|
if (pollFlag(STATUS_MEMORY_ERASE, STATUS_MEMORY_ERASE)) {
|
2023-09-01 19:24:33 +02:00
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("after erase sram", readStatusReg());
|
2019-12-06 07:27:08 +01:00
|
|
|
} else {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
|
|
|
|
|
send_command(XFER_DONE);
|
|
|
|
|
send_command(NOOP);
|
|
|
|
|
if (!disableCfg()) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("after erase sram", readStatusReg());
|
|
|
|
|
if (readStatusReg() & STATUS_DONE_FINAL) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Gowin::spi_gowin_write(const uint8_t *wr, uint8_t *rd, unsigned len) {
|
|
|
|
|
_jtag->shiftDR(wr, rd, len);
|
|
|
|
|
_jtag->toggleClk(6);
|
2019-12-06 07:27:08 +01:00
|
|
|
}
|
2021-09-15 20:18:49 +02:00
|
|
|
|
|
|
|
|
/* SPI wrapper
|
|
|
|
|
* extflash access may be done using specific mode or
|
|
|
|
|
* boundary scan. But former is only available with mode=[11]
|
|
|
|
|
* so use Bscan
|
|
|
|
|
*
|
|
|
|
|
* it's a bitbanging mode with:
|
|
|
|
|
* Pins Name of SPI Flash | SCLK | CS | DI | DO |
|
|
|
|
|
* Bscan Chain[7:0] | 7 6 | 5 4 | 3 2 | 1 0 |
|
|
|
|
|
* (ctrl & data) | 0 | 0 | 0 | 1 |
|
|
|
|
|
* ctrl 0 -> out, 1 -> in
|
|
|
|
|
* data 1 -> high, 0 -> low
|
|
|
|
|
* but all byte must be bit reversal...
|
|
|
|
|
*/
|
|
|
|
|
|
2023-08-08 15:54:27 +02:00
|
|
|
int Gowin::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len)
|
2021-09-15 20:18:49 +02:00
|
|
|
{
|
2023-11-19 13:29:15 +01:00
|
|
|
if (is_gw5a)
|
|
|
|
|
return spi_put_gw5a(cmd, tx, rx, len);
|
|
|
|
|
|
2021-09-15 20:18:49 +02:00
|
|
|
uint8_t jrx[len+1], jtx[len+1];
|
|
|
|
|
jtx[0] = cmd;
|
|
|
|
|
if (tx)
|
|
|
|
|
memcpy(jtx+1, tx, len);
|
|
|
|
|
else
|
|
|
|
|
memset(jtx+1, 0, len);
|
|
|
|
|
int ret = spi_put(jtx, (rx)? jrx : NULL, len+1);
|
|
|
|
|
if (rx)
|
2023-11-19 10:18:45 +01:00
|
|
|
memcpy(rx, jrx + 1, len);
|
2021-09-15 20:18:49 +02:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-08 15:54:27 +02:00
|
|
|
int Gowin::spi_put(const uint8_t *tx, uint8_t *rx, uint32_t len)
|
2021-09-15 20:18:49 +02:00
|
|
|
{
|
2023-11-19 13:29:15 +01:00
|
|
|
if (is_gw5a) {
|
|
|
|
|
uint8_t jrx[len];
|
|
|
|
|
int ret = spi_put_gw5a(tx[0], (len > 1) ? &tx[1] : NULL,
|
|
|
|
|
(rx) ? jrx : NULL, len - 1);
|
|
|
|
|
// FIXME: first byte is never read (but in most call it's not an issue
|
|
|
|
|
if (rx) {
|
|
|
|
|
rx[0] = 0;
|
|
|
|
|
memcpy(&rx[1], jrx, len - 1);
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 21:24:40 +02:00
|
|
|
if (is_gw2a) {
|
|
|
|
|
uint8_t jtx[len];
|
|
|
|
|
uint8_t jrx[len];
|
|
|
|
|
if (rx)
|
|
|
|
|
len++;
|
|
|
|
|
if (tx != NULL) {
|
|
|
|
|
for (uint32_t i = 0; i < len; i++)
|
|
|
|
|
jtx[i] = FsParser::reverseByte(tx[i]);
|
|
|
|
|
}
|
2023-09-01 19:24:33 +02:00
|
|
|
bool ret = send_command(0x16);
|
2022-09-06 21:24:40 +02:00
|
|
|
if (!ret)
|
|
|
|
|
return -1;
|
|
|
|
|
_jtag->set_state(Jtag::EXIT2_DR);
|
2023-09-01 19:24:33 +02:00
|
|
|
_jtag->shiftDR(jtx, (rx)? jrx:NULL, 8*len);
|
2022-09-06 21:24:40 +02:00
|
|
|
if (rx) {
|
|
|
|
|
for (uint32_t i=0; i < len; i++) {
|
|
|
|
|
rx[i] = FsParser::reverseByte(jrx[i]>>1) |
|
|
|
|
|
(jrx[i+1]&0x01);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* set CS/SCK/DI low */
|
|
|
|
|
uint8_t t = _spi_msk | _spi_do;
|
|
|
|
|
t &= ~_spi_cs;
|
2021-09-15 20:18:49 +02:00
|
|
|
spi_gowin_write(&t, NULL, 8);
|
|
|
|
|
_jtag->flush();
|
2022-09-06 21:24:40 +02:00
|
|
|
|
|
|
|
|
/* send bit/bit full tx content (or set di to 0 when NULL) */
|
2023-09-01 19:24:33 +02:00
|
|
|
for (unsigned l = 0; l < len; ++l) {
|
|
|
|
|
if (rx)
|
|
|
|
|
rx[l] = 0;
|
|
|
|
|
for (uint8_t b = 0, bm = 0x80; b < 8; ++b, bm >>= 1) {
|
|
|
|
|
uint8_t r;
|
|
|
|
|
t = _spi_msk | _spi_do;
|
|
|
|
|
if (tx != NULL && tx[l] & bm)
|
|
|
|
|
t |= _spi_di;
|
|
|
|
|
spi_gowin_write(&t, NULL, 8);
|
|
|
|
|
t |= _spi_sck;
|
|
|
|
|
spi_gowin_write(&t, (rx) ? &r : NULL, 8);
|
|
|
|
|
_jtag->flush();
|
|
|
|
|
/* if read reconstruct bytes */
|
|
|
|
|
if (rx && (r & _spi_do))
|
|
|
|
|
rx[l] |= bm;
|
2022-09-06 21:24:40 +02:00
|
|
|
}
|
2021-09-15 20:18:49 +02:00
|
|
|
}
|
2022-09-06 21:24:40 +02:00
|
|
|
/* set CS and unset SCK (next xfer) */
|
|
|
|
|
t &= ~_spi_sck;
|
|
|
|
|
t |= _spi_cs;
|
|
|
|
|
spi_gowin_write(&t, NULL, 8);
|
|
|
|
|
_jtag->flush();
|
2021-09-15 20:18:49 +02:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Gowin::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
|
|
|
|
|
uint32_t timeout, bool verbose)
|
|
|
|
|
{
|
2023-11-19 13:29:15 +01:00
|
|
|
if (is_gw5a)
|
|
|
|
|
return spi_wait_gw5a(cmd, mask, cond, timeout, verbose);
|
|
|
|
|
|
2022-09-06 21:24:40 +02:00
|
|
|
uint8_t tmp;
|
2021-09-15 20:18:49 +02:00
|
|
|
uint32_t count = 0;
|
|
|
|
|
|
2022-09-06 21:24:40 +02:00
|
|
|
if (is_gw2a) {
|
|
|
|
|
uint8_t rx[3];
|
|
|
|
|
uint8_t tx[3];
|
|
|
|
|
tx[0] = FsParser::reverseByte(cmd);
|
|
|
|
|
|
|
|
|
|
do {
|
2023-09-01 19:24:33 +02:00
|
|
|
bool ret = send_command(0x16);
|
2022-09-06 21:24:40 +02:00
|
|
|
if (!ret)
|
|
|
|
|
return -1;
|
|
|
|
|
_jtag->set_state(Jtag::EXIT2_DR);
|
2023-09-01 19:24:33 +02:00
|
|
|
_jtag->shiftDR(tx, rx, 8 * 3);
|
2022-09-06 21:24:40 +02:00
|
|
|
|
|
|
|
|
tmp = (FsParser::reverseByte(rx[1]>>1)) | (0x01 & rx[2]);
|
2023-09-01 19:24:33 +02:00
|
|
|
count++;
|
2022-09-06 21:24:40 +02:00
|
|
|
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);
|
|
|
|
|
} else {
|
|
|
|
|
uint8_t t;
|
2021-09-15 20:18:49 +02:00
|
|
|
|
2022-09-06 21:24:40 +02:00
|
|
|
/* set CS/SCK/DI low */
|
2021-09-15 20:18:49 +02:00
|
|
|
t = _spi_msk | _spi_do;
|
|
|
|
|
spi_gowin_write(&t, NULL, 8);
|
|
|
|
|
|
2022-09-06 21:24:40 +02:00
|
|
|
/* send command bit/bit */
|
2023-09-01 19:24:33 +02:00
|
|
|
for (uint8_t i = 0, bm = 0x80; i < 8; ++i, bm >>= 1) {
|
2022-09-06 21:24:40 +02:00
|
|
|
t = _spi_msk | _spi_do;
|
2023-09-01 19:24:33 +02:00
|
|
|
if ((cmd & bm) != 0)
|
2022-09-06 21:24:40 +02:00
|
|
|
t |= _spi_di;
|
2021-09-15 20:18:49 +02:00
|
|
|
spi_gowin_write(&t, NULL, 8);
|
|
|
|
|
t |= _spi_sck;
|
2022-09-06 21:24:40 +02:00
|
|
|
spi_gowin_write(&t, NULL, 8);
|
2021-09-15 20:18:49 +02:00
|
|
|
_jtag->flush();
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 21:24:40 +02:00
|
|
|
t = _spi_msk | _spi_do;
|
|
|
|
|
do {
|
|
|
|
|
tmp = 0;
|
|
|
|
|
/* read status register bit/bit with di == 0 */
|
2023-09-01 19:24:33 +02:00
|
|
|
for (uint8_t i = 0, bm = 0x80; i < 8; ++i, bm >>= 1) {
|
2022-09-06 21:24:40 +02:00
|
|
|
uint8_t r;
|
|
|
|
|
t &= ~_spi_sck;
|
|
|
|
|
spi_gowin_write(&t, NULL, 8);
|
|
|
|
|
t |= _spi_sck;
|
|
|
|
|
spi_gowin_write(&t, &r, 8);
|
|
|
|
|
_jtag->flush();
|
|
|
|
|
if ((r & _spi_do) != 0)
|
2023-09-01 19:24:33 +02:00
|
|
|
tmp |= bm;
|
2022-09-06 21:24:40 +02:00
|
|
|
}
|
2021-09-15 20:18:49 +02:00
|
|
|
|
2022-09-06 21:24:40 +02:00
|
|
|
count++;
|
|
|
|
|
if (count == timeout) {
|
|
|
|
|
printf("timeout: %x\n", tmp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (verbose)
|
|
|
|
|
printf("%x %x %x %u\n", tmp, mask, cond, count);
|
|
|
|
|
} while ((tmp & mask) != cond);
|
|
|
|
|
|
|
|
|
|
/* set CS & unset SCK (next xfer) */
|
|
|
|
|
t &= ~_spi_sck;
|
|
|
|
|
t |= _spi_cs;
|
|
|
|
|
spi_gowin_write(&t, NULL, 8);
|
|
|
|
|
_jtag->flush();
|
|
|
|
|
}
|
2021-09-15 20:18:49 +02:00
|
|
|
|
|
|
|
|
if (count == timeout) {
|
2022-09-06 21:24:40 +02:00
|
|
|
printf("%02x\n", tmp);
|
2021-09-15 20:18:49 +02:00
|
|
|
std::cout << "wait: Error" << std::endl;
|
|
|
|
|
return -ETIME;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2023-11-19 13:29:15 +01:00
|
|
|
|
|
|
|
|
bool Gowin::dumpFlash(uint32_t base_addr, uint32_t len)
|
|
|
|
|
{
|
|
|
|
|
bool ret = true;
|
|
|
|
|
/* enable SPI flash access */
|
|
|
|
|
if (!prepare_flash_access())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
SPIFlash flash(this, false, _verbose);
|
|
|
|
|
ret = flash.dump(_filename, base_addr, len, 256);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError(e.what());
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* reload bitstream */
|
|
|
|
|
return post_flash_access() && ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Gowin::prepare_flash_access()
|
|
|
|
|
{
|
|
|
|
|
_jtag->setClkFreq(10000000);
|
|
|
|
|
|
|
|
|
|
if (!eraseSRAM()) {
|
|
|
|
|
printError("Error: fail to erase SRAM");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_gw5a) {
|
|
|
|
|
if (!gw5a_enable_spi()) {
|
|
|
|
|
printError("Error: fail to switch GW5A to SPI mode");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else if (!is_gw2a) {
|
|
|
|
|
send_command(0x3D);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Gowin::post_flash_access()
|
|
|
|
|
{
|
|
|
|
|
bool ret = true;
|
|
|
|
|
|
|
|
|
|
if (is_gw5a) {
|
|
|
|
|
if (!gw5a_disable_spi()) {
|
|
|
|
|
printError("Error: fail to disable GW5A SPI mode");
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
} else if (!is_gw2a) {
|
|
|
|
|
if (!disableCfg()) {
|
|
|
|
|
printError("Error: fail to disable configuration");
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Specific implementation for Arora V GW5A FPGAs
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* interface mode is already configured to mimic SPI (mode 0).
|
|
|
|
|
* JTAG is LSB, SPI is MSB -> all byte must be reversed.
|
|
|
|
|
*/
|
|
|
|
|
int Gowin::spi_put_gw5a(const uint8_t cmd, const uint8_t *tx, uint8_t *rx,
|
|
|
|
|
uint32_t len)
|
|
|
|
|
{
|
|
|
|
|
uint32_t kLen = len + (rx ? 1 : 0); // cppcheck/lint happy
|
|
|
|
|
uint32_t bit_len = len * 8 + (rx ? 3 : 0); // 3bits delay when read
|
|
|
|
|
uint8_t jtx[kLen], jrx[kLen];
|
|
|
|
|
uint8_t _cmd = FsParser::reverseByte(cmd); // reverse cmd.
|
|
|
|
|
uint8_t curr_tdi = cmd & 0x01;
|
|
|
|
|
|
|
|
|
|
if (tx != NULL) { // SPI: MSB, JTAG: LSB -> reverse Bytes
|
|
|
|
|
for (uint32_t i = 0; i < len; i++)
|
|
|
|
|
jtx[i] = FsParser::reverseByte(tx[i]);
|
|
|
|
|
curr_tdi = tx[len-1] & 0x01;
|
|
|
|
|
} else {
|
|
|
|
|
memset(jtx, curr_tdi, kLen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set TMS/CS low by moving to a state where TMS == 0,
|
|
|
|
|
// first cmd bit is also sent here (ie before next TCK rise).
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE, _cmd & 0x01);
|
|
|
|
|
_cmd >>= 1;
|
|
|
|
|
|
|
|
|
|
// complete with 7 remaining cmd bits
|
|
|
|
|
if (0 != _jtag->read_write(&_cmd, NULL, 7, 0))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
// write/read the sequence. Force set to 0 to manage state here
|
|
|
|
|
// (with jtag last bit is sent with tms rise)
|
|
|
|
|
if (0 != _jtag->read_write(jtx, (rx) ? jrx : NULL, bit_len, 0))
|
|
|
|
|
return -1;
|
|
|
|
|
// set TMS/CS high by moving to a state where TMS == 1
|
|
|
|
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET, curr_tdi);
|
|
|
|
|
_jtag->toggleClk(5); // Required ?
|
|
|
|
|
_jtag->flushTMS(true);
|
|
|
|
|
if (rx) { // Reconstruct read sequence and drop first 3bits.
|
|
|
|
|
for (uint32_t i = 0; i < len; i++)
|
|
|
|
|
rx[i] = FsParser::reverseByte((jrx[i] >> 3) |
|
|
|
|
|
(((jrx[i+1]) & 0x07) << 5));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Gowin::spi_wait_gw5a(uint8_t cmd, uint8_t mask, uint8_t cond,
|
|
|
|
|
uint32_t timeout, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
uint8_t tmp;
|
|
|
|
|
uint32_t count = 0;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
// sent command and read flash answer
|
|
|
|
|
if (0 != spi_put_gw5a(cmd, NULL, &tmp, 1)) {
|
|
|
|
|
printError("Error: cant write/read status");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
if (count == timeout) {
|
|
|
|
|
printf("timeout: %x\n", tmp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (verbose) {
|
|
|
|
|
printf("%x %x %x %u\n", tmp, mask, cond, count);
|
|
|
|
|
}
|
|
|
|
|
} while ((tmp & mask) != cond);
|
|
|
|
|
|
|
|
|
|
return (count == timeout) ? -1 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Gowin::gw5a_enable_spi()
|
|
|
|
|
{
|
|
|
|
|
enableCfg();
|
|
|
|
|
send_command(0x3F);
|
|
|
|
|
disableCfg();
|
|
|
|
|
if (_verbose)
|
|
|
|
|
displayReadReg("toto", readStatusReg());
|
|
|
|
|
|
|
|
|
|
/* UG704 3.4.3 'ExtFlash Programming -> Program External Flash via JTAG-SPI' */
|
|
|
|
|
send_command(NOOP);
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
_jtag->toggleClk(126*8);
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
send_command(0x16);
|
|
|
|
|
send_command(0x00);
|
|
|
|
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
|
|
|
|
_jtag->toggleClk(625*8);
|
|
|
|
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET);
|
|
|
|
|
/* save current read/write edge cfg before switching to SPI mode0
|
|
|
|
|
* (rising edge: read / falling edge: write)
|
|
|
|
|
*/
|
|
|
|
|
_prev_wr_edge = _jtag->getWriteEdge();
|
|
|
|
|
_prev_rd_edge = _jtag->getReadEdge();
|
|
|
|
|
_jtag->setWriteEdge(JtagInterface::FALLING_EDGE);
|
|
|
|
|
_jtag->setReadEdge(JtagInterface::RISING_EDGE);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Gowin::gw5a_disable_spi()
|
|
|
|
|
{
|
|
|
|
|
/* reconfigure WR/RD edge and sent sequence to
|
|
|
|
|
* disable SPI mode
|
|
|
|
|
*/
|
|
|
|
|
_jtag->setWriteEdge(_prev_wr_edge);
|
|
|
|
|
_jtag->setReadEdge(_prev_rd_edge);
|
|
|
|
|
_jtag->flushTMS(true);
|
|
|
|
|
_jtag->flush();
|
|
|
|
|
// 1. sent 15 TMS pulse
|
|
|
|
|
// TEST_LOGIC_RESET to SELECT_DR_SCAN: 01
|
|
|
|
|
_jtag->set_state(Jtag::SELECT_DR_SCAN);
|
|
|
|
|
// SELECT_DR_SCAN to CAPTURE_DR: 0
|
|
|
|
|
_jtag->set_state(Jtag::CAPTURE_DR);
|
|
|
|
|
// CAPTURE_DR to EXIT1_DR: 1
|
|
|
|
|
_jtag->set_state(Jtag::EXIT1_DR);
|
|
|
|
|
// EXIT1_DR to EXIT2_DR: 01
|
|
|
|
|
_jtag->set_state(Jtag::EXIT2_DR);
|
|
|
|
|
// Now we have 3 pulses
|
|
|
|
|
for (int i = 0; i < 6; i++) { // 2 each loop: 12 pulses + 3 before
|
|
|
|
|
_jtag->set_state(Jtag::PAUSE_DR); // 010
|
|
|
|
|
_jtag->set_state(Jtag::EXIT2_DR); // 1
|
|
|
|
|
}
|
|
|
|
|
_jtag->set_state(Jtag::EXIT1_DR); // 01 : 16
|
|
|
|
|
_jtag->set_state(Jtag::PAUSE_DR); // 0
|
|
|
|
|
|
|
|
|
|
_jtag->flushTMS(true);
|
|
|
|
|
_jtag->flush();
|
|
|
|
|
// 2. 8 TCK clock cycle with TMS=1
|
|
|
|
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET); // 5 cycles
|
|
|
|
|
_jtag->toggleClk(5);
|
|
|
|
|
return true;
|
|
|
|
|
}
|