pld: add support for lattice certus devices

Change-Id: Ic50a724e5793000fca11f35ba848c2d317c3cbab
Signed-off-by: Daniel Anselmi <danselmi@gmx.ch>
Reviewed-on: https://review.openocd.org/c/openocd/+/7398
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
Daniel Anselmi 2022-12-12 09:49:51 +01:00 committed by Antonio Borneo
parent cf596a61db
commit e33eae340d
9 changed files with 383 additions and 10 deletions

View File

@ -8497,10 +8497,10 @@ for FPGA @var{num}.
@deffn {FPGA Driver} {lattice} [family] @deffn {FPGA Driver} {lattice} [family]
The FGPA families ECP2, ECP3 and ECP5 by Lattice are supported. The FGPA families ECP2, ECP3, ECP5, Certus and CertusPro by Lattice are supported.
This driver can be used to load the bitstream into the FPGA or read the status register and read/write the usercode register. This driver can be used to load the bitstream into the FPGA or read the status register and read/write the usercode register.
The option @option{family} is one of @var{ecp2 ecp3 ecp5}. This is needed when the JTAG ID of the device is not known by openocd (newer NX devices). The option @option{family} is one of @var{ecp2 ecp3 ecp5 certus}. This is needed when the JTAG ID of the device is not known by openocd (newer NX devices).
@deffn {Command} {lattice read_status} num @deffn {Command} {lattice read_status} num
Reads and displays the status register Reads and displays the status register

View File

@ -2,6 +2,7 @@
noinst_LTLIBRARIES += %D%/libpld.la noinst_LTLIBRARIES += %D%/libpld.la
%C%_libpld_la_SOURCES = \ %C%_libpld_la_SOURCES = \
%D%/certus.c \
%D%/ecp2_3.c \ %D%/ecp2_3.c \
%D%/ecp5.c \ %D%/ecp5.c \
%D%/lattice.c \ %D%/lattice.c \
@ -10,6 +11,7 @@ noinst_LTLIBRARIES += %D%/libpld.la
%D%/raw_bit.c \ %D%/raw_bit.c \
%D%/xilinx_bit.c \ %D%/xilinx_bit.c \
%D%/virtex2.c \ %D%/virtex2.c \
%D%/certus.h \
%D%/ecp2_3.h \ %D%/ecp2_3.h \
%D%/ecp5.h \ %D%/ecp5.h \
%D%/lattice.h \ %D%/lattice.h \

232
src/pld/certus.c Normal file
View File

@ -0,0 +1,232 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* Copyright (C) 2022 by Daniel Anselmi *
* danselmi@gmx.ch *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "lattice.h"
#include "lattice_cmd.h"
#define LSC_ENABLE_X 0x74
#define LSC_REFRESH 0x79
#define LSC_DEVICE_CTRL 0x7D
int lattice_certus_read_status(struct jtag_tap *tap, uint64_t *status, uint64_t out)
{
return lattice_read_u64_register(tap, LSC_READ_STATUS, status, out);
}
int lattice_certus_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out)
{
return lattice_read_u32_register(tap, READ_USERCODE, usercode, out, false);
}
int lattice_certus_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode)
{
LOG_ERROR("Not supported to write usercode on certus devices");
return ERROR_FAIL;
}
static int lattice_certus_enable_transparent_mode(struct jtag_tap *tap)
{
struct scan_field field;
int retval = lattice_set_instr(tap, LSC_ENABLE_X, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
uint8_t buffer = 0x0;
field.num_bits = 8;
field.out_value = &buffer;
field.in_value = NULL;
jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
jtag_add_runtest(2, TAP_IDLE);
return jtag_execute_queue();
}
static int lattice_certus_erase_device(struct lattice_pld_device *lattice_device)
{
struct jtag_tap *tap = lattice_device->tap;
if (!tap)
return ERROR_FAIL;
int retval = lattice_set_instr(tap, LSC_DEVICE_CTRL, TAP_IRPAUSE);
if (retval != ERROR_OK)
return retval;
struct scan_field field;
uint8_t buffer = 8;
field.num_bits = 8;
field.out_value = &buffer;
field.in_value = NULL;
jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
jtag_add_runtest(2, TAP_IDLE);
retval = jtag_execute_queue();
if (retval != ERROR_OK)
return retval;
retval = lattice_set_instr(tap, LSC_DEVICE_CTRL, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
buffer = 0;
field.num_bits = 8;
field.out_value = &buffer;
jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
jtag_add_runtest(2, TAP_IDLE);
retval = jtag_execute_queue();
if (retval != ERROR_OK)
return retval;
retval = lattice_set_instr(tap, ISC_ERASE, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
buffer = 0;
field.num_bits = 8;
field.out_value = &buffer;
jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
jtag_add_runtest(100, TAP_IDLE);
jtag_add_sleep(5000);
retval = jtag_execute_queue();
if (retval != ERROR_OK)
return retval;
/* check done is cleared and fail is cleared */
const uint64_t status_done_flag = 0x100;
const uint64_t status_fail_flag = 0x2000;
return lattice_verify_status_register_u64(lattice_device, 0x0, 0x0, status_done_flag | status_fail_flag);
}
static int lattice_certus_enable_programming(struct jtag_tap *tap)
{
struct scan_field field;
int retval = lattice_set_instr(tap, LSC_REFRESH, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
jtag_add_runtest(2, TAP_IDLE);
retval = jtag_execute_queue();
if (retval != ERROR_OK)
return retval;
retval = lattice_set_instr(tap, ISC_ENABLE, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
uint8_t buffer = 0;
field.num_bits = 8;
field.out_value = &buffer;
jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
jtag_add_runtest(2, TAP_IDLE);
return jtag_execute_queue();
}
static int lattice_certus_init_address(struct jtag_tap *tap)
{
int retval = lattice_set_instr(tap, LSC_INIT_ADDRESS, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
jtag_add_runtest(2, TAP_IDLE);
return jtag_execute_queue();
}
static int lattice_certus_exit_programming_mode(struct jtag_tap *tap)
{
int retval = lattice_set_instr(tap, ISC_DISABLE, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
jtag_add_runtest(2, TAP_IDLE);
retval = lattice_set_instr(tap, BYPASS, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
jtag_add_runtest(100, TAP_IDLE);
return jtag_execute_queue();
}
static int lattice_certus_program_config_map(struct jtag_tap *tap, struct lattice_bit_file *bit_file)
{
struct scan_field field;
int retval = lattice_set_instr(tap, LSC_BITSTREAM_BURST, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
field.num_bits = (bit_file->raw_bit.length - bit_file->offset) * 8;
field.out_value = bit_file->raw_bit.data + bit_file->offset;
field.in_value = NULL;
jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
return jtag_execute_queue();
}
int lattice_certus_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file)
{
struct jtag_tap *tap = lattice_device->tap;
if (!tap)
return ERROR_FAIL;
int retval = lattice_preload(lattice_device);
if (retval != ERROR_OK)
return retval;
/* check password protection is disabled */
const uint64_t status_pwd_protection = 0x20000;
retval = lattice_verify_status_register_u64(lattice_device, 0x0, 0x0, status_pwd_protection);
if (retval != ERROR_OK) {
LOG_ERROR("Password protection is set");
return retval;
}
retval = lattice_certus_enable_transparent_mode(tap);
if (retval != ERROR_OK)
return retval;
/* Check the SRAM Erase Lock */
const uint64_t status_otp = 0x40;
retval = lattice_verify_status_register_u64(lattice_device, 0x0, status_otp, status_otp);
if (retval != ERROR_OK) {
LOG_ERROR("NV User Feature Sector OTP is Set");
return retval;
}
/* Check the SRAM Lock */
const uint64_t status_write_protected = 0x400;
retval = lattice_verify_status_register_u64(lattice_device, 0x0, 0x0, status_write_protected);
if (retval != ERROR_OK) {
LOG_ERROR("NV User Feature Sector OTP is Set");
return retval;
}
retval = lattice_certus_enable_programming(tap);
if (retval != ERROR_OK) {
LOG_ERROR("failed to enable programming mode");
return retval;
}
retval = lattice_certus_erase_device(lattice_device);
if (retval != ERROR_OK) {
LOG_ERROR("erasing device failed");
return retval;
}
retval = lattice_certus_init_address(tap);
if (retval != ERROR_OK)
return retval;
retval = lattice_certus_program_config_map(tap, bit_file);
if (retval != ERROR_OK)
return retval;
const uint32_t expected = 0x100; // done
const uint32_t mask = expected |
0x3000 | // Busy Flag and Fail Flag
0xf000000; // BSE Error
retval = lattice_verify_status_register_u64(lattice_device, 0x0, 0x100, mask);
if (retval != ERROR_OK)
return retval;
return lattice_certus_exit_programming_mode(tap);
}

18
src/pld/certus.h Normal file
View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/***************************************************************************
* Copyright (C) 2022 by Daniel Anselmi *
* danselmi@gmx.ch *
***************************************************************************/
#ifndef OPENOCD_PLD_CERTUS_H
#define OPENOCD_PLD_CERTUS_H
#include "lattice.h"
int lattice_certus_read_status(struct jtag_tap *tap, uint64_t *status, uint64_t out);
int lattice_certus_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out);
int lattice_certus_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode);
int lattice_certus_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file);
#endif /* OPENOCD_PLD_CERTUS_H */

View File

@ -15,6 +15,7 @@
#include "lattice_bit.h" #include "lattice_bit.h"
#include "ecp2_3.h" #include "ecp2_3.h"
#include "ecp5.h" #include "ecp5.h"
#include "certus.h"
#define PRELOAD 0x1C #define PRELOAD 0x1C
@ -50,6 +51,9 @@ static const struct lattice_devices_elem lattice_devices[] = {
{0x01111043, 409, LATTICE_ECP5 /* "LAE5UM-25F" */}, {0x01111043, 409, LATTICE_ECP5 /* "LAE5UM-25F" */},
{0x01112043, 510, LATTICE_ECP5 /* "LAE5UM-45F" */}, {0x01112043, 510, LATTICE_ECP5 /* "LAE5UM-45F" */},
{0x01113043, 750, LATTICE_ECP5 /* "LAE5UM-85F" */}, {0x01113043, 750, LATTICE_ECP5 /* "LAE5UM-85F" */},
{0x310f0043, 362, LATTICE_CERTUS /* LFD2NX-17 */},
{0x310f1043, 362, LATTICE_CERTUS /* LFD2NX-40 */},
{0x010f4043, 362, LATTICE_CERTUS /* LFCPNX-100 */},
}; };
int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate) int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate)
@ -116,6 +120,27 @@ int lattice_read_u32_register(struct jtag_tap *tap, uint8_t cmd, uint32_t *in_va
return retval; return retval;
} }
int lattice_read_u64_register(struct jtag_tap *tap, uint8_t cmd, uint64_t *in_val,
uint64_t out_val)
{
struct scan_field field;
uint8_t buffer[8];
int retval = lattice_set_instr(tap, cmd, TAP_IDLE);
if (retval != ERROR_OK)
return retval;
h_u64_to_le(buffer, out_val);
field.num_bits = 64;
field.out_value = buffer;
field.in_value = buffer;
jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
retval = jtag_execute_queue();
if (retval == ERROR_OK)
*in_val = le_to_h_u64(buffer);
return retval;
}
int lattice_preload(struct lattice_pld_device *lattice_device) int lattice_preload(struct lattice_pld_device *lattice_device)
{ {
struct scan_field field; struct scan_field field;
@ -150,6 +175,8 @@ static int lattice_read_usercode(struct lattice_pld_device *lattice_device, uint
return lattice_ecp2_3_read_usercode(tap, usercode, out); return lattice_ecp2_3_read_usercode(tap, usercode, out);
else if (lattice_device->family == LATTICE_ECP5) else if (lattice_device->family == LATTICE_ECP5)
return lattice_ecp5_read_usercode(tap, usercode, out); return lattice_ecp5_read_usercode(tap, usercode, out);
else if (lattice_device->family == LATTICE_CERTUS)
return lattice_certus_read_usercode(tap, usercode, out);
return ERROR_FAIL; return ERROR_FAIL;
} }
@ -177,6 +204,8 @@ static int lattice_write_usercode(struct lattice_pld_device *lattice_device, uin
return lattice_ecp2_3_write_usercode(lattice_device, usercode); return lattice_ecp2_3_write_usercode(lattice_device, usercode);
else if (lattice_device->family == LATTICE_ECP5) else if (lattice_device->family == LATTICE_ECP5)
return lattice_ecp5_write_usercode(lattice_device, usercode); return lattice_ecp5_write_usercode(lattice_device, usercode);
else if (lattice_device->family == LATTICE_CERTUS)
return lattice_certus_write_usercode(lattice_device, usercode);
return ERROR_FAIL; return ERROR_FAIL;
} }
@ -194,12 +223,22 @@ static int lattice_read_status_u32(struct lattice_pld_device *lattice_device, ui
return ERROR_FAIL; return ERROR_FAIL;
} }
static int lattice_read_status_u64(struct lattice_pld_device *lattice_device, uint64_t *status,
uint64_t out)
{
if (!lattice_device->tap)
return ERROR_FAIL;
if (lattice_device->family == LATTICE_CERTUS)
return lattice_certus_read_status(lattice_device->tap, status, out);
return ERROR_FAIL;
}
int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out, int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out,
uint32_t expected, uint32_t mask, bool do_idle) uint32_t expected, uint32_t mask, bool do_idle)
{ {
uint32_t status; uint32_t status;
int retval = lattice_read_status_u32(lattice_device, &status, out, do_idle); int retval = lattice_read_status_u32(lattice_device, &status, out, do_idle);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
@ -212,13 +251,28 @@ int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device
return ERROR_OK; return ERROR_OK;
} }
int lattice_verify_status_register_u64(struct lattice_pld_device *lattice_device, uint64_t out,
uint64_t expected, uint64_t mask)
{
uint64_t status;
int retval = lattice_read_status_u64(lattice_device, &status, out);
if (retval != ERROR_OK)
return retval;
if ((status & mask) != expected) {
LOG_ERROR("verifying status register failed got: 0x%08" PRIx64 " expected: 0x%08" PRIx64,
status & mask, expected);
return ERROR_FAIL;
}
return ERROR_OK;
}
static int lattice_load_command(struct pld_device *pld_device, const char *filename) static int lattice_load_command(struct pld_device *pld_device, const char *filename)
{ {
if (!pld_device) if (!pld_device)
return ERROR_FAIL; return ERROR_FAIL;
struct lattice_pld_device *lattice_device = pld_device->driver_priv; struct lattice_pld_device *lattice_device = pld_device->driver_priv;
if (!lattice_device || !lattice_device->tap) if (!lattice_device || !lattice_device->tap)
return ERROR_FAIL; return ERROR_FAIL;
struct jtag_tap *tap = lattice_device->tap; struct jtag_tap *tap = lattice_device->tap;
@ -245,10 +299,14 @@ static int lattice_load_command(struct pld_device *pld_device, const char *filen
retval = lattice_ecp3_load(lattice_device, &bit_file); retval = lattice_ecp3_load(lattice_device, &bit_file);
break; break;
case LATTICE_ECP5: case LATTICE_ECP5:
case LATTICE_CERTUS:
if (bit_file.has_id && id != bit_file.idcode) if (bit_file.has_id && id != bit_file.idcode)
LOG_WARNING("Id on device (0x%8.8" PRIx32 ") and id in bit-stream (0x%8.8" PRIx32 ") don't match.", LOG_WARNING("Id on device (0x%8.8" PRIx32 ") and id in bit-stream (0x%8.8" PRIx32 ") don't match.",
id, bit_file.idcode); id, bit_file.idcode);
retval = lattice_ecp5_load(lattice_device, &bit_file); if (lattice_device->family == LATTICE_ECP5)
retval = lattice_ecp5_load(lattice_device, &bit_file);
else
retval = lattice_certus_load(lattice_device, &bit_file);
break; break;
default: default:
LOG_ERROR("loading unknown device family"); LOG_ERROR("loading unknown device family");
@ -283,6 +341,8 @@ PLD_DEVICE_COMMAND_HANDLER(lattice_pld_device_command)
family = LATTICE_ECP3; family = LATTICE_ECP3;
} else if (strcasecmp(CMD_ARGV[2], "ecp5") == 0) { } else if (strcasecmp(CMD_ARGV[2], "ecp5") == 0) {
family = LATTICE_ECP5; family = LATTICE_ECP5;
} else if (strcasecmp(CMD_ARGV[2], "certus") == 0) {
family = LATTICE_CERTUS;
} else { } else {
command_print(CMD, "unknown family"); command_print(CMD, "unknown family");
free(lattice_device); free(lattice_device);
@ -405,11 +465,18 @@ COMMAND_HANDLER(lattice_read_status_command_handler)
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
uint32_t status; if (lattice_device->family == LATTICE_CERTUS) {
const bool do_idle = lattice_device->family == LATTICE_ECP5; uint64_t status;
retval = lattice_read_status_u32(lattice_device, &status, 0x0, do_idle); retval = lattice_read_status_u64(lattice_device, &status, 0x0);
if (retval == ERROR_OK) if (retval == ERROR_OK)
command_print(CMD, "0x%8.8" PRIx32, status); command_print(CMD, "0x%016" PRIx64, status);
} else {
uint32_t status;
const bool do_idle = lattice_device->family == LATTICE_ECP5;
retval = lattice_read_status_u32(lattice_device, &status, 0x0, do_idle);
if (retval == ERROR_OK)
command_print(CMD, "0x%8.8" PRIx32, status);
}
return retval; return retval;
} }

View File

@ -23,10 +23,14 @@ struct lattice_pld_device {
int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate); int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate);
int lattice_read_u32_register(struct jtag_tap *tap, uint8_t cmd, uint32_t *in_val, int lattice_read_u32_register(struct jtag_tap *tap, uint8_t cmd, uint32_t *in_val,
uint32_t out_val, bool do_idle); uint32_t out_val, bool do_idle);
int lattice_read_u64_register(struct jtag_tap *tap, uint8_t cmd, uint64_t *in_val,
uint64_t out_val);
int lattice_verify_usercode(struct lattice_pld_device *lattice_device, uint32_t out, int lattice_verify_usercode(struct lattice_pld_device *lattice_device, uint32_t out,
uint32_t expected, uint32_t mask); uint32_t expected, uint32_t mask);
int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out, int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out,
uint32_t expected, uint32_t mask, bool do_idle); uint32_t expected, uint32_t mask, bool do_idle);
int lattice_verify_status_register_u64(struct lattice_pld_device *lattice_device, uint64_t out,
uint64_t expected, uint64_t mask);
int lattice_preload(struct lattice_pld_device *lattice_device); int lattice_preload(struct lattice_pld_device *lattice_device);
#endif /* OPENOCD_PLD_LATTICE_H */ #endif /* OPENOCD_PLD_LATTICE_H */

