pld: add support for lattice ecp2 and ecp3 devices
Change-Id: I29c227c37be464f7ecc97a30d9cf3da1442e2b7f Signed-off-by: Daniel Anselmi <danselmi@gmx.ch> Reviewed-on: https://review.openocd.org/c/openocd/+/7396 Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com> Tested-by: jenkins
This commit is contained in:
parent
8670ad4caa
commit
d35faaa35c
|
@ -8488,13 +8488,43 @@ openocd -f board/digilent_zedboard.cfg -c "init" \
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@deffn {Command} {virtex2 read_stat} num
|
@deffn {Command} {virtex2 read_stat} num
|
||||||
Reads and displays the Virtex-II status register (STAT)
|
Reads and displays the Virtex-II status register (STAT)
|
||||||
for FPGA @var{num}.
|
for FPGA @var{num}.
|
||||||
@end deffn
|
@end deffn
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@deffn {FPGA Driver} {lattice} [family]
|
||||||
|
The FGPA families ECP2 and ECP3 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).
|
||||||
|
|
||||||
|
@deffn {Command} {lattice read_status} num
|
||||||
|
Reads and displays the status register
|
||||||
|
for FPGA @var{num}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Command} {lattice read_user} num
|
||||||
|
Reads and displays the user register
|
||||||
|
for FPGA @var{num}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Command} {lattice write_user} num val
|
||||||
|
Writes the user register.
|
||||||
|
for FPGA @var{num} with value @var{val}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Command} {lattice set_preload} num length
|
||||||
|
Set the length of the register for the preload. This is needed when the JTAG ID of the device is not known by openocd (newer NX devices).
|
||||||
|
The load command for the FPGA @var{num} will use a length for the preload of @var{length}.
|
||||||
|
@end deffn
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@node General Commands
|
@node General Commands
|
||||||
@chapter General Commands
|
@chapter General Commands
|
||||||
@cindex commands
|
@cindex commands
|
||||||
|
|
|
@ -2,9 +2,17 @@
|
||||||
|
|
||||||
noinst_LTLIBRARIES += %D%/libpld.la
|
noinst_LTLIBRARIES += %D%/libpld.la
|
||||||
%C%_libpld_la_SOURCES = \
|
%C%_libpld_la_SOURCES = \
|
||||||
|
%D%/ecp2_3.c \
|
||||||
|
%D%/lattice.c \
|
||||||
|
%D%/lattice_bit.c \
|
||||||
%D%/pld.c \
|
%D%/pld.c \
|
||||||
|
%D%/raw_bit.c \
|
||||||
%D%/xilinx_bit.c \
|
%D%/xilinx_bit.c \
|
||||||
%D%/virtex2.c \
|
%D%/virtex2.c \
|
||||||
|
%D%/ecp2_3.h \
|
||||||
|
%D%/lattice.h \
|
||||||
|
%D%/lattice_bit.h \
|
||||||
%D%/pld.h \
|
%D%/pld.h \
|
||||||
|
%D%/raw_bit.h \
|
||||||
%D%/xilinx_bit.h \
|
%D%/xilinx_bit.h \
|
||||||
%D%/virtex2.h
|
%D%/virtex2.h
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
#define LSCC_REFRESH 0x23
|
||||||
|
#define ISC_ENABLE 0x15
|
||||||
|
#define LSCC_RESET_ADDRESS 0x21
|
||||||
|
#define ISC_PROGRAM_USERCODE 0x1A
|
||||||
|
#define ISC_ERASE 0x03
|
||||||
|
#define READ_USERCODE 0x17
|
||||||
|
#define ISC_DISABLE 0x1E
|
||||||
|
#define LSCC_READ_STATUS 0x53
|
||||||
|
#define LSCC_BITSTREAM_BURST 0x02
|
||||||
|
|
||||||
|
#define STATUS_DONE_BIT 0x00020000
|
||||||
|
#define STATUS_ERROR_BITS_ECP2 0x00040003
|
||||||
|
#define STATUS_ERROR_BITS_ECP3 0x00040007
|
||||||
|
#define REGISTER_ALL_BITS_1 0xffffffff
|
||||||
|
#define REGISTER_ALL_BITS_0 0x00000000
|
||||||
|
|
||||||
|
int lattice_ecp2_3_read_status(struct jtag_tap *tap, uint32_t *status, uint32_t out, bool do_idle)
|
||||||
|
{
|
||||||
|
return lattice_read_u32_register(tap, LSCC_READ_STATUS, status, out, do_idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lattice_ecp2_3_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_ecp2_3_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;
|
||||||
|
|
||||||
|
struct scan_field field;
|
||||||
|
uint8_t buffer[4];
|
||||||
|
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_ecp2_3_erase_device(struct lattice_pld_device *lattice_device)
|
||||||
|
{
|
||||||
|
struct jtag_tap *tap = lattice_device->tap;
|
||||||
|
if (!tap)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
/* program user code with all bits set */
|
||||||
|
int retval = lattice_set_instr(tap, ISC_PROGRAM_USERCODE, TAP_IRPAUSE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
struct scan_field field;
|
||||||
|
uint8_t buffer[4] = {0xff, 0xff, 0xff, 0xff};
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* verify every bit is set */
|
||||||
|
const uint32_t out = REGISTER_ALL_BITS_1;
|
||||||
|
const uint32_t mask = REGISTER_ALL_BITS_1;
|
||||||
|
const uint32_t expected_pre = REGISTER_ALL_BITS_1;
|
||||||
|
retval = lattice_verify_usercode(lattice_device, out, expected_pre, mask);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = lattice_set_instr(tap, ISC_ERASE, TAP_IDLE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
jtag_add_runtest(5, TAP_IDLE);
|
||||||
|
if (lattice_device->family == LATTICE_ECP2)
|
||||||
|
jtag_add_sleep(100000);
|
||||||
|
else
|
||||||
|
jtag_add_sleep(2000000);
|
||||||
|
|
||||||
|
retval = lattice_set_instr(tap, LSCC_RESET_ADDRESS, TAP_IDLE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
jtag_add_runtest(5, TAP_IDLE);
|
||||||
|
jtag_add_sleep(2000);
|
||||||
|
|
||||||
|
/* after erasing check all bits in user register are cleared */
|
||||||
|
const uint32_t expected_post = REGISTER_ALL_BITS_0;
|
||||||
|
return lattice_verify_usercode(lattice_device, out, expected_post, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lattice_ecp2_3_program_config_map(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_set_instr(tap, LSCC_RESET_ADDRESS, TAP_IDLE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
jtag_add_runtest(5, TAP_IDLE);
|
||||||
|
jtag_add_sleep(2000);
|
||||||
|
|
||||||
|
struct scan_field field;
|
||||||
|
retval = lattice_set_instr(tap, LSCC_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);
|
||||||
|
jtag_add_runtest(256, TAP_IDLE);
|
||||||
|
jtag_add_sleep(2000);
|
||||||
|
return jtag_execute_queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lattice_ecp2_3_exit_programming_mode(struct lattice_pld_device *lattice_device)
|
||||||
|
{
|
||||||
|
struct jtag_tap *tap = lattice_device->tap;
|
||||||
|
if (!tap)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
int 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 = lattice_set_instr(tap, BYPASS, TAP_IDLE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
jtag_add_runtest(100, TAP_IDLE);
|
||||||
|
jtag_add_sleep(1000);
|
||||||
|
return jtag_execute_queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
int lattice_ecp2_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;
|
||||||
|
|
||||||
|
/* Enable the programming mode */
|
||||||
|
retval = lattice_set_instr(tap, LSCC_REFRESH, TAP_IDLE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* Erase the device */
|
||||||
|
retval = lattice_ecp2_3_erase_device(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/* Program Fuse Map */
|
||||||
|
retval = lattice_ecp2_3_program_config_map(lattice_device, bit_file);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = lattice_ecp2_3_exit_programming_mode(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
const uint32_t out = REGISTER_ALL_BITS_1;
|
||||||
|
const uint32_t mask = STATUS_DONE_BIT | STATUS_ERROR_BITS_ECP2;
|
||||||
|
const uint32_t expected = STATUS_DONE_BIT;
|
||||||
|
return lattice_verify_status_register_u32(lattice_device, out, expected, mask, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lattice_ecp3_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;
|
||||||
|
|
||||||
|
/* Program Bscan register */
|
||||||
|
int retval = lattice_preload(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/* Enable the programming mode */
|
||||||
|
retval = lattice_set_instr(tap, LSCC_REFRESH, TAP_IDLE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
jtag_add_runtest(5, TAP_IDLE);
|
||||||
|
jtag_add_sleep(500000);
|
||||||
|
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_ecp2_3_erase_device(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/* Program Fuse Map */
|
||||||
|
retval = lattice_ecp2_3_program_config_map(lattice_device, bit_file);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = lattice_ecp2_3_exit_programming_mode(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
const uint32_t out = REGISTER_ALL_BITS_1;
|
||||||
|
const uint32_t mask = STATUS_DONE_BIT | STATUS_ERROR_BITS_ECP3;
|
||||||
|
const uint32_t expected = STATUS_DONE_BIT;
|
||||||
|
return lattice_verify_status_register_u32(lattice_device, out, expected, mask, false);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2022 by Daniel Anselmi *
|
||||||
|
* danselmi@gmx.ch *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef OPENOCD_PLD_ECP2_3_H
|
||||||
|
#define OPENOCD_PLD_ECP2_3_H
|
||||||
|
|
||||||
|
#include "lattice.h"
|
||||||
|
|
||||||
|
int lattice_ecp2_3_read_status(struct jtag_tap *tap, uint32_t *status, uint32_t out, bool do_idle);
|
||||||
|
int lattice_ecp2_3_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out);
|
||||||
|
int lattice_ecp2_3_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode);
|
||||||
|
int lattice_ecp2_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file);
|
||||||
|
int lattice_ecp3_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file);
|
||||||
|
|
||||||
|
#endif /* OPENOCD_PLD_ECP2_3_H */
|
|
@ -0,0 +1,435 @@
|
||||||
|
// 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 <jtag/jtag.h>
|
||||||
|
#include "pld.h"
|
||||||
|
#include "lattice_bit.h"
|
||||||
|
#include "ecp2_3.h"
|
||||||
|
|
||||||
|
#define PRELOAD 0x1C
|
||||||
|
|
||||||
|
struct lattice_devices_elem {
|
||||||
|
uint32_t id;
|
||||||
|
size_t preload_length;
|
||||||
|
enum lattice_family_e family;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct lattice_devices_elem lattice_devices[] = {
|
||||||
|
{0x01270043, 654, LATTICE_ECP2 /* ecp2-6e */},
|
||||||
|
{0x01271043, 643, LATTICE_ECP2 /* ecp2-12e */},
|
||||||
|
{0x01272043, 827, LATTICE_ECP2 /* ecp2-20e */},
|
||||||
|
{0x01274043, 1011, LATTICE_ECP2 /* ecp2-35e */},
|
||||||
|
{0x01273043, 1219, LATTICE_ECP2 /* ecp2-50e */},
|
||||||
|
{0x01275043, 654, LATTICE_ECP2 /* ecp2-70e */},
|
||||||
|
{0x01279043, 680, LATTICE_ECP2 /* ecp2m20e */},
|
||||||
|
{0x0127A043, 936, LATTICE_ECP2 /* ecp2m35e */},
|
||||||
|
{0x0127B043, 1056, LATTICE_ECP2 /* ecp2m50e */},
|
||||||
|
{0x0127C043, 1039, LATTICE_ECP2 /* ecp2m70e */},
|
||||||
|
{0x0127D043, 1311, LATTICE_ECP2 /* ecp2m100e */},
|
||||||
|
{0x01010043, 467, LATTICE_ECP3 /* ecp3 lae3-17ea & lfe3-17ea*/},
|
||||||
|
{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*/},
|
||||||
|
};
|
||||||
|
|
||||||
|
int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate)
|
||||||
|
{
|
||||||
|
struct scan_field field;
|
||||||
|
field.num_bits = tap->ir_length;
|
||||||
|
void *t = calloc(DIV_ROUND_UP(field.num_bits, 8), 1);
|
||||||
|
if (!t) {
|
||||||
|
LOG_ERROR("Out of memory");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
field.out_value = t;
|
||||||
|
buf_set_u32(t, 0, field.num_bits, new_instr);
|
||||||
|
field.in_value = NULL;
|
||||||
|
jtag_add_ir_scan(tap, &field, endstate);
|
||||||
|
free(t);
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lattice_check_device_family(struct lattice_pld_device *lattice_device)
|
||||||
|
{
|
||||||
|
if (lattice_device->family != LATTICE_UNKNOWN && lattice_device->preload_length != 0)
|
||||||
|
return ERROR_OK;
|
||||||
|
|
||||||
|
if (!lattice_device->tap || !lattice_device->tap->hasidcode)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(lattice_devices); ++i) {
|
||||||
|
if (lattice_devices[i].id == lattice_device->tap->idcode) {
|
||||||
|
if (lattice_device->family == LATTICE_UNKNOWN)
|
||||||
|
lattice_device->family = lattice_devices[i].family;
|
||||||
|
if (lattice_device->preload_length == 0)
|
||||||
|
lattice_device->preload_length = lattice_devices[i].preload_length;
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_ERROR("Unknown id! Specify family and preload-length manually.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lattice_read_u32_register(struct jtag_tap *tap, uint8_t cmd, uint32_t *in_val,
|
||||||
|
uint32_t out_val, bool do_idle)
|
||||||
|
{
|
||||||
|
struct scan_field field;
|
||||||
|
uint8_t buffer[4];
|
||||||
|
|
||||||
|
int retval = lattice_set_instr(tap, cmd, TAP_IDLE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
if (do_idle) {
|
||||||
|
jtag_add_runtest(2, TAP_IDLE);
|
||||||
|
jtag_add_sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
h_u32_to_le(buffer, out_val);
|
||||||
|
field.num_bits = 32;
|
||||||
|
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_u32(buffer);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lattice_preload(struct lattice_pld_device *lattice_device)
|
||||||
|
{
|
||||||
|
struct scan_field field;
|
||||||
|
size_t sz_bytes = DIV_ROUND_UP(lattice_device->preload_length, 8);
|
||||||
|
|
||||||
|
int retval = lattice_set_instr(lattice_device->tap, PRELOAD, TAP_IDLE);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
uint8_t *buffer = malloc(sz_bytes);
|
||||||
|
if (!buffer) {
|
||||||
|
LOG_ERROR("Out of memory");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
memset(buffer, 0xff, sz_bytes);
|
||||||
|
|
||||||
|
field.num_bits = lattice_device->preload_length;
|
||||||
|
field.out_value = buffer;
|
||||||
|
field.in_value = NULL;
|
||||||
|
jtag_add_dr_scan(lattice_device->tap, 1, &field, TAP_IDLE);
|
||||||
|
retval = jtag_execute_queue();
|
||||||
|
free(buffer);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lattice_read_usercode(struct lattice_pld_device *lattice_device, uint32_t *usercode, uint32_t out)
|
||||||
|
{
|
||||||
|
struct jtag_tap *tap = lattice_device->tap;
|
||||||
|
if (!tap)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3)
|
||||||
|
return lattice_ecp2_3_read_usercode(tap, usercode, out);
|
||||||
|
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lattice_verify_usercode(struct lattice_pld_device *lattice_device, uint32_t out,
|
||||||
|
uint32_t expected, uint32_t mask)
|
||||||
|
{
|
||||||
|
uint32_t usercode;
|
||||||
|
|
||||||
|
int retval = lattice_read_usercode(lattice_device, &usercode, out);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if ((usercode & mask) != expected) {
|
||||||
|
LOG_ERROR("verifying user code register failed got: 0x%08" PRIx32 " expected: 0x%08" PRIx32,
|
||||||
|
usercode & mask, expected);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lattice_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode)
|
||||||
|
{
|
||||||
|
if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3)
|
||||||
|
return lattice_ecp2_3_write_usercode(lattice_device, usercode);
|
||||||
|
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lattice_read_status_u32(struct lattice_pld_device *lattice_device, uint32_t *status,
|
||||||
|
uint32_t out, bool do_idle)
|
||||||
|
{
|
||||||
|
if (!lattice_device->tap)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3)
|
||||||
|
return lattice_ecp2_3_read_status(lattice_device->tap, status, out, do_idle);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if ((status & mask) != expected) {
|
||||||
|
LOG_ERROR("verifying status register failed got: 0x%08" PRIx32 " expected: 0x%08" PRIx32,
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!tap || !tap->hasidcode)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
int retval = lattice_check_device_family(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
struct lattice_bit_file bit_file;
|
||||||
|
retval = lattice_read_file(&bit_file, filename, lattice_device->family);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = ERROR_FAIL;
|
||||||
|
switch (lattice_device->family) {
|
||||||
|
case LATTICE_ECP2:
|
||||||
|
retval = lattice_ecp2_load(lattice_device, &bit_file);
|
||||||
|
break;
|
||||||
|
case LATTICE_ECP3:
|
||||||
|
retval = lattice_ecp3_load(lattice_device, &bit_file);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("loading unknown device family");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(bit_file.raw_bit.data);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
PLD_DEVICE_COMMAND_HANDLER(lattice_pld_device_command)
|
||||||
|
{
|
||||||
|
if (CMD_ARGC < 2 || CMD_ARGC > 3)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
struct jtag_tap *tap = jtag_tap_by_string(CMD_ARGV[1]);
|
||||||
|
if (!tap) {
|
||||||
|
command_print(CMD, "Tap: %s does not exist", CMD_ARGV[1]);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lattice_pld_device *lattice_device = malloc(sizeof(struct lattice_pld_device));
|
||||||
|
if (!lattice_device) {
|
||||||
|
LOG_ERROR("Out of memory");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
/* id is not known yet -> postpone lattice_check_device_family() */
|
||||||
|
enum lattice_family_e family = LATTICE_UNKNOWN;
|
||||||
|
if (CMD_ARGC == 3) {
|
||||||
|
if (strcasecmp(CMD_ARGV[2], "ecp2") == 0) {
|
||||||
|
family = LATTICE_ECP2;
|
||||||
|
} else if (strcasecmp(CMD_ARGV[2], "ecp3") == 0) {
|
||||||
|
family = LATTICE_ECP3;
|
||||||
|
} else {
|
||||||
|
command_print(CMD, "unknown family");
|
||||||
|
free(lattice_device);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lattice_device->tap = tap;
|
||||||
|
lattice_device->family = family;
|
||||||
|
lattice_device->preload_length = 0;
|
||||||
|
|
||||||
|
pld->driver_priv = lattice_device;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(lattice_read_usercode_register_command_handler)
|
||||||
|
{
|
||||||
|
int dev_id;
|
||||||
|
uint32_t usercode;
|
||||||
|
|
||||||
|
if (CMD_ARGC != 1)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], dev_id);
|
||||||
|
struct pld_device *device = get_pld_device_by_num(dev_id);
|
||||||
|
if (!device) {
|
||||||
|
command_print(CMD, "pld device '#%s' is out of bounds", CMD_ARGV[0]);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lattice_pld_device *lattice_device = device->driver_priv;
|
||||||
|
if (!lattice_device)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
int retval = lattice_check_device_family(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = lattice_read_usercode(lattice_device, &usercode, 0x0);
|
||||||
|
if (retval == ERROR_OK)
|
||||||
|
command_print(CMD, "0x%8.8" PRIx32, usercode);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(lattice_set_preload_command_handler)
|
||||||
|
{
|
||||||
|
int dev_id;
|
||||||
|
unsigned int preload_length;
|
||||||
|
|
||||||
|
if (CMD_ARGC != 2)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], dev_id);
|
||||||
|
struct pld_device *device = get_pld_device_by_num(dev_id);
|
||||||
|
if (!device) {
|
||||||
|
command_print(CMD, "pld device '#%s' is out of bounds", CMD_ARGV[0]);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], preload_length);
|
||||||
|
|
||||||
|
struct lattice_pld_device *lattice_device = device->driver_priv;
|
||||||
|
|
||||||
|
if (!lattice_device)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
lattice_device->preload_length = preload_length;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(lattice_write_usercode_register_command_handler)
|
||||||
|
{
|
||||||
|
int dev_id;
|
||||||
|
uint32_t usercode;
|
||||||
|
|
||||||
|
if (CMD_ARGC != 2)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], dev_id);
|
||||||
|
struct pld_device *device = get_pld_device_by_num(dev_id);
|
||||||
|
if (!device) {
|
||||||
|
command_print(CMD, "pld device '#%s' is out of bounds", CMD_ARGV[0]);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], usercode);
|
||||||
|
|
||||||
|
struct lattice_pld_device *lattice_device = device->driver_priv;
|
||||||
|
if (!lattice_device)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
int retval = lattice_check_device_family(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
return lattice_write_usercode(lattice_device, usercode);
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(lattice_read_status_command_handler)
|
||||||
|
{
|
||||||
|
int dev_id;
|
||||||
|
|
||||||
|
if (CMD_ARGC != 1)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], dev_id);
|
||||||
|
struct pld_device *device = get_pld_device_by_num(dev_id);
|
||||||
|
if (!device) {
|
||||||
|
command_print(CMD, "pld device '#%s' is out of bounds", CMD_ARGV[0]);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lattice_pld_device *lattice_device = device->driver_priv;
|
||||||
|
if (!lattice_device)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
int retval = lattice_check_device_family(lattice_device);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
uint32_t status;
|
||||||
|
retval = lattice_read_status_u32(lattice_device, &status, 0x0, false);
|
||||||
|
if (retval == ERROR_OK)
|
||||||
|
command_print(CMD, "0x%8.8" PRIx32, status);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct command_registration lattice_exec_command_handlers[] = {
|
||||||
|
{
|
||||||
|
.name = "read_status",
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.handler = lattice_read_status_command_handler,
|
||||||
|
.help = "reading status register from FPGA",
|
||||||
|
.usage = "num_pld",
|
||||||
|
}, {
|
||||||
|
.name = "read_user",
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.handler = lattice_read_usercode_register_command_handler,
|
||||||
|
.help = "reading usercode register from FPGA",
|
||||||
|
.usage = "num_pld",
|
||||||
|
}, {
|
||||||
|
.name = "write_user",
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.handler = lattice_write_usercode_register_command_handler,
|
||||||
|
.help = "writing usercode register to FPGA",
|
||||||
|
.usage = "num_pld value",
|
||||||
|
}, {
|
||||||
|
.name = "set_preload",
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.handler = lattice_set_preload_command_handler,
|
||||||
|
.help = "set length for preload (device specific)",
|
||||||
|
.usage = "num_pld value",
|
||||||
|
},
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct command_registration lattice_command_handler[] = {
|
||||||
|
{
|
||||||
|
.name = "lattice",
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "lattice specific commands",
|
||||||
|
.usage = "",
|
||||||
|
.chain = lattice_exec_command_handlers,
|
||||||
|
},
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pld_driver lattice_pld = {
|
||||||
|
.name = "lattice",
|
||||||
|
.commands = lattice_command_handler,
|
||||||
|
.pld_device_command = &lattice_pld_device_command,
|
||||||
|
.load = &lattice_load_command,
|
||||||
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2022 by Daniel Anselmi *
|
||||||
|
* danselmi@gmx.ch *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef OPENOCD_PLD_LATTICE_H
|
||||||
|
#define OPENOCD_PLD_LATTICE_H
|
||||||
|
|
||||||
|
#include <jtag/jtag.h>
|
||||||
|
#include "pld.h"
|
||||||
|
#include "lattice_bit.h"
|
||||||
|
|
||||||
|
#define BYPASS 0xFF
|
||||||
|
|
||||||
|
struct lattice_pld_device {
|
||||||
|
struct jtag_tap *tap;
|
||||||
|
size_t preload_length;
|
||||||
|
enum lattice_family_e family;
|
||||||
|
};
|
||||||
|
|
||||||
|
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_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_preload(struct lattice_pld_device *lattice_device);
|
||||||
|
|
||||||
|
#endif /* OPENOCD_PLD_LATTICE_H */
|
|
@ -0,0 +1,105 @@
|
||||||
|
// 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_bit.h"
|
||||||
|
#include "raw_bit.h"
|
||||||
|
#include "pld.h"
|
||||||
|
#include <helper/system.h>
|
||||||
|
#include <helper/log.h>
|
||||||
|
#include <helper/binarybuffer.h>
|
||||||
|
|
||||||
|
enum read_bit_state {
|
||||||
|
SEEK_HEADER_START,
|
||||||
|
SEEK_HEADER_END,
|
||||||
|
SEEK_PREAMBLE,
|
||||||
|
SEEK_ID,
|
||||||
|
DONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int lattice_read_bit_file(struct lattice_bit_file *bit_file, const char *filename, enum lattice_family_e family)
|
||||||
|
{
|
||||||
|
int retval = cpld_read_raw_bit_file(&bit_file->raw_bit, filename);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
bit_file->part = 0;
|
||||||
|
bit_file->has_id = false;
|
||||||
|
enum read_bit_state state = SEEK_HEADER_START;
|
||||||
|
for (size_t pos = 1; pos < bit_file->raw_bit.length && state != DONE; ++pos) {
|
||||||
|
switch (state) {
|
||||||
|
case SEEK_HEADER_START:
|
||||||
|
if (bit_file->raw_bit.data[pos] == 0 && bit_file->raw_bit.data[pos - 1] == 0xff)
|
||||||
|
state = SEEK_HEADER_END;
|
||||||
|
break;
|
||||||
|
case SEEK_HEADER_END:
|
||||||
|
if (pos + 6 < bit_file->raw_bit.length &&
|
||||||
|
strncmp((const char *)(bit_file->raw_bit.data + pos), "Part: ", 6) == 0) {
|
||||||
|
bit_file->part = (const char *)bit_file->raw_bit.data + pos + 6;
|
||||||
|
LOG_INFO("part found: %s\n", bit_file->part);
|
||||||
|
} else if (bit_file->raw_bit.data[pos] == 0xff && bit_file->raw_bit.data[pos - 1] == 0) {
|
||||||
|
bit_file->offset = pos;
|
||||||
|
state = (family != LATTICE_ECP2 && family != LATTICE_ECP3) ? SEEK_PREAMBLE : DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SEEK_PREAMBLE:
|
||||||
|
if (pos >= 4) {
|
||||||
|
uint32_t preamble = be_to_h_u32(bit_file->raw_bit.data + pos - 3);
|
||||||
|
switch (preamble) {
|
||||||
|
case 0xffffbdb3:
|
||||||
|
state = SEEK_ID;
|
||||||
|
break;
|
||||||
|
case 0xffffbfb3:
|
||||||
|
case 0xffffbeb3:
|
||||||
|
state = DONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SEEK_ID:
|
||||||
|
if (pos + 7 < bit_file->raw_bit.length && bit_file->raw_bit.data[pos] == 0xe2) {
|
||||||
|
bit_file->idcode = be_to_h_u32(&bit_file->raw_bit.data[pos + 4]);
|
||||||
|
bit_file->has_id = true;
|
||||||
|
state = DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != DONE) {
|
||||||
|
LOG_ERROR("parsing bitstream failed");
|
||||||
|
return ERROR_PLD_FILE_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = bit_file->offset; i < bit_file->raw_bit.length; i++)
|
||||||
|
bit_file->raw_bit.data[i] = flip_u32(bit_file->raw_bit.data[i], 8);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lattice_read_file(struct lattice_bit_file *bit_file, const char *filename, enum lattice_family_e family)
|
||||||
|
{
|
||||||
|
if (!filename || !bit_file)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
/* check if binary .bin or ascii .bit/.hex */
|
||||||
|
const char *file_suffix_pos = strrchr(filename, '.');
|
||||||
|
if (!file_suffix_pos) {
|
||||||
|
LOG_ERROR("Unable to detect filename suffix");
|
||||||
|
return ERROR_PLD_FILE_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcasecmp(file_suffix_pos, ".bit") == 0)
|
||||||
|
return lattice_read_bit_file(bit_file, filename, family);
|
||||||
|
|
||||||
|
LOG_ERROR("Filetype not supported");
|
||||||
|
return ERROR_PLD_FILE_LOAD_FAILED;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2022 by Daniel Anselmi *
|
||||||
|
* danselmi@gmx.ch *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef OPENOCD_PLD_LATTICE_BIT_H
|
||||||
|
#define OPENOCD_PLD_LATTICE_BIT_H
|
||||||
|
|
||||||
|
#include "helper/types.h"
|
||||||
|
#include "raw_bit.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct lattice_bit_file {
|
||||||
|
struct raw_bit_file raw_bit;
|
||||||
|
size_t offset;
|
||||||
|
uint32_t idcode;
|
||||||
|
const char *part; /* reuses memory in raw_bit_file */
|
||||||
|
bool has_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum lattice_family_e {
|
||||||
|
LATTICE_ECP2,
|
||||||
|
LATTICE_ECP3,
|
||||||
|
LATTICE_ECP5,
|
||||||
|
LATTICE_CERTUS,
|
||||||
|
LATTICE_UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
int lattice_read_file(struct lattice_bit_file *bit_file, const char *filename, enum lattice_family_e family);
|
||||||
|
|
||||||
|
#endif /* OPENOCD_PLD_LATTICE_BIT_H */
|
|
@ -18,9 +18,11 @@
|
||||||
|
|
||||||
/* pld drivers
|
/* pld drivers
|
||||||
*/
|
*/
|
||||||
|
extern struct pld_driver lattice_pld;
|
||||||
extern struct pld_driver virtex2_pld;
|
extern struct pld_driver virtex2_pld;
|
||||||
|
|
||||||
static struct pld_driver *pld_drivers[] = {
|
static struct pld_driver *pld_drivers[] = {
|
||||||
|
&lattice_pld,
|
||||||
&virtex2_pld,
|
&virtex2_pld,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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 "raw_bit.h"
|
||||||
|
#include "pld.h"
|
||||||
|
|
||||||
|
#include <helper/system.h>
|
||||||
|
#include <helper/log.h>
|
||||||
|
|
||||||
|
|
||||||
|
int cpld_read_raw_bit_file(struct raw_bit_file *bit_file, const char *filename)
|
||||||
|
{
|
||||||
|
FILE *input_file = fopen(filename, "rb");
|
||||||
|
|
||||||
|
if (!input_file) {
|
||||||
|
LOG_ERROR("Couldn't open %s: %s", filename, strerror(errno));
|
||||||
|
return ERROR_PLD_FILE_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(input_file, 0, SEEK_END);
|
||||||
|
long length = ftell(input_file);
|
||||||
|
fseek(input_file, 0, SEEK_SET);
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
fclose(input_file);
|
||||||
|
LOG_ERROR("Failed to get length of file %s: %s", filename, strerror(errno));
|
||||||
|
return ERROR_PLD_FILE_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
bit_file->length = (size_t)length;
|
||||||
|
|
||||||
|
bit_file->data = malloc(bit_file->length);
|
||||||
|
if (!bit_file->data) {
|
||||||
|
fclose(input_file);
|
||||||
|
LOG_ERROR("Out of memory");
|
||||||
|
return ERROR_PLD_FILE_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read_count = fread(bit_file->data, sizeof(char), bit_file->length, input_file);
|
||||||
|
fclose(input_file);
|
||||||
|
if (read_count != bit_file->length) {
|
||||||
|
free(bit_file->data);
|
||||||
|
bit_file->data = NULL;
|
||||||
|
return ERROR_PLD_FILE_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2022 by Daniel Anselmi *
|
||||||
|
* danselmi@gmx.ch *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef OPENOCD_PLD_RAW_BIN_H
|
||||||
|
#define OPENOCD_PLD_RAW_BIN_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct raw_bit_file {
|
||||||
|
size_t length;
|
||||||
|
uint8_t *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
int cpld_read_raw_bit_file(struct raw_bit_file *bit_file, const char *filename);
|
||||||
|
|
||||||
|
#endif /* OPENOCD_PLD_RAW_BIN_H */
|
|
@ -0,0 +1,31 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
if { [info exists CHIPNAME] } {
|
||||||
|
set _CHIPNAME $_CHIPNAME
|
||||||
|
} else {
|
||||||
|
set _CHIPNAME ecp2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Lattice ECP2 family
|
||||||
|
# TAP IDs are extracted from BSDL files found on this page:
|
||||||
|
# https://www.latticesemi.com/Products/FPGAandCPLD/LatticeECP2M
|
||||||
|
#
|
||||||
|
# LFE2M20E: 0x01279043
|
||||||
|
# LFE2M35E: 0x0127A043
|
||||||
|
# LFE2M50E: 0x0127B043
|
||||||
|
# LFE2M70E: 0x0127C043
|
||||||
|
# LFE2M100E: 0x0127D043
|
||||||
|
# LFEC2_6E: 0x01270043
|
||||||
|
# LFEC2_12E: 0x01271043
|
||||||
|
# LFEC2_20E: 0x01272043
|
||||||
|
# LFEC2_35E: 0x01274043
|
||||||
|
# LFEC2_50E: 0x01273043
|
||||||
|
# LFEC2_70E: 0x01275043
|
||||||
|
|
||||||
|
jtag newtap $_CHIPNAME tap -irlen 8 \
|
||||||
|
-expected-id 0x01279043 -expected-id 0x0127A043 -expected-id 0x0127B043 \
|
||||||
|
-expected-id 0x0127C043 -expected-id 0x0127D043 -expected-id 0x01270043 \
|
||||||
|
-expected-id 0x01271043 -expected-id 0x01272043 -expected-id 0x01274043 \
|
||||||
|
-expected-id 0x01273043 -expected-id 0x01275043
|
||||||
|
|
||||||
|
pld device lattice $_CHIPNAME.tap
|
|
@ -0,0 +1,22 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
if { [info exists CHIPNAME] } {
|
||||||
|
set _CHIPNAME $_CHIPNAME
|
||||||
|
} else {
|
||||||
|
set _CHIPNAME ecp3
|
||||||
|
}
|
||||||
|
|
||||||
|
# Lattice ECP3 family
|
||||||
|
# TAP IDs are extracted from BSDL files found on this page:
|
||||||
|
# https://www.latticesemi.com/Products/FPGAandCPLD/LatticeECP3
|
||||||
|
#
|
||||||
|
# LFE3_17: 0x01010043
|
||||||
|
# LFE3_35: 0x01012043
|
||||||
|
# LFE3_95: 0x01014043 and LFE3_70
|
||||||
|
# LFE3_150: 0x01015043
|
||||||
|
|
||||||
|
jtag newtap $_CHIPNAME tap -irlen 8 \
|
||||||
|
-expected-id 0x01010043 -expected-id 0x01012043 \
|
||||||
|
-expected-id 0x01014043 -expected-id 0x01015043
|
||||||
|
|
||||||
|
pld device lattice $_CHIPNAME.tap
|
Loading…
Reference in New Issue