You can subscribe to this list here.
| 2001 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(57) |
Oct
|
Nov
|
Dec
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2006 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(14) |
Nov
(36) |
Dec
(7) |
| 2007 |
Jan
(48) |
Feb
(10) |
Mar
(17) |
Apr
(8) |
May
(35) |
Jun
(28) |
Jul
(50) |
Aug
(71) |
Sep
(40) |
Oct
(19) |
Nov
(22) |
Dec
(143) |
| 2008 |
Jan
(184) |
Feb
(549) |
Mar
(381) |
Apr
(388) |
May
(148) |
Jun
(128) |
Jul
(502) |
Aug
(243) |
Sep
(136) |
Oct
(327) |
Nov
(252) |
Dec
(475) |
| 2009 |
Jan
(344) |
Feb
(185) |
Mar
(338) |
Apr
(826) |
May
(1559) |
Jun
(1429) |
Jul
(817) |
Aug
(451) |
Sep
(639) |
Oct
(935) |
Nov
(1222) |
Dec
(826) |
| 2010 |
Jan
(552) |
Feb
(532) |
Mar
(355) |
Apr
(206) |
May
(162) |
Jun
(203) |
Jul
(168) |
Aug
(232) |
Sep
(270) |
Oct
(259) |
Nov
(439) |
Dec
(468) |
| 2011 |
Jan
(224) |
Feb
(249) |
Mar
(278) |
Apr
(381) |
May
(316) |
Jun
(637) |
Jul
(544) |
Aug
(465) |
Sep
(159) |
Oct
(440) |
Nov
(139) |
Dec
|
| 2012 |
Jan
(204) |
Feb
(383) |
Mar
(295) |
Apr
(196) |
May
(590) |
Jun
(158) |
Jul
(167) |
Aug
(177) |
Sep
(179) |
Oct
(301) |
Nov
(144) |
Dec
(173) |
| 2013 |
Jan
(299) |
Feb
(120) |
Mar
(238) |
Apr
(140) |
May
(69) |
Jun
(133) |
Jul
(160) |
Aug
(107) |
Sep
(164) |
Oct
(196) |
Nov
(105) |
Dec
(74) |
| 2014 |
Jan
(205) |
Feb
(156) |
Mar
(175) |
Apr
(181) |
May
(162) |
Jun
(158) |
Jul
(117) |
Aug
(109) |
Sep
(148) |
Oct
(106) |
Nov
(82) |
Dec
(72) |
| 2015 |
Jan
(191) |
Feb
(205) |
Mar
(197) |
Apr
(163) |
May
(136) |
Jun
(36) |
Jul
(79) |
Aug
(55) |
Sep
(64) |
Oct
(146) |
Nov
(142) |
Dec
(78) |
| 2016 |
Jan
(65) |
Feb
(190) |
Mar
(53) |
Apr
(38) |
May
(95) |
Jun
(53) |
Jul
(58) |
Aug
(113) |
Sep
(96) |
Oct
(59) |
Nov
(136) |
Dec
(124) |
| 2017 |
Jan
(80) |
Feb
(109) |
Mar
(163) |
Apr
(78) |
May
(61) |
Jun
(73) |
Jul
(29) |
Aug
(47) |
Sep
(60) |
Oct
(76) |
Nov
(48) |
Dec
(35) |
| 2018 |
Jan
(138) |
Feb
(84) |
Mar
(109) |
Apr
(49) |
May
(24) |
Jun
(62) |
Jul
(96) |
Aug
(116) |
Sep
(53) |
Oct
(99) |
Nov
(80) |
Dec
(88) |
| 2019 |
Jan
(100) |
Feb
(141) |
Mar
(72) |
Apr
(174) |
May
(129) |
Jun
(102) |
Jul
(52) |
Aug
(45) |
Sep
(28) |
Oct
(43) |
Nov
(78) |
Dec
(47) |
| 2020 |
Jan
(113) |
Feb
(72) |
Mar
(94) |
Apr
(141) |
May
(82) |
Jun
(68) |
Jul
(125) |
Aug
(76) |
Sep
(33) |
Oct
(184) |
Nov
(61) |
Dec
(95) |
| 2021 |
Jan
(109) |
Feb
(77) |
Mar
(145) |
Apr
(116) |
May
(134) |
Jun
(113) |
Jul
(71) |
Aug
(118) |
Sep
(116) |
Oct
(92) |
Nov
(124) |
Dec
(68) |
| 2022 |
Jan
(57) |
Feb
(61) |
Mar
(57) |
Apr
(74) |
May
(86) |
Jun
(80) |
Jul
(43) |
Aug
(85) |
Sep
(120) |
Oct
(88) |
Nov
(100) |
Dec
(108) |
| 2023 |
Jan
(39) |
Feb
(56) |
Mar
(92) |
Apr
(81) |
May
(84) |
Jun
(72) |
Jul
(182) |
Aug
(82) |
Sep
(54) |
Oct
(68) |
Nov
(67) |
Dec
(75) |
| 2024 |
Jan
(79) |
Feb
(65) |
Mar
(42) |
Apr
(47) |
May
(68) |
Jun
(111) |
Jul
(43) |
Aug
(73) |
Sep
(100) |
Oct
(35) |
Nov
(100) |
Dec
(99) |
| 2025 |
Jan
(71) |
Feb
(68) |
Mar
(44) |
Apr
(40) |
May
(92) |
Jun
(45) |
Jul
(86) |
Aug
(60) |
Sep
(76) |
Oct
(69) |
Nov
(10) |
Dec
|
|
From: <ge...@op...> - 2025-10-14 13:58:02
|
This is an automated email from Gerrit. "Tomas Vanek <va...@fb...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9168 -- gerrit commit a5b21ec611d727f6b6bdc526a0c5ef392c6f652d Author: Tomas Vanek <va...@fb...> Date: Mon Oct 13 18:35:37 2025 +0200 target/esirisc_trace: rename macro conflicting with bits.h The esirisc_trace.c uses macro BIT_MASK(), same name as a macro from helper/bits.h Rename the former to allow including bits.h helper in target.h Change-Id: I0cc6a58e5aff3f48fa9a79a99bd28124f334c4e2 Signed-off-by: Tomas Vanek <va...@fb...> diff --git a/src/target/esirisc_trace.c b/src/target/esirisc_trace.c index 2dc08e5d2d..565511471f 100644 --- a/src/target/esirisc_trace.c +++ b/src/target/esirisc_trace.c @@ -18,7 +18,7 @@ #include "esirisc.h" -#define BIT_MASK(x) ((1 << (x)) - 1) +#define BITS_MASK(x) ((1 << (x)) - 1) /* Control Fields */ #define CONTROL_ST (1<<0) /* Start */ @@ -483,7 +483,7 @@ static int esirisc_trace_analyze_simple(struct command_invocation *cmd, uint8_t struct target *target = get_current_target(cmd->ctx); struct esirisc_common *esirisc = target_to_esirisc(target); struct esirisc_trace *trace_info = &esirisc->trace_info; - const uint32_t end_of_trace = BIT_MASK(trace_info->pc_bits) << 1; + const uint32_t end_of_trace = BITS_MASK(trace_info->pc_bits) << 1; const uint32_t num_bits = size * 8; int retval; -- |
|
From: <ge...@op...> - 2025-10-14 13:58:02
|
This is an automated email from Gerrit. "Tomas Vanek <va...@fb...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9171 -- gerrit commit db04cb87afc20d5f18be2cd071652af027c0d84b Author: Tomas Vanek <va...@fb...> Date: Tue Oct 14 15:29:55 2025 +0200 target/riscv: return ERROR_TARGET_NOT_HALTED instead of ERROR_FAIL where appropriate. Change-Id: I1881c0c6c437355007c3844556489162666023dc Signed-off-by: Tomas Vanek <va...@fb...> diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index f2713bf096..2c55334947 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -2344,7 +2344,7 @@ static int prep_for_vector_access(struct target *target, if (target->state != TARGET_HALTED) { LOG_TARGET_ERROR(target, "Unable to access vector register: target not halted"); - return ERROR_FAIL; + return ERROR_TARGET_NOT_HALTED; } if (prep_for_register_access(target, orig_mstatus, GDB_REGNO_VL) != ERROR_OK) return ERROR_FAIL; @@ -5498,7 +5498,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target, { if (target->state != TARGET_HALTED) { LOG_TARGET_ERROR(target, "Hart is not halted!"); - return ERROR_FAIL; + return ERROR_TARGET_NOT_HALTED; } LOG_TARGET_DEBUG(target, "resuming (operation=%s)", diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index f7e0b4221d..d7d5b488bb 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -5425,7 +5425,7 @@ COMMAND_HANDLER(riscv_exec_progbuf) if (target->state != TARGET_HALTED) { LOG_TARGET_ERROR(target, "exec_progbuf: Can't execute " "program buffer, target not halted."); - return ERROR_FAIL; + return ERROR_TARGET_NOT_HALTED; } if (riscv_progbuf_size(target) == 0) { @@ -6050,7 +6050,7 @@ static int riscv_step_rtos_hart(struct target *target) if (target->state != TARGET_HALTED) { LOG_TARGET_ERROR(target, "Hart isn't halted before single step!"); - return ERROR_FAIL; + return ERROR_TARGET_NOT_HALTED; } r->on_step(target); if (r->step_current_hart(target) != ERROR_OK) @@ -6240,7 +6240,7 @@ int riscv_enumerate_triggers(struct target *target) if (target->state != TARGET_HALTED) { LOG_TARGET_ERROR(target, "Unable to enumerate triggers: target not halted."); - return ERROR_FAIL; + return ERROR_TARGET_NOT_HALTED; } riscv_reg_t orig_tselect; -- |
|
From: <ge...@op...> - 2025-10-14 13:57:57
|
This is an automated email from Gerrit. "Tomas Vanek <va...@fb...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9169 -- gerrit commit 01b6b95787321440f5a9632cded04ba0535f5290 Author: Tomas Vanek <va...@fb...> Date: Mon Oct 13 15:39:05 2025 +0200 target: introduce target capability bits Add target_get_capab() call to identify which optional capabilities the target supports. For the beginning define bits for memory read/write while running and HW breakpoint/watchpoint manipulation at any state. Add simple usage in cortex_m.c Change-Id: I1a28f3707dfff9a2424090bb125990c7ea9a0387 Signed-off-by: Tomas Vanek <va...@fb...> diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index 42e9572602..b172d1bcd4 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -3101,6 +3101,13 @@ static int cortex_m_target_create(struct target *target) return ERROR_OK; } +static unsigned int cortex_m_get_capab(struct target *target) +{ + return TARGET_CAPAB_MEM_READ_WHILE_RUNNING + | TARGET_CAPAB_MEM_WRITE_WHILE_RUNNING + | TARGET_CAPAB_BP_WP_MANIP_AT_ANY_STATE; +} + /*--------------------------------------------------------------------------*/ static int cortex_m_verify_pointer(struct command_invocation *cmd, @@ -3405,4 +3412,6 @@ struct target_type cortexm_target = { .deinit_target = cortex_m_deinit_target, .profiling = cortex_m_profiling, + + .get_capab = cortex_m_get_capab, }; diff --git a/src/target/target.c b/src/target/target.c index bdf0ff244d..5f1c85c3a5 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -1474,6 +1474,13 @@ unsigned int target_data_bits(struct target *target) return 32; } +unsigned int target_get_capab(struct target *target) +{ + if (target->type->get_capab) + return target->type->get_capab(target); + return 0; +} + static int target_profiling(struct target *target, uint32_t *samples, uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds) { diff --git a/src/target/target.h b/src/target/target.h index 6efcc7677b..b1fc0001d8 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -20,6 +20,7 @@ #ifndef OPENOCD_TARGET_TARGET_H #define OPENOCD_TARGET_TARGET_H +#include <helper/bits.h> #include <helper/list.h> #include "helper/replacements.h" #include "helper/system.h" @@ -85,6 +86,10 @@ enum target_endianness { TARGET_BIG_ENDIAN = 1, TARGET_LITTLE_ENDIAN = 2 }; +#define TARGET_CAPAB_MEM_READ_WHILE_RUNNING BIT(0) +#define TARGET_CAPAB_MEM_WRITE_WHILE_RUNNING BIT(1) +#define TARGET_CAPAB_BP_WP_MANIP_AT_ANY_STATE BIT(2) + struct working_area { target_addr_t address; uint32_t size; @@ -690,6 +695,13 @@ unsigned int target_address_bits(struct target *target); */ unsigned int target_data_bits(struct target *target); +/** + * Return target capabilities as TARGET_CAPAB_ bits. + * + * This routine is a wrapper for target->type->get_capab + */ +unsigned int target_get_capab(struct target *target); + /** Return the *name* of this targets current state */ const char *target_state_name(const struct target *target); diff --git a/src/target/target_type.h b/src/target/target_type.h index ccbe03a476..6b1443c6b7 100644 --- a/src/target/target_type.h +++ b/src/target/target_type.h @@ -305,6 +305,10 @@ struct target_type { * will typically be 32 for 32-bit targets, and 64 for 64-bit targets. If * not implemented, it's assumed to be 32. */ unsigned int (*data_bits)(struct target *target); + + /* Return target capabilities as TARGET_CAPAB_ bits. + * Assume zero return (no optional capabilities) if not implemented */ + unsigned int (*get_capab)(struct target *target); }; // Keep in alphabetic order this list of targets -- |
|
From: <ge...@op...> - 2025-10-13 14:09:32
|
This is an automated email from Gerrit. "Name of user not set <din...@mi...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9025 -- gerrit commit 04da9013f3457813bb2c1e33a719edb295a26015 Author: DineshArasu-Microchip <din...@mi...> Date: Mon Oct 13 19:32:51 2025 +0530 Adding WBZ451, WBZ450, PIC32WM_BZ and WBZ351 microchip flash drivers support Change-Id: I491341d2c2d50d4976ea454a4f7e477033db92eb Signed-off-by: Dinesh Arasu <din...@mi...> Signed-off-by: DineshArasu-Microchip <din...@mi...> diff --git a/doc/openocd.texi b/doc/openocd.texi index 7f7c8892fe..b3493b5795 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -7064,6 +7064,32 @@ the appropriate at91sam7 target. @end deffn @end deffn +@anchor{pic32cx_bz} +@deffn {Flash Driver} {pic32cx_bz} +@cindex wbz451 +All members of the WBZ45x, PIC32CX_BZ2, PIC32WM_BZ6 microcontroller +families from Microchip include internal flash and use ARM's Cortex-M4 core. + +This driver supports: +@itemize +@item Row programming (1024 bytes per row) +@item Page erase (1024 bytes per row) +@item Skipping @code{0xFF}-only regions for faster flashing +@end itemize + +The driver is located at: @file{src/flash/nor/pic32cx_bz.c}. + +@example +flash bank $_FLASHNAME pic32cx_bz 0x01000000 0x00100000 0 0 $_TARGETNAME +@end example + +@deffn {Command} {pic32cx_bz dsu_reset_deassert} +This command releases internal reset held by DSU +and prepares reset vector catch in case of reset halt. +Command is used internally in event reset-deassert-post. +@end deffn +@end deffn + @deffn {Flash Driver} {avr} The AVR 8-bit microcontrollers from Atmel integrate flash memory. @emph{The current implementation is incomplete.} diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index f408559004..f64df62ff9 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -83,7 +83,8 @@ NOR_DRIVERS = \ %D%/w600.c \ %D%/xcf.c \ %D%/xmc1xxx.c \ - %D%/xmc4xxx.c + %D%/xmc4xxx.c \ + %D%/pic32cx_bz.c NORHEADERS = \ %D%/artery.h \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 2bd2043633..bd467c4e5e 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -313,5 +313,6 @@ extern const struct flash_driver w600_flash; extern const struct flash_driver xcf_flash; extern const struct flash_driver xmc1xxx_flash; extern const struct flash_driver xmc4xxx_flash; +extern const struct flash_driver pic32cx_bz_flash; #endif /* OPENOCD_FLASH_NOR_DRIVER_H */ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 6b0def6810..d4e3068e8a 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -92,6 +92,7 @@ static const struct flash_driver * const flash_drivers[] = { &xcf_flash, &xmc1xxx_flash, &xmc4xxx_flash, + &pic32cx_bz_flash, }; const struct flash_driver *flash_driver_find_by_name(const char *name) diff --git a/src/flash/nor/pic32cx_bz.c b/src/flash/nor/pic32cx_bz.c new file mode 100644 index 0000000000..a1748f3bb2 --- /dev/null +++ b/src/flash/nor/pic32cx_bz.c @@ -0,0 +1,570 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/************************************************************************************** + * Copyright (C) 2025 by Microchip Technologies Inc * + * Author: Dinesh Arasu - din...@mi... * + * * + * Description: Flash driver for WBZ and PIC32CX_BZx Microchip Curiosity Board * + **************************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "helper/binarybuffer.h" +#include <helper/time_support.h> +#include <jtag/jtag.h> +#include <target/cortex_m.h> +#include <stdbool.h> +#include <string.h> +#include <target/target_type.h> +#include <stdlib.h> +#include <inttypes.h> + +#define DSU_DID_REG 0x41000018U + +/* Generic NVM operation bits */ +#define NVMCON_NVMWREN (1 << 14) +#define NVMCON_NVMWR (1 << 15) +#define NVMCON_OP_WORD_PROG 0x4001 +#define NVMCON_OP_ROW_PROG 0x4003 +#define NVMCON_OP_PAGE_ERASE 0x4004 +#define NVMCON_OP_PBC 0x4007 +#define NVMCON_OP_MASK 0x7FFF + +/* RAM staging buffer address used across drivers */ +#define RAM_BUF_ADDR 0x20000000U + +/* default row size used by these parts */ +#define DEFAULT_ROW_SIZE 1024U + +/* NVMLBWP unlock key used in originals */ +#define NVMLBWP_UNLOCK_KEY 0x80000000U + +struct pic32cx_dev { + uint32_t device_id; + const char *name; + uint32_t flash_base; + uint32_t flash_size; + uint32_t row_size; + uint32_t page_size; + uint32_t pac_base; + uint32_t nvmcon; + uint32_t nvmkey; + uint32_t nvmaddr; + uint32_t nvmsrcaddr; + uint32_t nvmdata; + uint32_t nvmconset; + uint32_t nvmconclr; + uint32_t nvmlbwp; + uint32_t nvm_param; + uint32_t nvm_err_mask; +}; + +static const struct pic32cx_dev device_table[] = { + { + .device_id = 0x00009B8F, /* WBZ451 */ + .name = "WBZ451", + .flash_base = 0x01000000U, + .flash_size = 0x00100000U, /* 1 MB */ + .row_size = DEFAULT_ROW_SIZE, + .page_size = DEFAULT_ROW_SIZE, + .pac_base = 0x40000000U, + .nvmcon = 0x44000600U, + .nvmkey = 0x44000620U, + .nvmaddr = 0x44000630U, + .nvmsrcaddr = 0x440006C0U, + .nvmdata = 0x44000640U, + .nvmconset = 0x44000608U, + .nvmconclr = 0x44000604U, + .nvmlbwp = 0x440006F0U, + .nvm_param = 0x44000610U, + .nvm_err_mask = 0x3F00U + }, + { + .device_id = 0x00009B0B, /* WBZ450 */ + .name = "WBZ450", + .flash_base = 0x01000000U, + .flash_size = 0x00100000U, /* 1 MB */ + .row_size = DEFAULT_ROW_SIZE, + .page_size = DEFAULT_ROW_SIZE, + .pac_base = 0x40000000U, + .nvmcon = 0x44000600U, + .nvmkey = 0x44000620U, + .nvmaddr = 0x44000630U, + .nvmsrcaddr = 0x440006C0U, + .nvmdata = 0x44000640U, + .nvmconset = 0x44000608U, + .nvmconclr = 0x44000604U, + .nvmlbwp = 0x440006F0U, + .nvm_param = 0x44000610U, + .nvm_err_mask = 0x3F00U + }, + { + .device_id = 0x00009E03, /* WBZ351 */ + .name = "WBZ351", + .flash_base = 0x01000000U, + .flash_size = 0x00080000U, /* 512 KB */ + .row_size = DEFAULT_ROW_SIZE, + .page_size = DEFAULT_ROW_SIZE, + .pac_base = 0x40000000U, + .nvmcon = 0x44000600U, + .nvmkey = 0x44000620U, + .nvmaddr = 0x44000630U, + .nvmsrcaddr = 0x440006C0U, + .nvmdata = 0x44000640U, + .nvmconset = 0x44000608U, + .nvmconclr = 0x44000604U, + .nvmlbwp = 0x440006F0U, + .nvm_param = 0x44000610U, + .nvm_err_mask = 0x3F00U + }, + { + .device_id = 0x0001A800, /* PIC32WM_BZ6204 */ + .name = "PIC32WM", + .flash_base = 0x01000000U, + .flash_size = 0x00200000U, /* 2 MB */ + .row_size = DEFAULT_ROW_SIZE, + .page_size = DEFAULT_ROW_SIZE, + .pac_base = 0x40000000U, + .nvmcon = 0x44000600U, + .nvmkey = 0x44000620U, + .nvmaddr = 0x44000630U, + .nvmsrcaddr = 0x440006C0U, + .nvmdata = 0x44000640U, + .nvmconset = 0x44000608U, + .nvmconclr = 0x44000604U, + .nvmlbwp = 0x440006F0U, + .nvm_param = 0x44000610U, + .nvm_err_mask = 0x3F00U + } +}; + +struct pic32cx_priv { + struct target *target; + bool probed; + const struct pic32cx_dev *dev; + uint32_t page_size; + uint32_t num_pages; +}; + +static const struct pic32cx_dev *find_device_by_did(uint32_t did) +{ + for (size_t i = 0; i < ARRAY_SIZE(device_table); ++i) + if (device_table[i].device_id != 0 && device_table[i].device_id == did) + return &device_table[i]; + return NULL; +} + +static const struct pic32cx_dev *find_device_by_name(const char *name) +{ + if (!name) return NULL; + for (size_t i = 0; i < ARRAY_SIZE(device_table); ++i) + if (strstr(name, device_table[i].name) != NULL) + return &device_table[i]; + return NULL; +} + +/* unlock via PAC if locked (same as originals) */ +static int pic32cx_unlock_flash(const struct pic32cx_dev *dev, struct target *t) +{ + uint32_t status; + int res = target_read_u32(t, dev->pac_base + 0x18U, &status); + if (res != ERROR_OK) return res; + + if (status & (1 << 1)) { + LOG_INFO("PAC indicates NVMCTRL is locked. Attempting to unlock..."); + res = target_write_u32(t, dev->pac_base + 0x20U, (1 << 1)); + if (res != ERROR_OK) return res; + } + return target_read_u32(t, dev->pac_base + 0x18U, &status); +} + +/* Generic NVM command issuer derived from wbz451_issue_nvmcmd */ +static int pic32cx_issue_nvmcmd(const struct pic32cx_dev *dev, struct target *t, + uint32_t flash_addr, uint16_t cmd, uint32_t src_addr) +{ + int res; + + /* For boot/alias region operations we must write NVMLBWP */ + if (flash_addr < dev->flash_base) { + if (dev->nvmlbwp) { + res = target_write_u32(t, dev->nvmlbwp, NVMLBWP_UNLOCK_KEY); + if (res != ERROR_OK) return res; + } + } else { + res = pic32cx_unlock_flash(dev, t); + if (res != ERROR_OK) return res; + } + + /* Clear previous error flags */ + if (dev->nvmconclr) { + res = target_write_u32(t, dev->nvmconclr, dev->nvm_err_mask); + if (res != ERROR_OK) return res; + } + + /* Align dest to row */ + flash_addr &= ~(dev->row_size - 1U); + + /* Set NVMSRCADDR if ROW_PROGRAM */ + if (cmd == NVMCON_OP_ROW_PROG && dev->nvmsrcaddr) { + res = target_write_u32(t, dev->nvmsrcaddr, src_addr); + if (res != ERROR_OK) return res; + } + + /* Set NVMADDR */ + res = target_write_u32(t, dev->nvmaddr, flash_addr); + if (res != ERROR_OK) return res; + + /* Set WREN and operation */ + res = target_write_u32(t, dev->nvmcon, NVMCON_NVMWREN | (cmd & NVMCON_OP_MASK)); + if (res != ERROR_OK) return res; + + /* NVMKEY sequence */ + res = target_write_u32(t, dev->nvmkey, 0x00000000U); if (res != ERROR_OK) return res; + res = target_write_u32(t, dev->nvmkey, 0xAA996655U); if (res != ERROR_OK) return res; + res = target_write_u32(t, dev->nvmkey, 0x556699AAU); if (res != ERROR_OK) return res; + + /* Start operation */ + res = target_write_u32(t, dev->nvmconset, NVMCON_NVMWR); + if (res != ERROR_OK) return res; + + /* Wait for NVMWR to clear */ + uint32_t val; + int timeout = 10000; + do { + res = target_read_u32(t, dev->nvmcon, &val); + if (res != ERROR_OK) return res; + if (!--timeout) { + LOG_ERROR("Timeout waiting for NVMWR clear (addr: 0x%08" PRIx32 ", cmd: 0x%X)", flash_addr, cmd); + return ERROR_FAIL; + } + alive_sleep(1); + } while (val & NVMCON_NVMWR); + + if (val & dev->nvm_err_mask) { + LOG_ERROR("NVM error detected (NVMCON=0x%08" PRIx32 ")", val); + return ERROR_FAIL; + } + + /* Clear NVMWREN */ + return target_write_u32(t, dev->nvmconclr, NVMCON_NVMWREN); +} + +/* Probe: uses per-device probe logic (keeps original region checks) */ +static int pic32cx_probe(struct flash_bank *bank) +{ + struct pic32cx_priv *priv = bank->driver_priv; + if (!priv) return ERROR_FAIL; + if (priv->probed) return ERROR_OK; + + struct target *t = bank->target; + uint32_t did = 0; + int res = target_read_u32(t, DSU_DID_REG, &did); + + const struct pic32cx_dev *dev = NULL; + if (res == ERROR_OK) { + dev = find_device_by_did(did); + if (dev) + LOG_INFO("Detected device by DSU DID: %s (DID 0x%08" PRIx32 ")", dev->name, did); + } else { + LOG_WARNING("Failed to read DSU DID at 0x%08" PRIx32 ". Will try name-based detection.", DSU_DID_REG); + } + + /* name fallback */ + if (!dev) + dev = find_device_by_name(bank->name); + + if (!dev) { + LOG_WARNING("No device matched by DID or name. Falling back to WBZ451 defaults."); + dev = &device_table[0]; + } + + priv->dev = dev; + priv->target = t; + + /* Use device-specific probe ranges — reuse logic from original files */ + uint32_t base = bank->base; + uint32_t param = 0; + if (dev->nvm_param) + target_read_u32(t, dev->nvm_param, ¶m); + + /* Apply device-specific mapping (copied/adapted from originals) */ + if ((strcmp(dev->name, "WBZ451") == 0) || (strcmp(dev->name, "WBZ450") == 0)) { + if (base < 0x00005000U) { + priv->page_size = dev->row_size; + priv->num_pages = 20; /* 20 kB BootFlash */ + } else if (base >= 0x00005000U && base < 0x00006000U) { + priv->page_size = dev->row_size; priv->num_pages = 4; + } else if (base >= 0x00006000U && base < 0x00007000U) { + priv->page_size = dev->row_size; priv->num_pages = 4; + } else if (base >= 0x00045000U && base < 0x00047000U) { + priv->page_size = dev->row_size; priv->num_pages = 8; + } else if (base >= dev->flash_base && base < (dev->flash_base + dev->flash_size)) { + priv->page_size = dev->row_size; priv->num_pages = dev->flash_size / dev->row_size; + } else if (bank->base == 0xE000ED10U) { + priv->page_size = dev->row_size; priv->num_pages = 1; + } + } else if ((strcmp(dev->name, "WBZ351") == 0) || (strcmp(dev->name, "WBZ350") == 0)) { + /* mapping from wbz351.c */ + if (base < 0x00010000U) { + priv->page_size = dev->row_size; priv->num_pages = 64; + } else if (bank->base >= 0x00800000U && base < 0x00805000U) { + priv->page_size = dev->row_size; priv->num_pages = 20; + } else if (bank->base >= 0x00805000U && bank->base < 0x00806000U) { + priv->page_size = dev->row_size; priv->num_pages = 4; + } else if (bank->base >= 0x00806000U && bank->base < 0x00827000U) { + priv->page_size = dev->row_size; priv->num_pages = 132; + } else if (bank->base >= dev->flash_base && bank->base < (dev->flash_base + dev->flash_size)) { + priv->page_size = dev->row_size; priv->num_pages = dev->flash_size / dev->row_size; + } else if (bank->base == 0xE000ED10U) { + priv->page_size = dev->row_size; priv->num_pages = 1; + } + } else if (strcmp(dev->name, "PIC32WM") == 0) { /* PIC32WM_BZ6204 mapping similar to pic32wm.c */ + if (base < 0x00010000U) { + priv->page_size = dev->row_size; priv->num_pages = 64; + } else if (bank->base >= 0x00800000U && bank->base < 0x00810000U) { + priv->page_size = dev->row_size; priv->num_pages = 64; + } else if (bank->base >= 0x00810000U && bank->base < 0x00811000U) { + priv->page_size = dev->row_size; priv->num_pages = 4; + } else if (bank->base >= 0x00811000U && bank->base < 0x00812000U) { + priv->page_size = dev->row_size; priv->num_pages = 4; + } else if (bank->base >= dev->flash_base && bank->base < (dev->flash_base + dev->flash_size)) { + priv->page_size = dev->row_size; priv->num_pages = dev->flash_size / dev->row_size; + } else if (bank->base == 0xE000ED10U) { + priv->page_size = dev->row_size; priv->num_pages = 1; + } + } + + /* if not set above, fall back to a single page of row_size */ + if (priv->page_size == 0) { + priv->page_size = dev->row_size; + priv->num_pages = (dev->flash_size / dev->row_size); + } + + bank->size = priv->page_size * priv->num_pages; + bank->num_sectors = priv->num_pages; + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector)); + if (!bank->sectors) return ERROR_FAIL; + + for (unsigned int i = 0; i < bank->num_sectors; ++i) { + bank->sectors[i].offset = i * priv->page_size; + bank->sectors[i].size = priv->page_size; + bank->sectors[i].is_protected = 0; + } + + priv->probed = true; + LOG_INFO("%s: probe complete. base=0x%08" PRIx64 " size=0x%08" PRIx32 " pages=%" PRIu32, + dev->name, (uint64_t)bank->base, bank->size, priv->num_pages); + + return ERROR_OK; +} + +/* Erase: page erase (acts on sectors) */ +static int pic32cx_erase(struct flash_bank *bank, unsigned int first, unsigned int last) +{ + struct pic32cx_priv *p = bank->driver_priv; + if (!p) return ERROR_FAIL; + + struct target *t = bank->target; + if (t->state != TARGET_HALTED) return ERROR_TARGET_NOT_HALTED; + + const struct pic32cx_dev *dev = p->dev; + + for (unsigned int i = first; i <= last; ++i) { + uint32_t addr = bank->base + i * p->page_size; + int res = pic32cx_issue_nvmcmd(dev, t, addr, NVMCON_OP_PAGE_ERASE, 0); + if (res != ERROR_OK) return res; + } + return ERROR_OK; +} + +/* Write: row-by-row write (same pattern as originals) */ +static int pic32cx_write(struct flash_bank *bank, const uint8_t *buf, uint32_t offset, uint32_t count) +{ + struct pic32cx_priv *p = bank->driver_priv; + if (!p) return ERROR_FAIL; + + struct target *target = bank->target; + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + const struct pic32cx_dev *dev = p->dev; + uint32_t row_size = dev->row_size; + uint32_t addr = bank->base + offset; + const uint32_t end = addr + count; + + for (uint32_t row_addr = addr & ~(row_size - 1U); row_addr < end; row_addr += row_size) { + uint8_t *row_buf = malloc(row_size); + if (!row_buf) { + LOG_ERROR("Out of memory allocating row buffer"); + return ERROR_FAIL; + } + + int res = target_read_memory(target, row_addr, 4, row_size / 4, row_buf); + if (res != ERROR_OK) { + LOG_ERROR("Failed to read flash row at 0x%08" PRIx32, row_addr); + free(row_buf); + return res; + } + + for (uint32_t i = 0; i < row_size; ++i) { + uint32_t abs_addr = row_addr + i; + if (abs_addr >= addr && abs_addr < end) row_buf[i] = buf[abs_addr - addr]; + } + + res = target_write_buffer(target, RAM_BUF_ADDR, row_size, row_buf); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write row buffer to RAM at 0x%08" PRIx32, (uint32_t)RAM_BUF_ADDR); + free(row_buf); + return res; + } + + res = pic32cx_issue_nvmcmd(dev, target, row_addr, NVMCON_OP_ROW_PROG, RAM_BUF_ADDR); + if (res != ERROR_OK) { + LOG_ERROR("Failed to program row at 0x%08" PRIx32, row_addr); + free(row_buf); + return res; + } + + alive_sleep(2); + free(row_buf); + } + + LOG_INFO("%s: Write completed", p->dev->name); + return ERROR_OK; +} + +/* dsu_reset_deassert - tries to find a bank name (keeps compatibility) */ +COMMAND_HANDLER(pic32cx_handle_dsu_reset_deassert) +{ + struct target *target = get_current_target(CMD_CTX); + if (!target) + return ERROR_FAIL; + + struct flash_bank *bank = get_flash_bank_by_num_noprobe(0); + if (!bank) + return ERROR_FAIL; + + target_write_u8(target, 0x44000001U, 0x02U); + target_write_u32(target, 0x44000100U, 0x8000U); + target_write_u32(target, 0xE000ED0CU, 0x05FA0004U); + target_write_u32(target, 0xE000ED0CU, 0x05FA0000U); + alive_sleep(100); + return ERROR_OK; +} + +/* flash bank attach handler */ +FLASH_BANK_COMMAND_HANDLER(pic32cx_flash_bank_command) +{ + struct pic32cx_priv *chip = calloc(1, sizeof(*chip)); + if (!chip) return ERROR_FAIL; + chip->target = bank->target; + chip->probed = false; + chip->dev = &device_table[0]; /* default */ + chip->page_size = device_table[0].page_size; + bank->driver_priv = chip; + return ERROR_OK; +} + +/* erase_page and write_word wrappers that operate using first table device (manual exec) */ +COMMAND_HANDLER(pic32cx_handle_erase_page_command) +{ + struct target *target = get_current_target(CMD_CTX); + if (!target || CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t addr; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], addr); + + struct flash_bank *bank = get_flash_bank_by_num_noprobe(0); + if (!bank) + return ERROR_FAIL; + + struct pic32cx_priv *p = bank->driver_priv; + const struct pic32cx_dev *dev = (p && p->dev) ? p->dev : &device_table[0]; + + return pic32cx_issue_nvmcmd(dev, target, addr, NVMCON_OP_PAGE_ERASE, 0); +} + +COMMAND_HANDLER(pic32cx_handle_write_word_command) +{ + struct target *target = get_current_target(CMD_CTX); + if (!target || CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t addr, value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], addr); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + struct flash_bank *bank = get_flash_bank_by_num_noprobe(0); + if (!bank) + return ERROR_FAIL; + + struct pic32cx_priv *p = bank->driver_priv; + const struct pic32cx_dev *dev = (p && p->dev) ? p->dev : &device_table[0]; + + target_write_u32(target, dev->nvmdata, value); + return pic32cx_issue_nvmcmd(dev, target, addr, NVMCON_OP_WORD_PROG, 0); +} + +/* command array registration */ +static const struct command_registration pic32cx_exec_command_handlers[] = { + { + .name = "erase_page", + .handler = pic32cx_handle_erase_page_command, + .mode = COMMAND_EXEC, + .usage = "<address>", + .help = "Erase a flash page at the given address", + }, + { + .name = "write_word", + .handler = pic32cx_handle_write_word_command, + .mode = COMMAND_EXEC, + .usage = "<address> <32bit_hex_value>", + .help = "Write a 32-bit word to flash at the given address", + }, + { + .name = "dsu_reset_deassert", + .handler = pic32cx_handle_dsu_reset_deassert, + .mode = COMMAND_EXEC, + .usage = "", + .help = "Device-specific DSU reset deassert sequence", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration pic32cx_command_handlers[] = { + { + .name = "pic32cx_bz", + .mode = COMMAND_ANY, + .help = "pic32cx_bz flash command group", + .usage = "", + .chain = pic32cx_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver pic32cx_bz_flash = { + .name = "pic32cx_bz", + .commands = pic32cx_command_handlers, + .flash_bank_command = pic32cx_flash_bank_command, + .erase = pic32cx_erase, + .protect = NULL, + .write = pic32cx_write, + .read = default_flash_read, + .probe = pic32cx_probe, + .auto_probe = pic32cx_probe, + .erase_check = default_flash_blank_check, + .protect_check = NULL, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/target/image.h b/src/target/image.h index 03bc068d62..ac42870be7 100644 --- a/src/target/image.h +++ b/src/target/image.h @@ -25,7 +25,7 @@ #endif #define IMAGE_MAX_ERROR_STRING (256) -#define IMAGE_MAX_SECTIONS (512) +#define IMAGE_MAX_SECTIONS (2048) #define IMAGE_MEMORY_CACHE_SIZE (2048) diff --git a/tcl/board/microchip/pic32wm_bz6204_curiosity.cfg b/tcl/board/microchip/pic32wm_bz6204_curiosity.cfg new file mode 100644 index 0000000000..25081e3ea6 --- /dev/null +++ b/tcl/board/microchip/pic32wm_bz6204_curiosity.cfg @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# +# Microchip pic32wm_bz6204_curiosity. +# https://www.microchip.com/en-us/development-tool/ea81w68a +# + +source [find interface/cmsis-dap.cfg] + +set CHIPNAME pic32wm + +source [find target/pic32wm.cfg] + +reset_config srst_only diff --git a/tcl/board/microchip/wbz351_curiosity.cfg b/tcl/board/microchip/wbz351_curiosity.cfg new file mode 100644 index 0000000000..54606ea5e2 --- /dev/null +++ b/tcl/board/microchip/wbz351_curiosity.cfg @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# +# Microchip WBZ351 Xplained Pro evaluation kit. +# https://www.microchip.com/en-us/development-tool/ev19j06a +# + +source [find interface/cmsis-dap.cfg] + +set CHIPNAME wbz351 + +source [find target/wbz351.cfg] + +reset_config srst_only diff --git a/tcl/board/microchip/wbz450_curiosity.cfg b/tcl/board/microchip/wbz450_curiosity.cfg new file mode 100644 index 0000000000..a92f930c9e --- /dev/null +++ b/tcl/board/microchip/wbz450_curiosity.cfg @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# +# Microchip (former Atmel) WBZ451 Xplained Pro evaluation kit. +# https://www.microchip.com/en-us/development-tool/ev22l65a +# + +source [find interface/cmsis-dap.cfg] + +set CHIPNAME wbz450 + +source [find target/wbz450.cfg] + +reset_config srst_only diff --git a/tcl/board/microchip/wbz451_curiosity.cfg b/tcl/board/microchip/wbz451_curiosity.cfg new file mode 100644 index 0000000000..abf2f09f2f --- /dev/null +++ b/tcl/board/microchip/wbz451_curiosity.cfg @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# +# Microchip (former Atmel) WBZ451 Xplained Pro evaluation kit. +# https://www.microchip.com/en-us/development-tool/ev96b94a +# + +source [find interface/cmsis-dap.cfg] + +set CHIPNAME wbz451 + +source [find target/wbz451.cfg] + +reset_config srst_only diff --git a/tcl/target/pic32wm.cfg b/tcl/target/pic32wm.cfg new file mode 100644 index 0000000000..86ef611c1d --- /dev/null +++ b/tcl/target/pic32wm.cfg @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# OpenOCD Target Config for Microchip PIC32WM_BZ6 +# Author: Dinesh Arasu (din...@mi...) + +# --- Setup DAP and CPU --- +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME pic32wm +} + +if { [info exists ENDIAN] } { + set _ENDIAN $ENDIAN +} else { + set _ENDIAN little +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x04D8810B +} + +transport select swd + +swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_DAP_TAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +# --- Work Area in RAM --- +$_TARGETNAME configure -work-area-phys 0x20000000 \ + -work-area-size 0x80000 \ + -work-area-backup 0 + +# --- Reset Behavior --- +$_TARGETNAME configure -event reset-deassert-post { + pic32cx_bz dsu_reset_deassert +} + +reset_config srst_only srst_nogate + +adapter speed 2000 + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +# --- Flash Banks --- +flash bank pic32wm_flash pic32cx_bz 0x01000000 0x00200000 0 0 $_TARGETNAME + +flash bank pic32wm_Secure_boot pic32cx_bz 0x00000000 0x10000 0 0 $_TARGETNAME + +flash bank pic32wm_boot pic32cx_bz 0x00800000 0x10000 0 0 $_TARGETNAME + +flash bank pic32wm_device_config pic32cx_bz 0x00810000 0x1000 0 0 $_TARGETNAME + +flash bank pic32wm_otp_config pic32cx_bz 0x00811000 0x1000 0 0 $_TARGETNAME + +flash bank pic32wm_config_CM4F pic32cx_bz 0xE000ed10 0x100 0 0 $_TARGETNAME \ No newline at end of file diff --git a/tcl/target/wbz351.cfg b/tcl/target/wbz351.cfg new file mode 100644 index 0000000000..25965c629b --- /dev/null +++ b/tcl/target/wbz351.cfg @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# OpenOCD Target Config for Microchip WBZ351 +# Author: Dinesh Arasu (din...@mi...) + +# --- Setup DAP and CPU --- +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME wbz351 +} + +if { [info exists ENDIAN] } { + set _ENDIAN $ENDIAN +} else { + set _ENDIAN little +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x04D8810B +} + +transport select swd + +swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_DAP_TAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +# --- Work Area in RAM --- +$_TARGETNAME configure -work-area-phys 0x20000000 \ + -work-area-size 0x18000 \ + -work-area-backup 0 + +# --- Reset Behavior --- +$_TARGETNAME configure -event reset-deassert-post { + pic32cx_bz dsu_reset_deassert +} + +reset_config srst_only srst_nogate + +adapter speed 2000 + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +# --- Flash Banks --- +flash bank wbz351_flash pic32cx_bz 0x01000000 0x00080000 0 0 $_TARGETNAME + +flash bank wbz351_Secure_boot pic32cx_bz 0x00000000 0x10000 0 0 $_TARGETNAME + +flash bank wbz351_boot pic32cx_bz 0x00800000 0x5000 0 0 $_TARGETNAME + +flash bank wbz351_device_config pic32cx_bz 0x00805000 0x1000 0 0 $_TARGETNAME + +flash bank wbz351_otp_config pic32cx_bz 0x00806000 0x21000 0 0 $_TARGETNAME + +flash bank wbz351_config_CM4F pic32cx_bz 0xE000ed10 0x100 0 0 $_TARGETNAME + diff --git a/tcl/target/wbz450.cfg b/tcl/target/wbz450.cfg new file mode 100644 index 0000000000..efcc16f8a7 --- /dev/null +++ b/tcl/target/wbz450.cfg @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# OpenOCD Target Config for Microchip WBZ450 +# Author: Dinesh Arasu (din...@mi...) + +# --- Setup DAP and CPU --- +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME wbz450 +} + +if { [info exists ENDIAN] } { + set _ENDIAN $ENDIAN +} else { + set _ENDIAN little +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x04D8810B +} + +transport select swd + +swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_DAP_TAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +# --- Work Area in RAM --- +$_TARGETNAME configure -work-area-phys 0x20000000 \ + -work-area-size 0x20000 \ + -work-area-backup 0 + +# --- Reset Behavior --- +$_TARGETNAME configure -event reset-deassert-post { + pic32cx_bz dsu_reset_deassert +} + +reset_config srst_only srst_nogate + +adapter speed 2000 + +if {![using_hla]} { + cortex_m reset_config sysresetreq +} + +# --- Flash Banks --- +flash bank wbz450_flash pic32cx_bz 0x01000000 0x00100000 0 0 $_TARGETNAME + +flash bank wbz450_boot pic32cx_bz 0x00000000 0x5000 0 0 $_TARGETNAME + +flash bank wbz450_device_config pic32cx_bz 0x00005000 0x1000 0 0 $_TARGETNAME + +flash bank wbz450_otp pic32cx_bz 0x00006000 0x1000 0 0 $_TARGETNAME + +flash bank wbz450_configbits pic32cx_bz 0x00045000 0x2000 0 0 $_TARGETNAME + +flash bank wbz450_config_CM4F pic32cx_bz 0xE000ed10 0x100 0 0 $_TARGETNAME \ No newline at end of file diff --git a/tcl/target/wbz451.cfg b/tcl/target/wbz451.cfg new file mode 100644 index 0000000000..53e6bb5c5e --- /dev/null +++ b/tcl/target/wbz451.cfg @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# OpenOCD Target Config for Microchip WBZ451 +# Author: Dinesh Arasu (din...@mi...) + +# --- Setup DAP and CPU --- +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME wbz451 +} + +if { [info exists ENDIAN] } { + set _ENDIAN $ENDIAN +} else { + set _ENDIAN little +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x04D8810B +} + +transport select swd + +swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_DAP_TAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +# --- Work Area in RAM --- +$_TARGETNAME configure -work-area-phys 0x20000000 \ + -work-area-size 0x20000 \ + -work-area-backup 0 + +# --- Reset Behavior --- +$_TARGETNAME configure -event reset-deassert-post { + pic32cx_bz dsu_reset_deassert +} + +reset_config srst_only srst_nogate + +adapter speed 2000 + +if {![using_hla]} { + cortex_m reset_config sysresetreq +} + +# --- Flash Banks --- +flash bank wbz451_flash pic32cx_bz 0x01000000 0x00100000 0 0 $_TARGETNAME + +flash bank wbz451_boot pic32cx_bz 0x00000000 0x5000 0 0 $_TARGETNAME + +flash bank wbz451_device_config pic32cx_bz 0x00005000 0x1000 0 0 $_TARGETNAME + +flash bank wbz451_otp pic32cx_bz 0x00006000 0x1000 0 0 $_TARGETNAME + +flash bank wbz451_configbits pic32cx_bz 0x00045000 0x2000 0 0 $_TARGETNAME + +flash bank wbz451_config_CM4F pic32cx_bz 0xE000ed10 0x100 0 0 $_TARGETNAME \ No newline at end of file -- |
|
From: <ge...@op...> - 2025-10-12 10:26:50
|
This is an automated email from Gerrit. "Antonio Borneo <bor...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9167 -- gerrit commit 2e10729ccc3388832454d52997e7af4eeb8c8923 Author: Antonio Borneo <bor...@gm...> Date: Sun Oct 12 12:10:46 2025 +0200 target: cortex-m: fix support for armv8m caches Scan-build is unable to correctly follow the deferred loading of queued read, finalized by the atomic write, thus it incorrectly claims that the arrays d_u_ccsidr[] and i_ccsidr[] could be carry not initialized values: armv7m_cache.c:154:31: warning: 1st function call argument is an uninitialized value [core.CallAndMessage] cache->arch[cl].d_u_size = decode_ccsidr(d_u_ccsidr[cl]); armv7m_cache.c:172:29: warning: 1st function call argument is an uninitialized value [core.CallAndMessage] cache->arch[cl].i_size = decode_ccsidr(i_ccsidr[cl]); Initialize the arrays to zero to hide this false positive. Change-Id: I6d1e88093cb8807848643139647a571c1b566aa8 Signed-off-by: Antonio Borneo <bor...@gm...> Fixes: 04da6e2c6246 ("target: cortex-m: add support for armv8m caches") diff --git a/src/target/armv7m_cache.c b/src/target/armv7m_cache.c index cb57c0e25b..cc0c9d1404 100644 --- a/src/target/armv7m_cache.c +++ b/src/target/armv7m_cache.c @@ -111,7 +111,7 @@ int armv7m_identify_cache(struct target *target) clidr, cache->loc); // retrieve all available inner caches - uint32_t d_u_ccsidr[8], i_ccsidr[8]; + uint32_t d_u_ccsidr[8] = {0}, i_ccsidr[8] = {0}; for (unsigned int cl = 0; cl < cache->loc; cl++) { unsigned int ctype = FIELD_GET(CLIDR_CTYPE_MASK(cl + 1), clidr); -- |
|
From: <ge...@op...> - 2025-10-12 09:51:45
|
This is an automated email from Gerrit. "Antonio Borneo <bor...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9166 -- gerrit commit eb495be6e8ab72dacd477d2853d87fa326681ffa Author: Antonio Borneo <bor...@gm...> Date: Sun Oct 12 11:30:00 2025 +0200 helper/log: mark 'fmt' argument of alloc_*printf() as not NULL Even after commit e12ceddd5ee4 ("helper/log: mark `fmt` argument of `alloc_vprintf()` as format string"), the GCC compiler still reports that alloc_vprintf() could call vsnprintf() with a NULL format parameter. Inform the compiler that alloc_vprintf() cannot accept NULL as format string. Add an assert() in alloc_vprintf() so even compilers that do not use the function attribute 'nonnull' will play safe. While there, extend the same fixes to alloc_printf() too. Change-Id: Idfa4fe9c6dfb2acfbf434c392237937ae03f0e8a Signed-off-by: Antonio Borneo <bor...@gm...> Reported-by: Parshintsev Anatoly <ana...@sy...> diff --git a/src/helper/log.c b/src/helper/log.c index d8c4e09ac7..53463b526c 100644 --- a/src/helper/log.c +++ b/src/helper/log.c @@ -354,6 +354,8 @@ char *alloc_vprintf(const char *fmt, va_list ap) int len; char *string; + assert(fmt); + /* determine the length of the buffer needed */ va_copy(ap_copy, ap); len = vsnprintf(NULL, 0, fmt, ap_copy); diff --git a/src/helper/log.h b/src/helper/log.h index ac24f8e833..06770553c4 100644 --- a/src/helper/log.h +++ b/src/helper/log.h @@ -15,6 +15,7 @@ #define OPENOCD_HELPER_LOG_H #include <helper/command.h> +#include <helper/compiler.h> /* To achieve C99 printf compatibility in MinGW, gnu_printf should be * used for __attribute__((format( ... ))), with GCC v4.4 or later @@ -86,9 +87,9 @@ int log_add_callback(log_callback_fn fn, void *priv); int log_remove_callback(log_callback_fn fn, void *priv); char *alloc_vprintf(const char *fmt, va_list ap) - __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 1, 0))); + __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 1, 0))) __nonnull((1)); char *alloc_printf(const char *fmt, ...) - __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 1, 2))); + __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 1, 2))) __nonnull((1)); const char *find_nonprint_char(const char *buf, unsigned int buf_len); -- |
|
From: <ge...@op...> - 2025-10-11 16:48:26
|
This is an automated email from Gerrit. "Antonio Borneo <bor...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9165 -- gerrit commit aa7f9fd53e088e04cdabba8c97eedcb43556dbe9 Author: Antonio Borneo <bor...@gm...> Date: Tue Oct 7 14:03:15 2025 +0200 target: riscv: move the SMP commands under riscv For all the targets that support SMP, the sub-commands 'smp' and 'smp_gdb' are under the arch name: - aarch64 smp - cortex_a smp - cortex_m smp - esp32 smp - mips_m4k smp Keep consistency among OpenOCD commands, and move under the arch name 'riscv' the SMP subcommands. Change-Id: Iede7841c2df8161ff2c6fea3be561d1f26ad6cd0 Signed-off-by: Antonio Borneo <bor...@gm...> diff --git a/doc/openocd.texi b/doc/openocd.texi index a7c5d39d04..547e93b372 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11825,16 +11825,16 @@ When utilizing version 0.11 of the RISC-V Debug Specification, and DBUS registers, respectively. @end deffn -@deffn {Command} {smp} [on|off] +@deffn {Command} {riscv smp} [on|off] Display, enable or disable SMP handling mode. This command is needed only if user wants to temporary @b{disable} SMP handling for an existing SMP group (see @code{aarch64 smp} for additional information). To define an SMP group the command @code{target smp} should be used. @end deffn -@deffn {Command} {smp_gdb} [core_id] +@deffn {Command} {riscv smp_gdb} [core_id] Display/set the current core displayed in GDB. This is needed only if -@code{smp} was used. +@code{riscv smp} was used. @end deffn @deffn {Config Command} {riscv use_bscan_tunnel} width [type] diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index f8218af268..c606aa14ab 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -5849,6 +5849,9 @@ static const struct command_registration riscv_exec_command_handlers[] = { "When off, users need to take care of memory coherency themselves, for example by using " "`riscv exec_progbuf` to execute fence or CMO instructions." }, + { + .chain = smp_command_handlers + }, COMMAND_REGISTRATION_DONE }; @@ -5881,9 +5884,6 @@ static const struct command_registration riscv_command_handlers[] = { .usage = "", .chain = semihosting_common_handlers }, - { - .chain = smp_command_handlers - }, COMMAND_REGISTRATION_DONE }; -- |
|
From: <ge...@op...> - 2025-10-11 16:48:21
|
This is an automated email from Gerrit. "Antonio Borneo <bor...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9163 -- gerrit commit c776342959f4462204ab46bf3e6011fce8193a00 Author: Antonio Borneo <bor...@gm...> Date: Tue Oct 7 12:06:54 2025 +0200 target: riscv: fix memory leak in riscv_openocd_step_impl() The array 'wps_to_enable' is never freed. Scan build reports: src/target/riscv/riscv.c:4271:6: warning: Potential leak of memory pointed to by 'wps_to_enable' [unix.Malloc] Add the needed free(). While there, check if the allocation is successful. Change-Id: I00e7ade37a43a97dcc245113ad93c48784fce609 Signed-off-by: Antonio Borneo <bor...@gm...> diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 895550f0ab..4c801b2ddb 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -4221,9 +4221,15 @@ static int riscv_openocd_step_impl(struct target *target, bool current, RISCV_INFO(r); bool *wps_to_enable = calloc(r->trigger_count, sizeof(*wps_to_enable)); + if (!wps_to_enable) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + if (disable_watchpoints(target, wps_to_enable) != ERROR_OK) { LOG_TARGET_ERROR(target, "Failed to temporarily disable " "watchpoints before single-step."); + free(wps_to_enable); return ERROR_FAIL; } @@ -4268,6 +4274,8 @@ _exit: "after single-step."); } + free(wps_to_enable); + if (breakpoint && (riscv_add_breakpoint(target, breakpoint) != ERROR_OK)) { success = false; LOG_TARGET_ERROR(target, "Unable to restore the disabled breakpoint."); -- |
|
From: <ge...@op...> - 2025-10-11 16:48:21
|
This is an automated email from Gerrit. "Antonio Borneo <bor...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9164 -- gerrit commit f0416db46014364d5061b4e48452fa027a4c9c6a Author: Antonio Borneo <bor...@gm...> Date: Tue Oct 7 12:14:15 2025 +0200 target: riscv: fix double free() in parse_reg_ranges() The buffer 'args' is allocated and freed in the caller function parse_reg_ranges(). There is no reason to free it, only in some special case, in the called function parse_reg_ranges_impl(). Scan build reports: src/target/riscv/riscv.c:4537:2: warning: Attempt to free released memory [unix.Malloc] Drop the free() in the called function. Change-Id: I2e308670c502f8e140603b4e5c16fc568088e1a8 Signed-off-by: Antonio Borneo <bor...@gm...> diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 4c801b2ddb..f8218af268 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -4484,7 +4484,6 @@ static int parse_reg_ranges_impl(struct list_head *ranges, char *args, } } else { LOG_ERROR("Invalid argument '%s'.", arg); - free(args); return ERROR_COMMAND_SYNTAX_ERROR; } -- |
|
From: Agnelo D. (QUIC) <qui...@qu...> - 2025-10-09 11:41:16
|
Hi.
I am trying to add support for a new debug adapter called EUD(Embedded USB Debug) which is inbuilt into Qualcomm chipsets.
I added the following lines to the <openocd-root-folder>/configure.ac
-----------------------x------------------------x-------------------------
AS_IF([test "x$enable_eud" != "xno"], [
AS_IF([test -f "$srcdir/src/jtag/drivers/eud/configure.ac"], [
AX_CONFIG_SUBDIR_OPTION([src/jtag/drivers/eud], [--enable-subproject-build])
], [
AC_MSG_ERROR([Internal eud not found, run either 'git submodule init' and 'git submodule update' or disable eud with --disable-eud.])
])
])
-----------------------x------------------------x-------------------------
Running tools/checkpatch.sh on the patch throws up following error
-----------------------x------------------------x-------------------------
ERROR:EMBEDDED_FILENAME: It's generally not useful to have the filename in the file
#720: FILE: configure.ac:720:
+ AS_IF([test -f "$srcdir/src/jtag/drivers/eud/configure.ac"], [
-----------------------x------------------------x-------------------------
On line configure.ac:720, the "configure.ac" refers to a different configure.ac file under directory src/jtag/drivers/eud.
Is there some way to get around this error?
Thanks,
Agnelo
|
|
From: <ge...@op...> - 2025-10-08 06:31:06
|
This is an automated email from Gerrit. "zapb <de...@za...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9161 -- gerrit commit eae61beebbb41e65bc7479adb61b5b92bdfcd2a1 Author: Marc Schink <de...@za...> Date: Wed Oct 8 07:40:44 2025 +0200 adapters/cmsis-dap: Fix build without libusb The cmsis-dap core driver depends on libusb-related code which breaks the build when libusb is not available. Remove libusb dependency of the core driver to fix the build issue. For now, use an own timeout #define with the value of LIBUSB_TIMEOUT_MS but timeout handling should be better moved to the backends. However, this should be addressed in a dedicated patch. Change-Id: Ic5da392f8ab26b47466be199432432cdc08712ab Signed-off-by: Marc Schink <de...@za...> diff --git a/src/jtag/drivers/cmsis_dap.c b/src/jtag/drivers/cmsis_dap.c index e5e82d13d9..9f60a5ede4 100644 --- a/src/jtag/drivers/cmsis_dap.c +++ b/src/jtag/drivers/cmsis_dap.c @@ -37,7 +37,8 @@ #include <target/cortex_m.h> #include "cmsis_dap.h" -#include "libusb_helper.h" + +#define TIMEOUT_MS 6000 /* Create a dummy backend for 'backend' command if real one does not build */ #if BUILD_CMSIS_DAP_USB == 0 @@ -363,12 +364,12 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen) } uint8_t current_cmd = dap->command[0]; - int retval = dap->backend->write(dap, txlen, LIBUSB_TIMEOUT_MS); + int retval = dap->backend->write(dap, txlen, TIMEOUT_MS); if (retval < 0) return retval; /* get reply */ - retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS, CMSIS_DAP_BLOCKING); + retval = dap->backend->read(dap, TIMEOUT_MS, CMSIS_DAP_BLOCKING); if (retval < 0) return retval; @@ -872,7 +873,7 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) } } - int retval = dap->backend->write(dap, idx, LIBUSB_TIMEOUT_MS); + int retval = dap->backend->write(dap, idx, TIMEOUT_MS); if (retval < 0) { queued_retval = retval; goto skip; @@ -913,7 +914,7 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, enum cmsis_dap_blo } /* get reply */ - retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS, blocking); + retval = dap->backend->read(dap, TIMEOUT_MS, blocking); bool timeout = (retval == ERROR_TIMEOUT_REACHED || retval == 0); if (timeout && blocking == CMSIS_DAP_NON_BLOCKING) return; -- |
|
From: <ge...@op...> - 2025-10-08 06:31:03
|
This is an automated email from Gerrit. "zapb <de...@za...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9162 -- gerrit commit a8c977585e84d1e40e8c39b784d3c507445b3d34 Author: Marc Schink <de...@za...> Date: Wed Oct 8 08:24:39 2025 +0200 adapter/cmsis-dap: Add driver for TCP backend The cmsis-dap driver is not added to the list of drivers if none of the USB backends is available. Add cmsis-dap driver also if TCP backend is available. Change-Id: I877fac528e7102af74ee54dfcca77c5aded6a7ce Signed-off-by: Marc Schink <de...@za...> diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index e29937b587..caf5ace999 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -62,7 +62,7 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_CH347 == 1 &ch347_adapter_driver, #endif -#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1 +#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1 || BUILD_CMSIS_DAP_TCP == 1 &cmsis_dap_adapter_driver, #endif #if BUILD_DMEM == 1 -- |
|
From: Liviu I. <il...@li...> - 2025-10-05 10:08:24
|
> On 5 Oct 2025, at 12:51, Antonio Borneo <bor...@gm...> wrote: > > the drivers are disabled by default. They are not built unless you > explicitly enable them at configure. Yes, for consistency reasons between platforms, I prefer to explicitly enable/disable configuration options, and do not rely on defaults. > I have pushed > https://review.openocd.org/c/openocd/+/9159 > to prevent the build if the include file is missing. > > Thanks for the report. You're welcome! Regards, Liviu |
|
From: Antonio B. <bor...@gm...> - 2025-10-05 09:54:10
|
On Fri, Oct 3, 2025 at 1:19 PM Tony Ferranti OS <fer...@os...> wrote: > > > You also mention ADIv5/v6 DAP. > > There is the ADIv6 feature of "nested APs" that is not supported yet > > in OpenOCD (I should work on it in the next months). > > It's possible to have in ADIv6 one AP that is mapped in the memory > > space of a parent MEM-AP. This nesting can be multiple, with an AP > > behind another AP that is behind another AP ... > >If this is your case, keep in mind that it's not addressed yet. > > Antonio, > Please hold off on implementing OpenOCD "nested AP" support. Daniel Goehring <dgo...@os...> from Ampere Computing has been working on an implementation. It is undergoing testing but appears to be working with ADIv6 on Armv7/Armv8/Armv9 PEs and nested AHB, AXI, and APB Access Ports. He should be able to submit a patch for review in the coming weeks that matches the capabilities of other mainstream debuggers. Cool. I'm eager to have a look at this work Thanks Antonio |
|
From: Antonio B. <bor...@gm...> - 2025-10-05 09:51:32
|
On Sun, Oct 5, 2025 at 12:47 AM Liviu Ionescu <il...@li...> wrote: > > > I am about to make a new xPack release with the latest commit, and I noticed that the mingw-w64 build for Windows failed with: > > /home/ilg/Work/xpack-dev-tools/openocd-xpack.git/build-assets/build/win32-x64/sources/openocd.git/src/jtag/drivers/ep93xx.c:23:10: fatal error: sys/mman.h: No such file or directory > 23 | #include <sys/mman.h> > | ^~~~~~~~~~~~ > > /home/ilg/Work/xpack-dev-tools/openocd-xpack.git/build-assets/build/win32-x64/sources/openocd.git/src/jtag/drivers/at91rm9200.c:15:10: fatal error: sys/mman.h: No such file or directory > 15 | #include <sys/mman.h> > | ^~~~~~~~~~~~ > > /home/ilg/Work/xpack-dev-tools/openocd-xpack.git/build-assets/build/win32-x64/sources/openocd.git/src/jtag/drivers/bcm2835gpio.c:20:10: fatal error: sys/mman.h: No such file or directory > 20 | #include <sys/mman.h> > | ^~~~~~~~~~~~ > > /home/ilg/Work/xpack-dev-tools/openocd-xpack.git/build-assets/build/win32-x64/sources/openocd.git/src/jtag/drivers/imx_gpio.c:17:10: fatal error: sys/mman.h: No such file or directory > 17 | #include <sys/mman.h> > | ^~~~~~~~~~~~ > > It looks like some drivers use Unix memory mapped functions that are not available for Windows. > > I added explicit `--disable-ep93xx --disable-at91rm9200 --disable-bcm2835gpio --disable-imx_gpio` in the Windows configuration and the build passed. Hi Liviu, the drivers are disabled by default. They are not built unless you explicitly enable them at configure. We are moving toward a configure script that autodetects the build system and builds all the drivers it can, unless explicitly disabled by the command line These few drivers are special, but we will enable them too after v1.0.0. > If the drivers were intentionally excluded for Windows, I think that the configure step should not allow to enable these drivers, so to prevent the build to fail during compilation. I have pushed https://review.openocd.org/c/openocd/+/9159 to prevent the build if the include file is missing. Thanks for the report. > To be noted that in my previous release from February, the build passed with all these drivers enabled. In February the configure options '--enable-bcm2835gpio' and similar were only checked for ARM target build. If your Windows system was an X86, the options were silently ignored! Now they are checked. Regards Antonio |
|
From: <ge...@op...> - 2025-10-05 09:32:48
|
This is an automated email from Gerrit. "Antonio Borneo <bor...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9159 -- gerrit commit 9946b783844d3dd369d99acd3fe333b1667c3042 Author: Antonio Borneo <bor...@gm...> Date: Sun Oct 5 11:13:44 2025 +0200 configure.ac: add adapter dependency from sys/mman.h The adapter's driver that require the header file sys/mman.h should check for it and don't compile if it is not present. Add the check for sys/mman.h in configure.ac and prevent the build of the adapter's driver that depend on it. Change-Id: If0a518069e8fef9b41a67b633ec20e2f142a8b14 Signed-off-by: Antonio Borneo <bor...@gm...> diff --git a/configure.ac b/configure.ac index ed26d45e12..8335d52dd9 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,7 @@ AC_CHECK_HEADERS([netdb.h]) AC_CHECK_HEADERS([poll.h]) AC_CHECK_HEADERS([strings.h]) AC_CHECK_HEADERS([sys/ioctl.h]) +AC_CHECK_HEADERS([sys/mman.h]) AC_CHECK_HEADERS([sys/param.h]) AC_CHECK_HEADERS([sys/select.h]) AC_CHECK_HEADERS([sys/stat.h]) @@ -632,12 +633,14 @@ PROCESS_ADAPTERS([HIDAPI_USB1_ADAPTERS], ["x$use_hidapi" = "xyes" -a "x$use_libu PROCESS_ADAPTERS([LIBFTDI_ADAPTERS], ["x$use_libftdi" = "xyes"], [libftdi]) PROCESS_ADAPTERS([LIBFTDI_USB1_ADAPTERS], ["x$use_libftdi" = "xyes" -a "x$use_libusb1" = "xyes"], [libftdi and libusb-1.x]) PROCESS_ADAPTERS([LIBGPIOD_ADAPTERS], ["x$use_libgpiod" = "xyes"], [Linux libgpiod]) -PROCESS_ADAPTERS([DMEM_ADAPTER], ["x$is_linux" = "xyes"], [Linux /dev/mem]) +PROCESS_ADAPTERS([DMEM_ADAPTER], ["x$is_linux" = "xyes" -a "x$ac_cv_header_sys_mman_h" = "xyes"], [Linux /dev/mem]) PROCESS_ADAPTERS([SYSFSGPIO_ADAPTER], ["x$is_linux" = "xyes"], [Linux sysfs]) PROCESS_ADAPTERS([REMOTE_BITBANG_ADAPTER], [true], [unused]) PROCESS_ADAPTERS([CMSIS_DAP_TCP_ADAPTER], [true], [unused]) PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], ["x$use_internal_libjaylink" = "xyes" -o "x$use_libjaylink" = "xyes"], [libjaylink-0.2]) -PROCESS_ADAPTERS([XVC_ADAPTERS], ["x$is_linux" = "xyes" -a "x$ac_cv_header_linux_pci_h" = "xyes"], [Linux build]) +PROCESS_ADAPTERS([XVC_ADAPTERS], + ["x$is_linux" = "xyes" -a "x$ac_cv_header_linux_pci_h" = "xyes" -a "x$ac_cv_header_sys_mman_h" = "xyes"], + [Linux build with headers linux/pci.h and sys/mman.h]) PROCESS_ADAPTERS([SERIAL_PORT_ADAPTERS], ["x$can_build_buspirate" = "xyes"], [internal error: validation should happen beforehand]) PROCESS_ADAPTERS([PARALLEL_PORT_ADAPTER], [true], [unused]) @@ -649,8 +652,8 @@ PROCESS_ADAPTERS([JTAG_VPI_ADAPTER], [true], [unused]) PROCESS_ADAPTERS([RSHIM_ADAPTER], ["x$can_build_rshim" = "xyes"], [internal error: validation should happen beforehand]) PROCESS_ADAPTERS([AMTJTAGACCEL_ADAPTER], [true], [unused]) -PROCESS_ADAPTERS([HOST_ARM_BITBANG_ADAPTERS], [true], [unused]) -PROCESS_ADAPTERS([HOST_ARM_OR_AARCH64_BITBANG_ADAPTERS], [true], [unused]) +PROCESS_ADAPTERS([HOST_ARM_BITBANG_ADAPTERS], ["x$ac_cv_header_sys_mman_h" = "xyes"], [header sys/mman.h]) +PROCESS_ADAPTERS([HOST_ARM_OR_AARCH64_BITBANG_ADAPTERS], ["x$ac_cv_header_sys_mman_h" = "xyes"], [header sys/mman.h]) PROCESS_ADAPTERS([DUMMY_ADAPTER], [true], [unused]) AS_IF([test "x$enable_linuxgpiod" != "xno"], [ -- |
|
From: <ge...@op...> - 2025-10-05 09:32:44
|
This is an automated email from Gerrit. "Antonio Borneo <bor...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9160 -- gerrit commit e4917e718c50b78ff9e6ee17aa8c65ecf5b83d69 Author: Antonio Borneo <bor...@gm...> Date: Sun Oct 5 11:17:16 2025 +0200 configure.ac: rename 'adapterTuple' as 'adapter_driver' Commit ce3bf664c815 ("configure.ac: rename M4 macro 'adapter' to prevent accidental conflicts") renames the macro as 'adapterTuple' but since the macro name is printed in error messages, this creates cryptic errors like: configure: error: header sys/mman.h is required for adapterTuple "Bitbanging on EP93xx-based SBCs". Rename it as 'adapter_driver'. It keeps valid the purpose of the former renaming, while keeping readable the error message. Change-Id: Idd68270fbdf879153cd59f4cacf5036aa599b251 Signed-off-by: Antonio Borneo <bor...@gm...> Fixes: ce3bf664c815 ("configure.ac: rename M4 macro 'adapter' to prevent accidental conflicts") diff --git a/configure.ac b/configure.ac index 8335d52dd9..29ebc91f14 100644 --- a/configure.ac +++ b/configure.ac @@ -301,15 +301,15 @@ AS_IF([test "x$debug_malloc" = "xyes" -a "x$have_glibc" = "xyes"], [ ]) m4_define([AC_ARG_ADAPTERS], [ - m4_foreach([adapterTuple], [$1], - [AC_ARG_ENABLE(ADAPTER_OPT([adapterTuple]), - AS_HELP_STRING([--enable-ADAPTER_OPT([adapterTuple])[[[=yes/no/auto]]]], - [Enable building support for the ]ADAPTER_DESC([adapterTuple])[ (default is $2)]), + m4_foreach([adapter_driver], [$1], + [AC_ARG_ENABLE(ADAPTER_OPT([adapter_driver]), + AS_HELP_STRING([--enable-ADAPTER_OPT([adapter_driver])[[[=yes/no/auto]]]], + [Enable building support for the ]ADAPTER_DESC([adapter_driver])[ (default is $2)]), [case "${enableval}" in yes|no|auto) ;; - *) AC_MSG_ERROR([Option --enable-ADAPTER_OPT([adapterTuple]) has invalid value "${enableval}".]) ;; + *) AC_MSG_ERROR([Option --enable-ADAPTER_OPT([adapter_driver]) has invalid value "${enableval}".]) ;; esac], - [ADAPTER_VAR([adapterTuple])=$2]) + [ADAPTER_VAR([adapter_driver])=$2]) ]) ]) @@ -607,23 +607,24 @@ PKG_CHECK_MODULES([LIBJAYLINK], [libjaylink >= 0.2], # Arg $3: What prerequisites are missing, to be shown in an error message # if an adapter was requested but cannot be enabled. m4_define([PROCESS_ADAPTERS], [ - m4_foreach([adapterTuple], [$1], [ + m4_foreach([adapter_driver], [$1], [ AS_IF([test $2], [ - AS_IF([test "x$ADAPTER_VAR([adapterTuple])" != "xno"], [ - AC_DEFINE([BUILD_]ADAPTER_SYM([adapterTuple]), [1], - [1 if you want the ]ADAPTER_DESC([adapterTuple]).) + AS_IF([test "x$ADAPTER_VAR([adapter_driver])" != "xno"], [ + AC_DEFINE([BUILD_]ADAPTER_SYM([adapter_driver]), [1], + [1 if you want the ]ADAPTER_DESC([adapter_driver]).) ], [ - AC_DEFINE([BUILD_]ADAPTER_SYM([adapterTuple]), [0], - [0 if you do not want the ]ADAPTER_DESC([adapterTuple]).) + AC_DEFINE([BUILD_]ADAPTER_SYM([adapter_driver]), [0], + [0 if you do not want the ]ADAPTER_DESC([adapter_driver]).) ]) ], [ - AS_IF([test "x$ADAPTER_VAR([adapterTuple])" = "xyes"], [ - AC_MSG_ERROR([$3 is required for [adapterTuple] "ADAPTER_DESC([adapterTuple])".]) + AS_IF([test "x$ADAPTER_VAR([adapter_driver])" = "xyes"], [ + AC_MSG_ERROR([$3 is required for [adapter_driver] "ADAPTER_DESC([adapter_driver])".]) ]) - ADAPTER_VAR([adapterTuple])=no - AC_DEFINE([BUILD_]ADAPTER_SYM([adapterTuple]), [0], [0 if you do not want the ]ADAPTER_DESC([adapterTuple]).) + ADAPTER_VAR([adapter_driver])=no + AC_DEFINE([BUILD_]ADAPTER_SYM([adapter_driver]), [0], + [0 if you do not want the ]ADAPTER_DESC([adapter_driver]).) ]) - AM_CONDITIONAL(ADAPTER_SYM([adapterTuple]), [test "x$ADAPTER_VAR([adapterTuple])" != "xno"]) + AM_CONDITIONAL(ADAPTER_SYM([adapter_driver]), [test "x$ADAPTER_VAR([adapter_driver])" != "xno"]) ]) ]) @@ -832,7 +833,7 @@ echo echo echo OpenOCD configuration summary echo --------------------------------------------------- -m4_foreach([adapterTuple], [USB1_ADAPTERS, +m4_foreach([adapter_driver], [USB1_ADAPTERS, HIDAPI_ADAPTERS, HIDAPI_USB1_ADAPTERS, LIBFTDI_ADAPTERS, LIBFTDI_USB1_ADAPTERS, LIBGPIOD_ADAPTERS, @@ -854,8 +855,8 @@ m4_foreach([adapterTuple], [USB1_ADAPTERS, DUMMY_ADAPTER, OPTIONAL_LIBRARIES, COVERAGE], - [s=m4_format(["%-49s"], ADAPTER_DESC([adapterTuple])) - AS_CASE([$ADAPTER_VAR([adapterTuple])], + [s=m4_format(["%-49s"], ADAPTER_DESC([adapter_driver])) + AS_CASE([$ADAPTER_VAR([adapter_driver])], [auto], [ echo "$s"yes '(auto)' ], @@ -867,8 +868,8 @@ m4_foreach([adapterTuple], [USB1_ADAPTERS, ], [ AC_MSG_ERROR(m4_normalize([ - Error in [adapterTuple] "ADAPTER_ARG([adapterTuple])": Variable "ADAPTER_VAR([adapterTuple])" - has invalid value "$ADAPTER_VAR([adapterTuple])".])) + Error in [adapter_driver] "ADAPTER_ARG([adapter_driver])": Variable "ADAPTER_VAR([adapter_driver])" + has invalid value "$ADAPTER_VAR([adapter_driver])".])) ]) ]) echo -- |
|
From: Liviu I. <il...@li...> - 2025-10-04 22:45:10
|
I am about to make a new xPack release with the latest commit, and I noticed that the mingw-w64 build for Windows failed with:
/home/ilg/Work/xpack-dev-tools/openocd-xpack.git/build-assets/build/win32-x64/sources/openocd.git/src/jtag/drivers/ep93xx.c:23:10: fatal error: sys/mman.h: No such file or directory
23 | #include <sys/mman.h>
| ^~~~~~~~~~~~
/home/ilg/Work/xpack-dev-tools/openocd-xpack.git/build-assets/build/win32-x64/sources/openocd.git/src/jtag/drivers/at91rm9200.c:15:10: fatal error: sys/mman.h: No such file or directory
15 | #include <sys/mman.h>
| ^~~~~~~~~~~~
/home/ilg/Work/xpack-dev-tools/openocd-xpack.git/build-assets/build/win32-x64/sources/openocd.git/src/jtag/drivers/bcm2835gpio.c:20:10: fatal error: sys/mman.h: No such file or directory
20 | #include <sys/mman.h>
| ^~~~~~~~~~~~
/home/ilg/Work/xpack-dev-tools/openocd-xpack.git/build-assets/build/win32-x64/sources/openocd.git/src/jtag/drivers/imx_gpio.c:17:10: fatal error: sys/mman.h: No such file or directory
17 | #include <sys/mman.h>
| ^~~~~~~~~~~~
It looks like some drivers use Unix memory mapped functions that are not available for Windows.
I added explicit `--disable-ep93xx --disable-at91rm9200 --disable-bcm2835gpio --disable-imx_gpio` in the Windows configuration and the build passed.
If the drivers were intentionally excluded for Windows, I think that the configure step should not allow to enable these drivers, so to prevent the build to fail during compilation.
To be noted that in my previous release from February, the build passed with all these drivers enabled.
Regards,
Liviu
|
|
From: Tony F. OS <fer...@os...> - 2025-10-03 12:53:26
|
> You also mention ADIv5/v6 DAP. > There is the ADIv6 feature of "nested APs" that is not supported yet > in OpenOCD (I should work on it in the next months). > It's possible to have in ADIv6 one AP that is mapped in the memory > space of a parent MEM-AP. This nesting can be multiple, with an AP > behind another AP that is behind another AP ... >If this is your case, keep in mind that it's not addressed yet. Antonio, Please hold off on implementing OpenOCD "nested AP" support. Daniel Goehring <dgo...@os...> from Ampere Computing has been working on an implementation. It is undergoing testing but appears to be working with ADIv6 on Armv7/Armv8/Armv9 PEs and nested AHB, AXI, and APB Access Ports. He should be able to submit a patch for review in the coming weeks that matches the capabilities of other mainstream debuggers. - Tony Ferranti ________________________________________ From: Agnelo Dcosta (QUIC) <qui...@qu...> Sent: Thursday, October 2, 2025 12:45 PM To: Antonio Borneo Cc: Ashi Gupta; OpenOCD Subject: Re: OpenOCD support for debug of non-ARM subsystems accessible via ARM ADI DAP Some people who received this message don't often get email from qui...@qu.... Learn why this is important<https://aka.ms/LearnAboutSenderIdentification> Thanks for your response Antonio! > What kind of configuration do you have? MEM-AP or JTAG-AP? It is a MEM-AP configuration that is present. And with respect to ADIv6 - No, we are not using Nested APs As I mentioned in my previous mail. we have some implementation that is working for us for RISC-V core debug via the ARM DAP. But it needs to be made more generic. With guidance and inputs from you guys we would be glad to contribute towards adding the MEM-AP debug support in a proper manner. Regards, Agnelo ________________________________ From: Antonio Borneo <bor...@gm...> Sent: Thursday, October 2, 2025 3:57 PM To: Agnelo Dcosta <ag...@qt...> Cc: OpenOCD <ope...@li...>; Ashi Gupta <as...@qt...> Subject: Re: OpenOCD support for debug of non-ARM subsystems accessible via ARM ADI DAP WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros. On Wed, Oct 1, 2025 at 12:53 PM Agnelo Dcosta <ag...@qt...> wrote: > > Hi Antonio, > > On Qualcomm SoCs, along with the main ARMv8 CPU there exist several other subsystems/cores based on RISC-V, Xtensa and Qualcomm's own Hexagon DSP. Debug access to these cores is provisioned via the main ARM ADIv5/v6 DAP. > It appears, currently that there is no provision in the OpenOCD mainline to be able to debug non-ARM sub-systems accessible through the ARM DAP. > > For enabling debug of RISC-V cores on QCOM SoCs, we have experimented by making some hacks to the existing OpenOCD code and it is working for us. We, however, are not quite sure whether our approach is right. > > Would like to get your perspective on > (a) whether such configurations for debug of non-ARM cores via the ARM DAP should/would be supported and > (b) if yes, what should the approach be Hi Agnelo, such mixed SoCs are getting common. Also the new RPi2050 MCU should have both Cortex-M and RISC-V cores, but I ignore the details. So far there is no dev in OpenOCD for supporting such a mix, but any contribution is welcome. There are actually two ways to put a non-Arm core behind an Arm DAP: - using a standard Arm memory Access Port (MEM-AP) to access the debug bus of the non-Arm core; - using an Arm JTAG-AP and then implement in the SoC a JTAG chain that accesses the JTAG debug of the non-Arm core. The first case should require: - to provide the needed flags '-dap' and '-ap-num' to the command 'target create' (easy stuff) - decouple the R/W access on the debug bus of the core from the currently supported interface; there could be some JTAG API mixed in the target's code - check the presence of '-dap' info to switch between direct JTAG and DAP (in case of DAP we already manage SWD/JTAG cases). The second case is more tricky. While a preliminary support for JTAG-AP has been proposed in gerrit, I rejected it as not properly done. I want to have OpenOCD supporting multiple interfaces, at first. I mean handling two or more independent interfaces that connect to independent SoCs Once this is in place, the JTAG-AP should be added as a new interface, driven by its parent DAP and by the parent interface. This would allow addressing any possible core behind the JTAG-AP, with minor modifications to the target's code. What kind of configuration do you have? MEM-AP or JTAG-AP? You also mention ADIv5/v6 DAP. There is the ADIv6 feature of "nested APs" that is not supported yet in OpenOCD (I should work on it in the next months). It's possible to have in ADIv6 one AP that is mapped in the memory space of a parent MEM-AP. This nesting can be multiple, with an AP behind another AP that is behind another AP ... If this is your case, keep in mind that it's not addressed yet. Best Regards, Antonio |
|
From: Agnelo D. (QUIC) <qui...@qu...> - 2025-10-02 16:45:49
|
Thanks for your response Antonio! > What kind of configuration do you have? MEM-AP or JTAG-AP? It is a MEM-AP configuration that is present. And with respect to ADIv6 - No, we are not using Nested APs As I mentioned in my previous mail. we have some implementation that is working for us for RISC-V core debug via the ARM DAP. But it needs to be made more generic. With guidance and inputs from you guys we would be glad to contribute towards adding the MEM-AP debug support in a proper manner. Regards, Agnelo ________________________________ From: Antonio Borneo <bor...@gm...> Sent: Thursday, October 2, 2025 3:57 PM To: Agnelo Dcosta <ag...@qt...> Cc: OpenOCD <ope...@li...>; Ashi Gupta <as...@qt...> Subject: Re: OpenOCD support for debug of non-ARM subsystems accessible via ARM ADI DAP WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros. On Wed, Oct 1, 2025 at 12:53 PM Agnelo Dcosta <ag...@qt...> wrote: > > Hi Antonio, > > On Qualcomm SoCs, along with the main ARMv8 CPU there exist several other subsystems/cores based on RISC-V, Xtensa and Qualcomm's own Hexagon DSP. Debug access to these cores is provisioned via the main ARM ADIv5/v6 DAP. > It appears, currently that there is no provision in the OpenOCD mainline to be able to debug non-ARM sub-systems accessible through the ARM DAP. > > For enabling debug of RISC-V cores on QCOM SoCs, we have experimented by making some hacks to the existing OpenOCD code and it is working for us. We, however, are not quite sure whether our approach is right. > > Would like to get your perspective on > (a) whether such configurations for debug of non-ARM cores via the ARM DAP should/would be supported and > (b) if yes, what should the approach be Hi Agnelo, such mixed SoCs are getting common. Also the new RPi2050 MCU should have both Cortex-M and RISC-V cores, but I ignore the details. So far there is no dev in OpenOCD for supporting such a mix, but any contribution is welcome. There are actually two ways to put a non-Arm core behind an Arm DAP: - using a standard Arm memory Access Port (MEM-AP) to access the debug bus of the non-Arm core; - using an Arm JTAG-AP and then implement in the SoC a JTAG chain that accesses the JTAG debug of the non-Arm core. The first case should require: - to provide the needed flags '-dap' and '-ap-num' to the command 'target create' (easy stuff) - decouple the R/W access on the debug bus of the core from the currently supported interface; there could be some JTAG API mixed in the target's code - check the presence of '-dap' info to switch between direct JTAG and DAP (in case of DAP we already manage SWD/JTAG cases). The second case is more tricky. While a preliminary support for JTAG-AP has been proposed in gerrit, I rejected it as not properly done. I want to have OpenOCD supporting multiple interfaces, at first. I mean handling two or more independent interfaces that connect to independent SoCs Once this is in place, the JTAG-AP should be added as a new interface, driven by its parent DAP and by the parent interface. This would allow addressing any possible core behind the JTAG-AP, with minor modifications to the target's code. What kind of configuration do you have? MEM-AP or JTAG-AP? You also mention ADIv5/v6 DAP. There is the ADIv6 feature of "nested APs" that is not supported yet in OpenOCD (I should work on it in the next months). It's possible to have in ADIv6 one AP that is mapped in the memory space of a parent MEM-AP. This nesting can be multiple, with an AP behind another AP that is behind another AP ... If this is your case, keep in mind that it's not addressed yet. Best Regards, Antonio |
|
From: Tomas V. <to...@us...> - 2025-10-02 10:55:38
|
On 02/10/2025 12:27, Antonio Borneo wrote: > On Wed, Oct 1, 2025 at 12:53 PM Agnelo Dcosta <ag...@qt...> wrote: >> Hi Antonio, >> >> On Qualcomm SoCs, along with the main ARMv8 CPU there exist several other subsystems/cores based on RISC-V, Xtensa and Qualcomm's own Hexagon DSP. Debug access to these cores is provisioned via the main ARM ADIv5/v6 DAP. >> It appears, currently that there is no provision in the OpenOCD mainline to be able to debug non-ARM sub-systems accessible through the ARM DAP. >> >> For enabling debug of RISC-V cores on QCOM SoCs, we have experimented by making some hacks to the existing OpenOCD code and it is working for us. We, however, are not quite sure whether our approach is right. >> >> Would like to get your perspective on >> (a) whether such configurations for debug of non-ARM cores via the ARM DAP should/would be supported and >> (b) if yes, what should the approach be > Hi Agnelo, > > such mixed SoCs are getting common. > Also the new RPi2050 MCU should have both Cortex-M and RISC-V cores, > but I ignore the details. > So far there is no dev in OpenOCD for supporting such a mix, but any > contribution is welcome. > > There are actually two ways to put a non-Arm core behind an Arm DAP: > - using a standard Arm memory Access Port (MEM-AP) to access the debug > bus of the non-Arm core; > - using an Arm JTAG-AP and then implement in the SoC a JTAG chain that > accesses the JTAG debug of the non-Arm core. > > The first case should require: > - to provide the needed flags '-dap' and '-ap-num' to the command > 'target create' (easy stuff) > - decouple the R/W access on the debug bus of the core from the > currently supported interface; there could be some JTAG API mixed in > the target's code > - check the presence of '-dap' info to switch between direct JTAG and > DAP (in case of DAP we already manage SWD/JTAG cases). > > The second case is more tricky. While a preliminary support for > JTAG-AP has been proposed in gerrit, I rejected it as not properly > done. > I want to have OpenOCD supporting multiple interfaces, at first. I > mean handling two or more independent interfaces that connect to > independent SoCs > Once this is in place, the JTAG-AP should be added as a new interface, > driven by its parent DAP and by the parent interface. > This would allow addressing any possible core behind the JTAG-AP, with > minor modifications to the target's code. > > What kind of configuration do you have? MEM-AP or JTAG-AP? > > You also mention ADIv5/v6 DAP. > There is the ADIv6 feature of "nested APs" that is not supported yet > in OpenOCD (I should work on it in the next months). > It's possible to have in ADIv6 one AP that is mapped in the memory > space of a parent MEM-AP. This nesting can be multiple, with an AP > behind another AP that is behind another AP ... > If this is your case, keep in mind that it's not addressed yet. > > Best Regards, > Antonio > Hi Angelo, actually there is a working RISC-V DM over ADI MEM-AP support in OpenOCD forks https://github.com/tom-van/openocd-rp2350-riscv (my development) https://github.com/raspberrypi/openocd The work was sponsored by Raspberry Pi Ltd. It supports RISC-V cores in RP2350 chip and was also successfully tested with RISC-V coprocessor in nRF54L. The new code was not upstreamed yet because it is based on the newer RISC-V fork https://github.com/riscv-collab/riscv-openocd and we are currently working on syncing RISC-V updates to the upstream repo. Regards Tomas |
|
From: Antonio B. <bor...@gm...> - 2025-10-02 10:28:09
|
On Wed, Oct 1, 2025 at 12:53 PM Agnelo Dcosta <ag...@qt...> wrote: > > Hi Antonio, > > On Qualcomm SoCs, along with the main ARMv8 CPU there exist several other subsystems/cores based on RISC-V, Xtensa and Qualcomm's own Hexagon DSP. Debug access to these cores is provisioned via the main ARM ADIv5/v6 DAP. > It appears, currently that there is no provision in the OpenOCD mainline to be able to debug non-ARM sub-systems accessible through the ARM DAP. > > For enabling debug of RISC-V cores on QCOM SoCs, we have experimented by making some hacks to the existing OpenOCD code and it is working for us. We, however, are not quite sure whether our approach is right. > > Would like to get your perspective on > (a) whether such configurations for debug of non-ARM cores via the ARM DAP should/would be supported and > (b) if yes, what should the approach be Hi Agnelo, such mixed SoCs are getting common. Also the new RPi2050 MCU should have both Cortex-M and RISC-V cores, but I ignore the details. So far there is no dev in OpenOCD for supporting such a mix, but any contribution is welcome. There are actually two ways to put a non-Arm core behind an Arm DAP: - using a standard Arm memory Access Port (MEM-AP) to access the debug bus of the non-Arm core; - using an Arm JTAG-AP and then implement in the SoC a JTAG chain that accesses the JTAG debug of the non-Arm core. The first case should require: - to provide the needed flags '-dap' and '-ap-num' to the command 'target create' (easy stuff) - decouple the R/W access on the debug bus of the core from the currently supported interface; there could be some JTAG API mixed in the target's code - check the presence of '-dap' info to switch between direct JTAG and DAP (in case of DAP we already manage SWD/JTAG cases). The second case is more tricky. While a preliminary support for JTAG-AP has been proposed in gerrit, I rejected it as not properly done. I want to have OpenOCD supporting multiple interfaces, at first. I mean handling two or more independent interfaces that connect to independent SoCs Once this is in place, the JTAG-AP should be added as a new interface, driven by its parent DAP and by the parent interface. This would allow addressing any possible core behind the JTAG-AP, with minor modifications to the target's code. What kind of configuration do you have? MEM-AP or JTAG-AP? You also mention ADIv5/v6 DAP. There is the ADIv6 feature of "nested APs" that is not supported yet in OpenOCD (I should work on it in the next months). It's possible to have in ADIv6 one AP that is mapped in the memory space of a parent MEM-AP. This nesting can be multiple, with an AP behind another AP that is behind another AP ... If this is your case, keep in mind that it's not addressed yet. Best Regards, Antonio |
|
From: <ge...@op...> - 2025-10-02 04:09:33
|
This is an automated email from Gerrit. "Gaetan Perrot <gae...@sp...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9022 -- gerrit commit 0739b36d7f13782edbc0fe8c9282fac1542085fb Author: Takuya Sasaki <tak...@sp...> Date: Sat Jun 7 12:21:27 2025 +0900 flash: nor: Support NOR flash driver for SC-OBC Module A1 This commit supports the NOR flash driver for Space Cubics' `SC-OBC Module A1`. The `SC-OBC Module A1` is equipped with two `S25FL256L` SPI NOR flash devices, which are accessed through a proprietary QSPI IP core. To support selecting between the two flash devices, a custom parameter named `mem_no` has been added. The Page Program and Erase commands are defined in `spi.c` as follows: - Erase: 0xdc (block erase) - Page Program: 0x12 Both commands require a 4-byte address. Although the `S25FL256L` supports Quad Page Program, the Page Program is used instead. As noted in the datasheet, Quad Page Program offers no performance benefit when the clock speed exceeds 12Mbps, so the Page Program is used instead. Change-Id: If0592cfc8d1a873f23f4a11ae63b0fcc88bb46e1 Signed-off-by: Gaetan Perrot <gae...@sp...> Signed-off-by: Takuya Sasaki <tak...@sp...> diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index f408559004..f4a24888d0 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -62,6 +62,7 @@ NOR_DRIVERS = \ %D%/renesas_rpchf.c \ %D%/rp2xxx.c \ %D%/rsl10.c \ + %D%/scqspi.c \ %D%/sfdp.c \ %D%/sh_qspi.c \ %D%/sim3x.c \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 2bd2043633..0d322a9923 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -293,6 +293,7 @@ extern const struct flash_driver qn908x_flash; extern const struct flash_driver renesas_rpchf_flash; extern const struct flash_driver rp2xxx_flash; extern const struct flash_driver rsl10_flash; +extern const struct flash_driver scqspi_flash; extern const struct flash_driver sh_qspi_flash; extern const struct flash_driver sim3x_flash; extern const struct flash_driver stellaris_flash; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 6b0def6810..3b2f73209f 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -72,6 +72,7 @@ static const struct flash_driver * const flash_drivers[] = { &renesas_rpchf_flash, &rp2xxx_flash, &rsl10_flash, + &scqspi_flash, &sh_qspi_flash, &sim3x_flash, &stellaris_flash, diff --git a/src/flash/nor/scqspi.c b/src/flash/nor/scqspi.c new file mode 100644 index 0000000000..1dee168a12 --- /dev/null +++ b/src/flash/nor/scqspi.c @@ -0,0 +1,1031 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2025 Space Cubics Inc * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "spi.h" +#include <helper/bits.h> +#include <helper/time_support.h> +#include <target/image.h> + +/* Configuration Memory Register */ +#define SCOBCA1_FPGA_QSPI_CFG_BASE (0x40000000) + +/* Offset */ +#define SCQSPI_ACR_OFFSET (0x0000) /* QSPI Access Control Register */ +#define SCQSPI_TDR_OFFSET (0x0004) /* QSPI TX Data Register */ +#define SCQSPI_RDR_OFFSET (0x0008) /* QSPI RX Data Register */ +#define SCQSPI_ASR_OFFSET (0x000C) /* QSPI Access Status Register */ +#define SCQSPI_FIFOSR_OFFSET (0x0010) /* QSPI FIFO Status Register */ +#define SCQSPI_FIFORR_OFFSET (0x0014) /* QSPI FIFO Reset Register */ +#define SCQSPI_ISR_OFFSET (0x0020) /* QSPI Interrupt Status Register */ +#define SCQSPI_IER_OFFSET (0x0024) /* QSPI Interrupt Enable Register */ +#define SCQSPI_CCR_OFFSET (0x0030) /* QSPI Clock Control Register */ +#define SCQSPI_DCMSR_OFFSET (0x0034) /* QSPI Data Capture Mode Setting Register */ +#define SCQSPI_FTLSR_OFFSET (0x0038) /* QSPI FIFO Threshold Level Setting Register */ +#define SCQSPI_VER_OFFSET (0xF000) /* QSPI Controller IP Version Register */ + +/* QSPI Control Register for Configutation Memory */ +#define SCOBCA1_FPGA_NORFLASH_QSPI_ACR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_ACR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_TDR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_TDR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_RDR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_RDR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_ASR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_ASR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_FIFOSR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_FIFOSR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_FIFORR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_FIFORR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_ISR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_ISR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_IER (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_IER_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_CCR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_CCR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_DCMSR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_DCMSR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_FTLSR (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_FTLSR_OFFSET) +#define SCOBCA1_FPGA_NORFLASH_QSPI_VER (SCOBCA1_FPGA_QSPI_CFG_BASE + SCQSPI_VER_OFFSET) + +#define SCOBCA1_SYSREG_CFGMEMSEL(x) ((((x)&BIT(0)) << 4)) +#define SCOBCA1_SYSREG_CFGMEMMON(x) ((((x)&BIT(0)) << 5)) +#define SCOBCA1_SYSREG_CFGMEMCTL (0x4F000010) + +#define SCQSPI_DATA_MEM0_SS (0x01) +#define SCQSPI_CFG_MEM0 (0U) +#define SCQSPI_CFG_MEM1 (1U) +#define SCQSPI_ASR_IDLE (0x00) +#define SCQSPI_ASR_BUSY (0x01) +#define SCQSPI_RX_FIFO_MAX_BYTE (16U) +#define SCQSPI_TX_FIFO_MAX_BYTE (16U) +#define SCQSPI_SPI_MODE_QUAD (0x00020000) +#define SCQSPI_ERASE_BLOCK_WAIT_MS (800U) +#define SCQSPI_ERASE_SECTOR_WAIT_MS (10U) +#define SCQSPI_PAGE_BUFFER_BYTE (256U) +#define SCQSPI_REG_READ_RETRY(count) (count) +#define SCQSPI_CRC32_INIT (0xFFFFFFFF) +#define SCQSPI_CRC32_FINAL(crc) (~(crc)) +#define SCQSPI_VERIFY_CHUNK_SIZE (4096U) +#define SCQSPI_ALL_MASK GENMASK(31, 0) + +#define SYSREG_CFGMEMSEL_MON_MASK GENMASK(5, 4) /* CFGMEMSEL and CFGMEMMON bits mask */ + +struct scqspi_flash_bank { + struct target *target; + bool probed; + uint32_t flash_addr; + uint8_t spi_ss; + uint8_t mem_no; + struct flash_device dev; +}; + +/* ------------------------------------------------------------------------- + * Internal helper functions for scqspi flash driver implementation + * -------------------------------------------------------------------------*/ + +static bool verify(struct target *target, uint32_t addr, uint32_t exp, uint32_t read_mask, + uint32_t retry) +{ + uint32_t val; + int32_t ret; + + for (uint32_t i = 0; i <= retry; i++) { + ret = target_read_u32(target, addr, &val); + if (ret != ERROR_OK) { + LOG_ERROR("Faild to read the register value: %d", ret); + break; + } + val &= read_mask; + + if (val == exp) { + LOG_DEBUG("read32 [0x%08X] 0x%08x (exp:0x%08x) (retry:%d)", addr, val, exp, + i + 1); + return true; + } else if (i + 1 == retry) { + LOG_ERROR("read32 [0x%08X] 0x%08x (exp:0x%08x) (retry:%d)", addr, val, exp, + i + 1); + } else { + LOG_DEBUG("read32 [0x%08X] 0x%08x (exp:0x%08x) (retry:%d)", addr, val, exp, + i + 1); + } + usleep(1); + } + + LOG_ERROR("Verification failed: retry count: %d", retry); + return false; +} + +static bool is_qspi_control_done(struct target *target) +{ + int ret; + + LOG_DEBUG("Confirm QSPI Interrupt Status is `SPI Control Done`"); + + if (!verify(target, SCOBCA1_FPGA_NORFLASH_QSPI_ISR, 0x01, SCQSPI_ALL_MASK, + SCQSPI_REG_READ_RETRY(10))) { + LOG_ERROR("Confirm QSPI Interrupt Status failed"); + return false; + } + + LOG_DEBUG("Clear QSPI Interrupt Status"); + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_ISR, 0x01); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read QSPI Interrupt Status: %d", ret); + return false; + } + + if (!verify(target, SCOBCA1_FPGA_NORFLASH_QSPI_ISR, 0x00, SCQSPI_ALL_MASK, + SCQSPI_REG_READ_RETRY(10))) { + LOG_ERROR("Failed to read QSPI Interrupt Status"); + return false; + } + + return true; +} + +static bool is_qspi_idle(struct target *target) +{ + LOG_DEBUG("Confirm QSPI Access Status is `Idle`"); + + if (!verify(target, SCOBCA1_FPGA_NORFLASH_QSPI_ASR, SCQSPI_ASR_IDLE, SCQSPI_ALL_MASK, + SCQSPI_REG_READ_RETRY(10))) { + LOG_ERROR("QSPI (Config Memory) is busy"); + return false; + } + + return true; +} + +static int activate_spi_ss(struct target *target, uint32_t spi_mode) +{ + int ret; + + LOG_DEBUG("Activate SPI SS with %08x", spi_mode); + + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_ACR, spi_mode); + if (ret != ERROR_OK) + return ret; + + if (!is_qspi_idle(target)) + return ERROR_FLASH_BUSY; + + return ERROR_OK; +} + +static int inactivate_spi_ss(struct target *target) +{ + int ret; + + LOG_DEBUG("Inactivate SPI SS"); + + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_ACR, 0x00); + if (ret != ERROR_OK) + return ret; + + if (!is_qspi_idle(target)) + return ERROR_FLASH_BUSY; + + return ERROR_OK; +} + +static int reset_qspi_rxfifo(struct target *target) +{ + return target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_FIFORR, 0x01); +} + +static int clear_status_register(struct target *target, uint32_t spi_ss) +{ + int ret; + + ret = activate_spi_ss(target, spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to activate SPI SS : %d", ret); + goto end; + } + + /* Clear All ISR */ + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_ISR, 0xFFFFFFFF); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to clear ALL ISR : %d", ret); + goto inactive_ss; + } + + ret = reset_qspi_rxfifo(target); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to reset QSPI FIFO : %d", ret); + goto inactive_ss; + } + + LOG_DEBUG("Clear Status Register (Instructure:0x30) "); + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, 0x30); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to clear Status Register : %d", ret); + goto inactive_ss; + } + +inactive_ss: + ret = inactivate_spi_ss(target); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to inactivate SPI SS"); + goto end; + } + + if (!is_qspi_control_done(target)) { + LOG_ERROR("Confirm SPI Control is Done failed"); + ret = ERROR_FAIL; + } + +end: + return ret; +} + +static int select_mem(struct target *target, uint8_t mem_no) +{ + int ret; + uint32_t expval = SCOBCA1_SYSREG_CFGMEMSEL(mem_no) | SCOBCA1_SYSREG_CFGMEMMON(mem_no); + + LOG_DEBUG("Select Config Memory %d", mem_no); + + ret = target_write_u32(target, SCOBCA1_SYSREG_CFGMEMCTL, SCOBCA1_SYSREG_CFGMEMSEL(mem_no)); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write Config Memory %d", mem_no); + return ret; + } + + if (!verify(target, SCOBCA1_SYSREG_CFGMEMCTL, expval, SYSREG_CFGMEMSEL_MON_MASK, + SCQSPI_REG_READ_RETRY(1000))) { + LOG_ERROR("Failed to select Config Memory %d", mem_no); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int scqspi_init(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + int ret; + + ret = select_mem(target, scqspi_info->mem_no); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to select memory"); + return ret; + } + + LOG_DEBUG("Clear Status Register"); + ret = clear_status_register(target, scqspi_info->spi_ss); + if (ret != ERROR_OK) + LOG_ERROR("Failed to clear Status register"); + + return ret; +} + +static int scqspi_read_id(struct flash_bank *bank, uint32_t *id) +{ + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + struct target *target = bank->target; + uint8_t din[3] = {0, 0, 0}; + uint8_t rdata; + int ret; + + ret = activate_spi_ss(target, scqspi_info->spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to activate SPI SS"); + goto end; + } + + ret = reset_qspi_rxfifo(target); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to reset QSPI FIFO"); + goto inactivate_ss; + } + + LOG_DEBUG("Send SPIFLASH_READ_ID cmd"); + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, SPIFLASH_READ_ID); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write SPIFLASH_READ_ID command"); + goto inactivate_ss; + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto inactivate_ss; + } + + LOG_DEBUG("Read SPIFLASH_READ_ID"); + + /* Request RX data */ + for (int i = 0; i < sizeof(din); i++) { + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_RDR, 0x00); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to request RX data"); + goto inactivate_ss; + } + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto inactivate_ss; + } + + /* Read RX FIFO */ + for (int i = 0; i < sizeof(din); i++) { + ret = target_read_u8(target, SCOBCA1_FPGA_NORFLASH_QSPI_RDR, &rdata); + if (ret != ERROR_OK) + goto inactivate_ss; + + din[i] = rdata; + LOG_DEBUG("Read byte %d: 0x%02x", i, din[i]); + } + + *id = (din[2] << 16) | (din[1] << 8) | din[0]; + +inactivate_ss: + ret = inactivate_spi_ss(target); + if (ret != ERROR_OK) + LOG_ERROR("Failed to inactivate SPI SS"); + +end: + return ret; +} + +static int read_and_verify_rx_data(struct target *target, size_t exp_size, uint32_t *exp_val) +{ + int ret; + uint32_t rdata; + + ret = reset_qspi_rxfifo(target); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to reset QSPI FIFO"); + goto end; + } + + LOG_DEBUG("Request RX FIFO %zu byte", exp_size); + for (int i = 0; i < exp_size; i++) { + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_RDR, 0x00); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to resquest QSPI Data: %d", ret); + goto end; + } + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto end; + } + + LOG_DEBUG("Read RX FIFO %zu byte and verify the value", exp_size); + for (int i = 0; i < exp_size; i++) { + ret = target_read_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_RDR, &rdata); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read RX FIFO %zu byte", exp_size); + goto end; + } + + if (exp_val[i] != rdata) { + LOG_ERROR("Read RX FIFO %d byte failed: expected 0x%08x, got 0x%08x", i, + exp_val[i], rdata); + ret = ERROR_FAIL; + } + } + +end: + return ret; +} + +static int verify_status_register1(struct target *target, uint32_t spi_ss, size_t exp_size, + uint32_t *exp_val) +{ + int ret; + + ret = activate_spi_ss(target, spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to activate SPI SS : %d", ret); + goto end; + } + + LOG_DEBUG("Request Status Register"); + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, SPIFLASH_READ_STATUS); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write SPIFLASH_READ_STATUS command : %d", ret); + goto inactivate_ss; + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto inactivate_ss; + } + + /* Read RX data (2byte) adn Verify */ + ret = read_and_verify_rx_data(target, exp_size, exp_val); + if (ret != ERROR_OK) + LOG_ERROR("Failed to read and verify RX data : %d", ret); + +inactivate_ss: + ret = inactivate_spi_ss(target); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to inactivate SPI SS : %d", ret); + goto end; + } + + if (!is_qspi_control_done(target)) { + LOG_ERROR("Confirm SPI Control is Done failed"); + ret = ERROR_FAIL; + } + +end: + return ret; +} + +static bool is_write_enable(struct target *target, uint32_t spi_ss) +{ + LOG_DEBUG("Verify write enable"); + + int ret; + uint32_t exp_write_enable[] = {0x02, 0x02}; + + ret = verify_status_register1(target, spi_ss, ARRAY_SIZE(exp_write_enable), + exp_write_enable); + if (ret != ERROR_OK) { + LOG_ERROR("Write enable not set"); + return false; + } + + return true; +} + +static bool is_write_disable(struct target *target, uint32_t spi_ss) +{ + int ret; + uint32_t exp_write_disable[] = {0x00, 0x00}; + + LOG_DEBUG("Verify write disable"); + + ret = verify_status_register1(target, spi_ss, ARRAY_SIZE(exp_write_disable), + exp_write_disable); + if (ret != ERROR_OK) { + LOG_ERROR("Write disable not set"); + return false; + } + + return true; +} + +static int set_write_enable(struct target *target, uint32_t spi_ss) +{ + int ret; + + ret = activate_spi_ss(target, spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to activate SPI SS"); + goto end; + } + + LOG_DEBUG("Set `Write Enable`"); + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, SPIFLASH_WRITE_ENABLE); + if (ret != ERROR_OK) + LOG_ERROR("Failed to write SPIFLASH_WRITE_ENABLE command : %d", ret); + + ret = inactivate_spi_ss(target); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to inactivate SPI SS : %d", ret); + goto end; + } + + if (!is_qspi_control_done(target)) { + LOG_ERROR("Confirm SPI Control is Done failed"); + ret = ERROR_FAIL; + goto end; + } + + if (!is_write_enable(target, spi_ss)) + ret = ERROR_FAIL; + +end: + return ret; +} + +static int write_mem_addr_to_flash(struct target *target, uint32_t mem_addr) +{ + int ret; + uint8_t byte; + + /* Write a 4 bytes address memmory to flash */ + for (int i = 3; i >= 0; --i) { + byte = (mem_addr >> (i * 8)) & 0xFF; + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, byte); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write byte %d of Memory Address: 0x%08x", i, mem_addr); + break; + } + } + + return ret; +} + +static int erase_sector(struct flash_bank *bank, unsigned int sector) +{ + int ret; + struct target *target = bank->target; + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + uint32_t erase_cmd = scqspi_info->dev.erase_cmd; + uint32_t mem_addr = scqspi_info->flash_addr + bank->sectors[sector].offset; + + ret = activate_spi_ss(target, scqspi_info->spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to activate SPI SS : %d", ret); + goto end; + } + + LOG_DEBUG("Send Erase command : 0x%02x", erase_cmd); + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, erase_cmd); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write Erase command : %d", ret); + goto inactivate_ss; + } + + LOG_DEBUG("Send Memory Address (4byte) : mem_addr : 0x%08x", mem_addr); + ret = write_mem_addr_to_flash(target, mem_addr); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write 4 bytes Memory Address : %d", ret); + goto inactivate_ss; + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto end; + } + +inactivate_ss: + ret = inactivate_spi_ss(target); + if (ret != ERROR_OK) + LOG_ERROR("Failed to inactivate SPI SS : %d", ret); + +end: + return ret; +} + +static int qspi_erase_sector(struct flash_bank *bank, unsigned int sector) +{ + int ret; + struct target *target = bank->target; + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + + ret = set_write_enable(target, scqspi_info->spi_ss); + if (ret != ERROR_OK) + goto end; + + ret = erase_sector(bank, sector); + if (ret != ERROR_OK) + goto end; + + alive_sleep(SCQSPI_ERASE_BLOCK_WAIT_MS); + + if (!is_write_disable(target, scqspi_info->spi_ss)) + ret = ERROR_FAIL; + +end: + return ret; +} + +static int write_data_to_flash(struct target *target, const uint8_t *data, uint32_t size) +{ + int ret; + + for (int i = 0; i < size; i++) { + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, data[i]); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write TX Data"); + break; + } + } + return ret; +} + +static int scqspi_memory_data_write(struct target *target, uint32_t spi_ss, uint32_t pprog_cmd, + uint32_t mem_addr, uint32_t write_size, + const uint8_t *write_data) +{ + int ret; + + ret = activate_spi_ss(target, spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to activate SPI SS : %d", ret); + goto end; + } + + LOG_DEBUG("Send Page program instruction : 0x%08x", pprog_cmd); + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, pprog_cmd); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to send `Page program (4byte)` instruction"); + goto inactivate_ss; + } + + LOG_DEBUG("Send Memory Address (4byte)"); + ret = write_mem_addr_to_flash(target, mem_addr); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write memory address to flash"); + goto inactivate_ss; + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto inactivate_ss; + } + + ret = write_data_to_flash(target, write_data, write_size); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write data to flash : %d", ret); + goto inactivate_ss; + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto end; + } + +inactivate_ss: + ret = inactivate_spi_ss(target); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to inactivate SPI SS : %d", ret); + goto end; + } + + if (!is_qspi_control_done(target)) { + LOG_ERROR("Confirm SPI Control is Done failed"); + ret = ERROR_FAIL; + } + +end: + return ret; +} + +static int write_data(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, + uint32_t count) +{ + int ret; + struct target *target = bank->target; + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + uint32_t pprog_cmd = scqspi_info->dev.pprog_cmd; + uint32_t remaining; + uint32_t mem_addr; + uint32_t chunk_size; + uint32_t bufpos = 0; + + if (scqspi_info->dev.pprog_cmd == 0x00) { + LOG_ERROR("Program page not available for this device"); + ret = ERROR_FLASH_OPER_UNSUPPORTED; + goto end; + } + + remaining = count; + mem_addr = scqspi_info->flash_addr + offset; + + while (remaining > 0) { + chunk_size = + remaining > SCQSPI_PAGE_BUFFER_BYTE ? SCQSPI_PAGE_BUFFER_BYTE : remaining; + + LOG_DEBUG("Write %d byte [0x%02x] to 0x%08x from bufpos %d (remaing %d byte)", + chunk_size, buffer[bufpos], mem_addr, bufpos, remaining); + + LOG_DEBUG("Set to `Write Enable'"); + ret = set_write_enable(target, scqspi_info->spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to set write enable : %d", ret); + break; + } + + LOG_DEBUG("Write Data"); + ret = scqspi_memory_data_write(target, scqspi_info->spi_ss, pprog_cmd, mem_addr, + chunk_size, (buffer + bufpos)); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write data to flash at address 0x%08x : %d", mem_addr, + ret); + break; + } + + offset += SCQSPI_PAGE_BUFFER_BYTE; + + if (!is_write_disable(target, scqspi_info->spi_ss)) { + ret = ERROR_FAIL; + break; + } + + mem_addr += chunk_size; + bufpos += chunk_size; + remaining -= chunk_size; + } + +end: + return ret; +} + +/* ------------------------------------------------------------------------- + * Command handler functions + * -------------------------------------------------------------------------*/ + +/* + * @brief Flash bank command handler for SC QSPI NOR devices. + * + * Syntax: + * flash bank scqspi <base> <size> <chip_width> <bus_width> <target#> <flash + * start addr> <mem_no> + * + * <base> : Base address of the flash + * <size> : Auto (0) + * <chip_width> : Auto (0) + * <bus_width> : Auto (0) + * <target#> : Target to flash + * <flash start addr> : Flash address where to start writing + * <mem_no> : Configuration flash memory number + */ +FLASH_BANK_COMMAND_HANDLER(scqspi_flash_bank_command) +{ + struct scqspi_flash_bank *scqspi_info; + + if (CMD_ARGC < 8) + return ERROR_COMMAND_SYNTAX_ERROR; + + scqspi_info = malloc(sizeof(struct scqspi_flash_bank)); + if (!scqspi_info) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + bank->driver_priv = scqspi_info; + scqspi_info->probed = false; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], scqspi_info->flash_addr); + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[7], scqspi_info->mem_no); + /* For Configuration NOR flash, always 0x01 */ + scqspi_info->spi_ss = 0x01; + + return ERROR_OK; +} + +static int scqspi_erase(struct flash_bank *bank, unsigned int first, unsigned int last) +{ + int ret; + struct target *target = bank->target; + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + unsigned int sector; + + LOG_INFO("%s: from sector %u to sector %u (base: 0x%08x)", __func__, first, last, + scqspi_info->flash_addr); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(scqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (scqspi_info->dev.erase_cmd == 0x00) { + LOG_ERROR("Sector erase not available for this device"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + if ((last < first) || (last >= bank->num_sectors)) { + LOG_ERROR("Flash sector invalid"); + return ERROR_FLASH_SECTOR_INVALID; + } + + for (sector = first; sector <= last; sector++) { + if (bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + ret = ERROR_FLASH_PROTECTED; + goto end; + } + } + + for (sector = first; sector <= last; sector++) { + ret = qspi_erase_sector(bank, sector); + if (ret != ERROR_OK) { + LOG_ERROR("Flash sector_erase failed on sector %u : %d", sector, ret); + break; + } + LOG_INFO("Flash sector %u offset %d", sector, bank->sectors[sector].offset); + alive_sleep(SCQSPI_ERASE_SECTOR_WAIT_MS); + } + +end: + return ret; +} + +static int scqspi_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, + uint32_t count) +{ + int ret; + struct target *target = bank->target; + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + + LOG_INFO("%s: offset=0x%08x count=0x%08x (base: 0x%08x)", __func__, offset, count, + scqspi_info->flash_addr); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + ret = ERROR_TARGET_NOT_HALTED; + goto end; + } + + if (!(scqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + ret = ERROR_FLASH_BANK_NOT_PROBED; + goto end; + } + + if (offset + count > bank->size) { + LOG_ERROR("Flash access out of range: offset=%u, count=%u, bank_size=%u", offset, + count, bank->size); + return ERROR_FLASH_DST_OUT_OF_BANK; + } + + /* Check sector protection */ + for (uint32_t sector = 0; sector < bank->num_sectors; sector++) { + struct flash_sector *bs = &bank->sectors[sector]; + + if ((offset < (bs->offset + bs->size)) && ((offset + count - 1) >= bs->offset) && + bs->is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + ret = ERROR_FAIL; + goto end; + } + } + + ret = write_data(bank, buffer, offset, count); + if (ret != ERROR_OK) + LOG_ERROR("Failed to write data to flash : %d", ret); + +end: + return ret; +} + +static int scqspi_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + LOG_DEBUG("%s", __func__); + return ERROR_OK; +} + +static int scqspi_probe(struct flash_bank *bank) +{ + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + struct flash_sector *sectors; + uint32_t id = 0; + + int ret; + + if (scqspi_info->probed) + free(bank->sectors); + + scqspi_info->probed = false; + + ret = scqspi_init(bank); + if (ret != ERROR_OK) + goto end; + + ret = scqspi_read_id(bank, &id); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read flash ID"); + goto end; + } + + memset(&scqspi_info->dev, 0, sizeof(scqspi_info->dev)); + bool found = false; + for (const struct flash_device *p = flash_devices; p->name; p++) { + if (p->device_id == id) { + memcpy(&scqspi_info->dev, p, sizeof(scqspi_info->dev)); + found = true; + break; + } + } + + if (!found) { + LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id); + ret = ERROR_FAIL; + goto end; + } + + LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")", scqspi_info->dev.name, + scqspi_info->dev.device_id); + + bank->size = scqspi_info->dev.size_in_bytes; + LOG_DEBUG("bank size %d", scqspi_info->dev.size_in_bytes); + + if (scqspi_info->dev.sectorsize == 0) { + LOG_ERROR("No sectors found for this device"); + ret = ERROR_FAIL; + goto end; + } + if (scqspi_info->dev.pagesize == 0) { + LOG_ERROR("No page size found for this device"); + ret = ERROR_FAIL; + goto end; + } + + /* Create and fill sectors array */ + bank->num_sectors = scqspi_info->dev.size_in_bytes / scqspi_info->dev.sectorsize; + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (!sectors) { + LOG_ERROR("not enough memory"); + ret = ERROR_FAIL; + goto end; + } + + for (uint32_t sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * scqspi_info->dev.sectorsize; + sectors[sector].size = scqspi_info->dev.sectorsize; + sectors[sector].is_erased = -1; + sectors[sector].is_protected = 0; + } + + bank->sectors = sectors; + scqspi_info->probed = true; + +end: + return ret; +} + +static int scqspi_auto_probe(struct flash_bank *bank) +{ + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + + if (scqspi_info->probed) + return ERROR_OK; + + return scqspi_probe(bank); + ; +} + +static int scqspi_info(struct flash_bank *bank, struct command_invocation *cmd) +{ + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + + if (!(scqspi_info->probed)) { + command_print_sameline(cmd, "QSPI flash bank not probed yet"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + command_print_sameline( + cmd, + "flash \'%s\', device id = 0x%06" PRIx32 ", flash size = %" PRIu32 + "%sB(page size = %" PRIu32 ", read = 0x%02" PRIx8 ", qread = 0x%02" PRIx8 + ", pprog = 0x%02" PRIx8 ", mass_erase = 0x%02" PRIx8 ", sector size = %" PRIu32 + " %sB, sector_erase = 0x%02" PRIx8 ")", + scqspi_info->dev.name, scqspi_info->dev.device_id, + bank->size / 4096 ? bank->size / 1024 : bank->size, bank->size / 4096 ? "Ki" : "", + scqspi_info->dev.pagesize, scqspi_info->dev.read_cmd, scqspi_info->dev.qread_cmd, + scqspi_info->dev.pprog_cmd, scqspi_info->dev.chip_erase_cmd, + scqspi_info->dev.sectorsize / 4096 ? scqspi_info->dev.sectorsize / 1024 + : scqspi_info->dev.sectorsize, + scqspi_info->dev.sectorsize / 4096 ? "Ki" : "", scqspi_info->dev.erase_cmd); + + return ERROR_OK; +} + +static int scqspi_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last) +{ + unsigned int sector; + + for (sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + + if (set) { + LOG_WARNING("setting soft protection only, not related to flash's hardware " + "write protection"); + } + + return ERROR_OK; +} + +static int scqspi_verify(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, + uint32_t count) +{ + /* Not implemented */ + return ERROR_OK; +} + +static const struct command_registration scqspi_command_handlers[] = { + { + .name = "scqspi", + .mode = COMMAND_ANY, + .help = "scqspi flash command group", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver scqspi_flash = { + .name = "scqspi", + .commands = scqspi_command_handlers, + .flash_bank_command = scqspi_flash_bank_command, + .erase = scqspi_erase, + .protect = scqspi_protect, + .write = scqspi_write, + .read = scqspi_read, + .verify = scqspi_verify, + .probe = scqspi_probe, + .auto_probe = scqspi_auto_probe, + .info = scqspi_info, + .free_driver_priv = default_flash_free_driver_priv, +}; -- |
|
From: <ge...@op...> - 2025-10-02 04:09:32
|
This is an automated email from Gerrit. "Gaetan Perrot <gae...@sp...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9024 -- gerrit commit 1137dfae956cb785669018b262137362adef0886 Author: Gaetan Perrot <gae...@sp...> Date: Fri Jun 27 16:16:06 2025 +0900 flash: scqspi: Add support for verify in SC-QSPI driver This commit adds a verify function to the SC-QSPI flash driver for the SC-OBC Module A1. The verify routine reads back data from the flash and compares it against a provided buffer, allowing validation of programmed contents. This is useful during firmware updates, flash self-tests, and development. The implementation leverages the previously added read functionality and performs a byte-wise comparison between flash and memory data. Change-Id: Ibe84080df3af59cdd469ee5e8b8231e4b49b39aa Signed-off-by: Gaetan Perrot <gae...@sp...> diff --git a/src/flash/nor/scqspi.c b/src/flash/nor/scqspi.c index a5109e19f2..12efb76378 100644 --- a/src/flash/nor/scqspi.c +++ b/src/flash/nor/scqspi.c @@ -11,6 +11,7 @@ #include "imp.h" #include "spi.h" #include <helper/bits.h> +#include <helper/crc32.h> #include <helper/time_support.h> #include <target/image.h> @@ -86,9 +87,9 @@ static bool verify(struct target *target, uint32_t addr, uint32_t exp, uint32_t uint32_t retry) { uint32_t val; - int32_t ret; + int ret; - for (uint32_t i = 0; i <= retry; i++) { + for (int i = 0; i <= retry; i++) { ret = target_read_u32(target, addr, &val); if (ret != ERROR_OK) { LOG_ERROR("Faild to read the register value: %d", ret); @@ -877,6 +878,73 @@ end: return ret; } +static uint32_t calculate_image_crc32(const uint8_t *data, size_t len) +{ + uint32_t crc = SCQSPI_CRC32_INIT; + + while (len > 0) { + size_t chunk = len > SCQSPI_VERIFY_CHUNK_SIZE ? SCQSPI_VERIFY_CHUNK_SIZE : len; + crc = crc32_le(CRC32_POLY_LE, crc, data, chunk); + data += chunk; + len -= chunk; + } + + return SCQSPI_CRC32_FINAL(crc); +} + +static int calculate_flash_crc32(struct flash_bank *bank, uint32_t offset, size_t len, + uint32_t *crc_out) +{ + uint8_t block_buf[SCQSPI_VERIFY_CHUNK_SIZE]; + uint32_t crc = SCQSPI_CRC32_INIT; + int ret; + + while (len > 0) { + size_t chunk = len > SCQSPI_VERIFY_CHUNK_SIZE ? SCQSPI_VERIFY_CHUNK_SIZE : len; + + ret = scqspi_nor_quad_read(bank, block_buf, offset, chunk); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read flash at offset 0x%08" PRIx32, offset); + goto end; + } + + crc = crc32_le(CRC32_POLY_LE, crc, block_buf, chunk); + offset += chunk; + len -= chunk; + } + + *crc_out = SCQSPI_CRC32_FINAL(crc); + +end: + return ret; +} + +static int scqspi_nor_verify(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, + uint32_t count) +{ + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + uint32_t expected_crc = calculate_image_crc32(buffer, count); + uint32_t actual_crc; + int ret; + + LOG_DEBUG("Verify %d bytes from 0x%08x", count, (scqspi_info->flash_addr + offset)); + + ret = calculate_flash_crc32(bank, offset, count, &actual_crc); + if (ret != ERROR_OK) + goto end; + + if (actual_crc == expected_crc) + ret = ERROR_OK; + else { + LOG_ERROR("CRC mismatch : expected 0x%08" PRIx32 ", got 0x%08" PRIx32, expected_crc, + actual_crc); + ret = ERROR_FAIL; + } + +end: + return ret; +} + /* ------------------------------------------------------------------------- * Command handler functions * -------------------------------------------------------------------------*/ @@ -1186,10 +1254,39 @@ static int scqspi_protect(struct flash_bank *bank, int set, unsigned int first, } static int scqspi_verify(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, - uint32_t count) + uint32_t count) { - /* Not implemented */ - return ERROR_OK; + struct target *target = bank->target; + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + + int ret; + + LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32, __func__, offset, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + ret = ERROR_TARGET_NOT_HALTED; + goto end; + } + + if (!(scqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + ret = ERROR_FLASH_BANK_NOT_PROBED; + goto end; + } + + if (offset + count > bank->size) { + LOG_ERROR("Flash access out of range: offset=%u, count=%u, bank_size=%u", offset, + count, bank->size); + return ERROR_FLASH_DST_OUT_OF_BANK; + } + + ret = scqspi_nor_verify(bank, buffer, offset, count); + if (ret != ERROR_OK) + LOG_ERROR("Failed to read data on flash : %d", ret); + +end: + return ret; } static const struct command_registration scqspi_command_handlers[] = { -- |
|
From: <ge...@op...> - 2025-10-02 04:09:31
|
This is an automated email from Gerrit. "Gaetan Perrot <gae...@sp...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9023 -- gerrit commit 2ffee023a13e5680148e701f134e0a2eb18566d2 Author: Gaetan Perrot <gae...@sp...> Date: Thu Jun 26 16:05:39 2025 +0900 flash: scqspi: Add support for read in SC-QSPI driver This commit adds the read operation to the SC-QSPI driver for the SC-OBC Module A1 using the Quad I/O Read (QREAD) command. The read implementation uses the `0xEC` opcode, which enables fast quad read access to the `S25FL256L` NOR flash devices through the custom QSPI controller. The command supports 4-byte addressing and delivers high-speed read throughput suitable for application-level data access. This feature is required for enabling flash verification, logging, and loading data from non-volatile storage during runtime. Change-Id: Ia8d58a0014e8198ebd703501a796e34713c5d00c Signed-off-by: Gaetan Perrot <gae...@sp...> diff --git a/src/flash/nor/scqspi.c b/src/flash/nor/scqspi.c index 1dee168a12..a5109e19f2 100644 --- a/src/flash/nor/scqspi.c +++ b/src/flash/nor/scqspi.c @@ -60,6 +60,7 @@ #define SCQSPI_ERASE_BLOCK_WAIT_MS (800U) #define SCQSPI_ERASE_SECTOR_WAIT_MS (10U) #define SCQSPI_PAGE_BUFFER_BYTE (256U) +#define SCQSPI_DUMMY_CYCLE_COUNT (4U) #define SCQSPI_REG_READ_RETRY(count) (count) #define SCQSPI_CRC32_INIT (0xFFFFFFFF) #define SCQSPI_CRC32_FINAL(crc) (~(crc)) @@ -719,6 +720,163 @@ end: return ret; } +static int read_data_from_flash(struct target *target, const uint8_t *read_data, uint32_t read_size) +{ + int ret; + uint32_t bytes_transferred = 0; + + LOG_DEBUG("Read %u bytes from QSPI flash", read_size); + + while (bytes_transferred < read_size) { + uint32_t chunk = MIN(SCQSPI_RX_FIFO_MAX_BYTE, read_size - bytes_transferred); + + for (uint32_t byte_idx = 0; byte_idx < chunk; byte_idx++) { + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_RDR, 0x00); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to request RX data at index %u", + bytes_transferred + byte_idx); + goto end; + } + } + + for (uint32_t byte_idx = 0; byte_idx < chunk; byte_idx++) { + uint8_t byte; + ret = target_read_u8(target, SCOBCA1_FPGA_NORFLASH_QSPI_RDR, &byte); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read RX data at index %u", + bytes_transferred + byte_idx); + goto end; + } + ((uint8_t *)read_data)[bytes_transferred + byte_idx] = byte; + } + + bytes_transferred += chunk; + } + +end: + return ret; +} + +static int send_dummy_cycle(struct target *target, uint8_t dummy_count) +{ + uint32_t discard; + int ret; + + LOG_DEBUG("Send dummy cycle %d byte", dummy_count); + for (int i = 0; i < dummy_count; i++) { + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_RDR, 0x00); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to request RX data"); + goto end; + } + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto end; + } + + LOG_DEBUG("Discard dummy data"); + for (int i = 0; i < dummy_count; i++) { + ret = target_read_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_RDR, &discard); + if (ret != ERROR_OK) + goto end; + } + +end: + return ret; +} + +static int scqspi_nor_quad_read(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, + uint32_t count) +{ + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t mem_addr = scqspi_info->flash_addr + offset; + /* + * Workaround: scqspi_info->dev.qread_cmd is set to 0x00 by default, + * but for our NOR flash (S25FL256L), the correct Quad Read command is 0xEC. + * This is based on the datasheet. + * Reason for the mismatch is unclear, so we override it manually here. + */ + uint32_t qread_cmd = 0xEC; + int ret; + + LOG_DEBUG("Read %d bytes from 0x%08x", count, mem_addr); + + ret = activate_spi_ss(target, scqspi_info->spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to activate SPI SS : %d", ret); + goto end; + } + + LOG_DEBUG("Send Quad I/O Read instruction : 0x%08x", qread_cmd); + ret = target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, qread_cmd); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to send `Quad I/O Read (4byte)` instruction"); + goto inactivate_ss; + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto inactivate_ss; + } + + ret = activate_spi_ss(target, SCQSPI_SPI_MODE_QUAD + scqspi_info->spi_ss); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to activate SPI SS : %d", ret); + goto inactivate_ss; + } + + LOG_DEBUG("Send Memory Address (4byte)"); + ret = write_mem_addr_to_flash(target, mem_addr); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to write memory address to flash"); + goto inactivate_ss; + } + + LOG_DEBUG("Send Mode (0x00)"); + target_write_u32(target, SCOBCA1_FPGA_NORFLASH_QSPI_TDR, 0x00); + + LOG_DEBUG("Send Dummy Cycle"); + ret = send_dummy_cycle(target, SCQSPI_DUMMY_CYCLE_COUNT); + if (ret != ERROR_OK) { + LOG_ERROR("Failed send dummy cycle to flash"); + goto inactivate_ss; + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto inactivate_ss; + } + + ret = read_data_from_flash(target, buffer, count); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read data to flash : %d", ret); + goto inactivate_ss; + } + + if (!is_qspi_idle(target)) { + ret = ERROR_FLASH_BUSY; + goto inactivate_ss; + } + +inactivate_ss: + ret = inactivate_spi_ss(target); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to inactivate SPI SS : %d", ret); + goto end; + } + + if (!is_qspi_control_done(target)) { + LOG_ERROR("Confirm SPI Control is Done failed"); + ret = ERROR_FAIL; + } + +end: + return ret; +} + /* ------------------------------------------------------------------------- * Command handler functions * -------------------------------------------------------------------------*/ @@ -863,8 +1021,37 @@ end: static int scqspi_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) { - LOG_DEBUG("%s", __func__); - return ERROR_OK; + int ret; + struct target *target = bank->target; + struct scqspi_flash_bank *scqspi_info = bank->driver_priv; + + LOG_DEBUG("%s: offset=0x%08x count=0x%08x (base: 0x%08x)", __func__, offset, count, + scqspi_info->flash_addr); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + ret = ERROR_TARGET_NOT_HALTED; + goto end; + } + + if (!(scqspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + ret = ERROR_FLASH_BANK_NOT_PROBED; + goto end; + } + + if (offset + count > bank->size) { + LOG_ERROR("Flash access out of range: offset=%u, count=%u, bank_size=%u", offset, + count, bank->size); + return ERROR_FLASH_DST_OUT_OF_BANK; + } + + ret = scqspi_nor_quad_read(bank, buffer, offset, count); + if (ret != ERROR_OK) + LOG_ERROR("Failed to read data on flash : %d", ret); + +end: + return ret; } static int scqspi_probe(struct flash_bank *bank) -- |