// 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 #include "pld.h" #include "lattice_bit.h" #include "ecp2_3.h" #include "ecp5.h" #include "certus.h" #define PRELOAD 0x1C #define USER1 0x32 #define USER2 0x38 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*/}, {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" */}, {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) { 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_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; 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); 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; } 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); 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; } 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); else if (lattice_device->family == LATTICE_ECP5) return lattice_ecp5_read_status(lattice_device->tap, status, out, do_idle); 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; 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; } 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; 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; uint32_t id = tap->idcode; 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; 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); 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"); break; } free(bit_file.raw_bit.data); return retval; } static int lattice_get_ipdbg_hub(int user_num, struct pld_device *pld_device, struct pld_ipdbg_hub *hub) { if (!pld_device) return ERROR_FAIL; struct lattice_pld_device *pld_device_info = pld_device->driver_priv; if (!pld_device_info || !pld_device_info->tap) return ERROR_FAIL; hub->tap = pld_device_info->tap; if (user_num == 1) { hub->user_ir_code = USER1; } else if (user_num == 2) { hub->user_ir_code = USER2; } else { LOG_ERROR("lattice devices only have user register 1 & 2"); return ERROR_FAIL; } return ERROR_OK; } static int lattice_connect_spi_to_jtag(struct pld_device *pld_device) { if (!pld_device) return ERROR_FAIL; struct lattice_pld_device *pld_device_info = pld_device->driver_priv; int retval = lattice_check_device_family(pld_device_info); if (retval != ERROR_OK) return retval; if (pld_device_info->family == LATTICE_ECP2 || pld_device_info->family == LATTICE_ECP3) return lattice_ecp2_3_connect_spi_to_jtag(pld_device_info); else if (pld_device_info->family == LATTICE_ECP5) return lattice_ecp5_connect_spi_to_jtag(pld_device_info); else if (pld_device_info->family == LATTICE_CERTUS) return lattice_certus_connect_spi_to_jtag(pld_device_info); return ERROR_FAIL; } static int lattice_disconnect_spi_from_jtag(struct pld_device *pld_device) { if (!pld_device) return ERROR_FAIL; struct lattice_pld_device *pld_device_info = pld_device->driver_priv; int retval = lattice_check_device_family(pld_device_info); if (retval != ERROR_OK) return retval; if (pld_device_info->family == LATTICE_ECP2 || pld_device_info->family == LATTICE_ECP3) return lattice_ecp2_3_disconnect_spi_from_jtag(pld_device_info); else if (pld_device_info->family == LATTICE_ECP5) return lattice_ecp5_disconnect_spi_from_jtag(pld_device_info); else if (pld_device_info->family == LATTICE_CERTUS) return lattice_certus_disconnect_spi_from_jtag(pld_device_info); return ERROR_FAIL; } static int lattice_get_stuff_bits(struct pld_device *pld_device, unsigned int *facing_read_bits, unsigned int *trailing_write_bits) { if (!pld_device) return ERROR_FAIL; struct lattice_pld_device *pld_device_info = pld_device->driver_priv; int retval = lattice_check_device_family(pld_device_info); if (retval != ERROR_OK) return retval; if (pld_device_info->family == LATTICE_ECP2 || pld_device_info->family == LATTICE_ECP3) return lattice_ecp2_3_get_facing_read_bits(pld_device_info, facing_read_bits); else if (pld_device_info->family == LATTICE_ECP5) return lattice_ecp5_get_facing_read_bits(pld_device_info, facing_read_bits); else if (pld_device_info->family == LATTICE_CERTUS) return lattice_certus_get_facing_read_bits(pld_device_info, facing_read_bits); return ERROR_FAIL; } static int lattice_has_jtagspi_instruction(struct pld_device *device, bool *has_instruction) { *has_instruction = true; return ERROR_OK; } PLD_CREATE_COMMAND_HANDLER(lattice_pld_create_command) { if (CMD_ARGC != 4 && CMD_ARGC != 6) return ERROR_COMMAND_SYNTAX_ERROR; if (strcmp(CMD_ARGV[2], "-chain-position") != 0) return ERROR_COMMAND_SYNTAX_ERROR; struct jtag_tap *tap = jtag_tap_by_string(CMD_ARGV[3]); if (!tap) { command_print(CMD, "Tap: %s does not exist", CMD_ARGV[3]); return ERROR_FAIL; } /* id is not known yet -> postpone lattice_check_device_family() */ enum lattice_family_e family = LATTICE_UNKNOWN; if (CMD_ARGC == 6) { if (strcmp(CMD_ARGV[4], "-family") != 0) return ERROR_COMMAND_SYNTAX_ERROR; if (strcasecmp(CMD_ARGV[5], "ecp2") == 0) { family = LATTICE_ECP2; } else if (strcasecmp(CMD_ARGV[5], "ecp3") == 0) { family = LATTICE_ECP3; } else if (strcasecmp(CMD_ARGV[5], "ecp5") == 0) { family = LATTICE_ECP5; } else if (strcasecmp(CMD_ARGV[5], "certus") == 0) { family = LATTICE_CERTUS; } else { command_print(CMD, "unknown family"); 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; } 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) { uint32_t usercode; if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; struct pld_device *device = get_pld_device_by_name_or_numstr(CMD_ARGV[0]); if (!device) { command_print(CMD, "pld device '#%s' is out of bounds or unknown", 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) { unsigned int preload_length; if (CMD_ARGC != 2) return ERROR_COMMAND_SYNTAX_ERROR; struct pld_device *device = get_pld_device_by_name_or_numstr(CMD_ARGV[0]); if (!device) { command_print(CMD, "pld device '#%s' is out of bounds or unknown", 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) { uint32_t usercode; if (CMD_ARGC != 2) return ERROR_COMMAND_SYNTAX_ERROR; struct pld_device *device = get_pld_device_by_name_or_numstr(CMD_ARGV[0]); if (!device) { command_print(CMD, "pld device '#%s' is out of bounds or unknown", 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) { if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; struct pld_device *device = get_pld_device_by_name_or_numstr(CMD_ARGV[0]); if (!device) { command_print(CMD, "pld device '#%s' is out of bounds or unknown", 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; 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; } COMMAND_HANDLER(lattice_refresh_command_handler) { if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; struct pld_device *device = get_pld_device_by_name_or_numstr(CMD_ARGV[0]); if (!device) { command_print(CMD, "pld device '#%s' is out of bounds or unknown", 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; if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3) return lattice_ecp2_3_refresh(lattice_device); else if (lattice_device->family == LATTICE_ECP5) return lattice_ecp5_refresh(lattice_device); else if (lattice_device->family == LATTICE_CERTUS) return lattice_certus_refresh(lattice_device); return ERROR_FAIL; } 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 = "pld_name", }, { .name = "read_user", .mode = COMMAND_EXEC, .handler = lattice_read_usercode_register_command_handler, .help = "reading usercode register from FPGA", .usage = "pld_name", }, { .name = "write_user", .mode = COMMAND_EXEC, .handler = lattice_write_usercode_register_command_handler, .help = "writing usercode register to FPGA", .usage = "pld_name value", }, { .name = "set_preload", .mode = COMMAND_ANY, .handler = lattice_set_preload_command_handler, .help = "set length for preload (device specific)", .usage = "pld_name value", }, { .name = "refresh", .mode = COMMAND_EXEC, .handler = lattice_refresh_command_handler, .help = "refresh from configuration memory", .usage = "pld_name", }, 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_create_command = &lattice_pld_create_command, .load = &lattice_load_command, .get_ipdbg_hub = lattice_get_ipdbg_hub, .has_jtagspi_instruction = lattice_has_jtagspi_instruction, .connect_spi_to_jtag = lattice_connect_spi_to_jtag, .disconnect_spi_from_jtag = lattice_disconnect_spi_from_jtag, .get_stuff_bits = lattice_get_stuff_bits, };