@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.
-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
Reads and displays the status register
noinst_LTLIBRARIES += %D%/libpld.la
%C%_libpld_la_SOURCES = \
+ %D%/certus.c \
%D%/ecp2_3.c \
%D%/ecp5.c \
%D%/lattice.c \
%D%/raw_bit.c \
%D%/xilinx_bit.c \
%D%/virtex2.c \
+ %D%/certus.h \
%D%/ecp2_3.h \
%D%/ecp5.h \
%D%/lattice.h \
--- /dev/null
+// 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);
+}
--- /dev/null
+/* 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 */
#include "lattice_bit.h"
#include "ecp2_3.h"
#include "ecp5.h"
+#include "certus.h"
#define PRELOAD 0x1C
{0x01111043, 409, LATTICE_ECP5 /* "LAE5UM-25F" */},
{0x01112043, 510, LATTICE_ECP5 /* "LAE5UM-45F" */},
{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)
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)
{
struct scan_field field;
return lattice_ecp2_3_read_usercode(tap, usercode, out);
else if (lattice_device->family == LATTICE_ECP5)
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 lattice_ecp2_3_write_usercode(lattice_device, usercode);
else if (lattice_device->family == LATTICE_ECP5)
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;
}
+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,
uint32_t expected, uint32_t mask, bool do_idle)
{
uint32_t status;
-
int retval = lattice_read_status_u32(lattice_device, &status, out, do_idle);
if (retval != ERROR_OK)
return retval;
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)
{
if (!pld_device)
return ERROR_FAIL;
struct lattice_pld_device *lattice_device = pld_device->driver_priv;
-
if (!lattice_device || !lattice_device->tap)
return ERROR_FAIL;
struct jtag_tap *tap = lattice_device->tap;
retval = lattice_ecp3_load(lattice_device, &bit_file);
break;
case LATTICE_ECP5:
+ case LATTICE_CERTUS:
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.",
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;
default:
LOG_ERROR("loading unknown device family");
family = LATTICE_ECP3;
} else if (strcasecmp(CMD_ARGV[2], "ecp5") == 0) {
family = LATTICE_ECP5;
+ } else if (strcasecmp(CMD_ARGV[2], "certus") == 0) {
+ family = LATTICE_CERTUS;
} else {
command_print(CMD, "unknown family");
free(lattice_device);
if (retval != ERROR_OK)
return retval;
- 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);
+ if (lattice_device->family == LATTICE_CERTUS) {
+ uint64_t status;
+ retval = lattice_read_status_u64(lattice_device, &status, 0x0);
+ if (retval == ERROR_OK)
+ 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;
}
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,
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,
uint32_t expected, uint32_t mask);
int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out,
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);
#endif /* OPENOCD_PLD_LATTICE_H */
--- /dev/null
+# 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]
--- /dev/null
+# 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
--- /dev/null
+# 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