View File

@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# https://www.latticesemi.com/products/developmentboardsandkits/certuspro-nx-versa-board
adapter driver ftdi
ftdi vid_pid 0x0403 0x6010
ftdi channel 0
ftdi layout_init 0x0008 0x008b
reset_config none
transport select jtag
adapter speed 10000
source [find fpga/lattice_certuspro.cfg]

View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-or-later
if { [info exists CHIPNAME] } {
set _CHIPNAME $_CHIPNAME
} else {
set _CHIPNAME certus
}
# Lattice Certus
#
# Certus NX LFD2NX-17 0x310f0043
# Certus NX LFD2NX-40 0x310f1043
jtag newtap $_CHIPNAME tap -irlen 8 -irmask 0x83 -ircapture 0x1 \
-expected-id 0x310F1043 -expected-id 0x310F0043
pld device lattice $_CHIPNAME.tap

View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-or-later
if { [info exists CHIPNAME] } {
set _CHIPNAME $_CHIPNAME
} else {
set _CHIPNAME certuspro
}
# Lattice CertusPro
#
# 0x010f4043 - LFCPNX-100
# 0x 043 - LFCPNX-50
jtag newtap $_CHIPNAME tap -irlen 8 -irmask 0x83 -ircapture 0x1 \
-expected-id 0x010f4043
# -expected-id 0x01112043
pld device lattice $_CHIPNAME.tap