diff --git a/docs/hic/README.md b/docs/hic/README.md index 978c0ad11f..d179c6d2f0 100644 --- a/docs/hic/README.md +++ b/docs/hic/README.md @@ -22,3 +22,4 @@ Support for these HICs has been recently added. |-------------------------------|:----:|--------:|-------:|-------:|:---:| | [stm32f072x8](stm32f072xx.md) | M0 | 48 Mhz | 16 KB | 64 KB | FS | | [stm32f072xb](stm32f072xx.md) | M0 | 48 Mhz | 16 KB | 128 KB | FS | +| [stm32h743xx](stm32h743xx.md) | M7 | 480 Mhz | 1 MB | 2 MB | HS | diff --git a/docs/hic/stm32h743xx.md b/docs/hic/stm32h743xx.md new file mode 100644 index 0000000000..b89f627ae2 --- /dev/null +++ b/docs/hic/stm32h743xx.md @@ -0,0 +1,50 @@ +# stm32h743xx HIC + +Based on STM32H743IIK6TR chip ([Datasheet](https://www.st.com/resource/en/datasheet/stm32h743ii.pdf)): +- Cortex-M7 480 Mhz +- 2 MBytes Flash +- 1 MByte RAM +- High-speed USB 2.0 device controller: up to 9 bi-directional endpoints including EP0 (*) +- UFBGA176+25 packaging + +(*) "main features in peripheral-mode are the following: 1 bidirectional control endpoint0, 8 IN endpoints (EPs) configurable to support bulk, interrupt or isochronous transfers, 8 OUT endpoints configurable to support bulk, interrupt or isochronous transfers" (source: [RM0433](https://www.st.com/resource/en/reference_manual/rm0433-stm32h742-stm32h743753-and-stm32h750-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf)) + +## Memory Map + +| Region | Size | Start | End | +|----------|--------|-------------|-------------| +| Flash | 2048 KB| 0x0800_0000 | 0x1FF0_0000 | +| DTCM | 128 KB | 0x2000_0000 | 0x2400_0000 | +| AXI-SRAM | 384 KB | 0x2400_0000 | 0x3000_0000 | +| SRAM1 | 32 KB | 0x3000_0000 | 0x3002_0000 | +| SRAM2 | 16 KB | 0x3002_0000 | 0x3800_0000 | +| SRAM4 | 64 KB | 0x3800_0000 | 0x3880_0000 | + +Bootloader size is 128 KB + +## DAPLink default pin assignment + +| Signal | I/O | Symbol | Pin | +|-------------|:---:|---------|:---:| +| SWD / JTAG | +| SWCLK / TCK | O | PD7 | 56 | +| SWDIO / TMS | I/O | PD2 | 51 | +| SWO / TDO | I | PB7 | 24 | +| nRESET | O | PB0 | 17 | +| UART | +| UART RX | I | PD9 | 58 | +| UART TX | O | PD8 | 57 | +| Button | +| NF-RST But. | I | PI4 | 132 | +| LEDs | +| Connect. LED| O | PG0 | 96 | +| HID LED | O | PG2 | 98 | +| CDC LED | O | PG3 | 99 | +| MSC LED | O | PG4 | 100 | + +## UDB (Universal Debug Board) Additions +UDB is a special flavor of the stm32h743xx HIC, which enriches the DAPLink experience with additional features such as: +- A new USB CDC endpoint to flash and get logs from a second target. +- Another new USB CDC endpoint to interact with the HIC. Users can send easy shell commands to toggle pins, or to get any info from the HIC. +- Additional DAPLink debugging functionality: persistent fault info, breakpoints on asserts when debugger is connected to debug UDB, watchdog, backtraces +- Target power measurement diff --git a/projects.yaml b/projects.yaml index 8c921eff45..ed3849d855 100644 --- a/projects.yaml +++ b/projects.yaml @@ -109,6 +109,10 @@ module: - records/rtos/rtos-cm3.yaml - records/hic_hal/stm32f103xb.yaml - records/usb/usb-hid.yaml + hic_stm32h743xx: &module_hic_stm32h743xx + - records/rtos/rtos-cm4.yaml + - records/hic_hal/stm32h743xx.yaml + - records/usb/usb-hid.yaml projects: # HIC bootloaders and all target interfaces @@ -220,6 +224,16 @@ projects: - *module_hic_stm32f103xb - records/family/all_family.yaml - records/usb/usb-bulk.yaml + stm32h743xx_bl: &stm32h743xx_bl + - *module_bl + - records/hic_hal/stm32h743xx.yaml + - records/board/stm32h743xx_bl.yaml + stm32h743xx_if: &stm32h743xx_if + - *module_if + - *module_hic_stm32h743xx + - records/family/all_family.yaml + - records/usb/usb-bulk.yaml + # Other projects k20dx_ep_agora_if: @@ -618,6 +632,15 @@ projects: - *module_if - *module_hic_stm32f103xb - records/board/ublox_evk_odin_w2.yaml + stm32h743xx_udb_bl: + - *stm32h743xx_bl + - records/board/stm32h743xx_udb_bl.yaml + stm32h743xx_udb_if: + - *module_if + - *module_hic_stm32h743xx + - records/family/all_family.yaml + - records/usb/usb-bulk.yaml + - records/board/stm32h743xx_udb_if.yaml # extends DAPLink with additional features for the UDB project # Test projects diff --git a/records/board/stm32h743xx_bl.yaml b/records/board/stm32h743xx_bl.yaml new file mode 100644 index 0000000000..3ae62e229c --- /dev/null +++ b/records/board/stm32h743xx_bl.yaml @@ -0,0 +1,4 @@ +common: + sources: + board: + - source/board/stm32h743xx_bl.c diff --git a/records/board/stm32h743xx_udb_bl.yaml b/records/board/stm32h743xx_udb_bl.yaml new file mode 100644 index 0000000000..f52bc312be --- /dev/null +++ b/records/board/stm32h743xx_udb_bl.yaml @@ -0,0 +1,12 @@ +common: + macros: + - UDB + includes: + - source/board/udb/include/udb_version.h + sources: + hic_hal: + - source/board/udb/source/udb_version.c +tool_specific: + gcc_arm: + linker_file: + - source/board/udb/include/udb.ld diff --git a/records/board/stm32h743xx_udb_if.yaml b/records/board/stm32h743xx_udb_if.yaml new file mode 100644 index 0000000000..74f4fef299 --- /dev/null +++ b/records/board/stm32h743xx_udb_if.yaml @@ -0,0 +1,30 @@ +common: + macros: + # Add DAPLink macros below + - MAIN_TASK_STACK=2048 + # - DAPLINK_BOOTLOADER_UPDATE + - ASSERT_BUF_SIZE=65 + - USB_CDC_ACM_EP_COUNT=3 + - OVERRIDE_USB2UART + - DISABLE_DRAG_N_DROP + # Add UDB config macros below + - UDB + - SUPPORT_CMD_HISTORY + - NLUIF_PRINT_RESULT + # Add build feature macros below + - BUILD_FEATURE_UDB_ASSERT + includes: + # - projectfiles/make_gcc_arm/stm32h743xx_udb_bl/build/bootloader_image.c + - source/board/udb/console/include + - source/board/udb/include + - source/board/udb/lib/nlbacktrace/include + - source/board/udb/lib/nlutilities/include + sources: + board: + - source/board/stm32h743xx.c + hic_hal: + - source/board/udb/console/source + - source/board/udb/lib/nlbacktrace/source + - source/board/udb/lib/nlutilities/source + - source/board/udb/source + - source/board/udb/usb \ No newline at end of file diff --git a/records/hic_hal/stm32h743xx.yaml b/records/hic_hal/stm32h743xx.yaml new file mode 100644 index 0000000000..1b3ece8f3f --- /dev/null +++ b/records/hic_hal/stm32h743xx.yaml @@ -0,0 +1,66 @@ +common: + target: + - Cortex-M7 + core: + - Cortex-M7 + macros: + - INTERFACE_STM32H743 + - USE_HAL_DRIVER + - STM32H743xx + - DAPLINK_HIC_ID=0x97969940 # DAPLINK_HIC_ID_STM32H743II + - __packed=__packed # Prevent redefinition of __packed with ARMCC + - DAPLINK_NO_ASSERT_FILENAMES + - OS_CLOCK=240000000 + - OS_ROBIN_ENABLE=1 + - SECTOR_BUFFER_SIZE=32 # this is a small write buffer used in settings_rom.c + - DAPLINK_RELOCATE_SECTOR_BUF # this is relocating another sector buffer in iap_flash_intf.c which has to hold an entire sector and doesn't fit into our default DTCM RAM + includes: + - source/hic_hal/stm32/hal/STM32H7xx_HAL_Driver + - source/hic_hal/stm32/hal/STM32H7xx_HAL_Driver/Inc + - source/hic_hal/stm32/stm32h743xx + - source/hic_hal/stm32/stm32h743xx/cmsis + + sources: + hic_hal: + - source/hic_hal/stm32/hal/STM32H7xx_HAL_Driver/Src + - source/hic_hal/stm32/stm32h743xx + - source/hic_hal/stm32/stm32h743xx/cmsis + +tool_specific: + uvision: + misc: + ld_flags: + - --predefine="-I..\..\..\source\hic_hal\stm32\stm32h743xx" + c_flags: + - --no_unaligned_access + cxx_flags: + - --no_unaligned_access + asm_flags: + - --no_unaligned_access + sources: + hic_hal: + - source/hic_hal/stm32/stm32h743xx/armcc + armcc: + misc: + ld_flags: + - --predefine="-Isource/hic_hal/stm32/stm32h743xx" + c_flags: + - --no_unaligned_access + cxx_flags: + - --no_unaligned_access + asm_flags: + - --no_unaligned_access + sources: + hic_hal: + - source/hic_hal/stm32/stm32h743xx/armcc + gcc_arm: + misc: + ld_flags: + # for some reason GCC links in the non-thumb version, + # so need to manually tell it to use the right one + - -L /usr/lib/arm-none-eabi/newlib/thumb/ + sources: + hic_hal: + - source/hic_hal/stm32/stm32h743xx/gccarm + linker_file: + - source/hic_hal/stm32/stm32h743xx/stm32h743xx.ld \ No newline at end of file diff --git a/source/board/stm32h743xx.c b/source/board/stm32h743xx.c new file mode 100644 index 0000000000..be39c7bd63 --- /dev/null +++ b/source/board/stm32h743xx.c @@ -0,0 +1,166 @@ +/** + * @file stm32h743xx.c + * @brief board ID for the STM32 NUCLEO-F103RB board + * + * DAPLink Interface Firmware + * Copyright (c) 2009-2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "target_family.h" +#include "target_board.h" +#include + +#ifdef UDB +#include "i2c.h" +#include "udb_version.h" +#include "udb_extended_features_task.h" +#include "udb_power_measurement.h" +#include "udb_log.h" +#include "udb_version.h" +#include "nluif_udb-daplink.h" +#include "util.h" +#include "udb_fault_info.h" +#include "udb_watchdog.h" +#include "udb_fault_info.h" +#include "udb_uptime.h" + +#define UDB_WATCHDOG_TIMEOUT_S (10U) + +#define UDB_30MS_MULTIPLIER_TO_30SEC (1000) +#define UDB_30MS_MULTIPLIER_TO_10MIN (1000 * 20) + +#define UDB_LED_30MS_MULTIPLER_NORMAL (50ULL) +#define UDB_LED_30MS_MULTIPLER_UNCLEARED_FAULT_INFO (5ULL) + +#ifdef UDB_DEBUG +#define UDB_CHECK_FAULT_INFO_TARGET_COUNT UDB_30MS_MULTIPLIER_TO_30SEC +#else +#define UDB_CHECK_FAULT_INFO_TARGET_COUNT UDB_30MS_MULTIPLIER_TO_10MIN +#endif + +static uint64_t s_30ms_hook_counter = 0; +static uint64_t s_connected_led_blink_period = UDB_LED_30MS_MULTIPLER_NORMAL; + +extern void udb_config_init(void); + +static void udb_welcome_message(void) +{ + char ver_buf[UDB_VERSION_MAX_LENGTH]; + + printf("Welcome to\n"); + printf("\t ______ ______ \n"); + printf("\t|\\ /|( __ \\ ( ___ \\ \n"); + printf("\t| ) ( || ( \\ )| ( ) ) \n"); + printf("\t| | | || | ) || (__/ / \n"); + printf("\t| | | || | | || __ ( \n"); + printf("\t| | | || | ) || ( \\ \\ \n"); + printf("\t| (___) || (__/ )| )___) ) \n"); + printf("\t(_______)(______/ |/ \\___/ \n\n"); + + udb_get_interface_version((uint8_t*)ver_buf, UDB_VERSION_MAX_LENGTH); + printf("Interface version: %s\n", ver_buf); + udb_get_bootloader_version((uint8_t*)ver_buf, UDB_VERSION_MAX_LENGTH); + printf("Bootloader version: %s\n", ver_buf); + + printf("To know more about udb, visit go/udb.\n"); + printf("Please report issues at go/udb-bug.\n"); + printf("You can start typing commands.\n"); + + uif_prompt(); +} + +static void prerun_board_config(void) +{ + int status; + + udb_uptime_init(); + + status = i2c_init(); + util_assert(status == UDB_SUCCESS); + + udb_read_hw_version(); + udb_config_init(); + + udb_extended_features_task_create(); + + status = udb_power_measurement_init(); + util_assert(status == UDB_SUCCESS); + + udb_check_unexpected_watchdog_reset(); +} + +void board_30ms_hook() +{ + udb_uptime_update(); + if (udb_is_fault_info_uncleared()) + { + s_connected_led_blink_period = UDB_LED_30MS_MULTIPLER_UNCLEARED_FAULT_INFO; + } + else + { + s_connected_led_blink_period = UDB_LED_30MS_MULTIPLER_NORMAL; + } + + if (s_30ms_hook_counter == 0) + { + // Code that needs to run once after the bootloader update and all other + // initialization finished + + udb_watchdog_init(UDB_WATCHDOG_TIMEOUT_S); + udb_welcome_message(); + } + + udb_watchdog_refresh(); + + if ((s_30ms_hook_counter % s_connected_led_blink_period) == 0) + { + HAL_GPIO_TogglePin( CONNECTED_LED_PORT, CONNECTED_LED_PIN); + } + + if (((s_30ms_hook_counter % UDB_CHECK_FAULT_INFO_TARGET_COUNT) == 0) && udb_is_fault_info_uncleared()) + { + udb_print_fault_info(); + } + + if (udb_log_cdc_ready()) + { + udb_log_flush(); + } + + s_30ms_hook_counter++; +} +#endif // UDB + +// return non-zero value to make all image incompatible +uint8_t board_detect_incompatible_image(const uint8_t *data, uint32_t size) +{ + return 1; +} + +const board_info_t g_board_info = +{ + .info_version = kBoardInfoVersion, + .board_id = "0000", + .family_id = kStub_HWReset_FamilyID, + .daplink_drive_name = "DAPLINK_APP", + .prerun_board_config = prerun_board_config, + // TODO(https://github.com/ARMmbed/DAPLink/issues/963): + // MSC sometimes fails for unknown reason. + // Disable mass storage as a workaround temporarily. +#if 0 + .target_cfg = &target_device, +#endif +}; diff --git a/source/board/stm32h743xx_bl.c b/source/board/stm32h743xx_bl.c new file mode 100644 index 0000000000..bd504e9888 --- /dev/null +++ b/source/board/stm32h743xx_bl.c @@ -0,0 +1,60 @@ +/** + * @file stm32h743xx_bl.c + * @brief board ID and meta-data for the hardware interface circuit (HIC) based on STM32H743ii + * + * DAPLink Interface Firmware + * Copyright (c) 2009-2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "target_config.h" +#include "target_board.h" +#include "target_family.h" +#include "daplink_addr.h" + +/** +* List of start and size for each size of flash sector +* The size will apply to all sectors between the listed address and the next address +* in the list. +* The last pair in the list will have sectors starting at that address and ending +* at address start + size. +*/ +static const sector_info_t sectors_info[] = { + {DAPLINK_ROM_IF_START, DAPLINK_SECTOR_SIZE}, + }; + +// stm32h743ii target information +target_cfg_t target_device = { + .sectors_info = sectors_info, + .sector_info_length = (sizeof(sectors_info))/(sizeof(sector_info_t)), + .flash_regions[0].start = DAPLINK_ROM_IF_START, + .flash_regions[0].end = DAPLINK_ROM_IF_START + DAPLINK_ROM_IF_SIZE, + .flash_regions[0].flags = kRegionIsDefault, + .ram_regions[0].start = DAPLINK_RAM_APP_START, + .ram_regions[0].end = DAPLINK_RAM_APP_START + DAPLINK_RAM_APP_SIZE, + /* .flash_algo not needed for bootloader */ +}; + +//bootloader has no family +const target_family_descriptor_t *g_target_family = NULL; + +const board_info_t g_board_info = { + .info_version = kBoardInfoVersion, + .board_id = "0000", + .daplink_url_name = "HELP_FAQHTM", + .daplink_drive_name = "DAPLINK_BL", + .daplink_target_url = "https://mbed.com/daplink", + .target_cfg = &target_device, +}; diff --git a/source/board/udb/console/include/shell_cmd_adapter_type.h b/source/board/udb/console/include/shell_cmd_adapter_type.h new file mode 100644 index 0000000000..2620beda0b --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_adapter_type.h @@ -0,0 +1,10 @@ +#ifndef SHELL_COMMAND_ADAPTER_TYPE_H +#define SHELL_COMMAND_ADAPTER_TYPE_H + +#define UIF_CMD_ADAPTER_TYPE \ + DECLARE_UIF_CMD("adapter_type", 0, UIF_MAX_ARGS, 0, cmd_adapter_type, \ + "adapter flex type connected to the 60-pin UDC\t", "") \ + +void cmd_adapter_type(int argc, char *argv[]); + +#endif // SHELL_COMMAND_ADAPTER_TYPE_H diff --git a/source/board/udb/console/include/shell_cmd_btn.h b/source/board/udb/console/include/shell_cmd_btn.h new file mode 100644 index 0000000000..f3fd1b7eba --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_btn.h @@ -0,0 +1,20 @@ +#ifndef SHELL_CMD_BTN_H +#define SHELL_CMD_BTN_H + +#define UIF_CMD_BTN_MIN_ARGS (2) +#define UIF_CMD_BTN_MAX_ARGS (2) + +#define UIF_CMD_BTN \ + DECLARE_UIF_CMD("btn", UIF_CMD_BTN_MIN_ARGS, UIF_CMD_BTN_MAX_ARGS, 0, cmd_btn, \ + "The UDB board has a number of buttons to control gpio lines going to the DUTs. Use this command to remotely press the button.\n", " \n" \ + " - can be RST0_L, BOOT0_L, BTN0_L, RST1, BOOT1, BTN1\n" \ + " RST0_L - reset button for DUT 0 (active low)\n" \ + " BOOT0_L - boot button for DUT 0 (active low)\n" \ + " BTN0_L - user button for DUT 0 (active low)\n" \ + " RST1 - reset button for DUT 1 (active high)\n" \ + " BOOT1 - boot button for DUT 1 (active high)\n" \ + " BTN1 - user button for DUT 1 (active high)") \ + +void cmd_btn(int argc, char *argv[]); + +#endif // SHELL_CMD_BTN_H diff --git a/source/board/udb/console/include/shell_cmd_dut_reset.h b/source/board/udb/console/include/shell_cmd_dut_reset.h new file mode 100644 index 0000000000..2a269da79e --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_dut_reset.h @@ -0,0 +1,11 @@ +#ifndef SHELL_CMD_DUT_RESET_H +#define SHELL_CMD_DUT_RESET_H + +#define UIF_CMD_DUT_RESET \ + DECLARE_UIF_CMD("dut_reset", 1, 1, 0, cmd_dut_reset, \ + "Reset DUT", "\n" \ + " - can be 0 or 1") \ + +void cmd_dut_reset(int argc, char *argv[]); + +#endif // SHELL_CMD_DUT_RESET_H diff --git a/source/board/udb/console/include/shell_cmd_ext_relay.h b/source/board/udb/console/include/shell_cmd_ext_relay.h new file mode 100644 index 0000000000..1cc4a8e4f0 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_ext_relay.h @@ -0,0 +1,13 @@ +#ifndef SHELL_CMD_EXT_RELAY_H +#define SHELL_CMD_EXT_RELAY_H + +#define SHELL_CMD_EXT_RELAY_MIN_ARGS (1) +#define SHELL_CMD_EXT_RELAY_MAX_ARGS (1) + +#define UIF_CMD_EXT_RELAY \ + DECLARE_UIF_CMD("ext_relay", SHELL_CMD_EXT_RELAY_MIN_ARGS, SHELL_CMD_EXT_RELAY_MAX_ARGS, 0, cmd_ext_relay, \ + "control external relay", "on|off|status") \ + +void cmd_ext_relay(int argc, char *argv[]); + +#endif // SHELL_CMD_EXT_RELAY_H diff --git a/source/board/udb/console/include/shell_cmd_fault.h b/source/board/udb/console/include/shell_cmd_fault.h new file mode 100644 index 0000000000..c2eb07e003 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_fault.h @@ -0,0 +1,10 @@ +#ifndef SHELL_COMMAND_FAULT_H +#define SHELL_COMMAND_FAULT_H + +#define UIF_CMD_FAULT \ + DECLARE_UIF_CMD("fault", 1, UIF_MAX_ARGS, 0, cmd_fault, \ + "Fault testing command", "test_assert|test_watchdog") \ + +void cmd_fault(int argc, char *argv[]); + +#endif // SHELL_COMMAND_FAULT_H diff --git a/source/board/udb/console/include/shell_cmd_fault_info.h b/source/board/udb/console/include/shell_cmd_fault_info.h new file mode 100644 index 0000000000..2ef355a351 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_fault_info.h @@ -0,0 +1,15 @@ +#ifndef SHELL_CMD_FAULT_INFO_H +#define SHELL_CMD_FAULT_INFO_H + +#define UIF_CMD_FAULT_INFO_MIN_ARGS (1) +#define UIF_CMD_FAULT_INFO_MAX_ARGS (1) + +#define UIF_CMD_FAULT_INFO \ + DECLARE_UIF_CMD("fault_info", UIF_CMD_FAULT_INFO_MIN_ARGS, UIF_CMD_FAULT_INFO_MAX_ARGS, 0, cmd_fault_info, \ + "fault info utility", "print|clear\n" \ + " fault_info print - print fault info\n" \ + " fault_info clear - clear fault info\n") \ + +void cmd_fault_info(int argc, char *argv[]); + +#endif // SHELL_CMD_FAULT_INFO_H diff --git a/source/board/udb/console/include/shell_cmd_gpio.h b/source/board/udb/console/include/shell_cmd_gpio.h new file mode 100644 index 0000000000..8cfb483366 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_gpio.h @@ -0,0 +1,11 @@ +#ifndef SHELL_COMMAND_GPIO_H +#define SHELL_COMMAND_GPIO_H + +#define UIF_CMD_GPIO \ + DECLARE_UIF_CMD("gpio", 3, UIF_MAX_ARGS, 0, cmd_gpio, \ + "GPIO utility", \ + "read|in|pp_set|in_pu|in_pd|pp_clear|toggle ") \ + +void cmd_gpio(int argc, char *argv[]); + +#endif // SHELL_COMMAND_GPIO_H diff --git a/source/board/udb/console/include/shell_cmd_i2c.h b/source/board/udb/console/include/shell_cmd_i2c.h new file mode 100644 index 0000000000..dafa8dd9e9 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_i2c.h @@ -0,0 +1,17 @@ +#ifndef SHELL_COMMAND_I2C_H +#define SHELL_COMMAND_I2C_H + +#define UIF_CMD_I2C \ + DECLARE_UIF_CMD("i2c", 2, UIF_MAX_ARGS, 0, cmd_i2c, \ + "I2C2 utility", "probe |
[]\n" \ + " i2c probe - Probe for slaves\n" \ + " i2c
[] - Send and receive bits\n" \ + "\t - i2c bus (UDB only supports bus 2)\n" \ + "\t
- Chip address\n" \ + "\t - Register addr to start transfer at\n" \ + "\t - Number of bytes to xfer(read max: 32, write max: 4)\n" \ + "\t - Data to be sent. Omit if reading") \ + +void cmd_i2c(int argc, char *argv[]); + +#endif // SHELL_COMMAND_I2C_H diff --git a/source/board/udb/console/include/shell_cmd_measure_power.h b/source/board/udb/console/include/shell_cmd_measure_power.h new file mode 100644 index 0000000000..f78fdb8284 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_measure_power.h @@ -0,0 +1,11 @@ +#ifndef SHELL_COMMAND_MEASURE_POWER_H +#define SHELL_COMMAND_MEASURE_POWER_H + +#define UIF_CMD_MEASURE_POWER \ + DECLARE_UIF_CMD("measure_power", 0, UIF_MAX_ARGS, 0, cmd_measure_power, \ + "measure the voltage and current going through the type-c usb" \ + " and the usb on the debug adapter board connected to UDC\t", "") \ + +void cmd_measure_power(int argc, char *argv[]); + +#endif // SHELL_COMMAND_MEASURE_POWER_H diff --git a/source/board/udb/console/include/shell_cmd_pwm.h b/source/board/udb/console/include/shell_cmd_pwm.h new file mode 100644 index 0000000000..85de27fe22 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_pwm.h @@ -0,0 +1,11 @@ +#ifndef SHELL_COMMAND_PWM_H +#define SHELL_COMMAND_PWM_H + +#define UIF_CMD_PWM \ + DECLARE_UIF_CMD("pwm", 1, UIF_MAX_ARGS, 0, cmd_pwm, \ + "PWM utility", \ + "start |stop - Start to output TIM1 CH1 1.8V PWM to GPIOE 9 pin or stop it\n") \ + +void cmd_pwm(int argc, char *argv[]); + +#endif // SHELL_COMMAND_PWM_H diff --git a/source/board/udb/console/include/shell_cmd_reset.h b/source/board/udb/console/include/shell_cmd_reset.h new file mode 100644 index 0000000000..e6334e717e --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_reset.h @@ -0,0 +1,10 @@ +#ifndef SHELL_COMMAND_RESET_H +#define SHELL_COMMAND_RESET_H + +#define UIF_CMD_RESET \ + DECLARE_UIF_CMD("reset", 0, UIF_MAX_ARGS, 0, cmd_reset, \ + "Reset UDB", "") \ + +void cmd_reset(int argc, char *argv[]); + +#endif // SHELL_COMMAND_RESET_H diff --git a/source/board/udb/console/include/shell_cmd_reset_into_swu_mode.h b/source/board/udb/console/include/shell_cmd_reset_into_swu_mode.h new file mode 100644 index 0000000000..7fe1ff436c --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_reset_into_swu_mode.h @@ -0,0 +1,10 @@ +#ifndef SHELL_COMMAND_RESET_INTO_SWU_MODE_H +#define SHELL_COMMAND_RESET_INTO_SWU_MODE_H + +#define UIF_CMD_RESET_INTO_SWU_MODE \ + DECLARE_UIF_CMD("reset_into_swu_mode", 0, UIF_MAX_ARGS, 0, cmd_reset_into_swu_mode, \ + "Request to stay in bootloader mode for SWU\t", "") \ + +void cmd_reset_into_swu_mode(int argc, char *argv[]); + +#endif // SHELL_COMMAND_RESET_INTO_SWU_MODE_H diff --git a/source/board/udb/console/include/shell_cmd_swd_dut.h b/source/board/udb/console/include/shell_cmd_swd_dut.h new file mode 100644 index 0000000000..76a5317f5c --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_swd_dut.h @@ -0,0 +1,16 @@ +#ifndef SHELL_CMD_SWD_DUT_H +#define SHELL_CMD_SWD_DUT_H + +#define SHELL_CMD_SWD_DUT_MIN_ARGS (0) +#define SHELL_CMD_SWD_DUT_MAX_ARGS (1) + +#define UIF_CMD_SWD_DUT \ + DECLARE_UIF_CMD("swd_dut", SHELL_CMD_SWD_DUT_MIN_ARGS, SHELL_CMD_SWD_DUT_MAX_ARGS, 0, cmd_swd_dut, \ + "You can redirect SWD communication over UDB to either the default DUT0 or to the DUT1 connection. Use this command to switch between the two\n", "[0|1]\n" \ + " swd_dut 0 - use DUT 0 SWD\n" \ + " swd_dut 1 - use DUT 1 SWD\n" \ + " swd_dut - examine which DUT is in use") \ + +void cmd_swd_dut(int argc, char *argv[]); + +#endif // SHELL_CMD_SWD_DUT_H diff --git a/source/board/udb/console/include/shell_cmd_uptime.h b/source/board/udb/console/include/shell_cmd_uptime.h new file mode 100644 index 0000000000..61c1898786 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_uptime.h @@ -0,0 +1,10 @@ +#ifndef SHELL_CMD_UPTIME_H +#define SHELL_CMD_UPTIME_H + +#define UIF_CMD_UPTIME \ + DECLARE_UIF_CMD("uptime", 0, 0, 0, cmd_uptime, \ + "UDB uptime", "") \ + +void cmd_uptime(int argc, char *argv[]); + +#endif // SHELL_CMD_UPTIME_H diff --git a/source/board/udb/console/include/shell_cmd_version.h b/source/board/udb/console/include/shell_cmd_version.h new file mode 100644 index 0000000000..91279f2ce8 --- /dev/null +++ b/source/board/udb/console/include/shell_cmd_version.h @@ -0,0 +1,10 @@ +#ifndef SHELL_COMMAND_VERSION_H +#define SHELL_COMMAND_VERSION_H + +#define UIF_CMD_VERSION \ + DECLARE_UIF_CMD("version", 0, UIF_MAX_ARGS, 0, cmd_version, \ + "Print UDB version", "") \ + +void cmd_version(int argc, char *argv[]); + +#endif // SHELL_COMMAND_VERSION_H diff --git a/source/board/udb/console/source/shell_cmd_adapter_type.c b/source/board/udb/console/source/shell_cmd_adapter_type.c new file mode 100644 index 0000000000..0847419fd6 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_adapter_type.c @@ -0,0 +1,9 @@ +#include +#include "shell_cmd_adapter_type.h" +#include "udb_adapter_detector.h" + +void cmd_adapter_type(int argc, char *argv[]) +{ + udb_adapter_type_t adapter_type = udb_adapter_detector_get_adapter_type_adc(); + printf("%s\n", udb_adapter_detector_get_adapter_type_name(adapter_type)); +} diff --git a/source/board/udb/console/source/shell_cmd_btn.c b/source/board/udb/console/source/shell_cmd_btn.c new file mode 100644 index 0000000000..5040a037ea --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_btn.c @@ -0,0 +1,88 @@ +#include "shell_cmd_btn.h" + +#include +#include +#include +#include +#include +#include "IO_Config.h" + +static const char *s_dut_btn_names[DUT_PIN_GROUP_ID_COUNT] = +{ + [DUT_PIN_GROUP_ID_UDC0_RST_L] = "RST0_L", + [DUT_PIN_GROUP_ID_UDC0_BOOT_L] = "BOOT0_L", + [DUT_PIN_GROUP_ID_UDC0_BUTTON_L] = "BTN0_L", + [DUT_PIN_GROUP_ID_UDC1_RST] = "RST1", + [DUT_PIN_GROUP_ID_UDC1_BOOT] = "BOOT1", + [DUT_PIN_GROUP_ID_UDC1_BUTTON] = "BTN1", +}; + +void cmd_btn(int argc, char *argv[]) +{ + dut_pin_group_id_t dut_pin_grp = DUT_PIN_GROUP_ID_COUNT; + GPIO_PinState pin_state; + + for (dut_pin_group_id_t grp_id = 0; grp_id < DUT_PIN_GROUP_ID_COUNT; ++grp_id) + { + if (strcmp(argv[1], s_dut_btn_names[grp_id]) == 0) + { + dut_pin_grp = grp_id; + break; + } + } + + if (dut_pin_grp < DUT_PIN_GROUP_ID_COUNT) + { + if (strcmp(argv[2], "press") == 0) + { + if (gpio_dut_pin_group_is_active_high(dut_pin_grp) == true) + { + gpio_config_dut_pin_group(dut_pin_grp, DUT_PIN_GROUP_STATE_OUTPUT, GPIO_PULLUP); + } + else + { + gpio_config_dut_pin_group(dut_pin_grp, DUT_PIN_GROUP_STATE_OUTPUT, GPIO_PULLDOWN); + } + HAL_Delay(GPIO_CONFIG_DUT_DELAY_MS); + } + else if (strcmp(argv[2], "release") == 0) + { + if (gpio_dut_pin_group_is_active_high(dut_pin_grp) == true) + { + gpio_config_dut_pin_group(dut_pin_grp, DUT_PIN_GROUP_STATE_OUTPUT, GPIO_PULLDOWN); + } + else + { + gpio_config_dut_pin_group(dut_pin_grp, DUT_PIN_GROUP_STATE_OUTPUT, GPIO_PULLUP); + } + HAL_Delay(GPIO_CONFIG_DUT_DELAY_MS); + gpio_config_dut_pin_group(dut_pin_grp, DUT_PIN_GROUP_STATE_INPUT, GPIO_NOPULL); + } + else if (strcmp(argv[2], "tap") == 0) + { + if (gpio_dut_pin_group_is_active_high(dut_pin_grp) == true) + { + gpio_config_dut_pin_group(dut_pin_grp, DUT_PIN_GROUP_STATE_OUTPUT, GPIO_PULLUP); + HAL_Delay(GPIO_CONFIG_DUT_DELAY_MS); + gpio_write_dut_pin(dut_pin_grp, GPIO_PIN_RESET); + HAL_Delay(GPIO_WRITE_DUT_DELAY_MS); + } + else + { + gpio_config_dut_pin_group(dut_pin_grp, DUT_PIN_GROUP_STATE_OUTPUT, GPIO_PULLDOWN); + HAL_Delay(GPIO_CONFIG_DUT_DELAY_MS); + gpio_write_dut_pin(dut_pin_grp, GPIO_PIN_SET); + HAL_Delay(GPIO_WRITE_DUT_DELAY_MS); + } + gpio_config_dut_pin_group(dut_pin_grp, DUT_PIN_GROUP_STATE_INPUT, GPIO_NOPULL); + } + else + { + printf("%s is not a valid cmd\n", argv[2]); + } + } + else + { + printf("%s is not a valid button name\n", argv[1]); + } +} diff --git a/source/board/udb/console/source/shell_cmd_dut_reset.c b/source/board/udb/console/source/shell_cmd_dut_reset.c new file mode 100644 index 0000000000..8557eb1039 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_dut_reset.c @@ -0,0 +1,40 @@ +#include "shell_cmd_dut_reset.h" + +#include +#include +#include +#include "IO_Config.h" + +/* + * Reset pins going to the DUT go through level shifters. + * We can only drive DUT pins by programming the pull-up/pull-down registers. + * If we change a DUT pin to output, it will conflict with the level-transition IC. + * + * To reset DUT0, we change the direction to output, drive UDC0_RST_L low, delay, + * drive UDC0_RST_L high, delay, change direction back to input. + * To reset DUT1, we change the direction to output, drive UDC1_RST high, delay, + * drive UDC1_RST low, delay, change direction back to input. + */ +void cmd_dut_reset(int argc, char *argv[]) +{ + if (strcmp(argv[1], "0") == 0) + { + gpio_config_dut_pin_group(DUT_PIN_GROUP_ID_UDC0_RST_L, DUT_PIN_GROUP_STATE_OUTPUT, GPIO_PULLDOWN); + HAL_Delay(GPIO_CONFIG_DUT_DELAY_MS); + gpio_write_dut_pin(DUT_PIN_GROUP_ID_UDC0_RST_L, GPIO_PIN_SET); + HAL_Delay(GPIO_WRITE_DUT_DELAY_MS); + gpio_config_dut_pin_group(DUT_PIN_GROUP_ID_UDC0_RST_L, DUT_PIN_GROUP_STATE_INPUT, GPIO_NOPULL); + } + else if (strcmp(argv[1], "1") == 0) + { + gpio_config_dut_pin_group(DUT_PIN_GROUP_ID_UDC1_RST, DUT_PIN_GROUP_STATE_OUTPUT, GPIO_PULLUP); + HAL_Delay(GPIO_CONFIG_DUT_DELAY_MS); + gpio_write_dut_pin(DUT_PIN_GROUP_ID_UDC1_RST, GPIO_PIN_RESET); + HAL_Delay(GPIO_WRITE_DUT_DELAY_MS); + gpio_config_dut_pin_group(DUT_PIN_GROUP_ID_UDC1_RST, DUT_PIN_GROUP_STATE_INPUT, GPIO_NOPULL); + } + else + { + printf("dut_reset can not reset dut %s\n", argv[1]); + } +} diff --git a/source/board/udb/console/source/shell_cmd_ext_relay.c b/source/board/udb/console/source/shell_cmd_ext_relay.c new file mode 100644 index 0000000000..a83aa256f4 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_ext_relay.c @@ -0,0 +1,34 @@ +#include "shell_cmd_ext_relay.h" + +#include +#include +#include + +#include "IO_Config.h" + +void cmd_ext_relay(int argc, char * argv[]) +{ + if (strcmp(argv[1], "on") == 0) + { + HAL_GPIO_WritePin(UDC_EXT_RELAY_PORT, UDC_EXT_RELAY_PIN, GPIO_PIN_SET); + } + else if (strcmp(argv[1], "off") == 0) + { + HAL_GPIO_WritePin(UDC_EXT_RELAY_PORT, UDC_EXT_RELAY_PIN, GPIO_PIN_RESET); + } + else if (strcmp(argv[1], "status") == 0) + { + if (HAL_GPIO_ReadPin(UDC_EXT_RELAY_PORT, UDC_EXT_RELAY_PIN) == GPIO_PIN_SET) + { + printf("external relay is on\n"); + } + else + { + printf("external relay is off\n"); + } + } + else + { + printf("Invalid arg: %s\n", argv[1]); + } +} diff --git a/source/board/udb/console/source/shell_cmd_fault.c b/source/board/udb/console/source/shell_cmd_fault.c new file mode 100644 index 0000000000..468ba33b2c --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_fault.c @@ -0,0 +1,20 @@ +#include "shell_cmd_fault.h" +#include "util.h" +#include +#include + +void cmd_fault(int argc, char *argv[]) +{ + if (strcmp(argv[1], "test_assert") == 0) + { + util_assert(false); + } + else if (strcmp(argv[1], "test_watchdog") == 0) + { + while (1) {} + } + else + { + printf("ERROR: unknown cmd\n"); + } +} diff --git a/source/board/udb/console/source/shell_cmd_fault_info.c b/source/board/udb/console/source/shell_cmd_fault_info.c new file mode 100644 index 0000000000..e3ca4e0679 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_fault_info.c @@ -0,0 +1,28 @@ +#include "shell_cmd_fault_info.h" + +#include +#include +#include "udb_fault_info.h" + +void cmd_fault_info(int argc, char *argv[]) +{ + if (strcmp(argv[1], "print") == 0) + { + if (udb_is_fault_info_uncleared()) + { + udb_print_fault_info(); + } + else + { + printf("There's no saved fault info\n"); + } + } + else if (strcmp(argv[1], "clear") == 0) + { + udb_clear_fault_info(); + } + else + { + printf("Error: wrong args\n"); + } +} diff --git a/source/board/udb/console/source/shell_cmd_gpio.c b/source/board/udb/console/source/shell_cmd_gpio.c new file mode 100644 index 0000000000..b82babfe69 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_gpio.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include "shell_cmd_gpio.h" + +void cmd_gpio(int argc, char * argv[]) +{ + bool skip_request = false; + GPIO_PinState value = 0; + GPIO_InitTypeDef GPIO_InitStructure = {0}; + GPIO_TypeDef * port; + uint16_t pin; + + int port_arg = argv[2][0] - 'A'; + uint16_t pin_arg = strtoul(argv[3], NULL, 10); + + if ((port_arg < 0) || + (port_arg > (int)('I' - 'A')) || + (pin_arg > 15)) + { + printf("Port: %d Pin %u is not valid.\n", port_arg, pin_arg); + goto done; + } + + pin = (uint16_t)0x0001 << pin_arg; + port = (GPIO_TypeDef *)(D3_AHB1PERIPH_BASE + (port_arg * 0x0400UL)); + + if (strcmp(argv[1], "read") == 0) + { + skip_request = true; + } + else if (strcmp(argv[1], "in") == 0) + { + GPIO_InitStructure.Mode = GPIO_MODE_INPUT; + GPIO_InitStructure.Pull = GPIO_NOPULL; + } + else if (strcmp(argv[1], "in_pu") == 0) + { + GPIO_InitStructure.Mode = GPIO_MODE_INPUT; + GPIO_InitStructure.Pull = GPIO_PULLUP; + } + else if (strcmp(argv[1], "in_pd") == 0) + { + GPIO_InitStructure.Mode = GPIO_MODE_INPUT; + GPIO_InitStructure.Pull = GPIO_PULLDOWN; + } + else if (strcmp(argv[1], "pp_clear") == 0) + { + HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + } + else if (strcmp(argv[1], "pp_set") == 0) + { + HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET); + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + } + else if (strcmp(argv[1], "toggle") == 0) + { + HAL_GPIO_TogglePin(port, pin); + skip_request = true; + } + else + { + printf("Unknown command '%s'\n", argv[1]); + goto done; + } + + if (!skip_request) + { + GPIO_InitStructure.Pin = pin; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; + HAL_GPIO_Init(port, &GPIO_InitStructure); + } + value = HAL_GPIO_ReadPin(port, pin); + printf("GPIO port %c pin %u is %u\n", argv[2][0], pin_arg, value); + +done: + return; +} \ No newline at end of file diff --git a/source/board/udb/console/source/shell_cmd_i2c.c b/source/board/udb/console/source/shell_cmd_i2c.c new file mode 100644 index 0000000000..aea3653dfd --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_i2c.c @@ -0,0 +1,120 @@ +#include "shell_cmd_i2c.h" +#include "i2c.h" +#include +#include +#include + +#define I2C_ARGC_READ (5) +#define I2C_ARGC_WRITE (I2C_ARGC_READ + 1) + +// range for 7-bit addressing +#define I2C_SLAVE_ADDR_MIN (0x08) +#define I2C_SLAVE_ADDR_MAX (0x77) +#define UDB_I2C_CMD_READ_BUFFER_SIZE (32) +#define I2C_WRITE_MAX_LEN (sizeof(uint32_t) / sizeof(uint8_t)) + +void cmd_i2c(int argc, char *argv[]) +{ + if (strcmp(argv[1], "probe") == 0) + { + printf("probing...\n"); + + uint8_t bus_id = strtoul(argv[2], NULL, 0); + + if (bus_id < I2C_BUS_COUNT) + { + i2c_slave_t slave = + { + .bus_id = bus_id, + }; + + i2c_request(&slave); + for (uint16_t addr = I2C_SLAVE_ADDR_MIN; addr <= I2C_SLAVE_ADDR_MAX; ++addr) + { + slave.slave_addr = addr; + if (i2c_write(&slave, 0, 0, 0) == UDB_SUCCESS) + { + printf("%#.*x\n", 2, addr); + } + } + i2c_release(&slave); + } + else + { + printf("Error: UDB doesn't have bus %u\n", bus_id); + } + } + else if ((argc == I2C_ARGC_READ) || (argc == I2C_ARGC_WRITE)) + { + uint8_t bus_id = strtoul(argv[1], NULL, 0); + uint16_t slave_addr = strtoul(argv[2], NULL, 0); + uint8_t start_reg = strtoul(argv[3], NULL, 0); + uint16_t len = strtoul(argv[4], NULL, 0); + + i2c_slave_t slave = + { + .bus_id = bus_id, + .slave_addr = slave_addr, + }; + if (bus_id < I2C_BUS_COUNT) + { + if (argc == I2C_ARGC_READ) + { + printf("reading..\n"); + + uint8_t buffer[UDB_I2C_CMD_READ_BUFFER_SIZE]; + + if (len > UDB_I2C_CMD_READ_BUFFER_SIZE) + { + len = UDB_I2C_CMD_READ_BUFFER_SIZE; + printf("Error: max buffer size is %u\n", UDB_I2C_CMD_READ_BUFFER_SIZE); + } + + i2c_request(&slave); + + if (i2c_read(&slave, start_reg, buffer, len) == UDB_SUCCESS) + { + for (int i = 0; i < len; ++i) + { + printf("%d:0x%x\n", i, buffer[i]); + } + } + else + { + printf("Error: i2c read failed\n"); + } + + i2c_release(&slave); + } + else + { + printf("writing...\n"); + + uint32_t val = strtoul(argv[5], NULL, 0); + + if (len > I2C_WRITE_MAX_LEN) + { + len = I2C_WRITE_MAX_LEN; + printf("Error: max buffer size is %u\n", I2C_WRITE_MAX_LEN); + } + + i2c_request(&slave); + + if (i2c_write(&slave, start_reg, (uint8_t *)&val, len) != UDB_SUCCESS) + { + printf("Error: i2c write failed\n"); + } + + i2c_release(&slave); + } + } + else + { + printf("Error: UDB doesn't have bus %u\n", bus_id); + } + } + else + { + printf("Error: Invalid i2c command\n"); + } +} diff --git a/source/board/udb/console/source/shell_cmd_measure_power.c b/source/board/udb/console/source/shell_cmd_measure_power.c new file mode 100644 index 0000000000..af6362b73b --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_measure_power.c @@ -0,0 +1,65 @@ +#include +#include "shell_cmd_measure_power.h" +#include "udb_power_measurement.h" +#include "udb_errno.h" +#include "udb_version.h" + +void cmd_measure_power(int argc, char *argv[]) +{ + uint16_t voltage_mV; + uint32_t current_uA; + + int ret = udb_power_measurement_measure(); + + if (ret == UDB_SUCCESS) + { + for (udb_power_measurement_target_t target_type = 0; target_type < UDB_POWER_MEASUREMENT_TARGET_COUNT; ++target_type) + { + if (udb_power_measurement_can_measure_target(target_type) == false) + { + continue; + } + + printf("Target: %s\n", udb_power_measurement_get_target_name(target_type)); + + if (udb_power_measurement_can_measure_voltage(target_type) == true) + { + ret = udb_power_measurement_read_voltage_mV(target_type, &voltage_mV); + if (ret == UDB_SUCCESS) + { + printf("\tvoltage: %u mV\n", voltage_mV); + } + else + { + printf("\tERROR: can not read voltage\n"); + } + } + else + { + printf("\tDoesn't support reading %s voltage\n", udb_power_measurement_get_target_name(target_type)); + } + + if (udb_power_measurement_can_measure_current(target_type) == true) + { + ret = udb_power_measurement_read_current_uA(target_type, ¤t_uA); + + if (ret == UDB_SUCCESS) + { + printf("\tcurrent: %lu uA\n", current_uA); + } + else + { + printf("\tERROR: can not read current\n"); + } + } + else + { + printf("\tDoesn't support reading %s current\n", udb_power_measurement_get_target_name(target_type)); + } + } + } + else + { + printf("ERROR: can not measure UDB power\n"); + } +} diff --git a/source/board/udb/console/source/shell_cmd_pwm.c b/source/board/udb/console/source/shell_cmd_pwm.c new file mode 100644 index 0000000000..09fc997a98 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_pwm.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include "shell_cmd_pwm.h" + +#define TIM_PRESCALER 256 + +static TIM_HandleTypeDef timer = {.Instance = TIM1}; + +static uint32_t tim1_clk_div(uint32_t apb2clkdiv) +{ + switch (apb2clkdiv) { + case RCC_APB2_DIV1: + return 1; + case RCC_APB2_DIV4: + return 2; + case RCC_APB2_DIV8: + return 4; + default: // RCC_CFGR_PPRE2_DIV1 + util_assert(false); + return 1; + } +} + +static uint32_t ahb_clk_div(uint32_t ahbclkdiv) +{ + switch (ahbclkdiv) { + case RCC_HCLK_DIV1: + return 1; + case RCC_HCLK_DIV2: + return 2; + case RCC_HCLK_DIV4: + return 4; + case RCC_HCLK_DIV8: + return 8; + case RCC_HCLK_DIV16: + return 16; + default: + util_assert(false); + return 1; + } +} + +static void pwm_stop(void) +{ + __HAL_RCC_TIM1_CLK_ENABLE(); + + if (HAL_TIM_PWM_Stop(&timer, TIM_CHANNEL_1) != HAL_OK) + { + util_assert(false); + } + + if (HAL_TIM_Base_Stop(&timer) != HAL_OK) + { + util_assert(false); + } + + HAL_GPIO_DeInit(GPIOE, GPIO_PIN_9); + + if (HAL_TIM_Base_DeInit(&timer) != HAL_OK) { + util_assert(false); + } + + if (HAL_TIM_PWM_DeInit(&timer) != HAL_OK) { + util_assert(false); + } + + __HAL_RCC_TIM1_CLK_DISABLE(); +} + +static void pwm_start(uint16_t freq, uint16_t duty_cycle) +{ + RCC_ClkInitTypeDef clk_init = {0}; + TIM_OC_InitTypeDef pwm_config = {0}; + GPIO_InitTypeDef GPIO_InitStruct = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + + uint16_t pulse; + uint32_t unused; + uint16_t period; + uint32_t tim_clock; + + HAL_RCC_GetClockConfig(&clk_init, &unused); + tim_clock = SystemCoreClock / tim1_clk_div(clk_init.APB2CLKDivider) / ahb_clk_div(clk_init.AHBCLKDivider) / TIM_PRESCALER; + + if ((duty_cycle < 1) || (duty_cycle > 99)) + { + printf("ERROR: wrong duty cycle value\n"); + return; + } + + if ((freq < 1) || (freq > (tim_clock / 3))) + { + printf("ERROR: wrong freq value\n"); + return; + } + + period = tim_clock / freq; + pulse = (period * (100 - duty_cycle) / 100); + + timer.Init.Prescaler = TIM_PRESCALER - 1; + timer.Init.ClockDivision = 0; + timer.Init.CounterMode = TIM_COUNTERMODE_UP; + timer.Init.RepetitionCounter = 0; + timer.Init.Period = period - 1; + + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + + GPIO_InitStruct.Pin = GPIO_PIN_9; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Speed = GPIO_SPEED_LOW; + GPIO_InitStruct.Alternate = GPIO_AF1_TIM1; + + pwm_config.OCMode = TIM_OCMODE_PWM1; + pwm_config.Pulse = pulse; + pwm_config.OCPolarity = TIM_OCPOLARITY_HIGH; + pwm_config.OCNPolarity = TIM_OCPOLARITY_HIGH; + pwm_config.OCFastMode = TIM_OCFAST_DISABLE; + pwm_config.OCIdleState = TIM_OCIDLESTATE_RESET; + pwm_config.OCNIdleState = TIM_OCIDLESTATE_RESET; + + __HAL_RCC_TIM1_CLK_ENABLE(); + + if (HAL_TIM_Base_Init(&timer) != HAL_OK) + { + util_assert(false); + } + + if (HAL_TIM_PWM_Init(&timer) != HAL_OK) + { + util_assert(false); + } + + if (HAL_TIMEx_MasterConfigSynchronization(&timer, &sMasterConfig) != HAL_OK) + { + util_assert(false); + } + + HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); + + if (HAL_TIM_PWM_ConfigChannel(&timer, &pwm_config, TIM_CHANNEL_1) != HAL_OK) + { + util_assert(false); + } + + if (HAL_TIM_Base_Start(&timer) != HAL_OK) + { + util_assert(false); + } + + if (HAL_TIM_PWM_Start(&timer, TIM_CHANNEL_1) != HAL_OK) + { + util_assert(false); + } +} + +void cmd_pwm(int argc, char *argv[]) +{ + if (strcmp(argv[1], "start") == 0) + { + if (argc < 4) + { + printf("ERROR: missing args\n"); + return; + } + + uint16_t freq = strtoul(argv[2], NULL, 10); + uint16_t duty_cycle = strtoul(argv[3], NULL, 10); + + pwm_start(freq, duty_cycle); + } + else if (strcmp(argv[1], "stop") == 0) + { + pwm_stop(); + } + else + { + printf("ERROR: unknown cmd\n"); + } + + return; +} diff --git a/source/board/udb/console/source/shell_cmd_reset.c b/source/board/udb/console/source/shell_cmd_reset.c new file mode 100644 index 0000000000..2494912f9a --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_reset.c @@ -0,0 +1,8 @@ +#include +#include "shell_cmd_reset.h" +#include "udb_reset.h" + +void cmd_reset(int argc, char *argv[]) +{ + udb_reset(); +} diff --git a/source/board/udb/console/source/shell_cmd_reset_into_swu_mode.c b/source/board/udb/console/source/shell_cmd_reset_into_swu_mode.c new file mode 100644 index 0000000000..599ecbb098 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_reset_into_swu_mode.c @@ -0,0 +1,14 @@ +#include +#include "shell_cmd_reset_into_swu_mode.h" +#include "settings.h" +#include "udb_reset.h" +#include "stm32h7xx.h" + +void cmd_reset_into_swu_mode(int argc, char *argv[]) +{ + config_ram_set_hold_in_bl(true); + // need some delay to write the config + // if you reset immediately, it will not go to SWU mode + HAL_Delay(10); + udb_reset(); +} diff --git a/source/board/udb/console/source/shell_cmd_swd_dut.c b/source/board/udb/console/source/shell_cmd_swd_dut.c new file mode 100644 index 0000000000..5649a64868 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_swd_dut.c @@ -0,0 +1,25 @@ +#include "shell_cmd_swd_dut.h" + +#include +#include +#include "DAP_config.h" + +void cmd_swd_dut(int argc, char *argv[]) +{ + if (argc == 1) + { + printf("DUT %u\n", g_cur_swd_dut); + } + else if (strcmp(argv[1], "0") == 0) + { + change_swd_dut(SWD_DUT0); + } + else if (strcmp(argv[1], "1") == 0) + { + change_swd_dut(SWD_DUT1); + } + else + { + printf("Invalid arg %s\n", argv[1]); + } +} diff --git a/source/board/udb/console/source/shell_cmd_uptime.c b/source/board/udb/console/source/shell_cmd_uptime.c new file mode 100644 index 0000000000..60f1dcfab3 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_uptime.c @@ -0,0 +1,10 @@ +#include "shell_cmd_uptime.h" +#include +#include + +#include "udb_uptime.h" + +void cmd_uptime(int argc, char *argv[]) +{ + udb_uptime_print(); +} diff --git a/source/board/udb/console/source/shell_cmd_version.c b/source/board/udb/console/source/shell_cmd_version.c new file mode 100644 index 0000000000..b92d1f3887 --- /dev/null +++ b/source/board/udb/console/source/shell_cmd_version.c @@ -0,0 +1,12 @@ +#include "shell_cmd_version.h" +#include "udb_version.h" +#include + +void cmd_version(int argc, char *argv[]) +{ + char buf[UDB_VERSION_MAX_LENGTH]; + udb_get_interface_version((uint8_t*)buf, UDB_VERSION_MAX_LENGTH); + printf("Interface ver: %s\n", buf); + udb_get_bootloader_version((uint8_t*)buf, UDB_VERSION_MAX_LENGTH); + printf("Bootloader ver: %s\n\n", buf); +} diff --git a/source/board/udb/console/source/uif-cmdtab.c b/source/board/udb/console/source/uif-cmdtab.c new file mode 100644 index 0000000000..6a0ecc99d9 --- /dev/null +++ b/source/board/udb/console/source/uif-cmdtab.c @@ -0,0 +1,42 @@ +#include "nluif_udb-daplink.h" +#include "shell_cmd_gpio.h" +#include "shell_cmd_pwm.h" +#include "shell_cmd_version.h" +#include "shell_cmd_fault.h" +#include "shell_cmd_reset.h" +#include "shell_cmd_adapter_type.h" +#include "shell_cmd_measure_power.h" +#include "shell_cmd_reset_into_swu_mode.h" +#include "shell_cmd_i2c.h" +#include "shell_cmd_dut_reset.h" +#include "shell_cmd_fault_info.h" +#include "shell_cmd_uptime.h" +#include "shell_cmd_ext_relay.h" +#include "shell_cmd_swd_dut.h" +#include "shell_cmd_btn.h" + +const UIF_CMD UIF_CMDTAB[] = { + UIF_CMD_HELP, + UIF_CMD_GPIO, + UIF_CMD_PWM, + UIF_CMD_VERSION, + UIF_CMD_FAULT, + UIF_CMD_RESET, + UIF_CMD_ADAPTER_TYPE, + UIF_CMD_MEASURE_POWER, + UIF_CMD_RESET_INTO_SWU_MODE, + UIF_CMD_I2C, + UIF_CMD_DUT_RESET, + UIF_CMD_FAULT_INFO, + UIF_CMD_UPTIME, + UIF_CMD_EXT_RELAY, + UIF_CMD_SWD_DUT, + UIF_CMD_BTN, +}; + +const int UIF_NUM_CMD = sizeof (UIF_CMDTAB) / sizeof (UIF_CMDTAB[0]); + +const UIF_SETCMD UIF_SETCMDTAB[0] = { +}; + +const int UIF_NUM_SETCMD = 0; diff --git a/source/board/udb/include/DAP_vendor_ex.h b/source/board/udb/include/DAP_vendor_ex.h new file mode 100644 index 0000000000..4e930a16c1 --- /dev/null +++ b/source/board/udb/include/DAP_vendor_ex.h @@ -0,0 +1,22 @@ +#ifndef DAP_VENDOR_EX_H +#define DAP_VENDOR_EX_H + +typedef enum +{ + ID_DAP_VendorEx32_I2C_READ = ID_DAP_VendorExFirst, + ID_DAP_VendorEx33_I2C_WRITE, + ID_DAP_VendorEx34_DUT_PIN_GROUP_WRITE, + ID_DAP_VendorEx35_DUT_PWR_CTRL, + ID_DAP_VendorEx36_INTERFACE_VERSION_DETAILS, + ID_DAP_VendorEx37_HOLD_IN_BL, + ID_DAP_VendorEx38_RESET_DAPLINK, + ID_DAP_VendorEx39_READ_UDC_ADAPTER_TYPE_ADC, + ID_DAP_VendorEx40_MEASURE_POWER, + ID_DAP_VendorEx41_BOOTLOADER_VERSION_DETAILS, + ID_DAP_VendorEx42_CHANGE_SWD_DUT, + + // Add new commands before the last command + ID_DAP_VendorEx126_LAST = ID_DAP_VendorExLast, +} DAP_vendor_ex_cmd_t; + +#endif // DAP_VENDOR_EX_H diff --git a/source/board/udb/include/adc.h b/source/board/udb/include/adc.h new file mode 100644 index 0000000000..4d70131f32 --- /dev/null +++ b/source/board/udb/include/adc.h @@ -0,0 +1,39 @@ +/** + * @file adc.h + * @brief + * + * DAPLink Interface Firmware + * Copyright 2020 NXP + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ADC_H +#define ADC_H + +#include "IO_Config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void adc_init(void); +void adc_init_pins(void); +uint32_t adc_read_channel(uint32_t channelGroup, uint32_t channelNumber, uint32_t channelMux); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/board/udb/include/i2c.h b/source/board/udb/include/i2c.h new file mode 100644 index 0000000000..6b96124e35 --- /dev/null +++ b/source/board/udb/include/i2c.h @@ -0,0 +1,52 @@ +#ifndef I2C_H +#define I2C_H + +#include +#include +#include "udb_errno.h" +#include "I2C_STM32H7xx.h" + +typedef enum { + I2C_BUS_0, + I2C_BUS_1, + I2C_BUS_2, + I2C_BUS_COUNT, +} i2c_bus_t; + +typedef struct +{ + i2c_bus_t bus_id; + uint16_t slave_addr; +} i2c_slave_t; + +// Setup up I2C bus and enables interrupts +int i2c_init(void); + +void i2c_request(const i2c_slave_t *i2c_slave); +void i2c_release(const i2c_slave_t *i2c_slave); + +/* + * i2c_write + * + * i2c_slave: Target (slave) device to be communicated with + * reg_addr: Target device register to start writing to + * in_buf: bytes to be written to target device + * len: length of data, or number of bytes to be sequentially written + * + * Write sequential bytes, starting at given register address, on target I2C device + */ +int i2c_write(const i2c_slave_t *i2c_slave, uint8_t reg_addr, const uint8_t* in_buf, uint32_t len); + +/* + * i2c_read + * + * i2c_slave: Target (slave) device to be communicated with + * reg_addr: Target device register to start reading from + * out_buf: buffer to store data read from target device + * len: number of bytes to be read + * + * Read sequential bytes, starting at given register address, on target I2C device + */ +int i2c_read(const i2c_slave_t *i2c_slave, uint8_t reg_addr, uint8_t* out_buf, uint32_t len); + +#endif /* I2C_H */ diff --git a/source/board/udb/include/pac193x.h b/source/board/udb/include/pac193x.h new file mode 100644 index 0000000000..41407ff8cc --- /dev/null +++ b/source/board/udb/include/pac193x.h @@ -0,0 +1,181 @@ +/* + * Description: + * Define an interface for PMIC pac193xx. + */ +#ifndef _PAC193x_H_INCLUDED_ +#define _PAC193x_H_INCLUDED_ + +#include + +typedef enum +{ + PAC193X_REFRESH_REG = 0x00, + PAC193X_CTRL_REG = 0x01, + PAC193X_ACC_COUNT_REG = 0x02, + PAC193X_VPOWER1ACC_REG = 0x03, + PAC193X_VPOWER2ACC_REG = 0x04, + PAC193X_VPOWER3ACC_REG = 0x05, + PAC193X_VPOWER4ACC_REG = 0x06, + PAC193X_VBUS1_REG = 0x07, + PAC193X_VBUS2_REG = 0x08, + PAC193X_VBUS3_REG = 0x09, + PAC193X_VBUS4_REG = 0x0a, + PAC193X_VSENSE1_REG = 0x0b, + PAC193X_VSENSE2_REG = 0x0c, + PAC193X_VSENSE3_REG = 0x0d, + PAC193X_VSENSE4_REG = 0x0e, + PAC193X_VBUS1_AVG_REG = 0x0f, + PAC193X_VBUS2_AVG_REG = 0x10, + PAC193X_VBUS3_AVG_REG = 0x11, + PAC193X_VBUS4_AVG_REG = 0x12, + PAC193X_VSENSE1_AVG_REG = 0x13, + PAC193X_VSENSE2_AVG_REG = 0x14, + PAC193X_VSENSE3_AVG_REG = 0x15, + PAC193X_VSENSE4_AVG_REG = 0x16, + PAC193X_VPOWER1_AVG_REG = 0x17, + PAC193X_VPOWER2_AVG_REG = 0x18, + PAC193X_VPOWER3_AVG_REG = 0x19, + PAC193X_VPOWER4_AVG_REG = 0x1a, + PAC193X_CHANNEL_DIS_REG = 0x1c, + PAC193X_NEG_PWR_REG = 0x1d, + PAC193X_REFRESH_G_REG = 0x1e, + PAC193X_REFRESH_V_REG = 0x1f, + PAC193X_SLOW_REG = 0x20, + PAC193X_CTRL_ACT_REG = 0x21, + PAC193X_CHANNEL_DIS_ACT_REG = 0x22, + PAC193X_NEG_PWR_ACT_REG = 0x23, + PAC193X_CTRL_LAT_REG = 0x24, + PAC193X_CHANNEL_DIS_LAT_REG = 0x25, + PAC193X_NEG_PWR_LAT_REG = 0x26, + PAC193X_INVALID_REG = 0x30, + PAC193X_PRODUCT_ID_REG = 0xfd, + PAC193X_MANUFACTURER_ID_REG = 0xfe, + PAC193X_REVISION_ID_REG = 0xff, +} pac193x_reg_t; + +#define PAC193X_ACC_COUNT_REG_SIZE (3) +#define PAC193X_VPOWERNACC_REG_SIZE (6) +#define PAC193X_VBUSN_REG_SIZE (2) +#define PAC193X_VSENSEN_REG_SIZE (2) +#define PAC193X_VBUSN_AVG_REG_SIZE (2) +#define PAC193X_VSENSEN_AVG_REG_SIZE (2) +#define PAC193X_VPOWERN_AVG_REG_SIZE (4) +#define PAC193X_DEFAULT_REG_SIZE (1) + +typedef union +{ + struct + { + uint8_t ovf:1; + uint8_t ovf_alert_en:1; + uint8_t conv_alert_en:1; + uint8_t alert_pin_en:1; + uint8_t sing_mode:1; + uint8_t sleep_mode:1; + uint8_t sample_rate:2; + }; + uint8_t val; +} pac193x_ctrl_reg_t; + +typedef enum +{ + PAC193X_SAMPLE_RATE_1024 = 0x00, + PAC193X_SAMPLE_RATE_256 = 0x01, + PAC193X_SAMPLE_RATE_64 = 0x02, + PAC193X_SAMPLE_RATE_32 = 0x03 +} pac193x_sample_rate_t; + +#define PAC193X_CTRL_POR_VALUE (0x00) + +typedef union +{ + struct + { + uint8_t nop:1; + uint8_t noskip_en:1; + uint8_t byte_count_en:1; + uint8_t timeout_en:1; + uint8_t ch4_dis:1; + uint8_t ch3_dis:1; + uint8_t ch2_dis:1; + uint8_t ch1_dis:1; + }; + uint8_t val; +} pac193x_chan_dis_reg_t; + +#define PAC1931_CHANNEL_DIS_POR_VALUE (0x70) +#define PAC1932_CHANNEL_DIS_POR_VALUE (0x30) +#define PAC1933_CHANNEL_DIS_POR_VALUE (0x10) +#define PAC1934_CHANNEL_DIS_POR_VALUE (0x00) + +typedef union +{ + struct + { + uint8_t vbus4_bi_en:1; + uint8_t vbus3_bi_en:1; + uint8_t vbus2_bi_en:1; + uint8_t vbus1_bi_en:1; + uint8_t vsense4_bi_en:1; + uint8_t vsense3_bi_en:1; + uint8_t vsense2_bi_en:1; + uint8_t vsense1_bi_en:1; + }; + uint8_t val; +} pac193x_neg_pwr_reg_t; + +#define PAC193X_NEG_PWR_POR_VALUE (0x00) + +typedef union +{ + struct + { + uint8_t por_status:1; + uint8_t fall_refresh_v_en:1; + uint8_t fall_refresh_en:1; + uint8_t rise_refresh_v_en:1; + uint8_t rise_refresh_en:1; + uint8_t is_hi_to_lo:1; + uint8_t is_lo_to_hi:1; + uint8_t is_pull_high:1; + }; + uint8_t val; +} pac193x_slow_reg_t; + +#define PAC193X_SLOW_POR_VALUE (0x15) + +typedef struct +{ + pac193x_ctrl_reg_t ctrl_cfg; + pac193x_chan_dis_reg_t chan_dis_cfg; + pac193x_neg_pwr_reg_t neg_pwr_cfg; + pac193x_slow_reg_t slow_cfg; +} pac193x_cfg_t; + +typedef enum +{ + PAC193X_COMMAND_REFRESH = 0, + PAC193X_COMMAND_REFRESH_V = 1, + PAC193X_COMMAND_REFRESH_G = 2, + + // This is not a real command, it represents how many commands there are. + PAC193X_COMMAND_SIZE = 3, +} pac193x_command_type_t; + +// Need 1 ms for REFRESH to take effect, choose 3 to have some margin +#define PAC193X_REFRESH_STABLIZATION_TIME_MS (3) +// Although data sheet mentions 14.25 ms for the time to first communication, +// choose 100 based on experiments +#define PAC193X_INIT_STABLIZATION_TIME_MS (100) + +#define PAC193X_FULL_SCALE_VOLTAGE_MV (32000) +#define PAC193X_FULL_SCALE_RANGE_MV (100) + +#define PAC193X_UNIPOLAR_DENOMINATOR (1<<16) +#define PAC193X_BIPOLAR_DENOMINATOR (1<<15) + +int pac193x_init(const pac193x_cfg_t* cfg); +int pac193x_send_command(pac193x_command_type_t command_type); +int pac193x_read_reg(pac193x_reg_t reg_addr, uint32_t reg_size, void* out); + +#endif // _PAC193x_H_INCLUDED_ diff --git a/source/board/udb/include/udb.ld b/source/board/udb/include/udb.ld new file mode 100644 index 0000000000..0ce26ccdff --- /dev/null +++ b/source/board/udb/include/udb.ld @@ -0,0 +1,21 @@ +#include "daplink_addr.h" + +MEMORY +{ + m_udb_bl_version_rom (RW) : ORIGIN = DAPLINK_ROM_UDB_BL_VERSION_START, LENGTH = DAPLINK_ROM_UDB_BL_VERSION_SIZE +} + +SECTIONS +{ + .udb_bl_version_rom : + { + KEEP(*(udb_bl_version_rom)) + FILL(0xffffffff) + . = ORIGIN(m_udb_bl_version_rom) + LENGTH(m_udb_bl_version_rom) - 4; + /* Need some contents in this section or it won't be copied to bin or hex. The CRC will + * be placed here by post_build_script.py. */ + LONG(0x55555555) + } > m_udb_bl_version_rom +} + +#include "stm32h743xx.ld" diff --git a/source/board/udb/include/udb_adapter_detector.h b/source/board/udb/include/udb_adapter_detector.h new file mode 100644 index 0000000000..480226af6f --- /dev/null +++ b/source/board/udb/include/udb_adapter_detector.h @@ -0,0 +1,32 @@ +#ifndef _UDB_ADAPTER_DETECTOR_H_INCLUDED_ +#define _UDB_ADAPTER_DETECTOR_H_INCLUDED_ + +#include + +typedef enum +{ + ADAPTER_UDB_13_FLEX = 0, + ADAPTER_JOKER_FLEX = 1, + ADAPTER_UDB_6_FLEX = 2, + ADAPTER_UDB_17_FLEX = 3, + ADAPTER_UDB_12_FLEX = 4, + ADAPTER_JOKER_FLEX_V2 = 5, + ADAPTER_TYPE_6 = 6, + ADAPTER_TYPE_7 = 7, + ADAPTER_TYPE_8 = 8, + ADAPTER_TYPE_9 = 9, + ADAPTER_TYPE_10 = 10, + ADAPTER_TYPE_11 = 11, + ADAPTER_TYPE_12 = 12, + ADAPTER_TYPE_13 = 13, + ADAPTER_TYPE_14 = 14, + ADAPTER_TYPE_15 = 15, + ADAPTER_UNKNOWN = 16, + ADAPTER_NONE = 17 +} udb_adapter_type_t; + +void udb_adapter_detector_update_adapter_type_adc(void); +udb_adapter_type_t udb_adapter_detector_get_adapter_type_adc(void); +const char* udb_adapter_detector_get_adapter_type_name(udb_adapter_type_t type); + +#endif diff --git a/source/board/udb/include/udb_errno.h b/source/board/udb/include/udb_errno.h new file mode 100644 index 0000000000..b009409119 --- /dev/null +++ b/source/board/udb/include/udb_errno.h @@ -0,0 +1,8 @@ +#ifndef _UDB_ERRNO_H_INCLUDED_ +#define _UDB_ERRNO_H_INCLUDED_ + +#define UDB_SUCCESS 0 +#define UDB_ERROR 1 +#define UDB_BUSY 2 + +#endif // _UDB_ERRNO_H_INCLUDED_ diff --git a/source/board/udb/include/udb_extended_features_task.h b/source/board/udb/include/udb_extended_features_task.h new file mode 100644 index 0000000000..0bae68ebcd --- /dev/null +++ b/source/board/udb/include/udb_extended_features_task.h @@ -0,0 +1,11 @@ +/* + * Description: + * Create an auxiliary task so that we don't need to put UDB's logic in the main task. + * + */ +#ifndef _UDB_EXTENDED_FEATURES_TASK_INCLUDED_H_ +#define _UDB_EXTENDED_FEATURES_TASK_INCLUDED_H_ + +void udb_extended_features_task_create(void); + +#endif // _UDB_EXTENDED_FEATURE_TASK_INCLUDED_H_ diff --git a/source/board/udb/include/udb_fault_info.h b/source/board/udb/include/udb_fault_info.h new file mode 100644 index 0000000000..e200462a8d --- /dev/null +++ b/source/board/udb/include/udb_fault_info.h @@ -0,0 +1,15 @@ +#ifndef UDB_FAULT_INFO_H +#define UDB_FAULT_INFO_H + +#include +#include + +#define UDB_WRITE_BACKTRACE_INFO_DELAY_MS (100U) + +void udb_write_backtrace_info(const char *file, uint16_t line, uint32_t pc, uint32_t sp); +bool udb_is_fault_info_uncleared(void); +void udb_print_fault_info(void); +void udb_clear_fault_info(void); +void udb_check_unexpected_watchdog_reset(void); + +#endif // UDB_FAULT_INFO_H diff --git a/source/board/udb/include/udb_log.h b/source/board/udb/include/udb_log.h new file mode 100644 index 0000000000..aaca8dd4e5 --- /dev/null +++ b/source/board/udb/include/udb_log.h @@ -0,0 +1,15 @@ +#ifndef _UDB_LOG_H_INCLUDED_ +#define _UDB_LOG_H_INCLUDED_ + +#include +#include + +bool udb_log_cdc_ready(); +void udb_log_set_cdc_ready(bool ready); +void udb_log_push(char *ptr, uint16_t size); +/* + * Flush the logs to CDC_3 endpoint + */ +void udb_log_flush(void); + +#endif // _UDB_LOG_H_INCLUDED_ diff --git a/source/board/udb/include/udb_power_measurement.h b/source/board/udb/include/udb_power_measurement.h new file mode 100644 index 0000000000..4b1a3f4cff --- /dev/null +++ b/source/board/udb/include/udb_power_measurement.h @@ -0,0 +1,47 @@ +/* + * Description: + * Provide an interface to measure the power on UDB + */ +#ifndef _UDB_POWER_MEASUREMENT_H_INCLUDED_ +#define _UDB_POWER_MEASUREMENT_H_INCLUDED_ + +#include +#include + +/* + * Before P3, only two channels are available. + * channel 1: mainboard usb + * channel 2: adapter usb + * + * P3 and after, four channels are available but DUT0 and DUT1 can only measure voltage. + * channel 1: DUT0 + * channel 2: DUT1 + * channel 3: mainboard usb + * channel 4: adapter usb + */ +typedef enum +{ + UDB_POWER_MEASUREMENT_TARGET_MAINBOARD_USB = 0, + UDB_POWER_MEASUREMENT_TARGET_ADAPTER_USB = 1, + UDB_POWER_MEASUREMENT_TARGET_DUT0 = 2, + UDB_POWER_MEASUREMENT_TARGET_DUT1 = 3, + UDB_POWER_MEASUREMENT_TARGET_COUNT = 4, +} udb_power_measurement_target_t; + +typedef struct +{ + uint8_t voltage_reg[UDB_POWER_MEASUREMENT_TARGET_COUNT]; + uint8_t current_reg[UDB_POWER_MEASUREMENT_TARGET_COUNT]; +} pwr_measurement_dev_t; + +int udb_power_measurement_init(void); +int udb_power_measurement_measure(void); +int udb_power_measurement_read_voltage_mV(udb_power_measurement_target_t target, uint16_t *voltage_mV); +int udb_power_measurement_read_current_uA(udb_power_measurement_target_t target, uint32_t *current_uA); + +const char* udb_power_measurement_get_target_name(udb_power_measurement_target_t target); +bool udb_power_measurement_can_measure_voltage(udb_power_measurement_target_t target); +bool udb_power_measurement_can_measure_current(udb_power_measurement_target_t target); +bool udb_power_measurement_can_measure_target(udb_power_measurement_target_t target); + +#endif // _UDB_POWER_MEASUREMENT_H_INCLUDED_ diff --git a/source/board/udb/include/udb_reset.h b/source/board/udb/include/udb_reset.h new file mode 100644 index 0000000000..d6027894c9 --- /dev/null +++ b/source/board/udb/include/udb_reset.h @@ -0,0 +1,14 @@ +/* + Description: + UDB's own system reset functions. + */ +#ifndef _UDB_RESET_H_INCLUDED_ +#define _UDB_RESET_H_INCLUDED_ + +#include + +void udb_reset(void); + +void udb_reset_async(uint32_t delay_ms); + +#endif diff --git a/source/board/udb/include/udb_uart_b.h b/source/board/udb/include/udb_uart_b.h new file mode 100644 index 0000000000..aaf57244aa --- /dev/null +++ b/source/board/udb/include/udb_uart_b.h @@ -0,0 +1,18 @@ +#ifndef UDB_UART_B_H +#define UDB_UART_B_H + +#include +#include + +int32_t udb_uart_b_initialize(void); +int32_t udb_uart_b_uninitialize(void); +int32_t udb_uart_b_reset(void); +int32_t udb_uart_b_set_configuration(UART_Configuration *config); +int32_t udb_uart_b_get_configuration(UART_Configuration *config); +void udb_uart_b_set_control_line_state(uint16_t ctrl_bmp); +int32_t udb_uart_b_write_free(void); +int32_t udb_uart_b_write_data(uint8_t *data, uint16_t size); +int32_t udb_uart_b_read_data(uint8_t *data, uint16_t size); +void UART_B_IRQn_Handler(void); + +#endif // UDB_UART_B_H \ No newline at end of file diff --git a/source/board/udb/include/udb_uptime.h b/source/board/udb/include/udb_uptime.h new file mode 100644 index 0000000000..82015ff2fa --- /dev/null +++ b/source/board/udb/include/udb_uptime.h @@ -0,0 +1,11 @@ +#ifndef UDB_UPTIME_H +#define UDB_UPTIME_H + +#include + +void udb_uptime_init(void); +void udb_uptime_update(void); +void udb_uptime_print(void); +uint64_t udb_uptime_now_ms(void); + +#endif // UDB_UPTIME_H diff --git a/source/board/udb/include/udb_util.h b/source/board/udb/include/udb_util.h new file mode 100644 index 0000000000..d6be69a5e7 --- /dev/null +++ b/source/board/udb/include/udb_util.h @@ -0,0 +1,8 @@ +#ifndef UDB_UTIL_H +#define UDB_UTIL_H + +#include + +void print_time_from_ms(uint64_t time_ms); + +#endif // UDB_UTIL_H diff --git a/source/board/udb/include/udb_version.h b/source/board/udb/include/udb_version.h new file mode 100644 index 0000000000..061b8f32e0 --- /dev/null +++ b/source/board/udb/include/udb_version.h @@ -0,0 +1,33 @@ +/* + * udb_version.h + * Yang-te Chen + * yangtechen@google.com + * September 7, 2021 + * + */ + +#ifndef UDB_VERSION_H_ +#define UDB_VERSION_H_ + +#include "stm32h7xx.h" +#include "DAP_config.h" + +#define UDB_VERSION_MAX_LENGTH DAP_PACKET_SIZE + +typedef enum +{ + HW_VERSION_UNKNOWN, + HW_VERSION_P1, + HW_VERSION_P2, + HW_VERSION_P3, + HW_VERSION_P4, + HW_VERSION_P5, + HW_VERSION_COUNT, +} hw_version_t; + +void udb_read_hw_version(void); +int udb_get_interface_version(uint8_t *buffer, unsigned size); +int udb_get_bootloader_version(uint8_t *buffer, unsigned size); +hw_version_t udb_get_hw_version(void); + +#endif diff --git a/source/board/udb/include/udb_watchdog.h b/source/board/udb/include/udb_watchdog.h new file mode 100644 index 0000000000..8e3d36df0f --- /dev/null +++ b/source/board/udb/include/udb_watchdog.h @@ -0,0 +1,10 @@ +#ifndef UDB_WATCHDOG_H +#define UDB_WATCHDOG_H + +#include + +void udb_watchdog_init(uint32_t timeout_s); + +void udb_watchdog_refresh(void); + +#endif // UDB_WATCHDOG_H diff --git a/source/board/udb/lib/nlbacktrace/include/nlbacktrace_udb.h b/source/board/udb/lib/nlbacktrace/include/nlbacktrace_udb.h new file mode 100644 index 0000000000..8448a96e56 --- /dev/null +++ b/source/board/udb/lib/nlbacktrace/include/nlbacktrace_udb.h @@ -0,0 +1,22 @@ +#ifndef __NLBACKTRACE_H_INCLUDED__ +#define __NLBACKTRACE_H_INCLUDED__ + +#include +#include +#include +#include + +int nlbacktrace(uint32_t initialPC, uint32_t initialSP, uint32_t stackTop, uint32_t* buffer, size_t maxLen); +int nlbacktrace_with_lr(uint32_t initialPC, uint32_t initialLR, uint32_t initialSP, uint32_t stackTop, uint32_t* buffer, size_t maxLen); +int nlbacktrace_no_context(uint32_t initialSP, uint32_t stackTop, uint32_t* buffer, size_t maxLen, size_t minLen, size_t maxAttempts); + +/* Weak functions that can be overrided by a product to provide + * more product specific checks for validity. Current checks + * assume all of RAM is valid for stacks. Also, all of RAM, + * and the FLASH regions for bootloader and app are valid for + * code. + */ +bool isAddressValidForCode(uint32_t addr); +bool isAddressValidForStack(uint32_t addr); + +#endif // __NLBACKTRACE_H_INCLUDED__ diff --git a/source/board/udb/lib/nlbacktrace/source/nlbacktrace_udb.c b/source/board/udb/lib/nlbacktrace/source/nlbacktrace_udb.c new file mode 100644 index 0000000000..96b61b3e61 --- /dev/null +++ b/source/board/udb/lib/nlbacktrace/source/nlbacktrace_udb.c @@ -0,0 +1,536 @@ +// -*- Mode: C++; tab-width: 4 indent-tabs-mode: nil -*- +// +// Copyright (c) 2013 Nest Labs, Inc. +// All rights reserved. +// +// This document is the property of Nest. It is considered +// confidential and proprietary information. +// +// This document may not be reproduced or transmitted in any form, +// in whole or in part, without the express written permission of +// Nest. +// +// Description: +// This file implements the nlbacktrace() function. +// It's used to retrieve the backtrace after a crash without prior knowledge +// on what the stack frames look like (i.e. no need for frame pointer) +// + +/* + Theory of operation + + There are two types of functions: + - non-leaf: functions that make a call to another function + - leaf: functions that don't call other function. + + For non-leaf functions, the compiler always follows the pattern below when generating the code: + + The prologue of the function looks like this: a push (or store multiple and decrement with SP as destination), + And an optional SP decrement for local variables. + push/stmd sp!, {rx, ry, ....., lr} + sub sp, sp, #xx + + The epilogue has to undo what the prologue did in order to leave the SP where it was before the function call + add sp, sp, #xx + pop/ldm sp!, {rx, ry, ....., pc} + + The nlbacktrace function takes two parameters: initialPC, initialSP. + The logic to follow is: + - make currentPC=initialPC, currentSP=initialSP. + - walk backward from the currentPC until we find either a push or a stmfd with SP as destination + - decode the push/stmfd instruction to find out how many registers got pushed. + - look for the "sub sp" instruction and decode it (it's usually the first or second instruction down from the push. + - check if there was an additional "sub sp" before the push (this appears to be used in conjunction with variable arguments) + - compute currentSP = SP + 4 * num_of_registers_pushed + space that was subtracted for the local variables. + - compute LR = currentSP[-1] + - lather, rinse and repeat + + This procedure works fairly well for non-leaf function but doesn't work for leafs. + So if we get a crasher in a leaf we don't get the right backtrace. Unfortunately leafs are + just as likely to crash. + As a first level heuristics, we call the nlbacktrace function twice: once with PC and once with LR, + and we keep the deepest stack trace we receive. For non-leaf functions, stack traces based on the + PC should always be greater or equal in length to stack traces based on the LR. For leaf functions, + stack traces based on the PC will often be wrong because there won't be a push/stmfd in the leaf + function and the search finds the push/stmfd of some other functon instead, and unwinds the SP + incorrectly. The stack trace based on the LR should give better results for the leaf case. + This takes care of the vast majority of the crasher and lets us put together automated tools for stack tracing. + + When a stack is for a FreeRTOS task, FreeRTOS puts at the top of the stack + at the LR position the address of a function set by configTASK_RETURN_ADDRESS. + If not defined, it is a static function in FreeRTOS port.c typically called + prvTaskExitError(). It's purpose is to assert if a task's main function + ever returns, because that is not a valid usage case (a task is supposed + to call vTaskDelete() if it every is done executing). + + To help backtrace find the end, it's recommended that projects set + configTASK_RETURN_ADDRESS to &abort, and make sure abort is a terminal + leaf function (something like have it do __builtin_trap()). This function + has no push/stmfd since the compiler knows it's never going to return, + and if we find it on a backtrace, we're confident that we cannot have unwound + to it (since nothing is called from it) so it must be the end of backtrace marker. + + If configTASK_RETURN_ADDRESS isn't set to &abort or abort isn't a terminal + leaf function, backtrace will probably just keep unwinding and finding some + false LR values. +*/ + +#include "nlbacktrace_udb.h" + +#define INVALID_PC (0xffffffff) + +// Not currently defined in nllibc-lite, though it is in stdlib.h +// Some platforms have it, like nlplatform_em358x, but just in case +// we define a weak default. +void abort(void) __attribute__((weak)); + +void abort(void) +{ + __builtin_trap(); +} + +// K&R style count bit set +unsigned countBitSet(uint32_t value) +{ + unsigned i; + for (i = 0; value ; i++) + { + value &= (value - 1); + } + return i; +} + +/* Make weak to allow a product to override with a narrower check. + * This default version is very lenient, and is not aware of possible + * restrictions like having an MPU that prevents execute from RAM, + * or has guard regions in RAM with no access. + */ +bool isAddressValidForCode(uint32_t addr) __attribute__((weak)); +bool isAddressValidForCode(uint32_t addr) +{ + return true; +} + +/* Make weak to allow a product to override with a narrower check. + * This default version is very lenient, and is not aware of possible + * restrictions like having an MPU that prevents execute from RAM, + * or has guard regions in RAM with no access. + */ +bool isAddressValidForStack(uint32_t addr) __attribute__((weak)); +bool isAddressValidForStack(uint32_t addr) +{ + return true; +} + +bool checkSP(uint32_t addr, uint32_t stackTop) +{ + bool result; + if (stackTop) + { + result = addr < stackTop; + } + else + { + result = isAddressValidForStack(addr); + } + return result; +} + +static uint32_t get_modified_immediate_constant(uint32_t inst) +{ + uint8_t i_imm3_a; + + /* imm_const will be set below but Coverity can't figure it out. */ + uint32_t imm_const = 0; + + /* See A5.3.2 "Modified immediate constants in Thumb instructions" in the + * ARMv7-M Architectural Reference Manual, which explains how constants + * are encoded. + */ + i_imm3_a = ((inst & (0x1 << 26)) >> 22) | ((inst & (0x7 << 12)) >> 11) | ((inst & 0x80) >> 7); + + if (i_imm3_a < 0x8) + { + uint32_t imm8 = inst & 0xff; + + /* When i:imm3:a is less than 8, the lower two bits of imm3 give the + * following special encodings. */ + switch (i_imm3_a >> 1) + { + case 0x0: + imm_const = imm8; + break; + + case 0x1: + imm_const = imm8 << 16 | imm8; + break; + + case 0x2: + imm_const = imm8 << 24 | imm8 << 8; + break; + + case 0x3: + imm_const = imm8 << 24 | imm8 << 16 | imm8 << 8 | imm8; + break; + } + } + else + { + /* When i:imm3:a is greater than or equal to 8, it encodes a shift of + * imm8. Since i:imm3:a is 5-bits, its range here will be between 8 and + * 31 inclusive. */ + imm_const = ((inst & 0x7f) | 0x80) << (32 - i_imm3_a); + } + + return imm_const; +} + +/* Unwind the stack frame indicated by pc and sp. + * + * The return value is INVALID_PC if no lr-push could be found. Otherwise it is + * the found lr. + * + * sp is modified according to any stack pointer adjustments found in the + * current frame. + * + * The functions iterates backwards from pc, instruction by instruction, in two + * phases: + * + * 1. "lr search": + * Searching backwards from a (likely) faulting pc, looking for the + * instruction which pushed the link register. Along the way, any stack + * pointer adjustments from sub-sp instructions should be counted. This + * search continues indefinitely until either the push is found or an + * invalid pc is encountered. + * + * 2. "prologue search": + * If the lr search phase is successful, the search for sp modifying + * instructions continues for PROLOGUE_SEARCH_LEN instructions. This search + * includes both sub-sp instructions and non-lr pushing instructions. + */ +static uint32_t find_lr_and_sp_for_frame(uint32_t pc, uint32_t *sp, uint32_t stackTop) +{ +#define ARM_PUSH_T1_MASK 0xFF00 +#define ARM_PUSH_W_LR_T1_COMP 0xB500 +#define ARM_PUSH_NO_LR_T1_COMP 0xB400 +#define ARM_PUSH_T1_REG_MASK 0x01FF + +#define ARM_PUSH_T2_MASK 0xFFFFE000 +#define ARM_PUSH_W_LR_T2_COMP 0xE92D4000 +#define ARM_PUSH_NO_LR_T2_COMP 0xE92D0000 +#define ARM_PUSH_T2_REG_MASK 0x0000FFFF + +#define ARM_SUB_SP_IMM_T1_MASK 0xFF80 +#define ARM_SUB_SP_IMM_T1_COMP 0xB080 +#define ARM_SUB_SP_IMM_T2_MASK 0xFBEF8F00 +#define ARM_SUB_SP_IMM_T2_COMP 0xF1AD0D00 +#define ARM_SUB_SP_IMM_T3_MASK 0xFBFF8F00 +#define ARM_SUB_SP_IMM_T3_COMP 0xF2AD0D00 + +#define ARM_VPUSH_MASK 0xFFBF0E00 +#define ARM_VPUSH_COMP 0xED2D0A00 +#define ARM_VPUSH_REG_MASK 0x000000FF +#define ARM_VPUSH_REG_POS 0 + +#define PROLOGUE_SEARCH_LEN 2 + + uint32_t return_lr = INVALID_PC; + + /* sp_mod is used to accumulate stack pointer modifications in register + * (4-byte) increments. */ + uint32_t sp_mod = 0; + + /* Only match lr-pushes during lr search. */ + uint32_t push32_comp = ARM_PUSH_W_LR_T2_COMP; + uint16_t push16_comp = ARM_PUSH_W_LR_T1_COMP; + + uint16_t prologue_search_len = PROLOGUE_SEARCH_LEN; + + enum + { + lr_search, + lr_search_done, + prologue_search + } search_phase = lr_search; + + uint16_t inst16 = 0; + + while (1) + { + uint16_t last_inst16; + uint32_t inst32; + + if (!isAddressValidForCode(pc)) + { + break; + } + + last_inst16 = inst16; + inst16 = *((uint16_t *) pc); + inst32 = (inst16 << 16) | last_inst16; + + /* Look for relevant instructions. There are seven total, listed here + * with the referenced section from the ARMv7-M Architecture Reference + * Manual. + * 1. 32-bit push PUSH.W A7.7.99 (T2) + * 2. 32-bit sub sp SUB{S}.W ,SP,# A7.7.173 (T2) + * 3. 32-bit sub sp SUBW ,SP,# A7.7.173 (T3) + * 4. 16-bit push PUSH A7.7.99 (T1) + * 5. 16-bit sub sp SUB SP,SP,# A7.7.173 (T1) + * 6. 32-bit vpush 64 VPUSH <64-bit registers> A7.7.249 (T1) + * 7. 32-bit vpush 32 VPUSH <32-bit registers> A7.7.249 (T2) + */ + { + if ((inst32 & ARM_PUSH_T2_MASK) == push32_comp) + { + uint32_t regs_pushed; + + regs_pushed = countBitSet(inst32 & ARM_PUSH_T2_REG_MASK); + sp_mod += regs_pushed; + + if (search_phase == lr_search) + { + search_phase = lr_search_done; + } + } + else if ((inst32 & ARM_SUB_SP_IMM_T2_MASK) == ARM_SUB_SP_IMM_T2_COMP) + { + uint32_t imm32; + + /* Encoding T2 of SUB (SP minus immediate) encodes the immediate + * constant as a 'modified immediate constant'. */ + imm32 = get_modified_immediate_constant(inst32); + sp_mod += imm32/4; + } + else if ((inst32 & ARM_SUB_SP_IMM_T3_MASK) == ARM_SUB_SP_IMM_T3_COMP) + { + uint32_t imm32; + + /* Encoding T3 of SUB (SP minus immediate) encodes the immediate + * constant in three disjoint bitfields, i:imm3:imm8. */ + imm32 = ((inst32 & (0x1 << 26)) >> 15) | ((inst32 & (0x7 << 12)) >> 4) | (inst32 & 0xff); + sp_mod += imm32/4; + } + else if ((inst16 & ARM_PUSH_T1_MASK) == push16_comp) + { + uint32_t regs_pushed; + + regs_pushed = countBitSet(inst16 & ARM_PUSH_T1_REG_MASK); + sp_mod += regs_pushed; + + if (search_phase == lr_search) + { + search_phase = lr_search_done; + } + } + else if ((inst16 & ARM_SUB_SP_IMM_T1_MASK) == ARM_SUB_SP_IMM_T1_COMP) + { + uint32_t imm32; + + /* Encoding T1 of SUB (SP minus immediate) encodes the immediate + * constant in the lower 7 bits (imm7). It is interpreted as imm7:'00' + * so that it doesn't need to be divided by 4 before being added to + * sp_change. */ + imm32 = inst16 & 0x7f; + sp_mod += imm32; + } +/* Our compiler will set this flag indicating that we are using ARM's vector + * floating point extension. */ +#if defined(__VFP_FP__) + else if ((inst32 & ARM_VPUSH_MASK) == ARM_VPUSH_COMP) + { + uint32_t words_pushed; + + /* We can handle both encodings of VPUSH here since both encode + * the total number of words pushed directly regardless of + * whether they are ultimately pushing double or single registers. */ + words_pushed = (inst32 & ARM_VPUSH_REG_MASK) >> ARM_VPUSH_REG_POS; + sp_mod += words_pushed; + } +#endif + } + + if (search_phase == lr_search_done) + { + /* Found the lr push. Check the modified sp before + * dereferencing. */ + uint32_t tmp_lr; + uint32_t *tmp_sp = (uint32_t *)(*sp) + sp_mod; + + if (!checkSP((uint32_t)tmp_sp, stackTop)) + { + break; + } + + /* lr should be last register pushed, which on a descending stack + * means that it is at -1 offset. */ + tmp_lr = tmp_sp[-1]; + + if (!isAddressValidForCode(tmp_lr)) + { + break; + } + + return_lr = tmp_lr; + + /* Switch to prologue search. */ + search_phase = prologue_search; + push32_comp = ARM_PUSH_NO_LR_T2_COMP; + push16_comp = ARM_PUSH_NO_LR_T1_COMP; + } + + if ((search_phase == prologue_search) && (prologue_search_len-- == 0)) + { + break; + } + + /* Decrement the pc by one 16-bit instruction. */ + pc -= 2; + } + + *sp += sp_mod*4; + + return return_lr; +} + + +int nlbacktrace(uint32_t initialPC, uint32_t initialSP, uint32_t stackTop, uint32_t* buffer, size_t maxLen) +{ + size_t level = 0; + uint32_t currentSP = initialSP; + uint32_t currentPC = initialPC; + + // we assume that initialPC is valid + + while (level < maxLen) + { + // save the PC + *buffer++ = currentPC; + level++; + + // strip the Thumb bit before passing to find_lr_and_sp_for_frame + currentPC = currentPC & ~0x1; + + currentPC = find_lr_and_sp_for_frame(currentPC, ¤tSP, stackTop); + + if (currentPC == INVALID_PC) + { + break; + } + + // stop if lr is &abort since that's our special indicator + // of top of task stack. A real abort() would always be a leaf + // and at end of stack, not somewhere in the middle. + if (currentPC == (uint32_t)&abort) + { + break; + } + } + + return (int)level; +} + +int nlbacktrace_with_lr(uint32_t initialPC, uint32_t initialLR, uint32_t initialSP, uint32_t stackTop, uint32_t* buffer, size_t maxLen) +{ + /* Generally, the backtrace from the PC is the best. However, + * if the PC is in a leaf function (which has no preamble that pushes + * registers), the backtrace function will return either no result + * or a wrong result because the search for the PUSH instruction will + * find the PUSH of the wrong function. + */ + int num_backtraces_lr; + int num_backtraces; + bool pc_is_valid = isAddressValidForCode(initialPC); + if (((initialLR & ~0x1) == initialPC) || !isAddressValidForCode(initialLR)) + { + /* If the LR is invalid, or same as the PC, don't bother */ + num_backtraces_lr = 0; + } + else + { + /* Regardless of whether the PC is valid or not, try a LR + * based backtrace first + */ + num_backtraces_lr = nlbacktrace(initialLR, initialSP, stackTop, buffer+1, maxLen-1); + + /* If num_backtraces_lr is 0, but PC is valid, we add the PC to + * the buffer and increment num_backtraces_lr by 1. + * If num_backtraces_lr > 0, regardless of whether PC is valid or not, + * we add the PC to the buffer and increment num_backtraces_lr by 1 + * (this latter can happen if the fault was a bl to a bad address like 0x0). + * Only case where we don't want to do the increment is if num_backtraces_lr + * is 0 and PC is not valid. + */ + if ((num_backtraces_lr > 0) || pc_is_valid) + { + buffer[0] = initialPC; + num_backtraces_lr++; + } + } + /* Get a backtrace from the PC if it is valid */ + if (!pc_is_valid) + { + /* Return the LR backtrace count, if any */ + num_backtraces = num_backtraces_lr; + } + else + { + num_backtraces = nlbacktrace(initialPC, initialSP, stackTop, buffer, maxLen); + + /* Check if the LR backtrace was deeper, which can happen if the PC + * was in a leaf function without a preamble + */ + if (num_backtraces_lr > num_backtraces) + { + /* Redo lr backtrace to the buffer*/ + buffer[0] = initialPC; + num_backtraces = nlbacktrace(initialLR, initialSP, stackTop, buffer+1, maxLen-1) + 1; + } + } + return num_backtraces; +} + +/* We don't have a starting PC or LR. Look in the stack for a possible + * LR and backtrace from it. We check at most maxAttempts from the stack + * for a LR value that nlbacktrace() returns a backtrace of at least minLen + * size. If none is found using nlbacktrace(), we then fall back to + * just returning an array of possible LR values scanning from the initialSP + * to stackTop, stopping at maxLen entries or when we hit an invalid SP, + * whichever is hit first. + */ +int nlbacktrace_no_context(uint32_t initialSP, uint32_t stackTop, uint32_t* buffer, size_t maxLen, size_t minLen, size_t maxAttempts) +{ + size_t i; + uint32_t* currentSP = (uint32_t*)initialSP; + int num_backtraces = 0; + for (i = 0; i < maxAttempts && checkSP((uint32_t)currentSP, stackTop); i++) + { + uint32_t possibleLR = *currentSP++; + if ((possibleLR & 0x1) && isAddressValidForCode(possibleLR)) + { + num_backtraces = nlbacktrace(possibleLR, (uint32_t)currentSP, stackTop, buffer, maxLen); + if ((size_t)num_backtraces >= minLen) + { + goto done; + } + } + } + if ((size_t)num_backtraces < minLen) + { + /* nlbacktrace unable to find a valid backtrace, just return + * guesses of possible LRs + */ + currentSP = (uint32_t*)initialSP; + num_backtraces = 0; + while ((size_t)num_backtraces < maxLen && checkSP((uint32_t)currentSP, stackTop)) + { + uint32_t possibleLR = *currentSP++; + if ((possibleLR & 0x1) && isAddressValidForCode(possibleLR)) + { + buffer[num_backtraces++] = possibleLR; + } + } + } +done: + return num_backtraces; +} diff --git a/source/board/udb/lib/nlutilities/include/nluif_udb-daplink.h b/source/board/udb/lib/nlutilities/include/nluif_udb-daplink.h new file mode 100644 index 0000000000..eac8e452b1 --- /dev/null +++ b/source/board/udb/lib/nlutilities/include/nluif_udb-daplink.h @@ -0,0 +1,331 @@ +/* + * + * Copyright (c) 2012-2018 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines a framework for an interactive command line + * user interface. + * + * @note The actual commands, set/show parameters, and prompt are + * configured at the project level. + * + */ + +/* + * Copied to this repository from platform/nlutilities on 2021-12-22 and + * removed dependencies on other unneeded headers. + */ + +#ifndef NLUTILITIES_NLUIF_H +#define NLUTILITIES_NLUIF_H + +#include +#include + +/********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @def NLUIF_USE_MINISHELL + * + * @brief + * The minishell build feature uses smaller default values for max-args, + * max-line and max-prompt, and uses a pared down UIF command + * structure, eliding both syntax and descripton. + */ +#ifndef NLUIF_USE_MINISHELL +#define NLUIF_USE_MINISHELL 0 +#endif /* NLUIF_USE_MINISHELL */ + +/** + * @def NLUIF_USE_VERBOSE + * + * @brief + * The verbose build feature enables both usage syntax and command + * description UIF fields. + */ +#ifndef NLUIF_USE_VERBOSE +#define NLUIF_USE_VERBOSE !NLUIF_USE_MINISHELL +#endif /* NLUIF_USE_VERBOSE */ + +/** + * @def NLUIF_USE_DESCRIPTION + * + * @brief + * The description build feature enables the UIF description field. + */ +#ifndef NLUIF_USE_DESCRIPTION +#define NLUIF_USE_DESCRIPTION (NLUIF_USE_VERBOSE && !NLUIF_USE_MINISHELL) +#endif /* NLUIF_USE_DESCRIPTION */ + +/** + * @def NLUIF_USE_SYNTAX + * + * @brief + * The syntax build feature enables the UIF command syntax UIF field. + */ +#ifndef NLUIF_USE_SYNTAX +#define NLUIF_USE_SYNTAX (NLUIF_USE_VERBOSE && !NLUIF_USE_MINISHELL) +#endif /* NLUIF_USE_SYNTAX */ + +/** + * @def NLUIF_USE_VOID_FUNCS + * + * @brief + * The void functions build feature enables shell function entry + * points that have a void return value; otherwise, shell function + * entry points return a signed integer. + */ +#ifndef NLUIF_USE_VOID_FUNCS +#define NLUIF_USE_VOID_FUNCS 1 +#endif /* NLUIF_USE_VOID_FUNCS */ + +/** + * @def NLUIF_PRINT_RESULT + * + * @brief + * Enable the printing of the return code after a command is run, + * in the format of "DONE %d" + */ +#ifndef NLUIF_PRINT_RESULT +#define NLUIF_PRINT_RESULT 0 +#endif /* NLUIF_PRINT_RESULT */ + +#if !NLUIF_USE_MINISHELL +/* + * Maximum command line arguments + */ +#ifndef UIF_MAX_ARGS +#define UIF_MAX_ARGS 29 +#endif + +/* + * Maximum length of the command line + */ +#ifndef UIF_MAX_LINE +#define UIF_MAX_LINE 768 +#endif + +/* + * Maximum length of the prompt string + */ +#ifndef UIF_MAX_PROMPT +#define UIF_MAX_PROMPT 8 +#endif + +#else /* !NLUIF_USE_MINISHELL */ + +/* + * Maximum command line arguments + */ +#ifndef UIF_MAX_ARGS +#define UIF_MAX_ARGS 3 +#endif + +/* + * Maximum length of the command line + */ +#ifndef UIF_MAX_LINE +#define UIF_MAX_LINE 100 +#endif + +/* + * Maximum length of the prompt string + */ +#ifndef UIF_MAX_PROMPT +#define UIF_MAX_PROMPT 2 +#endif + +#endif /* !NLUIF_USE_MINISHELL */ + +/* + * Function prototypes + */ +void +uif_set_prompt(const char *s); + +void +uif_prompt(void); + +int +uif_handle_input_char(int ch); + +uint32_t +uif_get_value (const char *, bool *, int); + +void +uif_run_cmd (void); + +void +uif_run_cmd_string (const char *command); + +void +uif_cmd_help (int, char **); + +int +uif_cmd_minishell_help (int, char **); + +void +uif_cmd_set (int, char **); + +void +uif_cmd_show (int, char **); + +#define UIF_HISTORY_CNT 5 + +/* + * The command table entry data structure + */ + +#if NLUIF_USE_VOID_FUNCS +typedef void nl_uif_shell_func_return_t; +#else +typedef int nl_uif_shell_func_return_t; +#endif /* NLUIF_USE_VOID_FUNCS */ + +typedef nl_uif_shell_func_return_t (* nl_uif_shell_func_t)(int argc, char **argv); + +typedef struct +{ + const char * cmd; /* command name user types, ie. GO */ + nl_uif_shell_func_t func; /* actual function to call */ +#if !NLUIF_USE_MINISHELL + int min_args; /* min num of args command accepts */ + int max_args; /* max num of args command accepts */ + int flags; /* command flags (e.g. repeat) */ +#endif /* !NLUIF_USE_MINISHELL */ +#if NLUIF_USE_DESCRIPTION + const char * description; /* brief description of command */ +#endif /* NLUIF_USE_DESCRIPTION */ +#if !NLUIF_USE_MINISHELL +#if NLUIF_USE_SYNTAX + const void * syntax; /* syntax of command */ +#endif /* NLUIF_USE_SYNTAX*/ +#endif /* !NLUIF_USE_MINISHELL */ +} UIF_CMD; + +typedef UIF_CMD nl_uif_cmd_t; + +typedef void (*UIF_SYNTAX_FUNC)(const char *name); + +#if NLUIF_USE_DESCRIPTION && NLUIF_USE_SYNTAX +#define DECLARE_UIF_CMD(name, min, max, flags, func, desc, syntax) \ + { name, func, min, max, flags, desc, syntax } +#elif NLUIF_USE_DESCRIPTION && !NLUIF_USE_SYNTAX && !NLUIF_USE_MINISHELL +#define DECLARE_UIF_CMD(name, min, max, flags, func, desc, syntax) \ + { name, func, min, max, flags, desc } +#elif !NLUIF_USE_DESCRIPTION && NLUIF_USE_SYNTAX +#define DECLARE_UIF_CMD(name, min, max, flags, func, desc, syntax) \ + { name, func, min, max, flags, syntax } +#elif NLUIF_USE_MINISHELL && !NLUIF_USE_DESCRIPTION +#define DECLARE_UIF_CMD(name, min, max, flags, func, desc, syntax) \ + { name, func } +#define DECLARE_UIF_MINISHELL_CMD(name, func) \ + DECLARE_UIF_CMD(name, 0, 0, 0, func, NULL, NULL) +#elif NLUIF_USE_MINISHELL && NLUIF_USE_DESCRIPTION +#define DECLARE_UIF_CMD(name, min, max, flags, func, desc, syntax) \ + { name, func, desc } +#define DECLARE_UIF_MINISHELL_CMD(name, func) \ + DECLARE_UIF_CMD(name, 0, 0, 0, func, #func, NULL) +#else +#define DECLARE_UIF_CMD(name, min, max, flags, func, desc, syntax) \ + { name, func, min, max, flags } +#endif /* UIF_USE_DESCRIPTION && UIF_USE_SYNTAX */ + +/* + * Prototype and macro for size of the command table + */ +extern const UIF_CMD UIF_CMDTAB[]; +extern const int UIF_NUM_CMD; +#define UIF_CMDTAB_SIZE (sizeof(UIF_CMDTAB)/sizeof(UIF_CMD)) + +#define UIF_CMD_FLAG_REPEAT 0x1 +#define UIF_CMD_FLAG_SYNTAX_FUNC 0x2 + +/* + * Macros for User InterFace command table entries + */ +#ifndef UIF_CMD_HELP +#define UIF_CMD_HELP \ + DECLARE_UIF_CMD("?", 0, 1, 0, uif_cmd_help, "show available commands", ""), \ + DECLARE_UIF_CMD("help", 0, 1, 0, uif_cmd_help, "show available commands", "") +#endif + +#ifndef UIF_CMD_SET +#define UIF_CMD_SET \ + DECLARE_UIF_CMD("set", 0, 2, 0, uif_cmd_set, "Set Config", "