From cf596a61db7ebace9cf097ffcb332e4de0679398 Mon Sep 17 00:00:00 2001 From: Daniel Anselmi Date: Mon, 12 Dec 2022 09:49:51 +0100 Subject: pld: add support for lattice ecp5 devices Change-Id: Ib2f0933da3abe7429abca86d6aaa50ad85ce72c7 Signed-off-by: Daniel Anselmi Reviewed-on: https://review.openocd.org/c/openocd/+/7397 Reviewed-by: Antonio Borneo Tested-by: jenkins --- doc/openocd.texi | 4 +- src/pld/Makefile.am | 3 + src/pld/ecp5.c | 206 ++++++++++++++++++++++++++++++++++++++++++ src/pld/ecp5.h | 18 ++++ src/pld/lattice.c | 29 +++++- src/pld/lattice_cmd.h | 19 ++++ tcl/board/ecp5_evaluation.cfg | 19 ++++ tcl/fpga/lattice_ecp5.cfg | 2 + 8 files changed, 297 insertions(+), 3 deletions(-) create mode 100644 src/pld/ecp5.c create mode 100644 src/pld/ecp5.h create mode 100644 src/pld/lattice_cmd.h create mode 100644 tcl/board/ecp5_evaluation.cfg diff --git a/doc/openocd.texi b/doc/openocd.texi index 2d94de8..2d3ccfe 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -8497,10 +8497,10 @@ for FPGA @var{num}. @deffn {FPGA Driver} {lattice} [family] -The FGPA families ECP2 and ECP3 by Lattice are supported. +The FGPA families ECP2, ECP3 and ECP5 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}. 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}. 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 diff --git a/src/pld/Makefile.am b/src/pld/Makefile.am index 7cff09e..459792f 100644 --- a/src/pld/Makefile.am +++ b/src/pld/Makefile.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES += %D%/libpld.la %C%_libpld_la_SOURCES = \ %D%/ecp2_3.c \ + %D%/ecp5.c \ %D%/lattice.c \ %D%/lattice_bit.c \ %D%/pld.c \ @@ -10,8 +11,10 @@ noinst_LTLIBRARIES += %D%/libpld.la %D%/xilinx_bit.c \ %D%/virtex2.c \ %D%/ecp2_3.h \ + %D%/ecp5.h \ %D%/lattice.h \ %D%/lattice_bit.h \ + %D%/lattice_cmd.h \ %D%/pld.h \ %D%/raw_bit.h \ %D%/xilinx_bit.h \ diff --git a/src/pld/ecp5.c b/src/pld/ecp5.c new file mode 100644 index 0000000..298b55f --- /dev/null +++ b/src/pld/ecp5.c @@ -0,0 +1,206 @@ +// 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 ISC_PROGRAM_USERCODE 0xC2 + +#define STATUS_DONE_BIT 0x00000100 +#define STATUS_ERROR_BITS 0x00020040 +#define STATUS_FEA_OTP 0x00004000 +#define STATUS_FAIL_FLAG 0x00002000 +#define STATUS_BUSY_FLAG 0x00001000 +#define REGISTER_ALL_BITS_1 0xffffffff + +int lattice_ecp5_read_status(struct jtag_tap *tap, uint32_t *status, uint32_t out, bool do_idle) +{ + return lattice_read_u32_register(tap, LSC_READ_STATUS, status, out, do_idle); +} + +int lattice_ecp5_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out) +{ + return lattice_read_u32_register(tap, READ_USERCODE, usercode, out, true); +} + +int lattice_ecp5_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode) +{ + struct jtag_tap *tap = lattice_device->tap; + if (!tap) + return ERROR_FAIL; + + int retval = lattice_set_instr(tap, ISC_ENABLE, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + jtag_add_runtest(5, TAP_IDLE); + jtag_add_sleep(20000); + + retval = lattice_set_instr(tap, ISC_PROGRAM_USERCODE, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + + uint8_t buffer[4]; + struct scan_field field; + h_u32_to_le(buffer, usercode); + field.num_bits = 32; + field.out_value = buffer; + field.in_value = NULL; + jtag_add_dr_scan(tap, 1, &field, TAP_IDLE); + jtag_add_runtest(5, TAP_IDLE); + jtag_add_sleep(2000); + + retval = lattice_set_instr(tap, ISC_DISABLE, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + jtag_add_runtest(5, TAP_IDLE); + jtag_add_sleep(200000); + + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + return lattice_verify_usercode(lattice_device, 0x0, usercode, REGISTER_ALL_BITS_1); +} + +static int lattice_ecp5_enable_sram_programming(struct jtag_tap *tap) +{ + int retval = lattice_set_instr(tap, ISC_ENABLE, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + + struct scan_field field; + 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); + jtag_add_sleep(10000); + + return jtag_execute_queue(); +} + +static int lattice_ecp5_erase_sram(struct jtag_tap *tap) +{ + int retval = lattice_set_instr(tap, ISC_ERASE, TAP_IRPAUSE); + if (retval != ERROR_OK) + return retval; + + struct scan_field field; + uint8_t buffer = 1; + 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); + jtag_add_sleep(200000); + return jtag_execute_queue(); +} + +static int lattice_ecp5_init_address(struct jtag_tap *tap) +{ + int retval = lattice_set_instr(tap, LSC_INIT_ADDRESS, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + + struct scan_field field; + uint8_t buffer = 1; + 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); + jtag_add_sleep(10000); + return jtag_execute_queue(); +} + +static int lattice_ecp5_program_config_map(struct jtag_tap *tap, struct lattice_bit_file *bit_file) +{ + int retval = lattice_set_instr(tap, LSC_BITSTREAM_BURST, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + jtag_add_runtest(2, TAP_IDLE); + jtag_add_sleep(10000); + + struct scan_field field; + 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); + retval = lattice_set_instr(tap, BYPASS, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + jtag_add_runtest(100, TAP_IDLE); + jtag_add_sleep(10000); + + return jtag_execute_queue(); +} + +static int lattice_ecp5_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); + jtag_add_sleep(200000); + retval = lattice_set_instr(tap, BYPASS, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + jtag_add_runtest(2, TAP_IDLE); + jtag_add_sleep(1000); + return jtag_execute_queue(); +} + +int lattice_ecp5_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; + + retval = lattice_ecp5_enable_sram_programming(tap); + if (retval != ERROR_OK) + return retval; + + const uint32_t out = 0x0; + const uint32_t expected1 = 0x0; + const uint32_t mask1 = STATUS_ERROR_BITS | STATUS_FEA_OTP; + retval = lattice_verify_status_register_u32(lattice_device, out, expected1, mask1, true); + if (retval != ERROR_OK) + return retval; + + retval = lattice_ecp5_erase_sram(tap); + if (retval != ERROR_OK) + return retval; + + const uint32_t mask2 = STATUS_FAIL_FLAG | STATUS_BUSY_FLAG; + retval = lattice_verify_status_register_u32(lattice_device, out, expected1, mask2, false); + if (retval != ERROR_OK) + return retval; + + retval = lattice_ecp5_init_address(tap); + if (retval != ERROR_OK) + return retval; + + retval = lattice_ecp5_program_config_map(tap, bit_file); + if (retval != ERROR_OK) + return retval; + + retval = lattice_ecp5_exit_programming_mode(tap); + if (retval != ERROR_OK) + return retval; + + const uint32_t expected2 = STATUS_DONE_BIT; + const uint32_t mask3 = STATUS_DONE_BIT | STATUS_FAIL_FLAG; + return lattice_verify_status_register_u32(lattice_device, out, expected2, mask3, false); +} diff --git a/src/pld/ecp5.h b/src/pld/ecp5.h new file mode 100644 index 0000000..7b0c86b --- /dev/null +++ b/src/pld/ecp5.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2022 by Daniel Anselmi * + * danselmi@gmx.ch * + ***************************************************************************/ + +#ifndef OPENOCD_PLD_ECP5_H +#define OPENOCD_PLD_ECP5_H + +#include "lattice.h" + +int lattice_ecp5_read_status(struct jtag_tap *tap, uint32_t *status, uint32_t out, bool do_idle); +int lattice_ecp5_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out); +int lattice_ecp5_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode); +int lattice_ecp5_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file); + +#endif /* OPENOCD_PLD_ECP5_H */ diff --git a/src/pld/lattice.c b/src/pld/lattice.c index 489b189..f3d9c0d 100644 --- a/src/pld/lattice.c +++ b/src/pld/lattice.c @@ -14,6 +14,7 @@ #include "pld.h" #include "lattice_bit.h" #include "ecp2_3.h" +#include "ecp5.h" #define PRELOAD 0x1C @@ -39,6 +40,16 @@ static const struct lattice_devices_elem lattice_devices[] = { {0x01012043, 675, LATTICE_ECP3 /* ecp3 lae3-35ea & lfe3-35ea*/}, {0x01014043, 1077, LATTICE_ECP3 /* ecp3 lfe3-70ea & lfe3-70e & lfe3-95ea && lfe3-95e*/}, {0x01015043, 1326, LATTICE_ECP3 /* ecp3 lfe3-150e*/}, + {0x21111043, 409, LATTICE_ECP5 /* "LAE5U-12F & LFE5U-12F" */}, + {0x41111043, 409, LATTICE_ECP5 /* "LFE5U-25F" */}, + {0x41112043, 510, LATTICE_ECP5 /* "LFE5U-45F" */}, + {0x41113043, 750, LATTICE_ECP5 /* "LFE5U-85F" */}, + {0x81111043, 409, LATTICE_ECP5 /* "LFE5UM5G-25F" */}, + {0x81112043, 510, LATTICE_ECP5 /* "LFE5UM5G-45F" */}, + {0x81113043, 750, LATTICE_ECP5 /* "LFE5UM5G-85F" */}, + {0x01111043, 409, LATTICE_ECP5 /* "LAE5UM-25F" */}, + {0x01112043, 510, LATTICE_ECP5 /* "LAE5UM-45F" */}, + {0x01113043, 750, LATTICE_ECP5 /* "LAE5UM-85F" */}, }; int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate) @@ -137,6 +148,8 @@ static int lattice_read_usercode(struct lattice_pld_device *lattice_device, uint if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3) return lattice_ecp2_3_read_usercode(tap, usercode, out); + else if (lattice_device->family == LATTICE_ECP5) + return lattice_ecp5_read_usercode(tap, usercode, out); return ERROR_FAIL; } @@ -162,6 +175,8 @@ static int lattice_write_usercode(struct lattice_pld_device *lattice_device, uin { if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3) return lattice_ecp2_3_write_usercode(lattice_device, usercode); + else if (lattice_device->family == LATTICE_ECP5) + return lattice_ecp5_write_usercode(lattice_device, usercode); return ERROR_FAIL; } @@ -174,6 +189,8 @@ static int lattice_read_status_u32(struct lattice_pld_device *lattice_device, ui if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3) return lattice_ecp2_3_read_status(lattice_device->tap, status, out, do_idle); + else if (lattice_device->family == LATTICE_ECP5) + return lattice_ecp5_read_status(lattice_device->tap, status, out, do_idle); return ERROR_FAIL; } @@ -218,6 +235,7 @@ static int lattice_load_command(struct pld_device *pld_device, const char *filen if (retval != ERROR_OK) return retval; + uint32_t id = tap->idcode; retval = ERROR_FAIL; switch (lattice_device->family) { case LATTICE_ECP2: @@ -226,6 +244,12 @@ static int lattice_load_command(struct pld_device *pld_device, const char *filen case LATTICE_ECP3: retval = lattice_ecp3_load(lattice_device, &bit_file); break; + case LATTICE_ECP5: + 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); + break; default: LOG_ERROR("loading unknown device family"); break; @@ -257,6 +281,8 @@ PLD_DEVICE_COMMAND_HANDLER(lattice_pld_device_command) family = LATTICE_ECP2; } else if (strcasecmp(CMD_ARGV[2], "ecp3") == 0) { family = LATTICE_ECP3; + } else if (strcasecmp(CMD_ARGV[2], "ecp5") == 0) { + family = LATTICE_ECP5; } else { command_print(CMD, "unknown family"); free(lattice_device); @@ -380,7 +406,8 @@ COMMAND_HANDLER(lattice_read_status_command_handler) return retval; uint32_t status; - retval = lattice_read_status_u32(lattice_device, &status, 0x0, false); + 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); diff --git a/src/pld/lattice_cmd.h b/src/pld/lattice_cmd.h new file mode 100644 index 0000000..8d66ac4 --- /dev/null +++ b/src/pld/lattice_cmd.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2022 by Daniel Anselmi * + * danselmi@gmx.ch * + ***************************************************************************/ + +#ifndef OPENOCD_PLD_LATTICE_CMD_H +#define OPENOCD_PLD_LATTICE_CMD_H + +#define ISC_ERASE 0x0E +#define ISC_DISABLE 0x26 +#define LSC_READ_STATUS 0x3C +#define LSC_INIT_ADDRESS 0x46 +#define LSC_BITSTREAM_BURST 0x7A +#define READ_USERCODE 0xC0 +#define ISC_ENABLE 0xC6 + +#endif /* OPENOCD_PLD_LATTICE_CMD_H */ diff --git a/tcl/board/ecp5_evaluation.cfg b/tcl/board/ecp5_evaluation.cfg new file mode 100644 index 0000000..427037b --- /dev/null +++ b/tcl/board/ecp5_evaluation.cfg @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# Lattice ECP5 evaluation Kit +# https://www.latticesemi.com/view_document?document_id=52479 +# + +adapter driver ftdi +ftdi vid_pid 0x0403 0x6010 + +ftdi channel 0 +ftdi layout_init 0x0008 0x008b +reset_config none +transport select jtag +adapter speed 6000 + +source [find fpga/lattice_ecp5.cfg] + +#openocd -f board/ecp5_evaluation.cfg -c "init" -c "pld load 0 shared_folder/ecp5_blinker_impl1.bit" +#ipdbg -start -tap ecp5.tap -hub 0x32 -port 5555 -tool 0 diff --git a/tcl/fpga/lattice_ecp5.cfg b/tcl/fpga/lattice_ecp5.cfg index a94ada7..4144249 100644 --- a/tcl/fpga/lattice_ecp5.cfg +++ b/tcl/fpga/lattice_ecp5.cfg @@ -26,3 +26,5 @@ jtag newtap $_CHIPNAME tap -irlen 8 -irmask 0x83 -ircapture 0x1 \ -expected-id 0x21111043 -expected-id 0x41111043 -expected-id 0x41112043 \ -expected-id 0x41113043 -expected-id 0x81111043 -expected-id 0x81112043 \ -expected-id 0x81113043 + +pld device lattice $_CHIPNAME.tap -- cgit v1.1