pld: add support for lattice ecp5 devices
Change-Id: Ib2f0933da3abe7429abca86d6aaa50ad85ce72c7 Signed-off-by: Daniel Anselmi <danselmi@gmx.ch> Reviewed-on: https://review.openocd.org/c/openocd/+/7397 Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com> Tested-by: jenkins
This commit is contained in:
parent
d35faaa35c
commit
cf596a61db
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue