├── images ├── .gitkeep ├── bin10x10.png ├── wiring_128x64.png ├── DolphinCommon_56x48.png └── debugger_base_128x64.png ├── .gitignore ├── helpers ├── .DS_Store ├── risc_debug_inner.h ├── swio.h ├── swio_inner.h ├── wch_flasher.h ├── programs │ ├── get_machine_isa.c │ ├── program_flash_command.c │ ├── get_block_aligned.c │ ├── get_set_u32.c │ └── write_flash.c ├── programs.h ├── swio.c ├── wch_flasher_inner.h ├── riscv_debug.h ├── wch_flasher.c ├── swio_pin_magic.c └── riscv_debug.c ├── wch_swio_flasher.png ├── screenshots ├── wchf_about.png ├── wchf_debug.png ├── wchf_flash.png ├── wchf_main.png ├── wchf_wiring.png ├── wchf_app_utils.png ├── wchf_chip_erased.png ├── wchf_get_chip_info.png ├── wchf_get_chip_info_err.png └── wchf_flash_program_done.png ├── scenes ├── wch_swio_flasher_scene_config.h ├── wch_swio_flasher_scene.h ├── wch_swio_flasher_scene.c ├── wch_swio_flasher_scene_file_browser.c ├── wch_swio_flasher_scene_about.c ├── wch_swio_flasher_scene_wiring.c ├── wch_swio_flasher_scene_main.c ├── wch_swio_flasher_scene_debugger.c └── wch_swio_flasher_scene_flash.c ├── application.fam ├── LICENSE ├── utils.h ├── error_list.h ├── .github └── workflows │ └── build.yml ├── views ├── debugger_emulator.h ├── view_flasher.h ├── debugger_emulator.c └── view_flasher.c ├── config.h ├── nhc_link042_emulator.h ├── errors.c ├── wch_swio_flasher.h ├── errors.h ├── minichlink_debugger.h ├── ch32v_flipper_flasher.h ├── Readme.md ├── minichlink_debugger.c ├── wch_swio_flasher.c ├── ch32v_flipper_flasher.c └── nhc_link042_emulator.c /images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | .vscode 3 | .clang-format 4 | .editorconfig -------------------------------------------------------------------------------- /helpers/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/helpers/.DS_Store -------------------------------------------------------------------------------- /images/bin10x10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/images/bin10x10.png -------------------------------------------------------------------------------- /wch_swio_flasher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/wch_swio_flasher.png -------------------------------------------------------------------------------- /images/wiring_128x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/images/wiring_128x64.png -------------------------------------------------------------------------------- /screenshots/wchf_about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_about.png -------------------------------------------------------------------------------- /screenshots/wchf_debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_debug.png -------------------------------------------------------------------------------- /screenshots/wchf_flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_flash.png -------------------------------------------------------------------------------- /screenshots/wchf_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_main.png -------------------------------------------------------------------------------- /screenshots/wchf_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_wiring.png -------------------------------------------------------------------------------- /images/DolphinCommon_56x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/images/DolphinCommon_56x48.png -------------------------------------------------------------------------------- /images/debugger_base_128x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/images/debugger_base_128x64.png -------------------------------------------------------------------------------- /screenshots/wchf_app_utils.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_app_utils.png -------------------------------------------------------------------------------- /screenshots/wchf_chip_erased.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_chip_erased.png -------------------------------------------------------------------------------- /screenshots/wchf_get_chip_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_get_chip_info.png -------------------------------------------------------------------------------- /screenshots/wchf_get_chip_info_err.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_get_chip_info_err.png -------------------------------------------------------------------------------- /screenshots/wchf_flash_program_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sukvojte/wch_swio_flasher/HEAD/screenshots/wchf_flash_program_done.png -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene_config.h: -------------------------------------------------------------------------------- 1 | ADD_SCENE(wch_swio_flasher, main, Main) 2 | ADD_SCENE(wch_swio_flasher, debugger, Debugger) 3 | ADD_SCENE(wch_swio_flasher, flash, Flash) 4 | ADD_SCENE(wch_swio_flasher, wiring, Wiring) 5 | ADD_SCENE(wch_swio_flasher, about, About) 6 | ADD_SCENE(wch_swio_flasher, file_browser, FileBrowser) 7 | -------------------------------------------------------------------------------- /application.fam: -------------------------------------------------------------------------------- 1 | App( 2 | appid="wch_swio_flasher", 3 | name="WHC SWIO Flasher", 4 | apptype=FlipperAppType.EXTERNAL, 5 | entry_point="wch_swio_flasher_app", 6 | stack_size=2 * 1024, 7 | fap_libs=["assets"], 8 | fap_category="Tools", 9 | # Optional values 10 | fap_version="1.1", 11 | fap_icon="wch_swio_flasher.png", # 10x10 1-bit PNG 12 | fap_description="A WHC CH32V003 debugger/flasher tool", 13 | fap_author="Vojtech Suk", 14 | fap_weburl="https://github.com/sukvojte/wch_swio_flasher", 15 | fap_icon_assets="images", # Image assets to compile for this application 16 | ) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Vojtech Suk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #define _UI(v) ((unsigned int)(v)) 29 | #define FMT_4HEX "0x%08X" -------------------------------------------------------------------------------- /error_list.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #ifndef ERR 27 | #define ERR(name, code) 28 | #endif 29 | ERR(0, WchSwioFlasher_Ok) 30 | ERR(-1000, WchSwioFlasher_Error_Timeout) 31 | ERR(-2000, WchSwioFlasher_Error_TargetInInvalidState) 32 | #undef ERR -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "FAP: Build for multiple SDK sources" 2 | # This will build your app for dev and release channels on GitHub. 3 | # It will also build your app every day to make sure it's up to date with the latest SDK changes. 4 | # See https://github.com/marketplace/actions/build-flipper-application-package-fap for more information 5 | 6 | on: 7 | push: 8 | ## put your main branch name under "braches" 9 | #branches: 10 | # - master 11 | pull_request: 12 | schedule: 13 | # do a build every day 14 | - cron: "1 1 * * *" 15 | 16 | jobs: 17 | ufbt-build: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | include: 22 | - name: dev channel 23 | sdk-channel: dev 24 | - name: release channel 25 | sdk-channel: release 26 | # You can add unofficial channels here. See ufbt action docs for more info. 27 | name: 'ufbt: Build for ${{ matrix.name }}' 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v3 31 | - name: Build with ufbt 32 | uses: flipperdevices/flipperzero-ufbt-action@v0.1.1 33 | id: build-app 34 | with: 35 | sdk-channel: ${{ matrix.sdk-channel }} 36 | - name: Upload app artifacts 37 | uses: actions/upload-artifact@v3 38 | with: 39 | # See ufbt action docs for other output variables 40 | name: ${{ github.event.repository.name }}-${{ steps.build-app.outputs.suffix }} 41 | path: ${{ steps.build-app.outputs.fap-artifacts }} 42 | -------------------------------------------------------------------------------- /views/debugger_emulator.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | 30 | typedef struct DebuggerEmulator DebuggerEmulator; 31 | 32 | DebuggerEmulator* debugger_emulator_alloc(); 33 | 34 | void debugger_emulator_free(DebuggerEmulator* handle); 35 | 36 | View* debugger_emulator_get_view(DebuggerEmulator* handle); 37 | 38 | void debugger_emulator_set_animation_status(DebuggerEmulator* handle, uint8_t enabled); -------------------------------------------------------------------------------- /helpers/risc_debug_inner.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | /** 27 | This file is based on modified original from https://github.com/aappleby/PicoRVD 28 | */ 29 | 30 | #pragma once 31 | 32 | #include 33 | 34 | typedef union { 35 | struct { 36 | uint32_t REGNO : 16; 37 | uint32_t WRITE : 1; 38 | uint32_t TRANSFER : 1; 39 | uint32_t POSTEXEC : 1; 40 | uint32_t AARPOSTINC : 1; 41 | uint32_t AARSIZE : 3; 42 | uint32_t PAD0 : 1; 43 | uint32_t CMDTYPE : 8; 44 | }; 45 | uint32_t raw; 46 | } RVD_COMMAND; 47 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | //#define SWIO_TXRX_DEBUG_MSG_ENABLE 29 | //#define SWIO_TRIGGER_OUT_ENABLE 30 | 31 | #define WCHF_VERIFY_MAX_BUFFER_SIZE 1024 32 | #define WCHF_WAIT_FOR_WRITE_FLASH_TIMEOUT 1000 33 | 34 | //#define NCHLNK_TXRX_DEBUG_MSG_ENABLE 35 | #define NCHLNK_TX_SEMAPHORE_TIMEOUT 5000 36 | 37 | #define RVD_WAIT_FOR_DM_STATUS_TIMEOUT 500 38 | #define RVD_WAIT_FOR_CHIPINFO_TIMEOUT 500 39 | 40 | //#define RVD_TXRX_DEBUG_MSG_ENABLE 41 | #define RVD_CHECK_PROGRAM_UPLOAD_ENABLED 42 | #define RVD_CHECK_PROGRAM_EXECUTION_ENABLED 43 | 44 | #define RVD_NCHLINKEMU_TX_BUFF_SIZE 2048 45 | #define RVD_NCHLINKEMU_RX_BUFF_SIZE 2048 -------------------------------------------------------------------------------- /helpers/swio.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | #include 30 | 31 | #include "../errors.h" 32 | 33 | typedef struct { 34 | uint32_t dummy; 35 | } WchSwioFlasher_SWIO; 36 | 37 | WchSwioFlasher_SWIO* WchSwioFlasher_SWIO_create(); 38 | 39 | void WchSwioFlasher_SWIO_destroy(WchSwioFlasher_SWIO* handle); 40 | 41 | WchSwioFlasher_Error WchSwioFlasher_SWIO_init(WchSwioFlasher_SWIO* handle); 42 | 43 | WchSwioFlasher_Error 44 | WchSwioFlasher_SWIO_write(WchSwioFlasher_SWIO* handle, uint8_t address, uint32_t data); 45 | 46 | WchSwioFlasher_Error 47 | WchSwioFlasher_SWIO_read(WchSwioFlasher_SWIO* handle, uint8_t address, uint32_t* data); 48 | 49 | WchSwioFlasher_Error WchSwioFlasher_SWIO_hw_reset(WchSwioFlasher_SWIO* handle); -------------------------------------------------------------------------------- /nhc_link042_emulator.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "minichlink_debugger.h" 33 | #include "errors.h" 34 | 35 | typedef struct { 36 | uint32_t attached; 37 | FuriHalUsbInterface* usb_mode_prev; 38 | WchSwioFlasher_MinichlinkDebugger* debugger; 39 | FuriMutex* mutex; 40 | 41 | } WchSwioFlasher_NhcLink042Emu; 42 | 43 | WchSwioFlasher_NhcLink042Emu* 44 | WchSwioFlasher_NhcLink042Emu_create(WchSwioFlasher_MinichlinkDebugger* debugger); 45 | 46 | void WchSwioFlasher_NhcLink042Emu_destroy(WchSwioFlasher_NhcLink042Emu* handle); 47 | 48 | WchSwioFlasher_Error WchSwioFlasher_NhcLink042Emu_attach(WchSwioFlasher_NhcLink042Emu* handle); 49 | 50 | WchSwioFlasher_Error WchSwioFlasher_NhcLink042Emu_detach(WchSwioFlasher_NhcLink042Emu* handle); 51 | -------------------------------------------------------------------------------- /helpers/swio_inner.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include "swio.h" 29 | 30 | #define SWI_HW_RST_GPIO gpio_ext_pc1 31 | 32 | #define SWI_GPIO gpio_ext_pc0 33 | #define SWI_PORT_BASE GPIOC_BASE 34 | #define SWI_PORT_BSSR_OFFSET 0x18 35 | #define SWI_PORT_IDR_OFFSET 0x10 36 | #define SWI_PORT_MODER_OFFSET 0x00 37 | #define SWI_SET_MODE_OUTPUT_MASK GPIO_MODER_MODE0_0 38 | #define SWI_SET_MODE_INPUT_MASK (~SWI_SET_MODE_OUTPUT_MASK) 39 | #define SWI_SET_MASK GPIO_ODR_OD0 40 | #define SWI_RESET_MASK (SWI_SET_MASK << 16) 41 | 42 | #define WCH_DM_CPBR 0x7C 43 | #define WCH_DM_CFGR 0x7D 44 | #define WCH_DM_SHDWCFGR 0x7E 45 | 46 | typedef enum { 47 | WchSwioFlasher_SWIO_Read = 0, 48 | WchSwioFlasher_SWIO_Write = 1, 49 | } WchSwioFlasher_SWIO_RW; 50 | 51 | WchSwioFlasher_Error 52 | WchSwioFlasher_SWIO_rxtx(uint8_t address, WchSwioFlasher_SWIO_RW rw, uint32_t* data); -------------------------------------------------------------------------------- /helpers/wch_flasher.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | #include 28 | #include "../errors.h" 29 | #include "riscv_debug.h" 30 | 31 | typedef struct { 32 | WchSwioFlasher_RiscVDebug* debug; 33 | 34 | } WchSwioFlasher_WchFlasher; 35 | 36 | WchSwioFlasher_WchFlasher* WchSwioFlasher_WchFlasher_create(WchSwioFlasher_RiscVDebug* debug); 37 | 38 | void WchSwioFlasher_WchFlasher_destroy(WchSwioFlasher_WchFlasher* handle); 39 | 40 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_unlock_flash(WchSwioFlasher_WchFlasher* handle); 41 | 42 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_lock_flash(WchSwioFlasher_WchFlasher* handle); 43 | 44 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_wipe_chip(WchSwioFlasher_WchFlasher* handle); 45 | 46 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_write_flash( 47 | WchSwioFlasher_WchFlasher* handle, 48 | uint32_t dst_addr, 49 | void* blob, 50 | int size); 51 | -------------------------------------------------------------------------------- /errors.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include "errors.h" 27 | 28 | char* WchSwioFlasher_ErrorToString(WchSwioFlasher_Error error) { 29 | switch(error) { 30 | case WchSwioFlasher_Ok: 31 | return "no-error"; 32 | case WchSwioFlasher_Error_SwdResetDetected: 33 | return "swd-reset"; 34 | case WchSwioFlasher_Error_Timeout: 35 | return "timeout"; 36 | case WchSwioFlasher_Error_SwdParityCheckError: 37 | return "parity-error"; 38 | case WchSwioFlasher_Error_ProgramRunError: 39 | return "program-run-error"; 40 | case WchSwioFlasher_Error_TargetInInvalidState: 41 | return "target-in-invalid-state"; 42 | case WchSwioFlasher_Error_TargetNotKnown: 43 | return "target-not-known"; 44 | case WchSwioFlasher_Error_ProgramNotFinishedYet: 45 | return "program-not-finished-yet"; 46 | default: 47 | break; 48 | } 49 | 50 | return ""; 51 | } -------------------------------------------------------------------------------- /helpers/programs/get_machine_isa.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include "../programs.h" 28 | //------------------------------------------------------------------------------ 29 | // data0 = data to read 30 | // only clobbers A0, A1 31 | 32 | const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_get_machine_isa_program = { 33 | .name = "get_machine_isa", 34 | .clobbers = BIT_A0 | BIT_A1, 35 | .data = { 36 | //0xe0000537, // lui a0,0xe0000 37 | 0x0537, 38 | 0xe000, 39 | 40 | //0x0f450513, // addi a0,a0,0xF4 41 | 0x0513, 42 | 0x0f45, 43 | 44 | // csrr a1,misa 45 | 0x25F3, 46 | 0x3010, 47 | 48 | 0xC10C, // sw a1,0(a0) 49 | 50 | 0x9002, // ebreak 51 | 0x9002, // ebreak 52 | 0x9002, // ebreak 53 | 0x9002, // ebreak 54 | 0x9002, // ebreak 55 | 56 | 0x9002, // ebreak 57 | 0x9002, // ebreak 58 | 0x9002, // ebreak 59 | 0x9002, // ebreak 60 | }}; 61 | -------------------------------------------------------------------------------- /views/view_flasher.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | 30 | typedef struct ViewFlasher ViewFlasher; 31 | 32 | typedef enum { 33 | ViewFlasher_Action_ChipInfo = 0, 34 | ViewFlasher_Action_OpenFile, 35 | ViewFlasher_Action_EraseChip, 36 | ViewFlasher_Action_WriteChip, 37 | ViewFlasher_ActionCount, 38 | } ViewFlasher_View_Action; 39 | 40 | typedef void (*ViewFlasherDoActionCallback)(void* context, ViewFlasher_View_Action action); 41 | 42 | void view_flasher_register_action_callback( 43 | ViewFlasher* handle, 44 | ViewFlasherDoActionCallback cb, 45 | void* cb_context); 46 | 47 | ViewFlasher* view_flasher_alloc(); 48 | 49 | void view_flasher_free(ViewFlasher* handle); 50 | 51 | View* view_flasher_get_view(ViewFlasher* flasher); 52 | 53 | void view_flasher_set_animation_status(ViewFlasher* handle, uint8_t enabled); 54 | 55 | void view_flasher_display_text(ViewFlasher* handle, const char* message); 56 | 57 | void view_flasher_display_clear_text(ViewFlasher* handle); -------------------------------------------------------------------------------- /helpers/programs.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | 30 | typedef struct { 31 | const char* const name; 32 | uint32_t clobbers; 33 | const uint16_t data[16]; 34 | } WchSwioFlasher_RiscVProgram; 35 | 36 | #define BIT_T0 (1 << 5) 37 | #define BIT_T1 (1 << 6) 38 | #define BIT_T2 (1 << 7) 39 | #define BIT_S0 (1 << 8) 40 | #define BIT_S1 (1 << 9) 41 | #define BIT_A0 (1 << 10) 42 | #define BIT_A1 (1 << 11) 43 | #define BIT_A2 (1 << 12) 44 | #define BIT_A3 (1 << 13) 45 | #define BIT_A4 (1 << 14) 46 | #define BIT_A5 (1 << 15) 47 | 48 | extern const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_get_set_u32_program; 49 | extern const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_get_block_aligned_program; 50 | extern const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_get_machine_isa_program; 51 | extern const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_program_flash_command_program; 52 | extern const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_program_write_flash_program; 53 | -------------------------------------------------------------------------------- /helpers/programs/program_flash_command.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include "../programs.h" 28 | //------------------------------------------------------------------------------ 29 | // only clobbers A0, A1, A2, A3, A5 30 | 31 | const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_program_flash_command_program = { 32 | .name = "program_flash_command", 33 | .clobbers = BIT_A0 | BIT_A1 | BIT_A2 | BIT_A3 | BIT_A5, 34 | .data = { 35 | 0xc94c, // sw a1,20(a0) 36 | 0xc910, // sw a2,16(a0) 37 | 0xc914, // sw a3,16(a0) 38 | 39 | // waitloop: 40 | 0x455c, // lw a5,12(a0) 41 | 0x8b85, // andi a5,a5,1 42 | 0xfff5, // bnez a5, 43 | 44 | 0x2823, // sw zero,16(a0) 45 | 0x0005, // 46 | 47 | 0x9002, // ebreak 48 | 0x9002, // ebreak 49 | 0x9002, // ebreak 50 | 0x9002, // ebreak 51 | 0x9002, // ebreak 52 | 0x9002, // ebreak 53 | 0x9002, // ebreak 54 | 0x9002, // ebreak 55 | }}; -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | 30 | // Generate scene id and total number 31 | #define ADD_SCENE(prefix, name, id) WchSwioFlasherScene##id, 32 | typedef enum { 33 | #include "wch_swio_flasher_scene_config.h" 34 | WchSwioFlasherSceneNum, 35 | } WchSwioFlasherScene; 36 | #undef ADD_SCENE 37 | 38 | extern const SceneManagerHandlers wch_swio_flasher_scene_handlers; 39 | 40 | // Generate scene on_enter handlers declaration 41 | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); 42 | #include "wch_swio_flasher_scene_config.h" 43 | #undef ADD_SCENE 44 | 45 | // Generate scene on_event handlers declaration 46 | #define ADD_SCENE(prefix, name, id) \ 47 | bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); 48 | #include "wch_swio_flasher_scene_config.h" 49 | #undef ADD_SCENE 50 | 51 | // Generate scene on_exit handlers declaration 52 | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); 53 | #include "wch_swio_flasher_scene_config.h" 54 | #undef ADD_SCENE 55 | -------------------------------------------------------------------------------- /helpers/programs/get_block_aligned.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include "../programs.h" 28 | //------------------------------------------------------------------------------ 29 | // data1 = address. set low bit if this is a write 30 | // only clobbers A0, A1 31 | 32 | const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_get_block_aligned_program = { 33 | .name = "get_block_aligned", 34 | .clobbers = BIT_A0 | BIT_A1, 35 | .data = { 36 | //0xe0000537, // lui a0,0xe0000 37 | 0x0537, 38 | 0xe000, 39 | 40 | //0x0f450513, // addi a0,a0,0xF4 41 | 0x0513, 42 | 0x0f45, 43 | 44 | 0x414c, // lw a1,4(a0) 45 | 0x8985, // andi a1,a1,1 46 | 0xc591, // beqz a1,get_u32 47 | 48 | //set_u32: 49 | 0x414c, // lw a1,4(a0) 50 | 0x15fd, // addi a1,a1,-1 51 | 0x4108, // lw a0,0(a0) 52 | 0xc188, // sw a0,0(a1) 53 | 0x9002, // ebreak 54 | 55 | //get_u32: 56 | 0x414c, // lw a1,4(a0) 57 | 0x418c, // lw a1,0(a1) 58 | 0xc10c, // sw a1,0(a0) 59 | 0x9002, // ebreak 60 | }}; -------------------------------------------------------------------------------- /helpers/programs/get_set_u32.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include "../programs.h" 28 | 29 | //------------------------------------------------------------------------------ 30 | // data0 = data to write 31 | // data1 = address. set low bit if this is a write 32 | // only clobbers A0, A1 33 | 34 | const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_get_set_u32_program = { 35 | .name = "get_set_u32", 36 | .clobbers = BIT_A0 | BIT_A1, 37 | .data = { 38 | //0xe0000537, // lui a0,0xe0000 39 | 0x0537, 40 | 0xe000, 41 | 42 | //0x0f450513, // addi a0,a0,0xF4 43 | 0x0513, 44 | 0x0f45, 45 | 46 | 0x414c, // lw a1,4(a0) 47 | 0x8985, // andi a1,a1,1 48 | 0xc591, // beqz a1,get_u32 49 | 50 | //set_u32: 51 | 0x414c, // lw a1,4(a0) 52 | 0x15fd, // addi a1,a1,-1 53 | 0x4108, // lw a0,0(a0) 54 | 0xc188, // sw a0,0(a1) 55 | 0x9002, // ebreak 56 | 57 | //get_u32: 58 | 0x414c, // lw a1,4(a0) 59 | 0x418c, // lw a1,0(a1) 60 | 0xc10c, // sw a1,0(a0) 61 | 0x9002, // ebreak 62 | }}; 63 | -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include "wch_swio_flasher_scene.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // Generate scene on_enter handlers array 34 | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, 35 | void (*const wch_swio_flasher_on_enter_handlers[])(void*) = { 36 | #include "wch_swio_flasher_scene_config.h" 37 | }; 38 | #undef ADD_SCENE 39 | 40 | // Generate scene on_event handlers array 41 | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, 42 | bool (*const wch_swio_flasher_on_event_handlers[])(void* context, SceneManagerEvent event) = { 43 | #include "wch_swio_flasher_scene_config.h" 44 | }; 45 | #undef ADD_SCENE 46 | 47 | // Generate scene on_exit handlers array 48 | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, 49 | void (*const wch_swio_flasher_on_exit_handlers[])(void* context) = { 50 | #include "wch_swio_flasher_scene_config.h" 51 | }; 52 | #undef ADD_SCENE 53 | 54 | // Initialize scene handlers configuration structure 55 | const SceneManagerHandlers wch_swio_flasher_scene_handlers = { 56 | .on_enter_handlers = wch_swio_flasher_on_enter_handlers, 57 | .on_event_handlers = wch_swio_flasher_on_event_handlers, 58 | .on_exit_handlers = wch_swio_flasher_on_exit_handlers, 59 | .scene_num = WchSwioFlasherSceneNum, 60 | }; -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene_file_browser.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include "../wch_swio_flasher.h" 29 | #include "wch_swio_flasher_scene.h" 30 | 31 | static void file_browser_callback(void* context) { 32 | WchSwioFlasherApp* app = context; 33 | furi_assert(app); 34 | view_dispatcher_send_custom_event(app->view_dispatcher, SceneManagerEventTypeCustom); 35 | } 36 | 37 | void wch_swio_flasher_scene_file_browser_on_enter(void* context) { 38 | WchSwioFlasherApp* app = context; 39 | 40 | file_browser_set_callback(app->views.file_browser, file_browser_callback, app); 41 | 42 | file_browser_start(app->views.file_browser, app->views.file_path); 43 | 44 | view_dispatcher_switch_to_view(app->view_dispatcher, WchSwioFlasherViewFileBrowser); 45 | } 46 | 47 | bool wch_swio_flasher_scene_file_browser_on_event(void* context, SceneManagerEvent event) { 48 | WchSwioFlasherApp* app = context; 49 | bool consumed = false; 50 | 51 | if(event.type == SceneManagerEventTypeCustom) { 52 | scene_manager_next_scene(app->scene_manager, WchSwioFlasherSceneMain); 53 | consumed = true; 54 | } else if(event.type == SceneManagerEventTypeTick) { 55 | } 56 | 57 | furi_assert(app); 58 | view_dispatcher_send_custom_event(app->view_dispatcher, SceneManagerEventTypeCustom); 59 | 60 | return consumed; 61 | } 62 | 63 | void wch_swio_flasher_scene_file_browser_on_exit(void* context) { 64 | WchSwioFlasherApp* app = context; 65 | file_browser_stop(app->views.file_browser); 66 | } 67 | -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene_about.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "../wch_swio_flasher.h" 30 | #include "wch_swio_flasher_scene.h" 31 | 32 | void wch_swio_flasher_scene_about_on_enter(void* context) { 33 | WchSwioFlasherApp* app = context; 34 | 35 | widget_add_string_element( 36 | app->views.widget, 63, 2, AlignCenter, AlignTop, FontPrimary, "WCH Flasher v1.0"); 37 | 38 | widget_add_string_element( 39 | app->views.widget, 63, 24, AlignCenter, AlignCenter, FontSecondary, "author"); 40 | widget_add_string_element( 41 | app->views.widget, 63, 33, AlignCenter, AlignCenter, FontPrimary, "github.com/sukvojte"); 42 | 43 | widget_add_string_element( 44 | app->views.widget, 63, 48, AlignCenter, AlignCenter, FontSecondary, "with support by"); 45 | widget_add_string_element( 46 | app->views.widget, 63, 58, AlignCenter, AlignCenter, FontSecondary, "github.com/ah01"); 47 | 48 | view_dispatcher_switch_to_view(app->view_dispatcher, WchSwioFlasherViewWidget); 49 | } 50 | 51 | bool wch_swio_flasher_scene_about_on_event(void* context, SceneManagerEvent event) { 52 | WchSwioFlasherApp* app = context; 53 | UNUSED(app); 54 | bool consumed = false; 55 | 56 | if(event.type == SceneManagerEventTypeCustom) { 57 | consumed = true; 58 | } 59 | 60 | return consumed; 61 | } 62 | 63 | void wch_swio_flasher_scene_about_on_exit(void* context) { 64 | WchSwioFlasherApp* app = context; 65 | widget_reset(app->views.widget); 66 | } 67 | -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene_wiring.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "../wch_swio_flasher.h" 30 | #include "wch_swio_flasher_scene.h" 31 | 32 | void wch_swio_flasher_scene_wiring_on_enter(void* context) { 33 | WchSwioFlasherApp* app = context; 34 | 35 | widget_add_icon_element(app->views.widget, 0, 0, &I_wiring_128x64); 36 | 37 | widget_add_string_element( 38 | app->views.widget, 118, 7, AlignCenter, AlignCenter, FontSecondary, "GND"); 39 | 40 | widget_add_string_element( 41 | app->views.widget, 92, 7, AlignCenter, AlignCenter, FontSecondary, "SWIO"); 42 | 43 | widget_add_string_element( 44 | app->views.widget, 62, 7, AlignCenter, AlignCenter, FontSecondary, "RST"); 45 | 46 | widget_add_string_element( 47 | app->views.widget, 10, 7, AlignCenter, AlignCenter, FontSecondary, "3V3"); 48 | 49 | widget_add_string_element( 50 | app->views.widget, 50, 31, AlignCenter, AlignCenter, FontSecondary, "1k resistor"); 51 | 52 | view_dispatcher_switch_to_view(app->view_dispatcher, WchSwioFlasherViewWidget); 53 | } 54 | 55 | bool wch_swio_flasher_scene_wiring_on_event(void* context, SceneManagerEvent event) { 56 | WchSwioFlasherApp* app = context; 57 | UNUSED(app); 58 | bool consumed = false; 59 | 60 | if(event.type == SceneManagerEventTypeCustom) { 61 | consumed = true; 62 | } 63 | 64 | return consumed; 65 | } 66 | 67 | void wch_swio_flasher_scene_wiring_on_exit(void* context) { 68 | WchSwioFlasherApp* app = context; 69 | widget_reset(app->views.widget); 70 | } 71 | -------------------------------------------------------------------------------- /helpers/programs/write_flash.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include "../programs.h" 28 | //------------------------------------------------------------------------------ 29 | // only clobbers S0, A0, A1, A2, A3, A4, A5 30 | 31 | // A0 = 0x40022000 - flash registers base 32 | // A1 = 0xE00000F4 - ptr to DATA0 @ 0xE00000F4 33 | // A2 = dst_addr 34 | // A3 = BIT_CTLR_FTPG | BIT_CTLR_BUFLOAD 35 | // A4 = BIT_CTLR_FTPG | BIT_CTLR_STRT 36 | // A5 = BIT_CTLR_FTPG | BIT_CTLR_BUFRST 37 | 38 | const WchSwioFlasher_RiscVProgram WchSwioFlasher_RiscVDebug_program_write_flash_program = { 39 | .name = "write_flash", 40 | .clobbers = BIT_S0 | BIT_A0 | BIT_A1 | BIT_A2 | BIT_A3 | BIT_A4 | BIT_A5, 41 | .data = { 42 | // Copy word and trigger BUFLOAD 43 | 0x4180, // lw s0,0(a1) - load data byte from DATA0 44 | 0xc200, // sw s0,0(a2) - store data byte to 'dst_addr' 45 | 0xc914, // sw a3,16(a0) - store BIT_CTLR_FTPG | BIT_CTLR_BUFLOAD to FLASH_CTRL 46 | 47 | // waitloop1: Busywait for copy to complete - this seems to be required now? 48 | 0x4540, // lw s0,12(a0) - load STATR 49 | 0x8805, // andi s0,s0,1 - check BUSY flag 50 | 0xfc75, // bnez s0, 51 | 52 | // Advance dest pointer and trigger START if we ended a page 53 | 0x0611, // addi a2,a2,4 54 | 0x7413, // andi s0,a2,63 55 | 0x03f6, // 56 | 0xe419, // bnez s0, 57 | 0xc918, // sw a4,16(a0) 58 | 59 | // waitloop2: Busywait for page write to complete 60 | 0x4540, // lw s0,12(a0) 61 | 0x8805, // andi s0,s0,1 62 | 0xfc75, // bnez s0, 63 | 64 | // Reset buffer, don't need busywait as it'll complete before we send the 65 | // next dword. 66 | 0xc91c, // sw a5,16(a0) 67 | 68 | // Update page address 69 | 0xc950, // sw a2,20(a0) 70 | }}; -------------------------------------------------------------------------------- /wch_swio_flasher.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "views/debugger_emulator.h" 42 | #include "views/view_flasher.h" 43 | 44 | #include "minichlink_debugger.h" 45 | #include "nhc_link042_emulator.h" 46 | #include "helpers/riscv_debug.h" 47 | #include "helpers/swio.h" 48 | #include "helpers/wch_flasher.h" 49 | #include "ch32v_flipper_flasher.h" 50 | 51 | typedef struct App { 52 | Gui* gui; 53 | NotificationApp* notification; 54 | ViewDispatcher* view_dispatcher; 55 | SceneManager* scene_manager; 56 | //NotificationApp* notifications; 57 | //DialogsApp* dialogs; 58 | //Popup* popup; 59 | 60 | // 61 | //TextInput* text_input; 62 | struct { 63 | WchSwioFlasher_SWIO* swio; 64 | WchSwioFlasher_RiscVDebug* riscv_debug; 65 | WchSwioFlasher_WchFlasher* flasher; 66 | } helpers; 67 | 68 | struct { 69 | WchSwioFlasher_MinichlinkDebugger* mini_debugger; 70 | WchSwioFlasher_NhcLink042Emu* emulator; 71 | WchSwioFlasher_Ch32vFlipperFlasher* flasher; 72 | } services; 73 | 74 | struct { 75 | Widget* widget; 76 | DebuggerEmulator* debuger_emulator; 77 | ViewFlasher* flasher; 78 | Submenu* submenu; 79 | FuriString* file_path; 80 | FileBrowser* file_browser; 81 | } views; 82 | } WchSwioFlasherApp; 83 | 84 | typedef enum { 85 | WchSwioFlasherEventDebuggerExit, 86 | } WchSwioFlasherEvent; 87 | 88 | typedef enum { 89 | WchSwioFlasherViewWidget, 90 | WchSwioFlasherViewSubmenu, 91 | WchSwioFlasherViewFileBrowser, 92 | WchSwioFlasherViewDebuggerEmulator, 93 | WchSwioFlasherViewFlasher, 94 | } WchSwioFlasherView; 95 | 96 | void wch_swio_flasher_store_defaults(); 97 | 98 | void wch_swio_flasher_load_defaults(); -------------------------------------------------------------------------------- /errors.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | typedef enum { 29 | 30 | WchSwioFlasher_Ok = 0, 31 | WchSwioFlasher_Error_Timeout = -1000, 32 | WchSwioFlasher_Error_SwdResetDetected = -1001, 33 | WchSwioFlasher_Error_SwdParityCheckError = -1002, 34 | WchSwioFlasher_Error_TargetNotKnown = -2000, 35 | WchSwioFlasher_Error_TargetInInvalidState = -2001, 36 | WchSwioFlasher_Error_ProgramNotFinishedYet = -2002, 37 | WchSwioFlasher_Error_ProgramRunError = -2003, 38 | WchSwioFlasher_Error_DirtyRegs = -3001, 39 | WchSwioFlasher_Error_NotImplemented = -4000, 40 | WchSwioFlasher_Error_InvalidArgument = -4001, 41 | WchSwioFlasher_Error_NoFreeMemory = -4002, 42 | 43 | } WchSwioFlasher_Error; 44 | 45 | char* WchSwioFlasher_ErrorToString(WchSwioFlasher_Error error); 46 | 47 | #define ERROR_HANDLER_INIT() 48 | 49 | #define LOG_ERR(err) \ 50 | ({ \ 51 | FURI_LOG_E( \ 52 | TAG, \ 53 | "error '%s' (0x%08X) on %s:%d", \ 54 | WchSwioFlasher_ErrorToString(err), \ 55 | err, \ 56 | __FUNCTION__, \ 57 | __LINE__); \ 58 | err; \ 59 | }) 60 | 61 | #define LOG_ERR_M(err, msg, ...) \ 62 | ({ \ 63 | FURI_LOG_E( \ 64 | TAG, \ 65 | "error '%s' (0x%08X) on %s:%d: " msg, \ 66 | WchSwioFlasher_ErrorToString(err), \ 67 | err, \ 68 | __FUNCTION__, \ 69 | __LINE__, \ 70 | ##__VA_ARGS__); \ 71 | err; \ 72 | }) 73 | 74 | #define CHECK_ERR(fn) \ 75 | { \ 76 | WchSwioFlasher_Error err = fn; \ 77 | if(err != WchSwioFlasher_Ok) { \ 78 | return LOG_ERR(err); \ 79 | } \ 80 | } 81 | 82 | #define CHECK_ERR_M(fn, msg, ...) \ 83 | { \ 84 | WchSwioFlasher_Error err = fn; \ 85 | if(err != WchSwioFlasher_Ok) { \ 86 | return LOG_ERR_M(err, msg, ##__VA_ARGS__); \ 87 | } \ 88 | } 89 | -------------------------------------------------------------------------------- /minichlink_debugger.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | #include 30 | 31 | #include "errors.h" 32 | #include "helpers/swio.h" 33 | #include "helpers/riscv_debug.h" 34 | 35 | typedef enum { 36 | WchSwioFlasher_MinichlinkDebugger_InitSessionEvent = 0, 37 | WchSwioFlasher_MinichlinkDebugger_EndSessionEvent, 38 | WchSwioFlasher_MinichlinkDebugger_EventCount, 39 | } WchSwioFlasher_MinichlinkDebugger_Events; 40 | 41 | typedef struct _WchSwioFlasher_MinichlinkDebugger WchSwioFlasher_MinichlinkDebugger; 42 | 43 | typedef void (*WchSwioFlasher_MinichlinkDebugger_EventCb)( 44 | void* context, 45 | WchSwioFlasher_MinichlinkDebugger_Events ev); 46 | 47 | struct _WchSwioFlasher_MinichlinkDebugger { 48 | //FuriThread* worker_thread; 49 | //FuriMessageQueue* event_queue; 50 | //FuriStreamBuffer* data_stream; 51 | 52 | FuriTimer* timer; 53 | FuriMutex* mutex; 54 | uint32_t connected; 55 | uint32_t last_communication_time; 56 | 57 | WchSwioFlasher_SWIO* swio; 58 | WchSwioFlasher_RiscVDebug* riscv_debug; 59 | struct { 60 | WchSwioFlasher_MinichlinkDebugger_EventCb cb; 61 | void* context; 62 | } events[WchSwioFlasher_MinichlinkDebugger_EventCount]; 63 | }; 64 | 65 | WchSwioFlasher_MinichlinkDebugger* WchSwioFlasher_MinichlinkDebugger_create( 66 | WchSwioFlasher_SWIO* swio, 67 | WchSwioFlasher_RiscVDebug* riscv_debug); 68 | 69 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_registerEvent( 70 | WchSwioFlasher_MinichlinkDebugger* handle, 71 | WchSwioFlasher_MinichlinkDebugger_Events type, 72 | WchSwioFlasher_MinichlinkDebugger_EventCb cb, 73 | void* context); 74 | 75 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_unregisterEvent( 76 | WchSwioFlasher_MinichlinkDebugger* handle, 77 | WchSwioFlasher_MinichlinkDebugger_Events type); 78 | 79 | void WchSwioFlasher_MinichlinkDebugger_destroy(WchSwioFlasher_MinichlinkDebugger* handle); 80 | 81 | WchSwioFlasher_Error 82 | WchSwioFlasher_MinichlinkDebugger_initSession(WchSwioFlasher_MinichlinkDebugger* handle); 83 | 84 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_readRegister( 85 | WchSwioFlasher_MinichlinkDebugger* handle, 86 | uint8_t address, 87 | uint32_t* response); 88 | 89 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_writeRegister( 90 | WchSwioFlasher_MinichlinkDebugger* handle, 91 | uint8_t address, 92 | uint32_t data); 93 | 94 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_delayUs( 95 | WchSwioFlasher_MinichlinkDebugger* handle, 96 | uint32_t delay); 97 | 98 | WchSwioFlasher_Error 99 | WchSwioFlasher_MinichlinkDebugger_endSession(WchSwioFlasher_MinichlinkDebugger* handle); 100 | -------------------------------------------------------------------------------- /helpers/swio.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | /** 27 | This file is based on modified original from https://github.com/aappleby/PicoRVD 28 | */ 29 | 30 | #include "swio.h" 31 | #include "swio_inner.h" 32 | 33 | #include "../utils.h" 34 | #include "../config.h" 35 | 36 | #define TAG "WSF_SWIO" 37 | 38 | WchSwioFlasher_SWIO* WchSwioFlasher_SWIO_create() { 39 | WchSwioFlasher_SWIO* handle = malloc(sizeof(WchSwioFlasher_SWIO)); 40 | 41 | // TODO: check Flipper freq and MCU type 42 | 43 | furi_hal_gpio_init(&SWI_GPIO, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); 44 | furi_hal_gpio_init(&SWI_HW_RST_GPIO, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); 45 | 46 | furi_hal_gpio_write(&SWI_HW_RST_GPIO, true); 47 | furi_hal_gpio_write(&SWI_GPIO, true); 48 | 49 | return handle; 50 | } 51 | 52 | WchSwioFlasher_Error WchSwioFlasher_SWIO_hw_reset(WchSwioFlasher_SWIO* handle) { 53 | UNUSED(handle); 54 | furi_hal_gpio_write(&SWI_GPIO, true); 55 | furi_hal_gpio_write(&SWI_HW_RST_GPIO, false); 56 | furi_delay_ms(50); 57 | furi_hal_gpio_write(&SWI_HW_RST_GPIO, true); 58 | 59 | return WchSwioFlasher_Ok; 60 | } 61 | 62 | void WchSwioFlasher_SWIO_destroy(WchSwioFlasher_SWIO* handle) { 63 | furi_hal_gpio_init(&SWI_GPIO, GpioModeAnalog, GpioPullNo, GpioSpeedLow); 64 | free(handle); 65 | } 66 | 67 | WchSwioFlasher_Error WchSwioFlasher_SWIO_init(WchSwioFlasher_SWIO* handle) { 68 | UNUSED(handle); 69 | 70 | // Do it twice for sure 71 | for(uint8_t i = 0; i < 2; i++) { 72 | CHECK_ERR_M( 73 | WchSwioFlasher_SWIO_write(handle, WCH_DM_SHDWCFGR, 0x5AA50400), 74 | "unable to write magic to SHDWCFGR"); 75 | 76 | CHECK_ERR_M( 77 | WchSwioFlasher_SWIO_write(handle, WCH_DM_CFGR, 0x5AA50400), 78 | "unable to write magic to CFGR"); 79 | } 80 | 81 | uint32_t cpbr = 0; 82 | CHECK_ERR_M(WchSwioFlasher_SWIO_read(handle, WCH_DM_CPBR, &cpbr), "unable to read CPBR"); 83 | 84 | uint16_t swd_version = cpbr >> 16; 85 | switch(swd_version) { 86 | case 1: 87 | break; 88 | default: 89 | return LOG_ERR_M( 90 | WchSwioFlasher_Error_TargetNotKnown, 91 | "SWD version %04X isn't supported", 92 | _UI(swd_version)); 93 | } 94 | 95 | FURI_LOG_I(TAG, "Found %04X version SWD interface", _UI(swd_version)); 96 | 97 | return WchSwioFlasher_Ok; 98 | } 99 | 100 | WchSwioFlasher_Error 101 | WchSwioFlasher_SWIO_write(WchSwioFlasher_SWIO* handle, uint8_t address, uint32_t data) { 102 | UNUSED(handle); 103 | return WchSwioFlasher_SWIO_rxtx(address, WchSwioFlasher_SWIO_Write, &data); 104 | } 105 | 106 | WchSwioFlasher_Error 107 | WchSwioFlasher_SWIO_read(WchSwioFlasher_SWIO* handle, uint8_t address, uint32_t* data) { 108 | UNUSED(handle); 109 | return WchSwioFlasher_SWIO_rxtx(address, WchSwioFlasher_SWIO_Read, data); 110 | } 111 | -------------------------------------------------------------------------------- /ch32v_flipper_flasher.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "helpers/riscv_debug.h" 33 | #include "helpers/wch_flasher.h" 34 | 35 | #include "errors.h" 36 | 37 | typedef enum { 38 | WchSwioFlasher_CFF_ChipInfoCompleted = 0, 39 | WchSwioFlasher_CFF_OpenFileCompleted, 40 | WchSwioFlasher_CFF_EraseChipCompleted, 41 | WchSwioFlasher_CFF_WriteChipCompleted, 42 | WchSwioFlasher_CFF_ActionCount, 43 | } ViewFlasher_Action; 44 | 45 | typedef enum { 46 | VWchSwioFlasher_CFF_Ok = 0, 47 | VWchSwioFlasher_CFF_NoData, 48 | VWchSwioFlasher_CFF_InProgress, 49 | VWchSwioFlasher_CFF_ChipNotConnected, 50 | WchSwioFlasher_CFF_UnableToOpenFile, 51 | WchSwioFlasher_CFF_EmptyOrTooBigFile, 52 | VWchSwioFlasher_CFF_UnknownError, 53 | } WchSwioFlasher_CFF_ResultStatus; 54 | 55 | typedef struct { 56 | WchSwioFlasher_CFF_ResultStatus status; 57 | uint32_t flash_size; 58 | uint32_t esig_uniid[3]; 59 | } WchSwioFlasher_CFF_ChipInfo; 60 | 61 | typedef struct { 62 | WchSwioFlasher_CFF_ResultStatus status; 63 | } WchSwioFlasher_CFF_EraseChip; 64 | 65 | typedef struct { 66 | WchSwioFlasher_CFF_ResultStatus status; 67 | char* path; 68 | float percent; 69 | } WchSwioFlasher_CFF_WriteChip; 70 | 71 | typedef void (*WchSwioFlasher_CFF_Callback)(void* context, ViewFlasher_Action action); 72 | 73 | typedef struct WchSwioFlasher_Ch32vFlipperFlasher WchSwioFlasher_Ch32vFlipperFlasher; 74 | 75 | void WchSwioFlasher_Ch32vFlipperFlasher_event_callback( 76 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 77 | WchSwioFlasher_CFF_Callback cb, 78 | void* cb_context); 79 | 80 | WchSwioFlasher_Ch32vFlipperFlasher* WchSwioFlasher_Ch32vFlipperFlasher_create( 81 | WchSwioFlasher_RiscVDebug* debugger, 82 | WchSwioFlasher_WchFlasher* flasher); 83 | 84 | void WchSwioFlasher_Ch32vFlipperFlasher_destroy(WchSwioFlasher_Ch32vFlipperFlasher* handle); 85 | 86 | void WchSwioFlasher_Ch32vFlipperFlasher_erase_chip(WchSwioFlasher_Ch32vFlipperFlasher* handle); 87 | 88 | void WchSwioFlasher_Ch32vFlipperFlasher_erase_chip_data( 89 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 90 | WchSwioFlasher_CFF_EraseChip* data); 91 | 92 | void WchSwioFlasher_Ch32vFlipperFlasher_chip_info(WchSwioFlasher_Ch32vFlipperFlasher* handle); 93 | 94 | void WchSwioFlasher_Ch32vFlipperFlasher_chip_info_data( 95 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 96 | WchSwioFlasher_CFF_ChipInfo* data); 97 | 98 | void WchSwioFlasher_Ch32vFlipperFlasher_write_chip( 99 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 100 | char* path); 101 | 102 | void WchSwioFlasher_Ch32vFlipperFlasher_write_chip_data( 103 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 104 | WchSwioFlasher_CFF_WriteChip* data); 105 | 106 | void WchSwioFlasher_Ch32vFlipperFlasher_detach(WchSwioFlasher_Ch32vFlipperFlasher* handle); 107 | 108 | void WchSwioFlasher_Ch32vFlipperFlasher_attach(WchSwioFlasher_Ch32vFlipperFlasher* handle); -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene_main.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include "../wch_swio_flasher.h" 27 | #include "wch_swio_flasher_scene.h" 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | void wch_swio_flasher_scene_main_submenu_callback(void* context, uint32_t index) { 34 | furi_assert(context); 35 | WchSwioFlasherApp* app = context; 36 | 37 | view_dispatcher_send_custom_event(app->view_dispatcher, index); 38 | } 39 | 40 | void wch_swio_flasher_scene_main_on_enter(void* context) { 41 | WchSwioFlasherApp* app = context; 42 | Submenu* submenu = app->views.submenu; 43 | submenu_set_header(submenu, "WCH SWIO Flasher"); 44 | submenu_add_item( 45 | submenu, 46 | "Start debugger", 47 | WchSwioFlasherSceneDebugger, 48 | wch_swio_flasher_scene_main_submenu_callback, 49 | app); 50 | submenu_add_item( 51 | submenu, 52 | "Flash CH32Vxx", 53 | WchSwioFlasherSceneFlash, 54 | wch_swio_flasher_scene_main_submenu_callback, 55 | app); 56 | submenu_add_item( 57 | submenu, 58 | "Wiring", 59 | WchSwioFlasherSceneWiring, 60 | wch_swio_flasher_scene_main_submenu_callback, 61 | app); 62 | submenu_add_item( 63 | submenu, 64 | "About", 65 | WchSwioFlasherSceneAbout, 66 | wch_swio_flasher_scene_main_submenu_callback, 67 | app); 68 | 69 | submenu_set_selected_item( 70 | submenu, scene_manager_get_scene_state(app->scene_manager, WchSwioFlasherSceneMain)); 71 | 72 | view_dispatcher_switch_to_view(app->view_dispatcher, WchSwioFlasherViewSubmenu); 73 | } 74 | 75 | bool wch_swio_flasher_scene_main_on_event(void* context, SceneManagerEvent event) { 76 | furi_assert(context); 77 | 78 | WchSwioFlasherApp* app = context; 79 | bool consumed = false; 80 | if(event.type == SceneManagerEventTypeCustom) { 81 | if(event.event == WchSwioFlasherSceneDebugger) { 82 | scene_manager_next_scene(app->scene_manager, WchSwioFlasherSceneDebugger); 83 | consumed = true; 84 | } else if(event.event == WchSwioFlasherSceneFlash) { 85 | //scene_manager_set_scene_state(app->scene_manager, AvrIspSceneChipDetect, AvrIspViewProgrammer); 86 | //scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); 87 | scene_manager_next_scene(app->scene_manager, WchSwioFlasherSceneFlash); 88 | consumed = true; 89 | } else if(event.event == WchSwioFlasherSceneWiring) { 90 | scene_manager_next_scene(app->scene_manager, WchSwioFlasherSceneWiring); 91 | consumed = true; 92 | } else if(event.event == WchSwioFlasherSceneAbout) { 93 | scene_manager_next_scene(app->scene_manager, WchSwioFlasherSceneAbout); 94 | consumed = true; 95 | } 96 | } 97 | return consumed; 98 | } 99 | 100 | void wch_swio_flasher_scene_main_on_exit(void* context) { 101 | furi_assert(context); 102 | 103 | WchSwioFlasherApp* app = context; 104 | submenu_reset(app->views.submenu); 105 | } 106 | -------------------------------------------------------------------------------- /helpers/wch_flasher_inner.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | /** 27 | This file is based on modified original from https://github.com/aappleby/PicoRVD 28 | */ 29 | 30 | #pragma once 31 | 32 | #include 33 | 34 | typedef union { 35 | struct { 36 | uint32_t PG : 1; // Program enable 37 | uint32_t PER : 1; // Perform sector erase 38 | uint32_t MER : 1; // Perform full erase 39 | uint32_t PAD0 : 1; 40 | uint32_t OBG : 1; // Perform user-selected word programming 41 | uint32_t OBER : 1; // Perform user-selected word erasure 42 | uint32_t STRT : 1; // Start 43 | uint32_t LOCK : 1; // Flash lock status 44 | 45 | uint32_t PAD1 : 1; 46 | uint32_t OBWRE : 1; // User-selected word write enable 47 | uint32_t ERRIE : 1; // Error status interrupt control 48 | uint32_t PAD2 : 1; 49 | uint32_t EOPIE : 1; // EOP interrupt control 50 | uint32_t PAD3 : 2; 51 | uint32_t FLOCK : 1; // Fast programming mode lock 52 | 53 | uint32_t FTPG : 1; // Fast page programming? 54 | uint32_t FTER : 1; // Fast erase 55 | uint32_t BUFLOAD : 1; // "Cache data into BUF" 56 | uint32_t BUFRST : 1; // "BUF reset operation" 57 | uint32_t PAD4 : 12; 58 | }; 59 | uint32_t raw; 60 | } RWCH_FLASH_CTLR; 61 | 62 | typedef union { 63 | struct { 64 | uint32_t BUSY : 1; // True if flash busy 65 | uint32_t PAD0 : 3; 66 | uint32_t WRPRTERR : 1; // True if the flash was written while locked 67 | uint32_t EOP : 1; // True if flash finished programming 68 | uint32_t PAD1 : 8; 69 | uint32_t MODE : 1; // Something to do with boot area flash? 70 | uint32_t BOOT_LOCK : 1; // True if boot flash locked 71 | uint32_t PAD2 : 16; 72 | }; 73 | uint32_t raw; 74 | } RWCH_FLASH_STATR; 75 | 76 | #define ADDR_ESIG_FLACAP 0x1FFFF7E0 // Flash capacity register 0xXXXX 77 | #define ADDR_ESIG_UNIID1 0x1FFFF7E8 // UID register 1 0xXXXXXXXX 78 | #define ADDR_ESIG_UNIID2 0x1FFFF7EC // UID register 2 0xXXXXXXXX 79 | #define ADDR_ESIG_UNIID3 0x1FFFF7F0 // UID register 3 0xXXXXXXXX 80 | 81 | #define ADDR_FLASH_ACTLR 0x40022000 82 | #define ADDR_FLASH_KEYR 0x40022004 83 | #define ADDR_FLASH_OBKEYR 0x40022008 84 | #define ADDR_FLASH_STATR 0x4002200C 85 | #define ADDR_FLASH_CTLR 0x40022010 86 | #define ADDR_FLASH_ADDR 0x40022014 87 | #define ADDR_FLASH_OBR 0x4002201C 88 | #define ADDR_FLASH_WPR 0x40022020 89 | #define ADDR_FLASH_MKEYR 0x40022024 90 | #define ADDR_FLASH_BKEYR 0x40022028 91 | 92 | #define BIT_CTLR_PG (1 << 0) 93 | #define BIT_CTLR_PER (1 << 1) 94 | #define BIT_CTLR_MER (1 << 2) 95 | #define BIT_CTLR_OBG (1 << 4) 96 | #define BIT_CTLR_OBER (1 << 5) 97 | #define BIT_CTLR_STRT (1 << 6) 98 | #define BIT_CTLR_LOCK (1 << 7) 99 | #define BIT_CTLR_OBWRE (1 << 9) 100 | #define BIT_CTLR_ERRIE (1 << 10) 101 | #define BIT_CTLR_EOPIE (1 << 12) 102 | #define BIT_CTLR_FLOCK (1 << 15) 103 | #define BIT_CTLR_FTPG (1 << 16) 104 | #define BIT_CTLR_FTER (1 << 17) 105 | #define BIT_CTLR_BUFLOAD (1 << 18) 106 | #define BIT_CTLR_BUFRST (1 << 19) 107 | 108 | #define BIT_STATR_BUSY (1 << 0) 109 | #define BIT_STATR_WRPRTERR (1 << 4) 110 | #define BIT_STATR_EOP (1 << 5) 111 | #define BIT_STATR_MODE (1 << 14) 112 | #define BIT_STATR_LOCK (1 << 15) 113 | 114 | #define WCH_REG_S0 8 115 | #define WCH_REG_S1 9 116 | #define WCH_REG_A0 10 117 | #define WCH_REG_A1 11 118 | #define WCH_REG_A2 12 119 | #define WCH_REG_A3 13 120 | #define WCH_REG_A4 14 121 | #define WCH_REG_A5 15 -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene_debugger.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include "../wch_swio_flasher.h" 27 | #include 28 | #include 29 | #include 30 | 31 | #include "../minichlink_debugger.h" 32 | #include "../nhc_link042_emulator.h" 33 | #include "../minichlink_debugger.h" 34 | 35 | const NotificationSequence sequence_backlight_on = { 36 | &message_display_backlight_on, 37 | NULL, 38 | }; 39 | 40 | const NotificationSequence sequence_session_init = { 41 | &message_blink_start_10, 42 | &message_blink_set_color_red, 43 | &message_do_not_reset, 44 | NULL, 45 | }; 46 | 47 | const NotificationSequence sequence_session_end = { 48 | &message_red_0, 49 | &message_blink_stop, 50 | NULL, 51 | }; 52 | 53 | static void debugger_event_handler(void* context, WchSwioFlasher_MinichlinkDebugger_Events ev) { 54 | WchSwioFlasherApp* app = context; 55 | 56 | switch(ev) { 57 | case WchSwioFlasher_MinichlinkDebugger_InitSessionEvent: 58 | debugger_emulator_set_animation_status(app->views.debuger_emulator, 1); 59 | notification_message(app->notification, &sequence_backlight_on); 60 | notification_message(app->notification, &sequence_session_init); 61 | break; 62 | case WchSwioFlasher_MinichlinkDebugger_EndSessionEvent: 63 | debugger_emulator_set_animation_status(app->views.debuger_emulator, 0); 64 | notification_message(app->notification, &sequence_session_end); 65 | break; 66 | default: 67 | break; 68 | } 69 | } 70 | 71 | void wch_swio_flasher_scene_debugger_on_enter(void* context) { 72 | WchSwioFlasherApp* app = context; 73 | 74 | WchSwioFlasher_MinichlinkDebugger_registerEvent( 75 | app->services.mini_debugger, 76 | WchSwioFlasher_MinichlinkDebugger_InitSessionEvent, 77 | debugger_event_handler, 78 | context); 79 | WchSwioFlasher_MinichlinkDebugger_registerEvent( 80 | app->services.mini_debugger, 81 | WchSwioFlasher_MinichlinkDebugger_EndSessionEvent, 82 | debugger_event_handler, 83 | context); 84 | 85 | WchSwioFlasher_NhcLink042Emu_attach(app->services.emulator); 86 | 87 | view_dispatcher_switch_to_view(app->view_dispatcher, WchSwioFlasherViewDebuggerEmulator); 88 | } 89 | 90 | bool wch_swio_flasher_scene_debugger_on_event(void* context, SceneManagerEvent event) { 91 | WchSwioFlasherApp* app = context; 92 | UNUSED(app); 93 | bool consumed = false; 94 | 95 | if(event.type == SceneManagerEventTypeBack) { 96 | // TODO: stop debugger 97 | //consumed = true; 98 | } else if(event.type == SceneManagerEventTypeCustom) { 99 | switch(event.event) { 100 | case WchSwioFlasherEventDebuggerExit: 101 | break; 102 | } 103 | //view_dispatcher_stop(app->view_dispatcher); 104 | consumed = true; 105 | } 106 | 107 | return consumed; 108 | } 109 | 110 | void wch_swio_flasher_scene_debugger_on_exit(void* context) { 111 | WchSwioFlasherApp* app = context; 112 | notification_message(app->notification, &sequence_session_end); 113 | 114 | WchSwioFlasher_MinichlinkDebugger_unregisterEvent( 115 | app->services.mini_debugger, WchSwioFlasher_MinichlinkDebugger_InitSessionEvent); 116 | WchSwioFlasher_MinichlinkDebugger_unregisterEvent( 117 | app->services.mini_debugger, WchSwioFlasher_MinichlinkDebugger_EndSessionEvent); 118 | 119 | WchSwioFlasher_NhcLink042Emu_detach(app->services.emulator); 120 | widget_reset(app->views.widget); 121 | } 122 | -------------------------------------------------------------------------------- /helpers/riscv_debug.h: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #pragma once 27 | 28 | #include 29 | #include 30 | #include "../errors.h" 31 | #include "swio.h" 32 | #include "programs.h" 33 | 34 | typedef struct { 35 | WchSwioFlasher_SWIO* swio; 36 | 37 | // Cached target state, must stay in sync 38 | int reg_count; 39 | 40 | uint32_t prog_cache[8]; 41 | uint32_t prog_will_clobber; // Bits are 1 if running the current program will clober the reg 42 | 43 | uint32_t reg_cache[32]; 44 | uint32_t dirty_regs; // bits are 1 if we modified the reg on device 45 | uint32_t cached_regs; // bits are 1 if reg_cache[i] is valid 46 | } WchSwioFlasher_RiscVDebug; 47 | 48 | typedef struct { 49 | uint32_t flash_size; 50 | uint32_t esig_uniid[3]; 51 | } WchSwioFlasher_RiscVDebug_ChipInfo; 52 | 53 | WchSwioFlasher_RiscVDebug* WchSwioFlasher_RiscVDebug_create(WchSwioFlasher_SWIO* swio); 54 | 55 | void WchSwioFlasher_RiscVDebug_destroy(WchSwioFlasher_RiscVDebug* handle); 56 | 57 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_init(WchSwioFlasher_RiscVDebug* handle); 58 | 59 | typedef enum { 60 | WchSwioFlasher_RVD_ResetToHalt = 0, 61 | WchSwioFlasher_RVD_ResetToRun, 62 | WchSwioFlasher_RVD_ResetToRunNoCheck, 63 | } WchSwioFlasher_RiscVDebug_ResetType; 64 | 65 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_reset( 66 | WchSwioFlasher_RiscVDebug* handle, 67 | WchSwioFlasher_RiscVDebug_ResetType type); 68 | 69 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_load_prog( 70 | WchSwioFlasher_RiscVDebug* handle, 71 | const WchSwioFlasher_RiscVProgram* program); 72 | 73 | #define WchSwioFlasher_RiscVDebug_NO_TIMEOUT 0 74 | WchSwioFlasher_Error 75 | WchSwioFlasher_RiscVDebug_run_prog(WchSwioFlasher_RiscVDebug* handle, uint32_t timeout); 76 | 77 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_get_mem_u32( 78 | WchSwioFlasher_RiscVDebug* handle, 79 | uint32_t addr, 80 | uint32_t* result); 81 | 82 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_set_mem_u32( 83 | WchSwioFlasher_RiscVDebug* handle, 84 | uint32_t addr, 85 | uint32_t data); 86 | 87 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_get_gpr( 88 | WchSwioFlasher_RiscVDebug* handle, 89 | uint8_t index, 90 | uint32_t* value); 91 | 92 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_set_gpr( 93 | WchSwioFlasher_RiscVDebug* handle, 94 | uint8_t index, 95 | uint32_t value); 96 | 97 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_get_block_aligned( 98 | WchSwioFlasher_RiscVDebug* handle, 99 | uint32_t addr, 100 | void* dst, 101 | int size_bytes); 102 | 103 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_wait_for_reg( 104 | WchSwioFlasher_RiscVDebug* handle, 105 | uint32_t address, 106 | uint32_t mask, 107 | uint32_t flag, 108 | uint32_t timeout); 109 | 110 | WchSwioFlasher_Error 111 | WchSwioFlasher_RiscVDebug_set_data0(WchSwioFlasher_RiscVDebug* handle, uint32_t data); 112 | 113 | WchSwioFlasher_Error 114 | WchSwioFlasher_RiscVDebug_set_data1(WchSwioFlasher_RiscVDebug* handle, uint32_t data); 115 | 116 | WchSwioFlasher_Error 117 | WchSwioFlasher_RiscVDebug_get_data0(WchSwioFlasher_RiscVDebug* handle, uint32_t* data); 118 | 119 | WchSwioFlasher_Error 120 | WchSwioFlasher_RiscVDebug_get_data1(WchSwioFlasher_RiscVDebug* handle, uint32_t* data); 121 | 122 | WchSwioFlasher_Error 123 | WchSwioFlasher_RiscVDebug_set_abstractauto(WchSwioFlasher_RiscVDebug* handle, uint32_t data); 124 | 125 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_wait_for_abstractcs( 126 | WchSwioFlasher_RiscVDebug* handle, 127 | uint32_t mask, 128 | uint32_t flag, 129 | uint32_t timeout); 130 | 131 | WchSwioFlasher_Error 132 | WchSwioFlasher_RiscVDebug_get_status(WchSwioFlasher_RiscVDebug* handle, uint32_t* result); 133 | 134 | WchSwioFlasher_Error 135 | WchSwioFlasher_RiscVDebug_machine_isa(WchSwioFlasher_RiscVDebug* handle, uint32_t* result); 136 | 137 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_get_chip_info( 138 | WchSwioFlasher_RiscVDebug* handle, 139 | WchSwioFlasher_RiscVDebug_ChipInfo* info); 140 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Flipper Zero WCH SWIO Flasher and debugger 3 | 4 | WCH SWIO debugger and flasher emulated on Flipper Zero tool - tool for flashing and debugging CH32V003 chip. 5 | 6 | > tested only on CH32V003 7 | 8 | ## Features 9 | 10 | - [minichlink](https://github.com/cnlohr/ch32v003fun/tree/master/minichlink) compatible flasher/debugger 11 | - emulated NCH Link042 via bulk USB device (vid = 0x1986, pid = 0x0034) 12 | - **WARNING:** there is a bug when minichlink uses this emulated NCH Link042 and not properly close bulk transfer. In that case, data on incoming bulk endpoint is buffered in USB driver and not readed by minichlink application before exit. Next minichlink session will fail. Solution is reset USB communication (unplug/plug USB or exit/enter emulator in Flipper app), or patch minichlink app (see chapter Usage/Examples). 13 | - Erase and write program code to ch32v003fun (named as `Flash CH32Vxx` in menu) without connection to computer. Supported is: 14 | - Check wiring along with your chip idenficiation. 15 | - Erase whole chip. 16 | - Program bin file from Flipper Memory into chip Flash memory (limited to 32kB due to my laziness). 17 | 18 | ## Documentation 19 | 20 | In emulation mode, Flipper Zero act just as NCH Link042 programmer HW. All commands towards chip are driven from your computer via USB, even reset and debug session init. 21 | 22 | In programmer mode, Flipper Zero act as all-in-one device and initiate debug session with chip at his own. This allows some usable hacks with chip. Most important one is hard reset before any operation towards CH32Vxx chip. It is helpful when you using sleepmodes or low clock mode (in sleep mode or low clock frequency is not possible to establish debug session, so you can't simply program chip). 23 | 24 | Be care, SWIO is driven as open-drain bus with pull-up with high clock freqency (around 8MHz). It means, SWIO pin is sensitive to wire/pcb line capacity. In case of bad designs, Flipper Zero can not be able to 'catch' all bits from chip and communication will fail. But it is not that bad, with 1k - 5k resistor as external pull-up it work well even with dupont wires. Just be aware about that. 25 | 26 | ### Components 27 | 28 | #### SWIO bit bang emulation 29 | 30 | Main SWIO magic. Mostly written in ARM Assembler, not realy nice code and definitely not portable to other platform without huge rework. 31 | 32 | See: [RISC-V QingKeV2 Microprocessor Debug Manual](https://github.com/openwch/ch32v003/blob/main/RISC-V%20QingKeV2%20Microprocessor%20Debug%20Manual.pdf) and 33 | [RISC-V Debug Specification](https://github.com/riscv/riscv-debug-spec?tab=readme-ov-file#risc-v-debug-specification) 34 | 35 | - src/helpers/swio.c 36 | - src/helpers/swio_pin_magic.c 37 | 38 | #### WCH flasher & debugger 39 | 40 | > code based on [aappleby/PicoRVD](https://github.com/aappleby/PicoRVD) 41 | 42 | Used for for '*Erase and write program code*' feature. Code allows to control debug interface directly from Flipper Zero. 43 | 44 | - src/helpers/wch_flasher.c 45 | - src/helpers/riscv_debug.c 46 | 47 | #### CH32V003 (RiscV) programs 48 | 49 | Utility programs used to run in target RiscV chip. Mostly originated from [aappleby/PicoRVD](https://github.com/aappleby/PicoRVD). 50 | 51 | - src/helpers/programs/* 52 | 53 | #### Minichlink and NCH Link042 emulator 54 | 55 | Code responsible for emulation USB bulk communication with [minichlink](https://github.com/cnlohr/ch32v003fun/tree/master/minichlink). 56 | 57 | - src/nhc_link042_emulator.c 58 | - src/minichlink_debugger.c 59 | 60 | 61 | ## Usage/Examples 62 | 63 | Read chip info 64 | ```bash 65 | user@awesomemachine minichlink % ./minichlink -i 66 | Found NHC-Link042 Programmer 67 | Interface Setup 68 | USER/RDPR : e817/5aa5 69 | DATA1/DATA0: ff00/ff00 70 | WRPR1/WRPR0: 00ff/00ff 71 | WRPR3/WRPR2: 00ff/00ff 72 | Flash Size: 16 kB 73 | R32_ESIG_UNIID1: xxxxxxxx 74 | R32_ESIG_UNIID2: xxxxxxxx 75 | R32_ESIG_UNIID3: ffffffff 76 | ``` 77 | 78 | Erase chip 79 | ```bash 80 | user@awesomemachine minichlink % ./minichlink -E 81 | Found NHC-Link042 Programmer 82 | Interface Setup 83 | Whole-chip erase 84 | ``` 85 | 86 | minichlink patch (file `minichlink/nhc-link042.c`, function `TryInit_NHCLink042`) 87 | ```c 88 | // After line: 89 | libusb_claim_interface(hdev, 0); 90 | 91 | // Paste this: 92 | int readed; 93 | do{ 94 | status = libusb_bulk_transfer(hdev, 0x81, buff, 64, &readed, 10); 95 | } while(readed > 0 && status == 0); 96 | 97 | ``` 98 | ## Screenshots 99 | 100 | ### Applications / Utils 101 | 102 | ![Applications/Utils screen](./screenshots/wchf_app_utils.png) 103 | 104 | ### WCH SWIO Flasher main menu 105 | 106 | ![WCH SWIO Flasher main menu screen](./screenshots/wchf_main.png) 107 | 108 | ### NCH Link042 emulator 109 | 110 | ![NCH Link042 emulator screen](./screenshots/wchf_debug.png) 111 | 112 | ### WCH SWIO Flasher screen 113 | 114 | ![WCH SWIO Flasher menu screen](./screenshots/wchf_flash.png) 115 | 116 | ### WCH SWIO Flasher screen - Get chip info 117 | 118 | ![WCH SWIO Flasher - get chip info screen](./screenshots/wchf_get_chip_info.png) 119 | 120 | ![WCH SWIO Flasher - get chip info error screen](./screenshots/wchf_get_chip_info_err.png) 121 | 122 | ### WCH SWIO Flasher screen - Erase 123 | 124 | ![WCH SWIO Flasher - erase screen](./screenshots/wchf_chip_erased.png) 125 | 126 | ### WCH SWIO Flasher screen - Program chip done (Flash) 127 | 128 | ![WCH SWIO Flasher - program chip done screen](./screenshots/wchf_flash_program_done.png) 129 | 130 | ### Wiring 131 | 132 | ![Wiring screen](./screenshots/wchf_wiring.png) 133 | 134 | ### About 135 | 136 | ![About screen](./screenshots/wchf_about.png) 137 | 138 | ## Acknowledgements 139 | 140 | - [aappleby/PicoRVD](https://github.com/aappleby/PicoRVD) 141 | - [cnlohr/ch32v003fun](https://github.com/cnlohr/ch32v003fun) 142 | - [A Visual Guide to Flipper Zero GUI Modules](https://brodan.biz/blog/a-visual-guide-to-flipper-zero-gui-components/) 143 | 144 | 145 | ## License 146 | 147 | [MIT](https://choosealicense.com/licenses/mit/) 148 | 149 | -------------------------------------------------------------------------------- /minichlink_debugger.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "usb.h" 33 | #include "usb_hid.h" 34 | 35 | #include "utils.h" 36 | #include "errors.h" 37 | #include "minichlink_debugger.h" 38 | 39 | #define TAG "WCH_DBG" 40 | 41 | //static inline void fireEvent( WchSwioFlasher_MinichlinkDebugger* handle, WchSwioFlasher_MinichlinkDebugger_Events type); 42 | 43 | static void last_com_check_timer_callback(void* context) { 44 | WchSwioFlasher_MinichlinkDebugger* handle = (WchSwioFlasher_MinichlinkDebugger*)context; 45 | UNUSED(handle); 46 | 47 | uint32_t is_connected = handle->connected; 48 | 49 | uint32_t lastlock = furi_kernel_lock(); 50 | uint32_t last_activity = handle->last_communication_time; 51 | furi_kernel_restore_lock(lastlock); 52 | 53 | if((furi_get_tick() - last_activity) > furi_kernel_get_tick_frequency() / 2) { 54 | is_connected = 0; 55 | } else { 56 | is_connected = 1; 57 | } 58 | 59 | if(is_connected != handle->connected) { 60 | WchSwioFlasher_MinichlinkDebugger_Events type = 61 | is_connected ? WchSwioFlasher_MinichlinkDebugger_InitSessionEvent : 62 | WchSwioFlasher_MinichlinkDebugger_EndSessionEvent; 63 | 64 | uint32_t lastlock = furi_kernel_lock(); 65 | WchSwioFlasher_MinichlinkDebugger_EventCb cb = handle->events[type].cb; 66 | void* context = handle->events[type].context; 67 | furi_kernel_restore_lock(lastlock); 68 | 69 | if(cb != NULL) { 70 | UNUSED(context); 71 | cb(context, type); 72 | } 73 | 74 | handle->connected = is_connected; 75 | } 76 | 77 | //furi_mutex_acquire(handle->mutex, FuriWaitForever); 78 | 79 | //if(handle->connected && (furi_get_tick() - handle->last_communication_time) > 3000) { 80 | // handle->connected = 0; 81 | // fireEvent(handle, WchSwioFlasher_MinichlinkDebugger_EndSessionEvent); 82 | //} 83 | 84 | //furi_mutex_release(handle->mutex); 85 | } 86 | 87 | WchSwioFlasher_MinichlinkDebugger* WchSwioFlasher_MinichlinkDebugger_create( 88 | WchSwioFlasher_SWIO* swio, 89 | WchSwioFlasher_RiscVDebug* riscv_debug) { 90 | WchSwioFlasher_MinichlinkDebugger* handle = malloc(sizeof(WchSwioFlasher_MinichlinkDebugger)); 91 | handle->swio = swio; 92 | handle->riscv_debug = riscv_debug; 93 | handle->connected = 0; 94 | handle->last_communication_time = 0; 95 | 96 | //handle->mutex = furi_mutex_alloc(FuriMutexTypeNormal); 97 | handle->timer = furi_timer_alloc(last_com_check_timer_callback, FuriTimerTypePeriodic, handle); 98 | memset(handle->events, 0, sizeof(handle->events)); 99 | 100 | furi_timer_start(handle->timer, furi_kernel_get_tick_frequency() / 10); 101 | 102 | return handle; 103 | } 104 | 105 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_registerEvent( 106 | WchSwioFlasher_MinichlinkDebugger* handle, 107 | WchSwioFlasher_MinichlinkDebugger_Events type, 108 | WchSwioFlasher_MinichlinkDebugger_EventCb cb, 109 | void* context) { 110 | uint32_t lastlock = furi_kernel_lock(); 111 | handle->events[type].cb = cb; 112 | handle->events[type].context = context; 113 | furi_kernel_restore_lock(lastlock); 114 | return WchSwioFlasher_Ok; 115 | } 116 | 117 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_unregisterEvent( 118 | WchSwioFlasher_MinichlinkDebugger* handle, 119 | WchSwioFlasher_MinichlinkDebugger_Events type) { 120 | uint32_t lastlock = furi_kernel_lock(); 121 | handle->events[type].cb = NULL; 122 | handle->events[type].context = NULL; 123 | furi_kernel_restore_lock(lastlock); 124 | return WchSwioFlasher_Ok; 125 | } 126 | 127 | static void log_activity(WchSwioFlasher_MinichlinkDebugger* handle) { 128 | uint32_t lastlock = furi_kernel_lock(); 129 | handle->last_communication_time = furi_get_tick(); 130 | furi_kernel_restore_lock(lastlock); 131 | } 132 | 133 | WchSwioFlasher_Error 134 | WchSwioFlasher_MinichlinkDebugger_initSession(WchSwioFlasher_MinichlinkDebugger* handle) { 135 | UNUSED(handle); 136 | log_activity(handle); 137 | //CHECK_ERR_M(WchSwioFlasher_SWIO_init(handle->swio), "unable to init SWIO"); 138 | //CHECK_ERR_M(WchSwioFlasher_RiscVDebug_reset(handle->riscv_debug), "unable to reset DAP"); 139 | 140 | return WchSwioFlasher_Ok; 141 | } 142 | 143 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_readRegister( 144 | WchSwioFlasher_MinichlinkDebugger* handle, 145 | uint8_t address, 146 | uint32_t* response) { 147 | log_activity(handle); 148 | CHECK_ERR_M( 149 | WchSwioFlasher_SWIO_read(handle->swio, address, response), "unable to read register"); 150 | return WchSwioFlasher_Ok; 151 | } 152 | 153 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_writeRegister( 154 | WchSwioFlasher_MinichlinkDebugger* handle, 155 | uint8_t address, 156 | uint32_t data) { 157 | log_activity(handle); 158 | CHECK_ERR_M( 159 | WchSwioFlasher_SWIO_write(handle->swio, address, data), "unable to write register"); 160 | return WchSwioFlasher_Ok; 161 | } 162 | 163 | WchSwioFlasher_Error WchSwioFlasher_MinichlinkDebugger_delayUs( 164 | WchSwioFlasher_MinichlinkDebugger* handle, 165 | uint32_t delay) { 166 | UNUSED(handle); 167 | log_activity(handle); 168 | furi_delay_us(delay); 169 | return WchSwioFlasher_Ok; 170 | } 171 | 172 | WchSwioFlasher_Error 173 | WchSwioFlasher_MinichlinkDebugger_endSession(WchSwioFlasher_MinichlinkDebugger* handle) { 174 | UNUSED(handle); 175 | return WchSwioFlasher_Ok; 176 | } 177 | 178 | void WchSwioFlasher_MinichlinkDebugger_destroy(WchSwioFlasher_MinichlinkDebugger* handle) { 179 | furi_timer_stop(handle->timer); 180 | furi_timer_free(handle->timer); 181 | 182 | free(handle); 183 | } -------------------------------------------------------------------------------- /views/debugger_emulator.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include "debugger_emulator.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include "../utils.h" 32 | 33 | struct DebuggerEmulator { 34 | View* view; 35 | FuriTimer* timer; 36 | FuriMutex* mutex; 37 | uint32_t animation_enabled; 38 | }; 39 | 40 | typedef struct { 41 | uint16_t animation_counter; 42 | uint16_t animation_visible; 43 | } DebuggerEmulatorModel; 44 | 45 | static void draw_log0(Canvas* canvas, uint8_t x, uint8_t y) { 46 | canvas_draw_line(canvas, x, y + 1, x, y + 3); 47 | canvas_draw_line(canvas, x + 3, y + 1, x + 3, y + 3); 48 | canvas_draw_line(canvas, x + 1, y, x + 2, y); 49 | canvas_draw_line(canvas, x + 1, y + 4, x + 2, y + 4); 50 | } 51 | 52 | static void draw_log1(Canvas* canvas, uint8_t x, uint8_t y) { 53 | canvas_draw_line(canvas, x, y, x, y + 4); 54 | } 55 | 56 | #define MAX_ANIMATION_INCREMENT 4 57 | #define MAX_ANIMATION_VIEWPOINT_X 84 58 | #define MIN_ANIMATION_VIEWPOINT_X 47 59 | #define BLANK_WINDOW_SIZE 26 60 | #define ANIMATION_COUNTER_MAX_VALUE \ 61 | (((MAX_ANIMATION_VIEWPOINT_X - MIN_ANIMATION_VIEWPOINT_X) + BLANK_WINDOW_SIZE) * 2) 62 | #define ANIMATION_Y 22 63 | 64 | static void drawBits(Canvas* canvas, uint8_t value, uint8_t x, uint8_t y) { 65 | for(uint8_t i = 0; i < 6; i++) { 66 | if(value & (1 << i)) { 67 | if(x >= MIN_ANIMATION_VIEWPOINT_X && x <= MAX_ANIMATION_VIEWPOINT_X) { 68 | draw_log1(canvas, x, y); 69 | } 70 | x += 2; 71 | } else { 72 | if(x >= MIN_ANIMATION_VIEWPOINT_X && (x + 3) <= MAX_ANIMATION_VIEWPOINT_X) { 73 | draw_log0(canvas, x, y); 74 | } 75 | x += 5; 76 | } 77 | } 78 | } 79 | 80 | static void debugger_emulator_draw_callback(Canvas* canvas, void* _model) { 81 | DebuggerEmulatorModel* model = _model; 82 | UNUSED(model); 83 | 84 | canvas_draw_icon(canvas, 0, 0, &I_debugger_base_128x64); 85 | //47x26 86x26 86 | 87 | uint32_t offset = model->animation_counter % ANIMATION_COUNTER_MAX_VALUE; 88 | //offset = MAX_OFFSET; 89 | 90 | if(offset >= (ANIMATION_COUNTER_MAX_VALUE / 2)) { 91 | offset = ANIMATION_COUNTER_MAX_VALUE - offset; 92 | } 93 | 94 | drawBits(canvas, 0xaa, MAX_ANIMATION_VIEWPOINT_X - offset, ANIMATION_Y); 95 | 96 | canvas_set_font(canvas, FontPrimary); 97 | elements_multiline_text_aligned( 98 | canvas, 126, 62, AlignRight, AlignBottom, "NCH Link042 Emulator"); 99 | canvas_set_font(canvas, FontSecondary); 100 | 101 | elements_multiline_text_aligned( 102 | canvas, 126, 50, AlignRight, AlignBottom, "Press back to exit"); 103 | } 104 | 105 | static bool debugger_emulator_input_callback(InputEvent* event, void* context) { 106 | furi_assert(context); 107 | DebuggerEmulator* debugger = context; 108 | UNUSED(debugger); 109 | UNUSED(event); 110 | bool consumed = false; 111 | 112 | return consumed; 113 | } 114 | 115 | static void view_display_test_timer_callback(void* context) { 116 | DebuggerEmulator* instance = context; 117 | with_view_model( 118 | instance->view, 119 | DebuggerEmulatorModel * model, 120 | { 121 | if(model->animation_visible) { 122 | model->animation_counter += MAX_ANIMATION_INCREMENT; 123 | } 124 | 125 | if(model->animation_counter >= ANIMATION_COUNTER_MAX_VALUE) { 126 | model->animation_counter = 0; 127 | if(!instance->animation_enabled) { 128 | model->animation_visible = 0; 129 | } 130 | } 131 | }, 132 | true); 133 | } 134 | 135 | static void view_display_test_enter(void* context) { 136 | DebuggerEmulator* instance = context; 137 | 138 | furi_timer_start(instance->timer, furi_kernel_get_tick_frequency() / 4); 139 | } 140 | 141 | static void view_display_test_exit(void* context) { 142 | DebuggerEmulator* instance = context; 143 | furi_timer_stop(instance->timer); 144 | } 145 | 146 | DebuggerEmulator* debugger_emulator_alloc() { 147 | DebuggerEmulator* handle = malloc(sizeof(DebuggerEmulator)); 148 | 149 | handle->view = view_alloc(); 150 | view_allocate_model(handle->view, ViewModelTypeLocking, sizeof(DebuggerEmulatorModel)); 151 | 152 | with_view_model( 153 | handle->view, 154 | DebuggerEmulatorModel * model, 155 | { 156 | model->animation_counter = 0; 157 | model->animation_visible = 0; 158 | }, 159 | false); 160 | 161 | view_set_context(handle->view, handle); 162 | view_set_draw_callback(handle->view, debugger_emulator_draw_callback); 163 | view_set_input_callback(handle->view, debugger_emulator_input_callback); 164 | view_set_enter_callback(handle->view, view_display_test_enter); 165 | view_set_exit_callback(handle->view, view_display_test_exit); 166 | 167 | handle->timer = 168 | furi_timer_alloc(view_display_test_timer_callback, FuriTimerTypePeriodic, handle); 169 | 170 | handle->mutex = furi_mutex_alloc(FuriMutexTypeNormal); 171 | 172 | return handle; 173 | } 174 | 175 | void debugger_emulator_set_animation_status(DebuggerEmulator* handle, uint8_t enabled) { 176 | furi_mutex_acquire(handle->mutex, FuriWaitForever); 177 | handle->animation_enabled = enabled; 178 | 179 | with_view_model( 180 | handle->view, 181 | DebuggerEmulatorModel * model, 182 | { 183 | if(enabled && !model->animation_visible) { 184 | model->animation_counter = 0; 185 | model->animation_visible = 1; 186 | } 187 | }, 188 | false); 189 | furi_mutex_release(handle->mutex); 190 | } 191 | 192 | void debugger_emulator_free(DebuggerEmulator* handle) { 193 | furi_assert(handle); 194 | furi_timer_free(handle->timer); 195 | furi_mutex_free(handle->mutex); 196 | view_free(handle->view); 197 | free(handle); 198 | } 199 | 200 | View* debugger_emulator_get_view(DebuggerEmulator* debugger) { 201 | furi_assert(debugger); 202 | return debugger->view; 203 | } 204 | -------------------------------------------------------------------------------- /views/view_flasher.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include "view_flasher.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "../utils.h" 33 | 34 | #define TAG "WCH_VieFl" 35 | 36 | struct ViewFlasher { 37 | View* view; 38 | FuriTimer* timer; 39 | FuriMutex* mutex; 40 | uint32_t animation_enabled; 41 | }; 42 | 43 | typedef struct { 44 | struct { 45 | ViewFlasher_View_Action selected_action; 46 | } menu; 47 | struct { 48 | ViewFlasherDoActionCallback cb; 49 | void* context; 50 | } do_action; 51 | char* display_text; 52 | } ViewFlasherModel; 53 | /* 54 | static void draw_menu_item( 55 | Canvas* canvas, 56 | ViewFlasherModel* model, 57 | ViewFlasher_View_Action item_id, 58 | const char* text) { 59 | uint8_t box_y = (item_id * 16); 60 | uint8_t text_y = 4 + (item_id * 16); 61 | 62 | if(model->menu.selected_action == item_id) { 63 | canvas_set_color(canvas, ColorBlack); 64 | elements_slightly_rounded_box(canvas, 72, box_y, 55, 16); 65 | 66 | canvas_set_color(canvas, ColorWhite); 67 | elements_multiline_text_aligned(canvas, 100, text_y, AlignCenter, AlignTop, text); 68 | } else { 69 | canvas_set_color(canvas, ColorBlack); 70 | elements_multiline_text_aligned(canvas, 100, text_y, AlignCenter, AlignTop, text); 71 | } 72 | } 73 | */ 74 | static void view_flasher_draw_callback(Canvas* canvas, void* _model) { 75 | ViewFlasherModel* model = _model; 76 | UNUSED(model); 77 | elements_button_right(canvas, "Ok"); 78 | elements_progress_bar(canvas, 5, 40, 30, 0.3); 79 | 80 | /* 81 | //canvas_draw_icon(canvas, 0, 0, &I_flasher_base_128x64); 82 | canvas_set_color(canvas, ColorBlack); 83 | //elements_bold_rounded_frame(canvas, 0, 0, 70, 63); 84 | 85 | if(model->display_text != NULL) { 86 | elements_bold_rounded_frame(canvas, 0, 0, 73, 63); 87 | elements_text_box(canvas, 4, 4, 110, 55, AlignLeft, AlignTop, model->display_text, false); 88 | } 89 | 90 | //elements_progress_bar(canvas, 5, 40, 30, 0.3); 91 | 92 | draw_menu_item(canvas, model, ViewFlasher_Action_ChipInfo, "Chip info"); 93 | draw_menu_item(canvas, model, ViewFlasher_Action_OpenFile, "Open file"); 94 | draw_menu_item(canvas, model, ViewFlasher_Action_EraseChip, "Erase chip"); 95 | draw_menu_item(canvas, model, ViewFlasher_Action_WriteChip, "Write chip"); 96 | 97 | //canvas_set_font(canvas, FontPrimary); 98 | canvas_set_font(canvas, FontSecondary); 99 | 100 | */ 101 | } 102 | 103 | void view_flasher_display_text(ViewFlasher* handle, const char* message) { 104 | char* str = malloc(strlen(message) + 1); 105 | strcpy(str, message); 106 | with_view_model( 107 | handle->view, 108 | ViewFlasherModel * model, 109 | { 110 | if(model->display_text != NULL) { 111 | free(model->display_text); 112 | } 113 | model->display_text = str; 114 | }, 115 | true); 116 | } 117 | 118 | void view_flasher_display_clear_text(ViewFlasher* handle) { 119 | with_view_model( 120 | handle->view, 121 | ViewFlasherModel * model, 122 | { 123 | if(model->display_text != NULL) { 124 | free(model->display_text); 125 | } 126 | model->display_text = NULL; 127 | }, 128 | true); 129 | } 130 | 131 | void view_flasher_register_action_callback( 132 | ViewFlasher* handle, 133 | ViewFlasherDoActionCallback cb, 134 | void* cb_context) { 135 | with_view_model( 136 | handle->view, 137 | ViewFlasherModel * model, 138 | { 139 | model->do_action.cb = cb; 140 | model->do_action.context = cb_context; 141 | }, 142 | false); 143 | } 144 | 145 | static bool view_flasher_input_callback(InputEvent* event, void* context) { 146 | furi_assert(context); 147 | ViewFlasher* flasher = context; 148 | bool consumed = false; 149 | 150 | if(event->type == InputTypeShort || event->type == InputTypeRepeat) { 151 | with_view_model( 152 | flasher->view, 153 | ViewFlasherModel * model, 154 | { 155 | switch(event->key) { 156 | case InputKeyUp: 157 | if(model->menu.selected_action > 0) { 158 | model->menu.selected_action--; 159 | } 160 | consumed = true; 161 | break; 162 | case InputKeyDown: 163 | model->menu.selected_action++; 164 | if(model->menu.selected_action >= ViewFlasher_ActionCount) { 165 | model->menu.selected_action = ViewFlasher_ActionCount - 1; 166 | } 167 | consumed = true; 168 | break; 169 | case InputKeyOk: 170 | if(model->do_action.cb) { 171 | model->do_action.cb(model->do_action.context, model->menu.selected_action); 172 | } 173 | consumed = true; 174 | break; 175 | default: 176 | break; 177 | } 178 | }, 179 | true); 180 | } 181 | return consumed; 182 | } 183 | 184 | static void view_flasher_timer_callback(void* context) { 185 | ViewFlasher* instance = context; 186 | with_view_model( 187 | instance->view, ViewFlasherModel * model, { UNUSED(model); }, true); 188 | } 189 | 190 | static void view_flasher_enter(void* context) { 191 | ViewFlasher* instance = context; 192 | UNUSED(instance); 193 | //furi_timer_start(instance->timer, furi_kernel_get_tick_frequency() / 4); 194 | } 195 | 196 | static void view_flasher_exit(void* context) { 197 | ViewFlasher* instance = context; 198 | //view_flasher_display_clear_text(instance); 199 | UNUSED(instance); 200 | //furi_timer_stop(instance->timer); 201 | } 202 | 203 | ViewFlasher* view_flasher_alloc() { 204 | ViewFlasher* handle = malloc(sizeof(ViewFlasher)); 205 | 206 | handle->view = view_alloc(); 207 | view_allocate_model(handle->view, ViewModelTypeLocking, sizeof(ViewFlasherModel)); 208 | 209 | with_view_model( 210 | handle->view, 211 | ViewFlasherModel * model, 212 | { model->menu.selected_action = ViewFlasher_Action_ChipInfo; }, 213 | false); 214 | 215 | view_set_context(handle->view, handle); 216 | view_set_draw_callback(handle->view, view_flasher_draw_callback); 217 | view_set_input_callback(handle->view, view_flasher_input_callback); 218 | view_set_enter_callback(handle->view, view_flasher_enter); 219 | view_set_exit_callback(handle->view, view_flasher_exit); 220 | 221 | handle->timer = furi_timer_alloc(view_flasher_timer_callback, FuriTimerTypePeriodic, handle); 222 | 223 | handle->mutex = furi_mutex_alloc(FuriMutexTypeNormal); 224 | 225 | return handle; 226 | } 227 | /* 228 | void view_flasher_set_animation_status(ViewFlasher* handle, uint8_t enabled) { 229 | furi_mutex_acquire(handle->mutex, FuriWaitForever); 230 | handle->animation_enabled = enabled; 231 | 232 | with_view_model( 233 | handle->view, 234 | ViewFlasherModel * model, 235 | { 236 | if(enabled && !model->animation_visible) { 237 | model->animation_counter = 0; 238 | model->animation_visible = 1; 239 | } 240 | }, 241 | false); 242 | furi_mutex_release(handle->mutex); 243 | }*/ 244 | 245 | void view_flasher_free(ViewFlasher* handle) { 246 | furi_assert(handle); 247 | view_flasher_display_clear_text(handle); 248 | furi_timer_free(handle->timer); 249 | furi_mutex_free(handle->mutex); 250 | view_free(handle->view); 251 | free(handle); 252 | } 253 | 254 | View* view_flasher_get_view(ViewFlasher* flasher) { 255 | furi_assert(flasher); 256 | return flasher->view; 257 | } 258 | -------------------------------------------------------------------------------- /wch_swio_flasher.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | 28 | #include "wch_swio_flasher.h" 29 | #include "scenes/wch_swio_flasher_scene.h" 30 | #include 31 | #include 32 | #include 33 | #include "utils.h" 34 | 35 | #define TAG "WCH_SWIO_FL" 36 | 37 | #define SYSTEM_DEFAULTS_MAGIC 0xCAFED00D 38 | #define SYSTEM_DEFAULTS_FILENAME "defaults.txt" 39 | 40 | typedef struct { 41 | uint32_t magic; 42 | uint32_t last_file_len; 43 | char last_file[]; 44 | } system_defaults; 45 | 46 | static bool wch_swio_flasher_custom_event_callback(void* context, uint32_t event) { 47 | furi_assert(context); 48 | WchSwioFlasherApp* app = context; 49 | return scene_manager_handle_custom_event(app->scene_manager, event); 50 | } 51 | 52 | static bool wch_swio_flasher_back_event_callback(void* context) { 53 | furi_assert(context); 54 | WchSwioFlasherApp* app = context; 55 | return scene_manager_handle_back_event(app->scene_manager); 56 | } 57 | 58 | void wch_swio_flasher_load_defaults(WchSwioFlasherApp* app) { 59 | Storage* storage = furi_record_open(RECORD_STORAGE); 60 | 61 | // Allocate file 62 | File* file = storage_file_alloc(storage); 63 | 64 | furi_string_set_str(app->views.file_path, ""); 65 | 66 | if(!storage_file_open( 67 | file, APP_DATA_PATH(SYSTEM_DEFAULTS_FILENAME), FSAM_READ, FSOM_OPEN_EXISTING)) { 68 | FURI_LOG_E(TAG, "Failed to open config file"); 69 | } else { 70 | system_defaults head; // = malloc(sizeof(system_defaults) + last_file_len); 71 | if(!storage_file_read(file, &head, sizeof(head))) { 72 | FURI_LOG_E(TAG, "Failed to read header from config file"); 73 | } else { 74 | char* last_file = malloc(head.last_file_len); 75 | if(head.magic == SYSTEM_DEFAULTS_MAGIC && 76 | !storage_file_read(file, last_file, head.last_file_len)) { 77 | FURI_LOG_E(TAG, "Failed to read from config file"); 78 | } else { 79 | FURI_LOG_D(TAG, "Loaded default path: %s", last_file); 80 | furi_string_set_str(app->views.file_path, last_file); 81 | } 82 | free(last_file); 83 | } 84 | } 85 | 86 | storage_file_close(file); 87 | 88 | // Deallocate file 89 | storage_file_free(file); 90 | 91 | // Close storage 92 | furi_record_close(RECORD_STORAGE); 93 | } 94 | 95 | void wch_swio_flasher_store_defaults(WchSwioFlasherApp* app) { 96 | Storage* storage = furi_record_open(RECORD_STORAGE); 97 | 98 | // Allocate file 99 | File* file = storage_file_alloc(storage); 100 | 101 | if(!storage_file_open( 102 | file, APP_DATA_PATH(SYSTEM_DEFAULTS_FILENAME), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { 103 | FURI_LOG_E(TAG, "Failed to open config file"); 104 | } 105 | 106 | const char* last_file = furi_string_get_cstr(app->views.file_path); 107 | uint32_t last_file_len = strlen(last_file); 108 | system_defaults* sd = malloc(sizeof(system_defaults) + last_file_len); 109 | 110 | sd->magic = SYSTEM_DEFAULTS_MAGIC; 111 | sd->last_file_len = last_file_len; 112 | memcpy(sd->last_file, last_file, last_file_len); 113 | 114 | FURI_LOG_D(TAG, "Store default path: %s", last_file); 115 | 116 | if(!storage_file_write(file, sd, sizeof(system_defaults) + last_file_len)) { 117 | FURI_LOG_E(TAG, "Failed to write to config file"); 118 | } 119 | 120 | // Close file 121 | storage_file_close(file); 122 | 123 | // Deallocate file 124 | storage_file_free(file); 125 | 126 | // Deallocate data 127 | free(sd); 128 | 129 | // Close storage 130 | furi_record_close(RECORD_STORAGE); 131 | } 132 | 133 | WchSwioFlasherApp* wch_swio_flasher_app_alloc() { 134 | WchSwioFlasherApp* app = malloc(sizeof(WchSwioFlasherApp)); 135 | 136 | app->helpers.swio = WchSwioFlasher_SWIO_create(); 137 | app->helpers.riscv_debug = WchSwioFlasher_RiscVDebug_create(app->helpers.swio); 138 | app->helpers.flasher = WchSwioFlasher_WchFlasher_create(app->helpers.riscv_debug); 139 | 140 | app->services.mini_debugger = 141 | WchSwioFlasher_MinichlinkDebugger_create(app->helpers.swio, app->helpers.riscv_debug); 142 | app->services.emulator = WchSwioFlasher_NhcLink042Emu_create(app->services.mini_debugger); 143 | app->services.flasher = 144 | WchSwioFlasher_Ch32vFlipperFlasher_create(app->helpers.riscv_debug, app->helpers.flasher); 145 | 146 | app->gui = furi_record_open(RECORD_GUI); 147 | app->notification = furi_record_open(RECORD_NOTIFICATION); 148 | 149 | app->view_dispatcher = view_dispatcher_alloc(); 150 | app->scene_manager = scene_manager_alloc(&wch_swio_flasher_scene_handlers, app); 151 | view_dispatcher_enable_queue(app->view_dispatcher); 152 | 153 | view_dispatcher_set_event_callback_context(app->view_dispatcher, app); 154 | 155 | view_dispatcher_set_custom_event_callback( 156 | app->view_dispatcher, wch_swio_flasher_custom_event_callback); 157 | view_dispatcher_set_navigation_event_callback( 158 | app->view_dispatcher, wch_swio_flasher_back_event_callback); 159 | 160 | app->views.debuger_emulator = debugger_emulator_alloc(); 161 | view_dispatcher_add_view( 162 | app->view_dispatcher, 163 | WchSwioFlasherViewDebuggerEmulator, 164 | debugger_emulator_get_view(app->views.debuger_emulator)); 165 | 166 | // Widget 167 | app->views.widget = widget_alloc(); 168 | view_dispatcher_add_view( 169 | app->view_dispatcher, WchSwioFlasherViewWidget, widget_get_view(app->views.widget)); 170 | 171 | // SubMenu 172 | app->views.submenu = submenu_alloc(); 173 | view_dispatcher_add_view( 174 | app->view_dispatcher, WchSwioFlasherViewSubmenu, submenu_get_view(app->views.submenu)); 175 | 176 | // FileBrowser 177 | app->views.file_path = furi_string_alloc(); 178 | app->views.file_browser = file_browser_alloc(app->views.file_path); 179 | file_browser_configure(app->views.file_browser, "*", NULL, true, false, &I_bin10x10, true); 180 | view_dispatcher_add_view( 181 | app->view_dispatcher, 182 | WchSwioFlasherViewFileBrowser, 183 | file_browser_get_view(app->views.file_browser)); 184 | 185 | // Flasher 186 | app->views.flasher = view_flasher_alloc(); 187 | view_dispatcher_add_view( 188 | app->view_dispatcher, 189 | WchSwioFlasherViewFlasher, 190 | view_flasher_get_view(app->views.flasher)); 191 | 192 | view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); 193 | 194 | wch_swio_flasher_load_defaults(app); 195 | 196 | scene_manager_next_scene(app->scene_manager, WchSwioFlasherSceneMain); 197 | return app; 198 | } 199 | 200 | void wch_swio_flasher_app_free(WchSwioFlasherApp* app) { 201 | furi_assert(app); 202 | 203 | view_dispatcher_stop(app->view_dispatcher); 204 | 205 | view_dispatcher_remove_view(app->view_dispatcher, WchSwioFlasherViewWidget); 206 | view_dispatcher_remove_view(app->view_dispatcher, WchSwioFlasherViewSubmenu); 207 | view_dispatcher_remove_view(app->view_dispatcher, WchSwioFlasherViewDebuggerEmulator); 208 | view_dispatcher_remove_view(app->view_dispatcher, WchSwioFlasherViewFileBrowser); 209 | view_dispatcher_remove_view(app->view_dispatcher, WchSwioFlasherViewFlasher); 210 | 211 | widget_free(app->views.widget); 212 | submenu_free(app->views.submenu); 213 | file_browser_free(app->views.file_browser); 214 | furi_string_free(app->views.file_path); 215 | 216 | debugger_emulator_free(app->views.debuger_emulator); 217 | view_flasher_free(app->views.flasher); 218 | view_dispatcher_free(app->view_dispatcher); 219 | scene_manager_free(app->scene_manager); 220 | 221 | furi_record_close(RECORD_GUI); 222 | furi_record_close(RECORD_NOTIFICATION); 223 | 224 | WchSwioFlasher_NhcLink042Emu_destroy(app->services.emulator); 225 | WchSwioFlasher_MinichlinkDebugger_destroy(app->services.mini_debugger); 226 | WchSwioFlasher_Ch32vFlipperFlasher_destroy(app->services.flasher); 227 | 228 | WchSwioFlasher_WchFlasher_destroy(app->helpers.flasher); 229 | WchSwioFlasher_RiscVDebug_destroy(app->helpers.riscv_debug); 230 | WchSwioFlasher_SWIO_destroy(app->helpers.swio); 231 | 232 | free(app); 233 | } 234 | 235 | int32_t wch_swio_flasher_app(void* p) { 236 | UNUSED(p); 237 | 238 | WchSwioFlasherApp* app = wch_swio_flasher_app_alloc(); 239 | 240 | dolphin_deed(DolphinDeedPluginStart); 241 | 242 | view_dispatcher_run(app->view_dispatcher); 243 | 244 | wch_swio_flasher_app_free(app); 245 | 246 | return 0; 247 | } 248 | -------------------------------------------------------------------------------- /scenes/wch_swio_flasher_scene_flash.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "../utils.h" 31 | #include "../wch_swio_flasher.h" 32 | #include "wch_swio_flasher_scene.h" 33 | 34 | #define TAG "WCH_ScFl" 35 | 36 | typedef enum { 37 | SubMenu_Action_ChipInfo = 0, 38 | SubMenu_Action_OpenFile, 39 | SubMenu_Action_EraseChip, 40 | SubMenu_Action_WriteChip, 41 | SubMenu_ActionCount, 42 | } SubMenu_Action; 43 | 44 | static void wch_swio_flasher_scene_flash_on_action_done(void* context, ViewFlasher_Action action) { 45 | WchSwioFlasherApp* app = context; 46 | view_dispatcher_send_custom_event(app->view_dispatcher, action); 47 | } 48 | 49 | static void select_file_to_write(WchSwioFlasherApp* app) { 50 | DialogsFileBrowserOptions browser_options; 51 | dialog_file_browser_set_basic_options(&browser_options, ".bin", &I_bin10x10); 52 | browser_options.base_path = ANY_PATH("wch_firmare"); 53 | browser_options.skip_assets = true; 54 | 55 | DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); 56 | bool res = dialog_file_browser_show( 57 | dialogs, app->views.file_path, app->views.file_path, &browser_options); 58 | 59 | if(res) { 60 | FURI_LOG_D(TAG, "selected bin is %s", furi_string_get_cstr(app->views.file_path)); 61 | 62 | wch_swio_flasher_store_defaults(app); 63 | //view_dispatcher_switch_to_view(app->view_dispatcher, WchSwioFlasherViewFlasher); 64 | WchSwioFlasher_Ch32vFlipperFlasher_write_chip( 65 | app->services.flasher, (char*)furi_string_get_cstr(app->views.file_path)); 66 | } 67 | 68 | furi_record_close(RECORD_DIALOGS); 69 | } 70 | 71 | static void wch_swio_flasher_scene_flash_submenu_callback(void* context, uint32_t index) { 72 | furi_assert(context); 73 | WchSwioFlasherApp* app = context; 74 | 75 | switch(index) { 76 | case SubMenu_Action_ChipInfo: 77 | WchSwioFlasher_Ch32vFlipperFlasher_chip_info(app->services.flasher); 78 | break; 79 | case SubMenu_Action_EraseChip: 80 | WchSwioFlasher_Ch32vFlipperFlasher_erase_chip(app->services.flasher); 81 | break; 82 | case SubMenu_Action_WriteChip: 83 | select_file_to_write(app); 84 | break; 85 | default: 86 | break; 87 | } 88 | } 89 | 90 | void wch_swio_flasher_scene_flash_on_enter(void* context) { 91 | WchSwioFlasherApp* app = context; 92 | 93 | Submenu* submenu = app->views.submenu; 94 | submenu_set_header(submenu, "Flash CH32Vxx"); 95 | submenu_add_item( 96 | submenu, 97 | "Get chip info", 98 | SubMenu_Action_ChipInfo, 99 | wch_swio_flasher_scene_flash_submenu_callback, 100 | app); 101 | submenu_add_item( 102 | submenu, 103 | "Erase chip", 104 | SubMenu_Action_EraseChip, 105 | wch_swio_flasher_scene_flash_submenu_callback, 106 | app); 107 | submenu_add_item( 108 | submenu, 109 | "Write chip", 110 | SubMenu_Action_WriteChip, 111 | wch_swio_flasher_scene_flash_submenu_callback, 112 | app); 113 | 114 | submenu_set_selected_item( 115 | submenu, scene_manager_get_scene_state(app->scene_manager, WchSwioFlasherSceneFlash)); 116 | 117 | WchSwioFlasher_Ch32vFlipperFlasher_attach(app->services.flasher); 118 | WchSwioFlasher_Ch32vFlipperFlasher_event_callback( 119 | app->services.flasher, wch_swio_flasher_scene_flash_on_action_done, app); 120 | 121 | view_dispatcher_switch_to_view(app->view_dispatcher, WchSwioFlasherViewSubmenu); 122 | } 123 | 124 | static char* flasher_err_to_text(WchSwioFlasher_CFF_ResultStatus status) { 125 | switch(status) { 126 | case VWchSwioFlasher_CFF_ChipNotConnected: 127 | return "not connected"; 128 | case WchSwioFlasher_CFF_UnableToOpenFile: 129 | return "unable to open file"; 130 | default: 131 | return ""; 132 | } 133 | } 134 | 135 | static char* error_action_to_str(ViewFlasher_Action action) { 136 | switch(action) { 137 | case WchSwioFlasher_CFF_ChipInfoCompleted: 138 | return "Unable to get\nchip info!"; 139 | case WchSwioFlasher_CFF_EraseChipCompleted: 140 | return "Unable erase\nchip!"; 141 | case WchSwioFlasher_CFF_WriteChipCompleted: 142 | return "Unable write\nchip!"; 143 | default: 144 | return ""; 145 | } 146 | } 147 | 148 | static void show_error_dialog( 149 | WchSwioFlasherApp* app, 150 | ViewFlasher_Action action, 151 | WchSwioFlasher_CFF_ResultStatus status) { 152 | UNUSED(app); 153 | DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); 154 | DialogMessage* message = dialog_message_alloc(); 155 | 156 | dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); 157 | 158 | FuriString* error_message = furi_string_alloc(); 159 | furi_string_cat_printf( 160 | error_message, "%s\n'%s'", error_action_to_str(action), flasher_err_to_text(status)); 161 | 162 | dialog_message_set_text( 163 | message, furi_string_get_cstr(error_message), 64, 30, AlignCenter, AlignCenter); 164 | 165 | //dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6); 166 | dialog_message_set_buttons(message, "Back", NULL, NULL); 167 | dialog_message_show(dialogs, message); 168 | dialog_message_free(message); 169 | furi_string_free(error_message); 170 | furi_record_close(RECORD_DIALOGS); 171 | } 172 | 173 | static void show_chip_info_dialog(WchSwioFlasherApp* app, WchSwioFlasher_CFF_ChipInfo* data) { 174 | UNUSED(app); 175 | DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); 176 | DialogMessage* message = dialog_message_alloc(); 177 | 178 | dialog_message_set_header(message, "Chip info", 64, 0, AlignCenter, AlignTop); 179 | 180 | FuriString* text = furi_string_alloc(); 181 | furi_string_cat_printf( 182 | text, 183 | "Flash size: %lu kB\nESIG0: " FMT_4HEX "\nESIG1: " FMT_4HEX "\nESIG2: " FMT_4HEX, 184 | data->flash_size, 185 | _UI(data->esig_uniid[0]), 186 | _UI(data->esig_uniid[1]), 187 | _UI(data->esig_uniid[2])); 188 | 189 | dialog_message_set_text(message, furi_string_get_cstr(text), 2, 15, AlignLeft, AlignTop); 190 | 191 | //dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6); 192 | dialog_message_set_buttons(message, NULL, NULL, "Ok"); 193 | dialog_message_show(dialogs, message); 194 | dialog_message_free(message); 195 | furi_string_free(text); 196 | furi_record_close(RECORD_DIALOGS); 197 | } 198 | 199 | static void show_ok_dialog(WchSwioFlasherApp* app, const char* text) { 200 | UNUSED(app); 201 | DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); 202 | DialogMessage* message = dialog_message_alloc(); 203 | 204 | dialog_message_set_header(message, "Success", 64, 0, AlignCenter, AlignTop); 205 | dialog_message_set_text(message, text, 64, 31, AlignCenter, AlignCenter); 206 | dialog_message_set_buttons(message, NULL, NULL, "Ok"); 207 | dialog_message_show(dialogs, message); 208 | dialog_message_free(message); 209 | 210 | furi_record_close(RECORD_DIALOGS); 211 | } 212 | 213 | static void handle_chip_info_completed(WchSwioFlasherApp* app) { 214 | WchSwioFlasher_CFF_ChipInfo data; 215 | WchSwioFlasher_Ch32vFlipperFlasher_chip_info_data(app->services.flasher, &data); 216 | FURI_LOG_D(TAG, "chip_info finish %d", data.status); 217 | 218 | switch(data.status) { 219 | case VWchSwioFlasher_CFF_Ok: { 220 | show_chip_info_dialog(app, &data); 221 | break; 222 | } 223 | case VWchSwioFlasher_CFF_NoData: 224 | // TODO throw error 225 | break; 226 | default: 227 | show_error_dialog(app, WchSwioFlasher_CFF_ChipInfoCompleted, data.status); 228 | break; 229 | } 230 | } 231 | 232 | static void handle_erase_chip_completed(WchSwioFlasherApp* app) { 233 | WchSwioFlasher_CFF_EraseChip data; 234 | WchSwioFlasher_Ch32vFlipperFlasher_erase_chip_data(app->services.flasher, &data); 235 | FURI_LOG_D(TAG, "erase_chip finish %d", data.status); 236 | 237 | switch(data.status) { 238 | case VWchSwioFlasher_CFF_Ok: { 239 | show_ok_dialog(app, "Chip erased"); 240 | break; 241 | } 242 | case VWchSwioFlasher_CFF_NoData: 243 | // TODO throw error 244 | break; 245 | default: 246 | show_error_dialog(app, WchSwioFlasher_CFF_EraseChipCompleted, data.status); 247 | break; 248 | } 249 | } 250 | 251 | static void handle_write_chip_completed(WchSwioFlasherApp* app) { 252 | WchSwioFlasher_CFF_EraseChip data; 253 | WchSwioFlasher_Ch32vFlipperFlasher_erase_chip_data(app->services.flasher, &data); 254 | FURI_LOG_D(TAG, "write_chip finish %d", data.status); 255 | 256 | switch(data.status) { 257 | case VWchSwioFlasher_CFF_Ok: { 258 | show_ok_dialog(app, "Chip programmed"); 259 | break; 260 | } 261 | case VWchSwioFlasher_CFF_NoData: 262 | // TODO throw error 263 | break; 264 | default: 265 | show_error_dialog(app, WchSwioFlasher_CFF_WriteChipCompleted, data.status); 266 | break; 267 | } 268 | } 269 | 270 | bool wch_swio_flasher_scene_flash_on_event(void* context, SceneManagerEvent event) { 271 | WchSwioFlasherApp* app = context; 272 | UNUSED(app); 273 | bool consumed = false; 274 | 275 | if(event.type == SceneManagerEventTypeCustom) { 276 | switch(event.event) { 277 | case WchSwioFlasher_CFF_ChipInfoCompleted: 278 | handle_chip_info_completed(app); 279 | break; 280 | case WchSwioFlasher_CFF_EraseChipCompleted: 281 | handle_erase_chip_completed(app); 282 | break; 283 | case WchSwioFlasher_CFF_WriteChipCompleted: 284 | handle_write_chip_completed(app); 285 | break; 286 | default: 287 | break; 288 | } 289 | consumed = true; 290 | } 291 | 292 | return consumed; 293 | } 294 | 295 | void wch_swio_flasher_scene_flash_on_exit(void* context) { 296 | WchSwioFlasherApp* app = context; 297 | 298 | WchSwioFlasher_Ch32vFlipperFlasher_detach(app->services.flasher); 299 | 300 | submenu_reset(app->views.submenu); 301 | 302 | //widget_reset(app->views.widget); 303 | } 304 | -------------------------------------------------------------------------------- /helpers/wch_flasher.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | /** 27 | This file is based on modified original from https://github.com/aappleby/PicoRVD 28 | */ 29 | 30 | #include "wch_flasher.h" 31 | #include "wch_flasher_inner.h" 32 | #include "debug_defines.h" 33 | #include "programs.h" 34 | #include "../utils.h" 35 | #include "../config.h" 36 | 37 | #define TAG "WSF_FLSH" 38 | 39 | WchSwioFlasher_WchFlasher* WchSwioFlasher_WchFlasher_create(WchSwioFlasher_RiscVDebug* debug) { 40 | WchSwioFlasher_WchFlasher* handle = malloc(sizeof(WchSwioFlasher_WchFlasher)); 41 | handle->debug = debug; 42 | return handle; 43 | } 44 | 45 | void WchSwioFlasher_WchFlasher_destroy(WchSwioFlasher_WchFlasher* handle) { 46 | free(handle); 47 | } 48 | 49 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_lock_flash(WchSwioFlasher_WchFlasher* handle) { 50 | RWCH_FLASH_CTLR ctrl = {.raw = 0}; 51 | FURI_LOG_D(TAG, "lock_flash"); 52 | 53 | // Read reg 54 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_mem_u32(handle->debug, ADDR_FLASH_CTLR, &ctrl.raw)); 55 | 56 | // Lock flash 57 | ctrl.LOCK = 1; 58 | ctrl.FLOCK = 1; 59 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32(handle->debug, ADDR_FLASH_CTLR, ctrl.raw)); 60 | 61 | // Check lock status 62 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_mem_u32(handle->debug, ADDR_FLASH_CTLR, &ctrl.raw)) 63 | 64 | if(!ctrl.LOCK) { 65 | return LOG_ERR_M(WchSwioFlasher_Error_TargetInInvalidState, "flash did not lock"); 66 | } 67 | 68 | if(!ctrl.FLOCK) { 69 | return LOG_ERR_M( 70 | WchSwioFlasher_Error_TargetInInvalidState, "flash did not lock fast mode"); 71 | } 72 | 73 | FURI_LOG_D(TAG, "lock_flash done"); 74 | 75 | return WchSwioFlasher_Ok; 76 | } 77 | 78 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_unlock_flash(WchSwioFlasher_WchFlasher* handle) { 79 | FURI_LOG_D(TAG, "unlock_flash"); 80 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32(handle->debug, ADDR_FLASH_KEYR, 0x45670123)); 81 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32(handle->debug, ADDR_FLASH_KEYR, 0xCDEF89AB)); 82 | 83 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32(handle->debug, ADDR_FLASH_MKEYR, 0x45670123)); 84 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32(handle->debug, ADDR_FLASH_MKEYR, 0xCDEF89AB)); 85 | 86 | // Check lock status 87 | RWCH_FLASH_CTLR ctrl = {.raw = 0}; 88 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_mem_u32(handle->debug, ADDR_FLASH_CTLR, &ctrl.raw)) 89 | 90 | if(ctrl.LOCK) { 91 | return LOG_ERR_M(WchSwioFlasher_Error_TargetInInvalidState, "flash did not unlock"); 92 | } 93 | 94 | if(ctrl.FLOCK) { 95 | return LOG_ERR_M( 96 | WchSwioFlasher_Error_TargetInInvalidState, "flash did not unlock fast mode"); 97 | } 98 | 99 | FURI_LOG_D(TAG, "unlock_flash done"); 100 | return WchSwioFlasher_Ok; 101 | } 102 | 103 | static WchSwioFlasher_Error run_flash_command( 104 | WchSwioFlasher_WchFlasher* handle, 105 | uint32_t addr, 106 | uint32_t ctl1, 107 | uint32_t ctl2) { 108 | CHECK_ERR(WchSwioFlasher_RiscVDebug_load_prog( 109 | handle->debug, &WchSwioFlasher_RiscVDebug_program_flash_command_program)); 110 | 111 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr(handle->debug, 10, 0x40022000)); 112 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr(handle->debug, 11, addr)); 113 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr(handle->debug, 12, ctl1)); 114 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr(handle->debug, 13, ctl2)); 115 | 116 | CHECK_ERR(WchSwioFlasher_RiscVDebug_run_prog(handle->debug, 500)); 117 | return WchSwioFlasher_Ok; 118 | } 119 | 120 | WchSwioFlasher_Error 121 | WchSwioFlasher_WchFlasher_wipe_page(WchSwioFlasher_WchFlasher* handle, uint32_t dst_addr) { 122 | CHECK_ERR(WchSwioFlasher_RiscVDebug_reset(handle->debug, WchSwioFlasher_RVD_ResetToHalt)); 123 | CHECK_ERR(WchSwioFlasher_WchFlasher_unlock_flash(handle)); 124 | dst_addr |= 0x08000000; 125 | run_flash_command(handle, dst_addr, BIT_CTLR_FTER, BIT_CTLR_FTER | BIT_CTLR_STRT); 126 | return WchSwioFlasher_Ok; 127 | } 128 | 129 | WchSwioFlasher_Error 130 | WchSwioFlasher_WchFlasher_wipe_sector(WchSwioFlasher_WchFlasher* handle, uint32_t dst_addr) { 131 | CHECK_ERR(WchSwioFlasher_RiscVDebug_reset(handle->debug, WchSwioFlasher_RVD_ResetToHalt)); 132 | CHECK_ERR(WchSwioFlasher_WchFlasher_unlock_flash(handle)); 133 | dst_addr |= 0x08000000; 134 | run_flash_command(handle, dst_addr, BIT_CTLR_PER, BIT_CTLR_PER | BIT_CTLR_STRT); 135 | return WchSwioFlasher_Ok; 136 | } 137 | 138 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_wipe_chip(WchSwioFlasher_WchFlasher* handle) { 139 | CHECK_ERR(WchSwioFlasher_RiscVDebug_reset(handle->debug, WchSwioFlasher_RVD_ResetToHalt)); 140 | 141 | CHECK_ERR(WchSwioFlasher_WchFlasher_unlock_flash(handle)); 142 | uint32_t dst_addr = 0x08000000; 143 | run_flash_command(handle, dst_addr, BIT_CTLR_MER, BIT_CTLR_MER | BIT_CTLR_STRT); 144 | return WchSwioFlasher_Ok; 145 | } 146 | 147 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_write_flash( 148 | WchSwioFlasher_WchFlasher* handle, 149 | uint32_t dst_addr, 150 | void* blob, 151 | int size) { 152 | FURI_LOG_D(TAG, "write_flash"); 153 | 154 | CHECK_ERR(WchSwioFlasher_WchFlasher_unlock_flash(handle)); 155 | 156 | if(size & 0x03) { 157 | return LOG_ERR_M( 158 | WchSwioFlasher_Error_InvalidArgument, "bad size alignment" FMT_4HEX, _UI(size)); 159 | } 160 | 161 | int size_dwords = size / 4; 162 | 163 | dst_addr |= 0x08000000; 164 | 165 | // setup FLASH registers and clear internal 64B buffer 166 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32(handle->debug, ADDR_FLASH_ADDR, dst_addr)); 167 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32( 168 | handle->debug, ADDR_FLASH_CTLR, BIT_CTLR_FTPG | BIT_CTLR_BUFRST)); 169 | 170 | // Wait until buffer is cleared (until is FLASH busy) 171 | CHECK_ERR(WchSwioFlasher_RiscVDebug_wait_for_reg( 172 | handle->debug, 173 | ADDR_FLASH_STATR, 174 | BIT_STATR_EOP, 175 | BIT_STATR_EOP, 176 | WCHF_WAIT_FOR_WRITE_FLASH_TIMEOUT)); 177 | 178 | // Load flash program 179 | CHECK_ERR(WchSwioFlasher_RiscVDebug_load_prog( 180 | handle->debug, &WchSwioFlasher_RiscVDebug_program_write_flash_program)); 181 | 182 | // Setup GPR 183 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr( 184 | handle->debug, WCH_REG_A0, 0x40022000)); // flash registers base 185 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr( 186 | handle->debug, WCH_REG_A1, 0xE00000F4)); // DATA0 @ 0xE00000F4 187 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr(handle->debug, WCH_REG_A2, dst_addr)); 188 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr( 189 | handle->debug, WCH_REG_A3, BIT_CTLR_FTPG | BIT_CTLR_BUFLOAD)); 190 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr( 191 | handle->debug, WCH_REG_A4, BIT_CTLR_FTPG | BIT_CTLR_STRT)); 192 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_gpr( 193 | handle->debug, WCH_REG_A5, BIT_CTLR_FTPG | BIT_CTLR_BUFRST)); 194 | 195 | bool first_word = true; 196 | int page_count = (size_dwords + 15) / 16; 197 | 198 | uint32_t page_size = 64; // TODO move to config 199 | 200 | // Start feeding dwords to prog_write_flash. 201 | 202 | //uint32_t busy_time = 0; 203 | 204 | for(int page = 0; page < page_count; page++) { 205 | for(int dword_idx = 0; dword_idx < 16; dword_idx++) { 206 | int offset = (page * page_size) + (dword_idx * sizeof(uint32_t)); 207 | uint32_t* src = (uint32_t*)((uint8_t*)blob + offset); 208 | 209 | // We have to write full pages only, so if we run out of source data we 210 | // write 0xDEADBEEF in the empty space. 211 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_data0( 212 | handle->debug, dword_idx < size_dwords ? *src : 0xDEADBEEF)); 213 | 214 | if(first_word) { 215 | // There's a chip bug here - we can't set AUTOCMD before COMMAND or 216 | // things break all weird 217 | 218 | // This run_prog _must_ include a busywait 219 | CHECK_ERR(WchSwioFlasher_RiscVDebug_run_prog( 220 | handle->debug, WCHF_WAIT_FOR_WRITE_FLASH_TIMEOUT)); 221 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_abstractauto(handle->debug, 0x00000001)); 222 | first_word = false; 223 | } else { 224 | // We can write flash slightly faster if we only busy-wait at the end 225 | // of each page, but I am wary... 226 | // Waiting here takes 54443 us to write 564 bytes 227 | //uint32_t time_a = time_us_32(); 228 | CHECK_ERR(WchSwioFlasher_RiscVDebug_wait_for_abstractcs( 229 | handle->debug, DM_ABSTRACTCS_BUSY, 0, WCHF_WAIT_FOR_WRITE_FLASH_TIMEOUT)); 230 | 231 | //uint32_t time_b = time_us_32(); 232 | //busy_time += time_b - time_a; 233 | } 234 | } 235 | // This is the end of a page 236 | // Waiting here instead of the above takes 42847 us to write 564 bytes 237 | //uint32_t time_a = time_us_32(); 238 | //while (rvd->get_abstractcs().BUSY) {} 239 | //uint32_t time_b = time_us_32(); 240 | //busy_time += time_b - time_a; 241 | } 242 | 243 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_abstractauto(handle->debug, 0x00000000)); 244 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32(handle->debug, ADDR_FLASH_CTLR, 0)); 245 | 246 | // Write 1 to clear EOP. Not sure if we need to do this... 247 | RWCH_FLASH_STATR statr = {.raw = 0}; 248 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_mem_u32(handle->debug, ADDR_FLASH_STATR, &statr.raw)); 249 | statr.EOP = 1; 250 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_mem_u32(handle->debug, ADDR_FLASH_STATR, statr.raw)); 251 | 252 | //printf("busy_time %d\n", busy_time); 253 | 254 | FURI_LOG_D(TAG, "write_flash done"); 255 | 256 | return WchSwioFlasher_Ok; 257 | } 258 | 259 | /* 260 | //WchSwioFlasher_Error WchSwioFlasher_WchFlasher_wipe_chip(WchSwioFlasher_WchFlasher* handle) { 261 | WchSwioFlasher_Error WchSwioFlasher_WchFlasher_verify_flash( 262 | WchSwioFlasher_WchFlasher* handle, 263 | uint32_t dst_addr, 264 | void* blob, 265 | int size) { 266 | FURI_LOG_D(TAG, "verify_flash"); 267 | //LOG("WCHFlash::verify_flash(0x%08x, 0x%08x, %d)\n", dst_addr, blob, size); 268 | 269 | if(size > MAX_VERIFY_SIZE) { 270 | return LOG_ERR_M( 271 | WchSwioFlasher_Error_InvalidArgument, 272 | "maximum size for verify is %d", 273 | MAX_VERIFY_SIZE); 274 | } 275 | 276 | dst_addr |= 0x08000000; 277 | 278 | uint8_t* readback = malloc(size); 279 | if(!readback) { 280 | return LOG_ERR_M(WchSwioFlasher_Error_NoFreeMemory, "unable to allocate readback buffer"); 281 | } 282 | 283 | rvd->get_block_aligned(dst_addr, readback, size); 284 | 285 | uint8_t* data = (uint8_t*)blob; 286 | bool mismatch = false; 287 | for(int i = 0; i < size; i++) { 288 | if(data[i] != readback[i]) { 289 | LOG_R( 290 | "Flash readback failed at address 0x%08x - want 0x%02x, got 0x%02x\n", 291 | dst_addr + i, 292 | data[i], 293 | readback[i]); 294 | mismatch = true; 295 | } 296 | } 297 | 298 | delete[] readback; 299 | 300 | FURI_LOG_D(TAG, "verify_flash done"); 301 | free(readback); 302 | return !mismatch; 303 | } 304 | 305 | // read flash size 306 | NHCLinkWriteReg32: A3 05 E0 F7 FF 1F 00 00 307 | NHCLinkWriteReg32: A3 17 00 10 24 00 00 00 308 | NHCLinkReadReg32 TX: A2 16 00 00 00 00 00 00 309 | NHCLinkReadReg32 RX: A2 02 00 00 08 00 00 00 310 | NHCLinkReadReg32 TX: A2 04 00 10 24 00 00 00 311 | NHCLinkReadReg32 RX: A2 10 00 FF FF 00 00 00 312 | */ -------------------------------------------------------------------------------- /helpers/swio_pin_magic.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include "swio.h" 27 | #include "swio_inner.h" 28 | #include "../utils.h" 29 | #include "../config.h" 30 | 31 | // Ugly code with some ASM timing magic 32 | 33 | #define TAG "WCH_SWIO_FL" 34 | 35 | // not my code, thx john-bollinger@stackoverflow ... 36 | static uint8_t countBit1Fast(uint32_t n) { 37 | n = (n & 0x55555555u) + ((n >> 1) & 0x55555555u); 38 | n = (n & 0x33333333u) + ((n >> 2) & 0x33333333u); 39 | n = (n & 0x0f0f0f0fu) + ((n >> 4) & 0x0f0f0f0fu); 40 | n = (n & 0x00ff00ffu) + ((n >> 8) & 0x00ff00ffu); 41 | n = (n & 0x0000ffffu) + ((n >> 16) & 0x0000ffffu); 42 | return n; 43 | } 44 | 45 | uint8_t calcParity(uint8_t address, WchSwioFlasher_SWIO_RW rw, uint32_t data) { 46 | uint8_t cnt = 1; // header 47 | 48 | cnt += countBit1Fast(address & 0x7F); 49 | cnt += rw & 0x01; 50 | cnt += countBit1Fast(data); 51 | 52 | return cnt & 0x01; 53 | } 54 | #pragma GCC push_options 55 | #pragma GCC optimize("O0") 56 | WchSwioFlasher_Error 57 | WchSwioFlasher_SWIO_rxtx(uint8_t address, WchSwioFlasher_SWIO_RW rw, uint32_t* data) { 58 | //asm volatile("bkpt 0x00"); 59 | 60 | uint32_t header = (address << 1) | (rw & 0x01); 61 | uint32_t result = (uint32_t)-1; 62 | uint32_t txrx_data = *data; 63 | 64 | if(rw == WchSwioFlasher_SWIO_Write) { 65 | uint8_t parity = calcParity(address, rw, txrx_data); 66 | #ifdef SWIO_TXRX_DEBUG_MSG_ENABLE 67 | FURI_LOG_D(TAG, "TX to %02X (parity %d): " FMT_4HEX, address, parity, _UI(*data)); 68 | #endif 69 | 70 | if(parity) { 71 | header |= 0x80000000; 72 | } 73 | } 74 | 75 | FURI_CRITICAL_ENTER(); 76 | 77 | #ifdef SWIO_TRIGGER_OUT_ENABLE 78 | furi_hal_gpio_write(&SWI_TRIG_GPIO, false); 79 | #endif 80 | // ----------------------------------- 81 | // r2 - data buffer (store header, tx and rx data) 82 | // r3 - timeout counter (rx only) 83 | // r4 - tmp register (eg.: load & store to IO) 84 | // r5 - io port base 85 | // r6 - store MODER state (input/output settings) 86 | // r8 - bit counter 87 | asm volatile( 88 | "nop \n" // for better visibility of this code in dissasembly :) 89 | 90 | "MOV r2, %[header]\n" // prepare header 91 | "AND %[result],%[result], #0x80000000 \n" // prepare result (without obstructing tx parity - gcc share register for header and result) 92 | 93 | "movw r5, :lower16:%[port_base] \n" // store port base 94 | "movt r5, :upper16:%[port_base] \n" 95 | 96 | "LDR r6, [r5,%[moder_offset]] \n" // load current MODER 97 | "ORR r6, r6, %[mode_out_mask] \n" // prepare MODER to IO pin as output 98 | "STR r6, [r5,%[moder_offset]] \n" // set IO pin as output 99 | 100 | // =========================================================== 101 | // --- start bit --- 102 | 103 | "write_sb: \n" // write start bit procedure 104 | 105 | // drive IO pin to 'log0' 106 | "MOV r4, %[reset_mask] \n" 107 | "STR r4, [r5, %[bssr_offset]] \n" // set pin to low 108 | 109 | "BL delay_sb_l \n" 110 | "MOV r4, %[set_mask] \n" 111 | "STR r4, [r5, %[bssr_offset]] \n" // set pin to high 112 | "BL delay_sb_h \n" 113 | 114 | // --- start bit completed --- 115 | // =========================================================== 116 | // --- write address and r/w bit start 117 | 118 | "MOV r8, #7\n" // setup counter 119 | 120 | "tx_hdr_loop: \n" 121 | 122 | "MOV r4, %[reset_mask] \n" 123 | "STR r4, [r5, %[bssr_offset]] \n" // set pin to low 124 | //"BL delay_ad_l \n" // wait in line low level 125 | "NOP \n" 126 | "NOP \n" 127 | "NOP \n" 128 | "NOP \n" 129 | 130 | "TST r2, #0x80 \n" // Test bit0 for value 131 | "BNE write_hdr_high\n" 132 | 133 | "BL delay \n" // wait log0 134 | "BL delay \n" 135 | 136 | "write_hdr_high: \n" 137 | "MOV r4, %[set_mask] \n" 138 | "STR r4, [r5, %[bssr_offset]] \n" // set pin to high 139 | 140 | "NOP \n" // delay high 141 | 142 | "NOP \n" 143 | "NOP \n" 144 | 145 | "NOP \n" 146 | "NOP \n" 147 | "NOP \n" 148 | "NOP \n" 149 | 150 | //"BL delay_ad_h \n" // delay high 151 | 152 | "LSL r2, r2, #1\n" // shift data 153 | "SUBS r8, r8, #1 \n" // decrement counter 154 | "BPL tx_hdr_loop \n" // loop if counter != 0 155 | 156 | // --- write address and r/w bit completed 157 | // =========================================================== 158 | // --- determine read/write 159 | 160 | "TST r2, #0x100 \n" // Test if transfer is write 161 | "BNE tx_data \n" 162 | "B rx_data \n" 163 | 164 | // --- determine read/write completed 165 | // =========================================================== 166 | // --- write data 167 | 168 | "tx_data: \n" 169 | "MOV r2, %[data] \n" // load data 170 | "MOV r8, #32 \n" // setup counter 171 | 172 | "tx_loop: \n" 173 | 174 | "MOV r4, %[reset_mask] \n" 175 | "STR r4, [r5,%[bssr_offset]] \n" // set pin to low 176 | 177 | // magic delay 178 | "NOP \n" 179 | "NOP \n" 180 | "NOP \n" 181 | "NOP \n" 182 | "NOP \n" 183 | "NOP \n" 184 | "NOP \n" 185 | 186 | "TST r2, #0x80000000 \n" // Test bit0 for value 187 | "BNE write_high\n" 188 | 189 | // magic delay 190 | "BL delay \n" // wait log0 191 | "BL delay \n" 192 | 193 | "write_high: \n" 194 | "MOV r4, %[set_mask] \n" 195 | "STR r4, [r5,%[bssr_offset]] \n" // set pin to high 196 | 197 | // magic delay 198 | "NOP \n" 199 | "NOP \n" 200 | "NOP \n" 201 | "NOP \n" 202 | 203 | // move to next tx bit 204 | "LSL r2, r2, #1\n" // shift data 205 | 206 | // handle parity bit 207 | "CMP r8, #0x01 \n" // test if next bit will be parity 208 | "BNE tx_loop_dec_cnt\n" // branch if next bit != parity bit 209 | "AND r2, %[header], 0x80000000 \n" // load parity bit 210 | 211 | "tx_loop_dec_cnt: \n" 212 | "SUBS r8, r8, #1 \n" // decrement counter 213 | "BPL tx_loop\n" // loop if counter != 0 214 | 215 | "B exit \n" // End transder 216 | 217 | // --- write data completed 218 | // =========================================================== 219 | // --- read data 220 | 221 | "rx_data: \n" 222 | 223 | // prepare counter and data regiter 224 | "MOV r8, #32\n" // setup bit counter 225 | "MOV r2, #0 \n" // Set empty value to data register 226 | 227 | // bit rx main loop 228 | "rx_loop: \n" 229 | "LSL r2, r2, #1\n" // shift data 230 | 231 | // drive IO output with log0 for start rx 232 | "ORR r6, r6, %[mode_out_mask] \n" // pin output mask 233 | "STR r6, [r5,%[moder_offset]] \n" // set IO as output 234 | "MOV r4, %[reset_mask] \n" // set to log0 IO mask 235 | "STR r4, [r5,%[bssr_offset]] \n" // set IO to log0 236 | 237 | // magic log0 delay 238 | "NOP \n" 239 | "NOP \n" 240 | "NOP \n" 241 | //"NOP \n" 242 | 243 | // set-up delay counter 244 | "MOV r3, #0xffff \n" // Reset timeout TODO: proper value 245 | 246 | // start listening 247 | "AND r6, r6, %[mode_in_mask] \n" // pin input mask 248 | "STR r6, [r5,%[moder_offset]] \n" // set IO as input 249 | "MOV r4, %[set_mask] \n" // set to log1 IO mask 250 | "STR r4, [r5,%[bssr_offset]] \n" // set IO to log1 251 | 252 | // magic delay because of capacity of wires ... 253 | "NOP \n" 254 | "NOP \n" 255 | "NOP \n" 256 | "NOP \n" 257 | "NOP \n" 258 | "NOP \n" 259 | "NOP \n" 260 | "NOP \n" 261 | 262 | // Test RX pin if is log0 or log1 there 263 | "LDR r4, [r5,%[idr_offset]] \n" // read pin 264 | "TST r4, %[set_mask] \n" // Test pin state 265 | "BEQ rx_wait_for_edge\n" // pin is in log0, skip store 'one' 266 | 267 | // Store bit 268 | "ORR r2,r2, #1 \n" // set 'one' to data reg 269 | "B rx_loop_continue \n" // in case of log1, skip 'rx_wait_for_edge' (we have't time for it) 270 | 271 | // 'zero' bit detected, we need to wait for rising edge of signal for continue 272 | "rx_wait_for_edge: \n" 273 | "SUBS r3, r3, #1 \n" // decrement timeout counter 274 | "BEQ exit_err_reset\n" // error when timeout counter == 0 275 | "LDR r4, [r5,%[idr_offset]] \n" // read IO pin 276 | "TST r4, %[set_mask] \n" // Test IO pin state 277 | "BEQ rx_wait_for_edge\n" // loop if IO pin != log1 278 | 279 | // after rising edge, continue with rx 280 | "rx_loop_continue: \n" 281 | 282 | "CMP r8, #0x01 \n" // test if next bit will be parity 283 | "BNE rx_loop_dec_cnt\n" // branch if next bit != parity bit 284 | "MOV %[data], r2 \n" // store data 285 | 286 | "rx_loop_dec_cnt: \n" 287 | "SUBS r8, r8, #1 \n" // decrement counter 288 | "BPL rx_loop \n" // loop if counter != 0 289 | 290 | // rx done, store parity 291 | "AND %[result], r2, #0x01 \n" // store parity to result (bit0) 292 | 293 | // --- read data completed 294 | // =========================================================== 295 | // --- read parity 296 | 297 | "B exit \n" 298 | 299 | // --- read data completed 300 | // =========================================================== 301 | // --- misc utils 302 | 303 | "delay: \n" 304 | "nop \n" 305 | "nop \n" 306 | 307 | "nop \n" 308 | "nop \n" 309 | "nop \n" 310 | "nop \n" 311 | "nop \n" 312 | "nop \n" 313 | "nop \n" 314 | 315 | "nop \n" 316 | "nop \n" 317 | "nop \n" 318 | 319 | "nop \n" 320 | "nop \n" 321 | "delay_sb_h: \n" 322 | "nop \n" 323 | "nop \n" 324 | 325 | "delay_sb_l: \n" 326 | "delay_rx_l: \n" 327 | "nop \n" 328 | "delay_ad_l: \n" 329 | "delay_tx_l: \n" 330 | "BX LR \n" 331 | 332 | // --- misc utils end 333 | // =========================================================== 334 | // --- error handlers 335 | 336 | "exit_err_reset: \n" 337 | "MOV %[result],0x1000 \n" 338 | 339 | // --- error handlers end 340 | // =========================================================== 341 | // --- exit ... 342 | 343 | "exit: \n" 344 | : [result] "=r"(result), [data] "=r"(txrx_data) 345 | : [header] "r"(header), 346 | "1"(txrx_data), 347 | [port_base] "i"(SWI_PORT_BASE), 348 | [bssr_offset] "i"(SWI_PORT_BSSR_OFFSET), 349 | [idr_offset] "i"(SWI_PORT_IDR_OFFSET), 350 | [moder_offset] "i"(SWI_PORT_MODER_OFFSET), 351 | [set_mask] "i"(SWI_SET_MASK), 352 | [reset_mask] "i"(SWI_RESET_MASK), 353 | [mode_out_mask] "i"(SWI_SET_MODE_OUTPUT_MASK), 354 | [mode_in_mask] "i"(SWI_SET_MODE_INPUT_MASK) 355 | 356 | : "r2", "r3", "r4", "r5", "r6", "r8"); 357 | 358 | #ifdef SWIO_TRIGGER_OUT_ENABLE 359 | furi_hal_gpio_write(&SWI_TRIG_GPIO, true); 360 | #endif 361 | 362 | FURI_CRITICAL_EXIT(); 363 | 364 | if(rw == WchSwioFlasher_SWIO_Read) { 365 | *data = txrx_data; 366 | uint8_t parity = result & 0x01; 367 | 368 | #ifdef SWIO_TXRX_DEBUG_MSG_ENABLE 369 | FURI_LOG_D(TAG, "RX from %02X (parity %d): " FMT_4HEX, address, parity, _UI(*data)); 370 | #endif 371 | if(parity != calcParity(address, rw, txrx_data)) { 372 | return WchSwioFlasher_Error_SwdParityCheckError; 373 | } 374 | } 375 | 376 | if((result & 0x1000) != 0) { 377 | FURI_LOG_E(TAG, "RX Error"); 378 | return WchSwioFlasher_Error_SwdResetDetected; 379 | } 380 | 381 | return WchSwioFlasher_Ok; 382 | } 383 | #pragma GCC pop_options 384 | -------------------------------------------------------------------------------- /ch32v_flipper_flasher.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include "ch32v_flipper_flasher.h" 27 | 28 | #include 29 | #include "utils.h" 30 | 31 | #define TAG "WCH_CFF" 32 | 33 | struct WchSwioFlasher_Ch32vFlipperFlasher { 34 | struct { 35 | WchSwioFlasher_CFF_Callback cb; 36 | void* cb_context; 37 | } callbacks; 38 | struct { 39 | WchSwioFlasher_RiscVDebug* debugger; 40 | WchSwioFlasher_WchFlasher* flasher; 41 | } helpers; 42 | 43 | struct { 44 | WchSwioFlasher_CFF_ChipInfo chip_info; 45 | WchSwioFlasher_CFF_EraseChip erase_chip; 46 | WchSwioFlasher_CFF_WriteChip write_chip; 47 | } history; 48 | 49 | FuriThread* worker_thread; 50 | FuriMessageQueue* event_queue; 51 | }; 52 | 53 | typedef struct { 54 | enum { 55 | Ev_ChipInfo = 0, 56 | Ev_OpenFile, 57 | Ev_EraseChip, 58 | Ev_WriteChip, 59 | Ev_Stop, 60 | } type; 61 | } Event; 62 | 63 | static void fire_event(WchSwioFlasher_Ch32vFlipperFlasher* handle, ViewFlasher_Action type) { 64 | if(handle->callbacks.cb) { 65 | handle->callbacks.cb(handle->callbacks.cb_context, type); 66 | } 67 | } 68 | 69 | static WchSwioFlasher_CFF_ResultStatus map_error_status(WchSwioFlasher_Error err) { 70 | switch(err) { 71 | case WchSwioFlasher_Ok: 72 | return VWchSwioFlasher_CFF_Ok; 73 | case WchSwioFlasher_Error_Timeout: 74 | case WchSwioFlasher_Error_SwdResetDetected: 75 | case WchSwioFlasher_Error_SwdParityCheckError: 76 | case WchSwioFlasher_Error_TargetNotKnown: 77 | case WchSwioFlasher_Error_TargetInInvalidState: 78 | case WchSwioFlasher_Error_ProgramNotFinishedYet: 79 | case WchSwioFlasher_Error_ProgramRunError: 80 | case WchSwioFlasher_Error_DirtyRegs: { 81 | return VWchSwioFlasher_CFF_ChipNotConnected; 82 | } 83 | default: 84 | return VWchSwioFlasher_CFF_UnknownError; 85 | } 86 | } 87 | 88 | static void handle_chip_info(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 89 | UNUSED(handle); 90 | FURI_LOG_D(TAG, "handle_chip_info"); 91 | 92 | WchSwioFlasher_CFF_ChipInfo* ci = &handle->history.chip_info; 93 | 94 | WchSwioFlasher_RiscVDebug_ChipInfo info = { 95 | .esig_uniid = {0, 0, 0}, 96 | .flash_size = 0, 97 | }; 98 | 99 | WchSwioFlasher_Error err = 100 | WchSwioFlasher_RiscVDebug_get_chip_info(handle->helpers.debugger, &info); 101 | if(err != WchSwioFlasher_Ok) { 102 | LOG_ERR(err); 103 | } 104 | ci->status = map_error_status(err); 105 | ci->flash_size = info.flash_size; 106 | memcpy(ci->esig_uniid, info.esig_uniid, sizeof(ci->esig_uniid)); 107 | 108 | fire_event(handle, WchSwioFlasher_CFF_ChipInfoCompleted); 109 | } 110 | 111 | static void handle_erase_chip(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 112 | UNUSED(handle); 113 | FURI_LOG_D(TAG, "handle_erase_chip"); 114 | 115 | WchSwioFlasher_CFF_EraseChip* ec = &handle->history.erase_chip; 116 | WchSwioFlasher_Error err = WchSwioFlasher_WchFlasher_wipe_chip(handle->helpers.flasher); 117 | if(err != WchSwioFlasher_Ok) { 118 | LOG_ERR(err); 119 | } 120 | ec->status = map_error_status(err); 121 | 122 | fire_event(handle, WchSwioFlasher_CFF_EraseChipCompleted); 123 | } 124 | 125 | static void handle_write_chip(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 126 | WchSwioFlasher_CFF_WriteChip* wc = &handle->history.write_chip; 127 | 128 | FURI_LOG_D(TAG, "handle_write_chip"); 129 | 130 | WchSwioFlasher_Error err; 131 | 132 | Storage* storage = furi_record_open(RECORD_STORAGE); 133 | File* file = storage_file_alloc(storage); 134 | 135 | // Open file 136 | FURI_LOG_D(TAG, "Open bin file to write"); 137 | if(!storage_file_open(file, handle->history.write_chip.path, FSAM_READ, FSOM_OPEN_EXISTING)) { 138 | FURI_LOG_E(TAG, "Failed to open bin file"); 139 | wc->status = WchSwioFlasher_CFF_UnableToOpenFile; 140 | storage_file_free(file); 141 | furi_record_close(RECORD_STORAGE); 142 | return; 143 | } 144 | 145 | // Get file size 146 | FURI_LOG_D(TAG, "Get file size"); 147 | uint64_t file_size = 0; 148 | if(storage_file_seek(file, 32 * 1024, false)) { // TODO: proper seek to end 149 | file_size = storage_file_tell(file); 150 | storage_file_seek(file, 0, true); 151 | } 152 | FURI_LOG_D(TAG, "File size is %llu", file_size); 153 | 154 | // Erase chip 155 | FURI_LOG_D(TAG, "Wipe chip"); 156 | err = WchSwioFlasher_WchFlasher_wipe_chip(handle->helpers.flasher); 157 | if(err != WchSwioFlasher_Ok) { 158 | LOG_ERR(err); 159 | 160 | storage_file_close(file); 161 | storage_file_free(file); 162 | furi_record_close(RECORD_STORAGE); 163 | 164 | wc->status = map_error_status(err); 165 | fire_event(handle, WchSwioFlasher_CFF_WriteChipCompleted); 166 | return; 167 | } 168 | 169 | wc->percent = 0.1; 170 | 171 | // Load data 172 | FURI_LOG_D(TAG, "Flash data"); 173 | 174 | if(file_size > 32 * 1024 || file_size == 0) { 175 | storage_file_close(file); 176 | storage_file_free(file); 177 | furi_record_close(RECORD_STORAGE); 178 | 179 | wc->status = WchSwioFlasher_CFF_EmptyOrTooBigFile; 180 | fire_event(handle, WchSwioFlasher_CFF_WriteChipCompleted); 181 | 182 | return; 183 | } 184 | 185 | char* data = malloc(file_size); 186 | uint32_t address = 0; 187 | uint32_t to_write = 0; 188 | 189 | // Write page 190 | 191 | to_write = storage_file_read(file, data, file_size); 192 | if(to_write == file_size) { 193 | FURI_LOG_D(TAG, "Write %lu on " FMT_4HEX, to_write, _UI(address)); 194 | err = WchSwioFlasher_WchFlasher_write_flash( 195 | handle->helpers.flasher, address, data, to_write); 196 | 197 | address += to_write; 198 | 199 | if(err != WchSwioFlasher_Ok) { 200 | LOG_ERR(err); 201 | } 202 | } 203 | 204 | FURI_LOG_D(TAG, "Writed %lu of %llu", address, file_size); 205 | wc->percent = 0.9; 206 | 207 | free(data); 208 | storage_file_close(file); 209 | storage_file_free(file); 210 | furi_record_close(RECORD_STORAGE); 211 | 212 | if(err != WchSwioFlasher_Ok) { 213 | LOG_ERR(err); 214 | wc->status = map_error_status(err); 215 | fire_event(handle, WchSwioFlasher_CFF_WriteChipCompleted); 216 | return; 217 | } 218 | 219 | // Finally start chip 220 | FURI_LOG_D(TAG, "Reset to run"); 221 | err = WchSwioFlasher_RiscVDebug_reset( 222 | handle->helpers.debugger, WchSwioFlasher_RVD_ResetToRunNoCheck); 223 | wc->status = map_error_status(err); 224 | 225 | wc->percent = 1; 226 | 227 | fire_event(handle, WchSwioFlasher_CFF_WriteChipCompleted); 228 | } 229 | 230 | static int32_t cff_create_thread_callback(void* context) { 231 | furi_assert(context); 232 | WchSwioFlasher_Ch32vFlipperFlasher* handle = context; 233 | 234 | for(;;) { 235 | Event event; 236 | FuriStatus status = furi_message_queue_get(handle->event_queue, &event, FuriWaitForever); 237 | if(status != FuriStatusOk) { 238 | FURI_LOG_E(TAG, "Message queue get error: %d", status); 239 | furi_delay_ms(100); 240 | continue; 241 | } 242 | 243 | if(event.type == Ev_Stop) { 244 | break; 245 | } 246 | 247 | switch(event.type) { 248 | case Ev_ChipInfo: 249 | handle_chip_info(handle); 250 | break; 251 | case Ev_EraseChip: 252 | handle_erase_chip(handle); 253 | break; 254 | case Ev_WriteChip: 255 | handle_write_chip(handle); 256 | break; 257 | default: 258 | break; 259 | } 260 | } 261 | 262 | return 0; 263 | } 264 | 265 | WchSwioFlasher_Ch32vFlipperFlasher* WchSwioFlasher_Ch32vFlipperFlasher_create( 266 | WchSwioFlasher_RiscVDebug* debugger, 267 | WchSwioFlasher_WchFlasher* flasher) { 268 | WchSwioFlasher_Ch32vFlipperFlasher* handle = 269 | malloc(sizeof(WchSwioFlasher_Ch32vFlipperFlasher)); 270 | 271 | handle->helpers.debugger = debugger; 272 | handle->helpers.flasher = flasher; 273 | 274 | handle->event_queue = furi_message_queue_alloc(10, sizeof(Event)); 275 | handle->worker_thread = 276 | furi_thread_alloc_ex("MusicWorker", 1024, cff_create_thread_callback, handle); 277 | 278 | return handle; 279 | } 280 | 281 | static void send_stop(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 282 | Event event = { 283 | .type = Ev_Stop, 284 | }; 285 | 286 | FuriStatus result = furi_message_queue_put(handle->event_queue, &event, 0); 287 | if(result != FuriStatusOk) { 288 | FURI_LOG_E(TAG, "Message queue put error: %d", result); 289 | } 290 | } 291 | 292 | void WchSwioFlasher_Ch32vFlipperFlasher_detach(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 293 | if(furi_thread_get_state(handle->worker_thread) != FuriThreadStateStopped) { 294 | send_stop(handle); 295 | furi_thread_join(handle->worker_thread); 296 | } 297 | 298 | if(handle->history.write_chip.path != NULL) { 299 | free(handle->history.write_chip.path); 300 | handle->history.write_chip.path = NULL; 301 | } 302 | } 303 | 304 | void WchSwioFlasher_Ch32vFlipperFlasher_attach(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 305 | // Just for sure 306 | WchSwioFlasher_Ch32vFlipperFlasher_detach(handle); 307 | 308 | handle->history.chip_info.status = VWchSwioFlasher_CFF_NoData; 309 | 310 | furi_thread_start(handle->worker_thread); 311 | } 312 | 313 | void WchSwioFlasher_Ch32vFlipperFlasher_destroy(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 314 | WchSwioFlasher_Ch32vFlipperFlasher_detach(handle); 315 | 316 | furi_thread_free(handle->worker_thread); 317 | furi_message_queue_free(handle->event_queue); 318 | 319 | free(handle); 320 | } 321 | 322 | void WchSwioFlasher_Ch32vFlipperFlasher_event_callback( 323 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 324 | WchSwioFlasher_CFF_Callback cb, 325 | void* cb_context) { 326 | handle->callbacks.cb = cb; 327 | handle->callbacks.cb_context = cb_context; 328 | } 329 | 330 | void WchSwioFlasher_Ch32vFlipperFlasher_chip_info_data( 331 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 332 | WchSwioFlasher_CFF_ChipInfo* data) { 333 | memcpy(data, &handle->history.chip_info, sizeof(WchSwioFlasher_CFF_ChipInfo)); 334 | } 335 | 336 | void WchSwioFlasher_Ch32vFlipperFlasher_chip_info(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 337 | Event event = { 338 | .type = Ev_ChipInfo, 339 | }; 340 | 341 | FuriStatus result = furi_message_queue_put(handle->event_queue, &event, 0); 342 | if(result != FuriStatusOk) { 343 | FURI_LOG_E(TAG, "Message queue put error: %d", result); 344 | } 345 | } 346 | 347 | void WchSwioFlasher_Ch32vFlipperFlasher_erase_chip_data( 348 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 349 | WchSwioFlasher_CFF_EraseChip* data) { 350 | memcpy(data, &handle->history.erase_chip, sizeof(WchSwioFlasher_CFF_EraseChip)); 351 | } 352 | 353 | void WchSwioFlasher_Ch32vFlipperFlasher_erase_chip(WchSwioFlasher_Ch32vFlipperFlasher* handle) { 354 | Event event = { 355 | .type = Ev_EraseChip, 356 | }; 357 | 358 | FuriStatus result = furi_message_queue_put(handle->event_queue, &event, 0); 359 | if(result != FuriStatusOk) { 360 | FURI_LOG_E(TAG, "Message queue put error: %d", result); 361 | } 362 | } 363 | 364 | void WchSwioFlasher_Ch32vFlipperFlasher_write_chip( 365 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 366 | char* path) { 367 | Event event = { 368 | .type = Ev_WriteChip, 369 | }; 370 | 371 | if(handle->history.write_chip.path != NULL) { 372 | free(handle->history.write_chip.path); 373 | handle->history.write_chip.path = NULL; 374 | } 375 | 376 | uint32_t len = strlen(path); 377 | handle->history.write_chip.path = malloc(len + 1); 378 | strcpy(handle->history.write_chip.path, path); 379 | 380 | FuriStatus result = furi_message_queue_put(handle->event_queue, &event, 0); 381 | if(result != FuriStatusOk) { 382 | FURI_LOG_E(TAG, "Message queue put error: %d", result); 383 | } 384 | } 385 | 386 | void WchSwioFlasher_Ch32vFlipperFlasher_write_chip_data( 387 | WchSwioFlasher_Ch32vFlipperFlasher* handle, 388 | WchSwioFlasher_CFF_WriteChip* data) { 389 | memcpy(data, &handle->history.write_chip, sizeof(WchSwioFlasher_CFF_WriteChip)); 390 | } -------------------------------------------------------------------------------- /nhc_link042_emulator.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | //#include 30 | #include 31 | #include 32 | #include 33 | #include "usb.h" 34 | #include "usb_hid.h" 35 | 36 | #include "utils.h" 37 | #include "errors.h" 38 | #include "nhc_link042_emulator.h" 39 | #include "config.h" 40 | 41 | #define EP_RX 0x01 42 | #define EP_RX_SIZE 96 43 | 44 | #define EP_TX 0x81 45 | #define EP_TX_SIZE 64 46 | 47 | #define TAG "NCHL_EMU" 48 | 49 | // enum and definition from furi_hal_usb_i.h which is missing in 0.92.2 uFBT build stack 50 | #define USB_EP0_SIZE 8 51 | enum UsbDevDescStr { 52 | UsbDevLang = 0, 53 | UsbDevManuf = 1, 54 | UsbDevProduct = 2, 55 | UsbDevSerial = 3, 56 | }; 57 | 58 | static FuriStreamBuffer* rx_stream; 59 | //static FuriStreamBuffer* tx_stream; 60 | static FuriThread* worker_thread; 61 | static usbd_device* current_dev; 62 | static FuriSemaphore* tx_semaphore; 63 | 64 | typedef enum { 65 | WorkerEvtStop = (1 << 0), 66 | WorkerEvtRxDone = (1 << 1), 67 | } WorkerEvtFlags; 68 | 69 | #define WORKER_ALL_EVENTS (WorkerEvtStop | WorkerEvtRxDone) 70 | 71 | // Device descriptor 72 | static struct usb_device_descriptor builk_device_desc = { 73 | .bLength = sizeof(struct usb_device_descriptor), 74 | .bDescriptorType = USB_DTYPE_DEVICE, 75 | .bcdUSB = VERSION_BCD(2, 0, 0), 76 | .bDeviceClass = USB_CLASS_PER_INTERFACE, 77 | .bDeviceSubClass = USB_SUBCLASS_NONE, 78 | .bDeviceProtocol = USB_PROTO_NONE, 79 | .bMaxPacketSize0 = USB_EP0_SIZE, 80 | .idVendor = 0x1986, 81 | .idProduct = 0x0034, 82 | .bcdDevice = VERSION_BCD(1, 0, 0), 83 | .iManufacturer = UsbDevManuf, 84 | .iProduct = UsbDevProduct, 85 | .iSerialNumber = NO_DESCRIPTOR, 86 | .bNumConfigurations = 1, 87 | }; 88 | 89 | struct BulkIadDescriptor { 90 | struct usb_interface_descriptor data; 91 | struct usb_endpoint_descriptor data_eprx; 92 | struct usb_endpoint_descriptor data_eptx; 93 | }; 94 | 95 | struct BulkConfigDescriptor { 96 | struct usb_config_descriptor config; 97 | struct BulkIadDescriptor iad_0; 98 | } __attribute__((packed)); 99 | 100 | static const struct BulkConfigDescriptor bulk_cfg_desc = { 101 | .config = 102 | { 103 | .bLength = sizeof(struct usb_config_descriptor), 104 | .bDescriptorType = USB_DTYPE_CONFIGURATION, 105 | .wTotalLength = sizeof(struct BulkConfigDescriptor), 106 | .bNumInterfaces = 1, 107 | 108 | .bConfigurationValue = 1, 109 | .iConfiguration = NO_DESCRIPTOR, 110 | .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, 111 | .bMaxPower = USB_CFG_POWER_MA(100), 112 | }, 113 | .iad_0 = 114 | { 115 | .data = 116 | { 117 | .bLength = sizeof(struct usb_interface_descriptor), 118 | .bDescriptorType = USB_DTYPE_INTERFACE, 119 | .bInterfaceNumber = 0, 120 | .bAlternateSetting = 0, 121 | .bNumEndpoints = 2, 122 | .bInterfaceClass = USB_CLASS_PER_INTERFACE, 123 | .bInterfaceSubClass = USB_SUBCLASS_NONE, 124 | .bInterfaceProtocol = USB_PROTO_NONE, 125 | .iInterface = NO_DESCRIPTOR, 126 | }, 127 | .data_eprx = 128 | { 129 | .bLength = sizeof(struct usb_endpoint_descriptor), 130 | .bDescriptorType = USB_DTYPE_ENDPOINT, 131 | .bEndpointAddress = EP_RX, 132 | .bmAttributes = USB_EPTYPE_BULK, 133 | .wMaxPacketSize = EP_RX_SIZE, 134 | .bInterval = 0x01, 135 | }, 136 | .data_eptx = 137 | { 138 | .bLength = sizeof(struct usb_endpoint_descriptor), 139 | .bDescriptorType = USB_DTYPE_ENDPOINT, 140 | .bEndpointAddress = EP_TX, 141 | .bmAttributes = USB_EPTYPE_BULK, 142 | .wMaxPacketSize = EP_TX_SIZE, 143 | .bInterval = 0x01, 144 | }, 145 | }, 146 | }; 147 | 148 | static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc."); 149 | static const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("WCH SWIO Flasher"); 150 | //static const struct usb_string_descriptor dev_serial_desc = USB_STRING_DESC("s2-ch32xx-pgm-v0"); 151 | 152 | // Control requests handler 153 | usbd_respond ep_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { 154 | UNUSED(callback); 155 | UNUSED(dev); 156 | 157 | switch(req->bRequest) { 158 | default: 159 | return usbd_fail; 160 | } 161 | 162 | return usbd_fail; 163 | } 164 | 165 | static void txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { 166 | if(event == usbd_evt_eptx) { 167 | // TX event 168 | //if(furi_stream_buffer_bytes_available(tx_stream) > 0) { 169 | // char buff[64]; 170 | // if(size_t len = furi_stream_buffer_receive(tx_stream, buff, sizeof(buff), 0)) { 171 | // usbd_ep_write(dev, ep, buff, ) 172 | // } 173 | //} 174 | furi_semaphore_release(tx_semaphore); 175 | } else if(ep == EP_RX) { 176 | char buff[128]; 177 | uint32_t readed = usbd_ep_read(dev, ep, buff, sizeof(buff)); 178 | if(readed > 0) { 179 | furi_stream_buffer_send(rx_stream, buff, readed, 0); 180 | furi_thread_flags_set(furi_thread_get_id(worker_thread), WorkerEvtRxDone); 181 | } 182 | } 183 | } 184 | 185 | // Configure endpoints 186 | usbd_respond ep_config(usbd_device* dev, uint8_t cfg) { 187 | switch(cfg) { 188 | case 0: 189 | // deconfiguring device 190 | usbd_ep_deconfig(dev, EP_RX); 191 | usbd_reg_endpoint(dev, EP_RX, 0); 192 | usbd_ep_deconfig(dev, EP_TX); 193 | usbd_reg_endpoint(dev, EP_TX, 0); 194 | return usbd_ack; 195 | case 1: 196 | // configuring device 197 | usbd_ep_config(dev, EP_RX, USB_EPTYPE_BULK, EP_RX_SIZE); 198 | usbd_reg_endpoint(dev, EP_RX, txrx_ep_callback); 199 | //usbd_ep_write(dev, EP_RX, 0, 0); 200 | 201 | usbd_ep_config(dev, EP_TX, USB_EPTYPE_BULK, EP_TX_SIZE); 202 | usbd_reg_endpoint(dev, EP_TX, txrx_ep_callback); 203 | //usbd_ep_write(dev, EP_TX, 0, 0); 204 | return usbd_ack; 205 | default: 206 | return usbd_fail; 207 | } 208 | } 209 | 210 | static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { 211 | UNUSED(intf); 212 | FuriHalUsbHidConfig* cfg = (FuriHalUsbHidConfig*)ctx; 213 | UNUSED(cfg); 214 | 215 | current_dev = dev; 216 | usbd_reg_config(dev, ep_config); 217 | usbd_reg_control(dev, ep_control); 218 | 219 | usbd_connect(dev, true); 220 | } 221 | 222 | static void hid_deinit(usbd_device* dev) { 223 | UNUSED(dev); 224 | current_dev = NULL; 225 | usbd_reg_config(dev, NULL); 226 | usbd_reg_control(dev, NULL); 227 | } 228 | 229 | static void hid_on_wakeup(usbd_device* dev) { 230 | UNUSED(dev); 231 | } 232 | 233 | static void hid_on_suspend(usbd_device* dev) { 234 | UNUSED(dev); 235 | } 236 | 237 | static FuriHalUsbInterface bulk_if = { 238 | .dev_descr = &builk_device_desc, 239 | .cfg_descr = (void*)&bulk_cfg_desc, 240 | .str_manuf_descr = (void*)&dev_manuf_desc, 241 | .str_prod_descr = (void*)&dev_prod_desc, 242 | //.str_serial_descr = (void*)&dev_serial_desc, 243 | .init = hid_init, 244 | .deinit = hid_deinit, 245 | .suspend = hid_on_suspend, 246 | .wakeup = hid_on_wakeup, 247 | }; 248 | 249 | WchSwioFlasher_NhcLink042Emu* 250 | WchSwioFlasher_NhcLink042Emu_create(WchSwioFlasher_MinichlinkDebugger* debugger) { 251 | WchSwioFlasher_NhcLink042Emu* handle = malloc(sizeof(WchSwioFlasher_NhcLink042Emu)); 252 | handle->debugger = debugger; 253 | handle->mutex = furi_mutex_alloc(FuriMutexTypeNormal); 254 | return handle; 255 | } 256 | 257 | static WchSwioFlasher_Error rxData(WchSwioFlasher_NhcLink042Emu* handle) { 258 | size_t len = 0; 259 | char buff[64]; 260 | static uint8_t cnt = 0; 261 | 262 | while(1) { 263 | len = furi_stream_buffer_receive(rx_stream, buff, sizeof(buff), 0); 264 | if(len == 0) { 265 | break; 266 | } 267 | /* 268 | FURI_LOG_D( 269 | TAG, 270 | "RX: %02X %02X %02X %02X %02X %02X %02X %02X", 271 | buff[0], 272 | buff[1], 273 | buff[2], 274 | buff[3], 275 | buff[4], 276 | buff[5], 277 | buff[6], 278 | buff[7]); 279 | */ 280 | switch(buff[0]) { 281 | case 0xa0: 282 | // init 283 | #ifdef NCHLNK_TXRX_DEBUG_MSG_ENABLE 284 | FURI_LOG_D(TAG, "init session"); 285 | #endif 286 | WchSwioFlasher_MinichlinkDebugger_initSession(handle->debugger); 287 | break; 288 | case 0xa1: 289 | // exit 290 | #ifdef NCHLNK_TXRX_DEBUG_MSG_ENABLE 291 | FURI_LOG_D(TAG, "exit session"); 292 | #endif 293 | WchSwioFlasher_MinichlinkDebugger_endSession(handle->debugger); 294 | break; 295 | case 0xa6: { 296 | // delay 297 | uint8_t delay = *((uint32_t*)&buff[1]); 298 | #ifdef NCHLNK_TXRX_DEBUG_MSG_ENABLE 299 | FURI_LOG_D(TAG, "delay %d", delay); 300 | #endif 301 | WchSwioFlasher_MinichlinkDebugger_delayUs(handle->debugger, delay); 302 | break; 303 | } 304 | case 0xa2: { 305 | // read reg 306 | uint8_t addr = buff[1]; 307 | uint32_t reg_value; 308 | memset(buff, 0, sizeof(buff)); 309 | buff[7] = ++cnt; // TX counter (for easier solving problems with minichlink) 310 | if(WchSwioFlasher_MinichlinkDebugger_readRegister( 311 | handle->debugger, addr, ®_value) == WchSwioFlasher_Ok) { 312 | buff[0] = 0xa2; // restore command name 313 | *((uint32_t*)&buff[1]) = reg_value; 314 | #ifdef NCHLNK_TXRX_DEBUG_MSG_ENABLE 315 | FURI_LOG_D(TAG, "read reg 0x%02X = " FMT_4HEX, addr, _UI(reg_value)); 316 | #endif 317 | } else { 318 | FURI_LOG_E(TAG, "read reg 0x%02X failed", addr); 319 | } 320 | 321 | FURI_CRITICAL_ENTER(); 322 | usbd_device* dev = current_dev; 323 | FURI_CRITICAL_EXIT(); 324 | 325 | if(dev) { 326 | FuriStatus lock_status = furi_semaphore_acquire( 327 | tx_semaphore, furi_ms_to_ticks(NCHLNK_TX_SEMAPHORE_TIMEOUT)); 328 | if(lock_status != FuriStatusOk) { 329 | FURI_LOG_E(TAG, "unable to acquire TX semaphore " FMT_4HEX, _UI(lock_status)); 330 | break; 331 | } 332 | /* 333 | FURI_LOG_D( 334 | TAG, 335 | "TX: %02X %02X %02X %02X %02X %02X %02X %02X", 336 | buff[0], 337 | buff[1], 338 | buff[2], 339 | buff[3], 340 | buff[4], 341 | buff[5], 342 | buff[6], 343 | buff[7]); 344 | */ 345 | int32_t wr = usbd_ep_write(dev, EP_TX, buff, sizeof(buff)); 346 | if(wr != sizeof(buff)) { 347 | FURI_LOG_E(TAG, "unable to write data error " FMT_4HEX, _UI(wr)); 348 | } 349 | 350 | } else { 351 | FURI_LOG_E(TAG, "unable to write response, USB not ready"); 352 | } 353 | 354 | break; 355 | } 356 | case 0xa3: { 357 | // write reg 358 | uint8_t reg = buff[1]; 359 | uint32_t data = *((uint32_t*)&buff[2]); 360 | #ifdef NCHLNK_TXRX_DEBUG_MSG_ENABLE 361 | FURI_LOG_D(TAG, "write reg 0x%02X to " FMT_4HEX, reg, _UI(data)); 362 | #endif 363 | WchSwioFlasher_MinichlinkDebugger_writeRegister(handle->debugger, reg, data); 364 | break; 365 | } 366 | default: 367 | FURI_LOG_E(TAG, "unknown command 0x%02X", buff[0]); 368 | break; 369 | } 370 | } 371 | 372 | return WchSwioFlasher_Ok; 373 | } 374 | 375 | static int32_t emulator_worker(void* context) { 376 | furi_assert(context); 377 | WchSwioFlasher_NhcLink042Emu* handle = context; 378 | UNUSED(handle); 379 | 380 | while(1) { 381 | uint32_t events = 382 | furi_thread_flags_wait(WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); 383 | furi_check(!(events & FuriFlagError)); 384 | 385 | if(events & WorkerEvtStop) break; 386 | if(events & WorkerEvtRxDone) { 387 | rxData(handle); 388 | } 389 | } 390 | return 0; 391 | } 392 | 393 | WchSwioFlasher_Error WchSwioFlasher_NhcLink042Emu_attach(WchSwioFlasher_NhcLink042Emu* handle) { 394 | furi_check(furi_mutex_acquire(handle->mutex, FuriWaitForever) == FuriStatusOk); 395 | if(!handle->attached) { 396 | rx_stream = furi_stream_buffer_alloc(RVD_NCHLINKEMU_RX_BUFF_SIZE, 4); 397 | //tx_stream = furi_stream_buffer_alloc(RVD_NCHLINKEMU_TX_BUFF_SIZE, 4); 398 | 399 | furi_check(rx_stream != NULL); 400 | //furi_check(tx_stream != NULL); 401 | 402 | handle->usb_mode_prev = furi_hal_usb_get_config(); 403 | furi_hal_usb_unlock(); 404 | furi_check(furi_hal_usb_set_config(&bulk_if, NULL) == true); 405 | 406 | worker_thread = furi_thread_alloc_ex("NhcLink042EmuWorker", 1024, emulator_worker, handle); 407 | tx_semaphore = furi_semaphore_alloc(1, 1); 408 | 409 | handle->attached = 1; 410 | 411 | furi_thread_start(worker_thread); 412 | } 413 | furi_check(furi_mutex_release(handle->mutex) == FuriStatusOk); 414 | 415 | return WchSwioFlasher_Ok; 416 | } 417 | 418 | WchSwioFlasher_Error WchSwioFlasher_NhcLink042Emu_detach(WchSwioFlasher_NhcLink042Emu* handle) { 419 | furi_check(furi_mutex_acquire(handle->mutex, FuriWaitForever) == FuriStatusOk); 420 | if(handle->attached) { 421 | furi_thread_flags_set(furi_thread_get_id(worker_thread), WorkerEvtStop); 422 | furi_thread_join(worker_thread); 423 | furi_thread_free(worker_thread); 424 | 425 | furi_stream_buffer_free(rx_stream); 426 | //furi_stream_buffer_free(tx_stream); 427 | furi_semaphore_free(tx_semaphore); 428 | furi_check(furi_hal_usb_set_config(handle->usb_mode_prev, NULL)); 429 | handle->attached = 0; 430 | } 431 | furi_check(furi_mutex_release(handle->mutex) == FuriStatusOk); 432 | 433 | return WchSwioFlasher_Ok; 434 | } 435 | 436 | void WchSwioFlasher_NhcLink042Emu_destroy(WchSwioFlasher_NhcLink042Emu* handle) { 437 | furi_mutex_free(handle->mutex); 438 | 439 | free(handle); 440 | } -------------------------------------------------------------------------------- /helpers/riscv_debug.c: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2023 Vojtech Suk (https://github.com/sukvojte) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | /** 27 | This file is based on modified original from https://github.com/aappleby/PicoRVD 28 | */ 29 | 30 | #include 31 | #include "../utils.h" 32 | #include "riscv_debug.h" 33 | #include "risc_debug_inner.h" 34 | #include "debug_defines.h" 35 | #include "wch_flasher_inner.h" 36 | #include "programs.h" 37 | #include "../config.h" 38 | 39 | #define TAG "WSF_RVD" 40 | 41 | #ifdef RVD_CHECK_PROGRAM_EXECUTION_ENABLED 42 | static const char* abstractsc_cmderr[] = { 43 | "no-error", 44 | "cmd-exec-error", // Abstract command execution to write to command, abstractcs, abstractauto registers or read and write to data and progbuf registers 45 | "unsupported-cmd", // Does not support current abstract command 46 | "exception", // Execution of abstract command with exception 47 | "not-halted", // The microprocessor is not halted or unavailable and cannot execute abstract commands, 48 | "bus-error", // Bus error 49 | "parity-err", // Parity bit error during communication 50 | "other", // Other errors 51 | }; 52 | #endif 53 | 54 | WchSwioFlasher_RiscVDebug* WchSwioFlasher_RiscVDebug_create(WchSwioFlasher_SWIO* swio) { 55 | WchSwioFlasher_RiscVDebug* handle = malloc(sizeof(WchSwioFlasher_RiscVDebug)); 56 | handle->swio = swio; 57 | handle->reg_count = 16; 58 | handle->prog_will_clobber = 0; 59 | return handle; 60 | } 61 | 62 | void WchSwioFlasher_RiscVDebug_destroy(WchSwioFlasher_RiscVDebug* handle) { 63 | free(handle); 64 | } 65 | 66 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_init(WchSwioFlasher_RiscVDebug* handle) { 67 | for(int i = 0; i < 8; i++) { 68 | handle->prog_cache[i] = 0xDEADBEEF; 69 | } 70 | for(int i = 0; i < 32; i++) { 71 | handle->reg_cache[i] = 0xDEADBEEF; 72 | } 73 | handle->dirty_regs = 0; 74 | handle->cached_regs = 0; 75 | 76 | return WchSwioFlasher_Ok; 77 | } 78 | 79 | static inline WchSwioFlasher_Error 80 | set_dmcontrol(WchSwioFlasher_RiscVDebug* handle, uint32_t dmcontrol) { 81 | return WchSwioFlasher_SWIO_write(handle->swio, DM_DMCONTROL, dmcontrol); 82 | } 83 | 84 | static inline WchSwioFlasher_Error 85 | get_dmstatus(WchSwioFlasher_RiscVDebug* handle, uint32_t* dmstatus) { 86 | return WchSwioFlasher_SWIO_read(handle->swio, DM_DMSTATUS, dmstatus); 87 | } 88 | 89 | WchSwioFlasher_Error 90 | WchSwioFlasher_RiscVDebug_get_data0(WchSwioFlasher_RiscVDebug* handle, uint32_t* data) { 91 | return WchSwioFlasher_SWIO_read(handle->swio, DM_DATA0, data); 92 | } 93 | 94 | WchSwioFlasher_Error 95 | WchSwioFlasher_RiscVDebug_get_data1(WchSwioFlasher_RiscVDebug* handle, uint32_t* data) { 96 | return WchSwioFlasher_SWIO_read(handle->swio, DM_DATA1, data); 97 | } 98 | 99 | WchSwioFlasher_Error 100 | WchSwioFlasher_RiscVDebug_set_data0(WchSwioFlasher_RiscVDebug* handle, uint32_t data) { 101 | return WchSwioFlasher_SWIO_write(handle->swio, DM_DATA0, data); 102 | } 103 | 104 | WchSwioFlasher_Error 105 | WchSwioFlasher_RiscVDebug_set_data1(WchSwioFlasher_RiscVDebug* handle, uint32_t data) { 106 | return WchSwioFlasher_SWIO_write(handle->swio, DM_DATA1, data); 107 | } 108 | 109 | WchSwioFlasher_Error 110 | WchSwioFlasher_RiscVDebug_set_command(WchSwioFlasher_RiscVDebug* handle, RVD_COMMAND* command) { 111 | return WchSwioFlasher_SWIO_write(handle->swio, DM_COMMAND, command->raw); 112 | } 113 | 114 | WchSwioFlasher_Error 115 | WchSwioFlasher_RiscVDebug_get_abstractcs(WchSwioFlasher_RiscVDebug* handle, uint32_t* data) { 116 | return WchSwioFlasher_SWIO_read(handle->swio, DM_ABSTRACTCS, data); 117 | } 118 | 119 | WchSwioFlasher_Error 120 | WchSwioFlasher_RiscVDebug_set_abstractauto(WchSwioFlasher_RiscVDebug* handle, uint32_t data) { 121 | return WchSwioFlasher_SWIO_write(handle->swio, DM_ABSTRACTAUTO, data); 122 | } 123 | 124 | static WchSwioFlasher_Error wait_for_flag( 125 | WchSwioFlasher_RiscVDebug* handle, 126 | uint32_t address, 127 | uint32_t mask, 128 | uint32_t flag, 129 | uint32_t timeout) { 130 | UNUSED(handle); 131 | 132 | uint32_t tm = furi_get_tick(); 133 | uint32_t val = (uint32_t)-1; 134 | do { 135 | CHECK_ERR(WchSwioFlasher_SWIO_read(handle->swio, address, &val)); 136 | 137 | if((furi_get_tick() - tm) > timeout) { 138 | return LOG_ERR_M( 139 | WchSwioFlasher_Error_Timeout, 140 | "wait_for_flag timeout (flag " FMT_4HEX ", mask " FMT_4HEX 141 | ", register %02X, last readout " FMT_4HEX ")", 142 | _UI(flag), 143 | _UI(mask), 144 | _UI(address), 145 | _UI(val)); 146 | } 147 | 148 | } while((val & mask) != flag); 149 | 150 | return WchSwioFlasher_Ok; 151 | } 152 | 153 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_wait_for_reg( 154 | WchSwioFlasher_RiscVDebug* handle, 155 | uint32_t address, 156 | uint32_t mask, 157 | uint32_t flag, 158 | uint32_t timeout) { 159 | UNUSED(handle); 160 | 161 | uint32_t tm = furi_get_tick(); 162 | uint32_t val = (uint32_t)-1; 163 | do { 164 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_mem_u32(handle, address, &val)); 165 | 166 | if((furi_get_tick() - tm) > timeout) { 167 | return LOG_ERR_M( 168 | WchSwioFlasher_Error_Timeout, 169 | "wait_for_reg timeout (flag " FMT_4HEX ", mask " FMT_4HEX ", register " FMT_4HEX 170 | ", last readout " FMT_4HEX ")", 171 | _UI(flag), 172 | _UI(mask), 173 | _UI(address), 174 | _UI(val)); 175 | } 176 | 177 | } while((val & mask) != flag); 178 | 179 | return WchSwioFlasher_Ok; 180 | } 181 | 182 | static inline WchSwioFlasher_Error 183 | wait_for_dmstatus(WchSwioFlasher_RiscVDebug* handle, uint32_t mask, uint32_t flag) { 184 | return wait_for_flag(handle, DM_DMSTATUS, mask, flag, RVD_WAIT_FOR_DM_STATUS_TIMEOUT); 185 | } 186 | 187 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_wait_for_abstractcs( 188 | WchSwioFlasher_RiscVDebug* handle, 189 | uint32_t mask, 190 | uint32_t flag, 191 | uint32_t timeout) { 192 | return wait_for_flag(handle, DM_ABSTRACTCS, mask, flag, timeout); 193 | } 194 | 195 | static WchSwioFlasher_Error 196 | get_csr(WchSwioFlasher_RiscVDebug* handle, uint16_t index, uint32_t* value) { 197 | RVD_COMMAND cmd = {.raw = 0}; 198 | 199 | cmd.REGNO = index; 200 | cmd.TRANSFER = 1; 201 | cmd.AARSIZE = 2; 202 | 203 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_command(handle, &cmd)); 204 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_data0(handle, value)); 205 | 206 | return WchSwioFlasher_Ok; 207 | } 208 | 209 | static WchSwioFlasher_Error 210 | set_csr(WchSwioFlasher_RiscVDebug* handle, uint16_t index, uint32_t value) { 211 | RVD_COMMAND cmd = {.raw = 0}; 212 | 213 | cmd.REGNO = index; 214 | cmd.WRITE = 1; 215 | cmd.TRANSFER = 1; 216 | cmd.AARSIZE = 2; 217 | 218 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_data0(handle, value)); 219 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_command(handle, &cmd)); 220 | 221 | return WchSwioFlasher_Ok; 222 | } 223 | 224 | static WchSwioFlasher_Error get_dcsr(WchSwioFlasher_RiscVDebug* handle, uint32_t* value) { 225 | return get_csr(handle, CSR_DCSR, value); 226 | } 227 | 228 | static WchSwioFlasher_Error set_dcsr(WchSwioFlasher_RiscVDebug* handle, uint32_t value) { 229 | return set_csr(handle, CSR_DCSR, value); 230 | } 231 | 232 | static WchSwioFlasher_Error get_dpc(WchSwioFlasher_RiscVDebug* handle, uint32_t* value) { 233 | return get_csr(handle, CSR_DPC, value); 234 | } 235 | 236 | static WchSwioFlasher_Error set_dpc(WchSwioFlasher_RiscVDebug* handle, uint32_t value) { 237 | return set_csr(handle, CSR_DPC, value); 238 | } 239 | 240 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_get_gpr( 241 | WchSwioFlasher_RiscVDebug* handle, 242 | uint8_t index, 243 | uint32_t* value) { 244 | if(index == 16) { 245 | CHECK_ERR(get_dpc(handle, value)); 246 | return WchSwioFlasher_Ok; 247 | } 248 | 249 | RVD_COMMAND cmd = {.raw = 0}; 250 | 251 | cmd.REGNO = 0x1000 | index; 252 | cmd.TRANSFER = 1; 253 | cmd.AARSIZE = 2; 254 | 255 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_command(handle, &cmd)); 256 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_data0(handle, value)); 257 | 258 | return WchSwioFlasher_Ok; 259 | } 260 | 261 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_set_gpr( 262 | WchSwioFlasher_RiscVDebug* handle, 263 | uint8_t index, 264 | uint32_t value) { 265 | if(index == 16) { 266 | CHECK_ERR(set_dpc(handle, value)); 267 | return WchSwioFlasher_Ok; 268 | } 269 | 270 | RVD_COMMAND cmd = {.raw = 0}; 271 | 272 | cmd.REGNO = 0x1000 | index; 273 | cmd.WRITE = 1; 274 | cmd.TRANSFER = 1; 275 | cmd.AARSIZE = 2; 276 | 277 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_data0(handle, value)); 278 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_command(handle, &cmd)); 279 | 280 | return WchSwioFlasher_Ok; 281 | } 282 | 283 | static WchSwioFlasher_Error __attribute__((unused)) 284 | enable_breakpoints(WchSwioFlasher_RiscVDebug* handle) { 285 | FURI_LOG_D(TAG, "enable_breakpoints"); 286 | 287 | uint32_t dmstatus = 0; 288 | CHECK_ERR_M(get_dmstatus(handle, &dmstatus), "unable to get DMSTATUS"); 289 | 290 | if((dmstatus & DM_DMSTATUS_ALLHALTED) == 0) { 291 | return LOG_ERR(WchSwioFlasher_Error_TargetInInvalidState); 292 | } 293 | 294 | uint32_t dcsr = 0; 295 | CHECK_ERR_M(get_dcsr(handle, &dcsr), "unable to get DCSR"); 296 | FURI_LOG_D(TAG, "previous DCSR = " FMT_4HEX, _UI(dcsr)); 297 | 298 | dcsr |= CSR_DCSR_EBREAKM | CSR_DCSR_EBREAKS | CSR_DCSR_EBREAKU | CSR_DCSR_STOPCOUNT | 299 | CSR_DCSR_STOPTIME; 300 | CHECK_ERR_M(set_dcsr(handle, dcsr), "unable to set DCSR"); 301 | FURI_LOG_D(TAG, "current DCSR = " FMT_4HEX, _UI(dcsr)); 302 | 303 | FURI_LOG_D(TAG, "enable_breakpoints done"); 304 | 305 | return WchSwioFlasher_Ok; 306 | } 307 | /* 308 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_load_prog( 309 | WchSwioFlasher_RiscVDebug* handle, 310 | const char* name, 311 | uint32_t* prog, 312 | uint32_t clobber) {*/ 313 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_load_prog( 314 | WchSwioFlasher_RiscVDebug* handle, 315 | const WchSwioFlasher_RiscVProgram* program) { 316 | #ifdef RVD_TXRX_DEBUG_MSG_ENABLE 317 | FURI_LOG_D(TAG, "load_prog name=%s", program->name); 318 | #endif 319 | 320 | uint32_t* prog = (uint32_t*)program->data; 321 | // Upload any PROG{N} word that changed. 322 | for(int i = 0; i < 8; i++) { 323 | if(handle->prog_cache[i] != prog[i]) { 324 | CHECK_ERR(WchSwioFlasher_SWIO_write(handle->swio, DM_PROGBUF0 + i, prog[i])); 325 | #ifdef RVD_CHECK_PROGRAM_UPLOAD_ENABLED 326 | uint32_t v; 327 | CHECK_ERR(WchSwioFlasher_SWIO_read(handle->swio, DM_PROGBUF0 + i, &v)); 328 | if(prog[i] != v) { 329 | return LOG_ERR_M( 330 | WchSwioFlasher_Error_TargetInInvalidState, "program upload failed"); 331 | } 332 | #endif 333 | handle->prog_cache[i] = prog[i]; 334 | } 335 | } 336 | 337 | // Save any registers this program is going to clobber. 338 | for(int i = 0; i < handle->reg_count; i++) { 339 | if(program->clobbers & (1 << i)) { 340 | if(handle->cached_regs & (1 << i)) { 341 | if(handle->dirty_regs & (1 << i)) { 342 | uint32_t val; 343 | CHECK_ERR_M( 344 | WchSwioFlasher_RiscVDebug_get_gpr(handle, i, &val), 345 | "unable to get reg[%d]", 346 | i); 347 | handle->reg_cache[i] = val; 348 | handle->cached_regs |= (1 << i); 349 | } else { 350 | return LOG_ERR_M( 351 | WchSwioFlasher_Error_DirtyRegs, 352 | "reg %d is about to be clobbered, but we can't get a clean copy because it's already dirty", 353 | i); 354 | } 355 | } 356 | } 357 | } 358 | 359 | handle->prog_will_clobber = program->clobbers; 360 | 361 | //LOG("RVDebug::load_prog() done\n"); 362 | #ifdef RVD_TXRX_DEBUG_MSG_ENABLE 363 | FURI_LOG_D(TAG, "load_prog done"); 364 | #endif 365 | 366 | return WchSwioFlasher_Ok; 367 | } 368 | 369 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_get_chip_info( 370 | WchSwioFlasher_RiscVDebug* handle, 371 | WchSwioFlasher_RiscVDebug_ChipInfo* info) { 372 | CHECK_ERR_M( 373 | WchSwioFlasher_RiscVDebug_reset(handle, WchSwioFlasher_RVD_ResetToHalt), 374 | "unable to halt target"); 375 | 376 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_mem_u32(handle, ADDR_ESIG_FLACAP, &info->flash_size)); 377 | info->flash_size &= 0xffff; 378 | 379 | CHECK_ERR( 380 | WchSwioFlasher_RiscVDebug_get_mem_u32(handle, ADDR_ESIG_UNIID1, &info->esig_uniid[0])); 381 | CHECK_ERR( 382 | WchSwioFlasher_RiscVDebug_get_mem_u32(handle, ADDR_ESIG_UNIID2, &info->esig_uniid[1])); 383 | CHECK_ERR( 384 | WchSwioFlasher_RiscVDebug_get_mem_u32(handle, ADDR_ESIG_UNIID3, &info->esig_uniid[2])); 385 | 386 | FURI_LOG_D( 387 | TAG, 388 | "Detected chip flash: %lu kB, ESIG: " FMT_4HEX ", " FMT_4HEX ", " FMT_4HEX, 389 | info->flash_size, 390 | _UI(info->esig_uniid[0]), 391 | _UI(info->esig_uniid[1]), 392 | _UI(info->esig_uniid[2])); 393 | 394 | CHECK_ERR_M( 395 | WchSwioFlasher_RiscVDebug_reset(handle, WchSwioFlasher_RVD_ResetToRunNoCheck), 396 | "unable to run target"); 397 | 398 | return WchSwioFlasher_Ok; 399 | } 400 | 401 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_reset( 402 | WchSwioFlasher_RiscVDebug* handle, 403 | WchSwioFlasher_RiscVDebug_ResetType type) { 404 | FURI_LOG_D(TAG, "reset_cpu and %s", type != WchSwioFlasher_RVD_ResetToHalt ? "run" : "halt"); 405 | 406 | // ----- 407 | // see RISC-V QingKeyV2 Microprocessor Debug Manual for Reset/Halt/Resume Microprocessor usecases 408 | // ----- 409 | 410 | // Do HW reset 411 | CHECK_ERR_M(WchSwioFlasher_SWIO_hw_reset(handle->swio), "unable to toggle HW reset"); 412 | furi_delay_ms(50); 413 | 414 | CHECK_ERR_M(WchSwioFlasher_SWIO_init(handle->swio), "unable to init SWIO"); 415 | 416 | // Make the debug module work properly. 417 | CHECK_ERR_M(set_dmcontrol(handle, 0x80000001), "unable to init debug module"); 418 | furi_delay_ms(1); 419 | 420 | // Initiate a halt request. 421 | CHECK_ERR_M(set_dmcontrol(handle, 0x80000001), "unable to initiate halt"); 422 | furi_delay_ms(1); 423 | 424 | if(type == WchSwioFlasher_RVD_ResetToRun || type == WchSwioFlasher_RVD_ResetToRunNoCheck) { 425 | // Clear the halt request bit. 426 | CHECK_ERR_M(set_dmcontrol(handle, 0x00000001), "unable to clear halt"); 427 | furi_delay_ms(1); 428 | 429 | // Initiate a core reset request. 430 | CHECK_ERR_M(set_dmcontrol(handle, 0x00000003), "unable to send reset core signal"); 431 | furi_delay_ms(1); 432 | } else if(type == WchSwioFlasher_RVD_ResetToHalt) { 433 | // Initiate a core reset request and hold the halt request. 434 | CHECK_ERR_M(set_dmcontrol(handle, 0x80000003), "unable to send reset core and hold halt"); 435 | furi_delay_ms(1); 436 | } 437 | 438 | // Get the debug module status information, check rdata[19:18], if the value is 0b11, it means the processor has been reset, otherwise the reset failed. 439 | CHECK_ERR(wait_for_dmstatus( 440 | handle, 441 | DM_DMSTATUS_ALLRESUMEACK | DM_DMSTATUS_ANYHAVERESET, 442 | DM_DMSTATUS_ANYHAVERESET | DM_DMSTATUS_ANYHAVERESET)); 443 | 444 | if(type == WchSwioFlasher_RVD_ResetToRun || type == WchSwioFlasher_RVD_ResetToRunNoCheck) { 445 | // Clear the reset signal 446 | CHECK_ERR_M(set_dmcontrol(handle, 0x00000001), "unable to clear reset signal"); 447 | furi_delay_ms(1); 448 | 449 | // Clear the reset status signal, this bit is valid for write 1 and read constant 0. 450 | CHECK_ERR_M(set_dmcontrol(handle, 0x10000001), "unable to clear reset status signal"); 451 | furi_delay_ms(1); 452 | } else if(type == WchSwioFlasher_RVD_ResetToHalt) { 453 | // Clear the reset signal and hold the halt request. 454 | CHECK_ERR_M(set_dmcontrol(handle, 0x80000001), "unable to clear reset signal"); 455 | furi_delay_ms(1); 456 | 457 | // Clear the reset status signal and hold the halt request. 458 | CHECK_ERR_M(set_dmcontrol(handle, 0x90000001), "unable to clear reset status signal"); 459 | furi_delay_ms(1); 460 | } 461 | 462 | if(type != WchSwioFlasher_RVD_ResetToRunNoCheck) { 463 | // Get the debug module status information, check rdata[19:18], if the value is 0b00, it means the processor reset status has been cleared, otherwise the clearing fails. 464 | CHECK_ERR( 465 | wait_for_dmstatus(handle, DM_DMSTATUS_ALLRESUMEACK | DM_DMSTATUS_ANYHAVERESET, 0)); 466 | } 467 | 468 | if(type == WchSwioFlasher_RVD_ResetToHalt) { 469 | // Clear the halt request when the processor is reset and haltd again. 470 | CHECK_ERR_M(set_dmcontrol(handle, 0x0000001), "unable to clear halt req"); 471 | furi_delay_ms(1); 472 | } 473 | 474 | /* 475 | // Set reset request 476 | CHECK_ERR_M(set_dmcontrol(handle, 0x80000003), "unable to HAVE RESET"); 477 | CHECK_ERR(wait_for_dmstatus(handle, DM_DMSTATUS_ALLHAVERESET, DM_DMSTATUS_ALLHAVERESET)); 478 | 479 | // Clear reset request and hold halt request 480 | CHECK_ERR_M(set_dmcontrol(handle, 0x80000001), "unable to clear reset and hold halt"); 481 | // this busywait seems to be required or we hang 482 | CHECK_ERR(wait_for_dmstatus(handle, DM_DMSTATUS_ALLHALTED, DM_DMSTATUS_ALLHALTED)); 483 | 484 | // Clear HAVERESET 485 | CHECK_ERR_M(set_dmcontrol(handle, 0x90000001), "unable to clear HAVE RESET"); 486 | CHECK_ERR(wait_for_dmstatus(handle, DM_DMSTATUS_ALLHAVERESET, 0)); 487 | 488 | // Clear halt request 489 | CHECK_ERR_M(set_dmcontrol(handle, 0x00000001), "unable to clear halt request"); 490 | 491 | uint32_t dmstatus; 492 | CHECK_ERR(get_dmstatus(handle, &dmstatus)); 493 | FURI_LOG_D(TAG, ">>> dmstatus " FMT_4HEX, _UI(dmstatus)); 494 | 495 | // Reset cached state 496 | CHECK_ERR(WchSwioFlasher_RiscVDebug_init(handle)); 497 | 498 | // Resetting the CPU also resets DCSR, redo it. 499 | CHECK_ERR(enable_breakpoints(handle)); 500 | */ 501 | FURI_LOG_D(TAG, "reset_cpu done"); 502 | 503 | return WchSwioFlasher_Ok; 504 | } 505 | 506 | WchSwioFlasher_Error 507 | WchSwioFlasher_RiscVDebug_run_prog(WchSwioFlasher_RiscVDebug* handle, uint32_t timeout) { 508 | uint32_t abstracts; 509 | RVD_COMMAND cmd = {.raw = 0}; 510 | cmd.POSTEXEC = 1; 511 | 512 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_command(handle, &cmd)); 513 | 514 | if(timeout != 0) { 515 | CHECK_ERR( 516 | WchSwioFlasher_RiscVDebug_wait_for_abstractcs(handle, DM_ABSTRACTCS_BUSY, 0, timeout)); 517 | } else { 518 | // It takes 40 usec to do _anything_ over the debug interface, so if the 519 | // program is "fast" then we should _never_ see BUSY... right? 520 | 521 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_abstractcs(handle, &abstracts)); 522 | if(abstracts & DM_ABSTRACTCS_BUSY) { 523 | return LOG_ERR(WchSwioFlasher_Error_ProgramNotFinishedYet); 524 | } 525 | } 526 | #ifdef RVD_CHECK_PROGRAM_EXECUTION_ENABLED 527 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_abstractcs(handle, &abstracts)); 528 | 529 | uint32_t err; 530 | switch(abstracts & DM_ABSTRACTCS_CMDERR) { 531 | case DM_ABSTRACTCS_CMDERR_NONE: 532 | break; 533 | default: 534 | err = (abstracts & DM_ABSTRACTCS_CMDERR) >> DM_ABSTRACTCS_CMDERR_OFFSET; 535 | return LOG_ERR_M( 536 | WchSwioFlasher_Error_ProgramRunError, 537 | "program run failed '%s' (err: %d)", 538 | abstractsc_cmderr[err], 539 | _UI(err)); 540 | } 541 | #endif 542 | handle->dirty_regs |= handle->prog_will_clobber; 543 | 544 | return WchSwioFlasher_Ok; 545 | } 546 | 547 | static WchSwioFlasher_Error 548 | get_mem_u32_aligned(WchSwioFlasher_RiscVDebug* handle, uint32_t addr, uint32_t* result) { 549 | // Address must be aligned to 4 bytes 550 | if(addr & 0x03) { 551 | return LOG_ERR_M( 552 | WchSwioFlasher_Error_InvalidArgument, "bad address alignment" FMT_4HEX, _UI(addr)); 553 | } 554 | 555 | CHECK_ERR(WchSwioFlasher_RiscVDebug_load_prog( 556 | handle, &WchSwioFlasher_RiscVDebug_get_set_u32_program)); 557 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_data1(handle, addr)); 558 | 559 | CHECK_ERR(WchSwioFlasher_RiscVDebug_run_prog(handle, 10)); 560 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_data0(handle, result)); 561 | 562 | return WchSwioFlasher_Ok; 563 | } 564 | 565 | static WchSwioFlasher_Error 566 | set_mem_u32_aligned(WchSwioFlasher_RiscVDebug* handle, uint32_t addr, uint32_t data) { 567 | // Address must be aligned to 4 bytes 568 | if(addr & 0x03) { 569 | return LOG_ERR_M( 570 | WchSwioFlasher_Error_InvalidArgument, "bad address alignment" FMT_4HEX, _UI(addr)); 571 | } 572 | 573 | CHECK_ERR(WchSwioFlasher_RiscVDebug_load_prog( 574 | handle, &WchSwioFlasher_RiscVDebug_get_set_u32_program)); 575 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_data0(handle, data)); 576 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_data1(handle, addr | 1)); 577 | 578 | CHECK_ERR(WchSwioFlasher_RiscVDebug_run_prog(handle, 10)); 579 | 580 | return WchSwioFlasher_Ok; 581 | } 582 | 583 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_get_mem_u32( 584 | WchSwioFlasher_RiscVDebug* handle, 585 | uint32_t addr, 586 | uint32_t* result) { 587 | uint32_t offset = addr & 3; 588 | uint32_t addr_lo = (addr + 0) & ~3; 589 | uint32_t addr_hi = (addr + 3) & ~3; 590 | 591 | uint32_t data_lo; 592 | CHECK_ERR(get_mem_u32_aligned(handle, addr_lo, &data_lo)); 593 | 594 | if(offset == 0) { 595 | *result = data_lo; 596 | return WchSwioFlasher_Ok; 597 | } 598 | 599 | uint32_t data_hi; 600 | CHECK_ERR(get_mem_u32_aligned(handle, addr_hi, &data_hi)); 601 | 602 | *result = (data_lo >> (offset * 8)) | (data_hi << (32 - offset * 8)); 603 | 604 | return WchSwioFlasher_Ok; 605 | } 606 | 607 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_set_mem_u32( 608 | WchSwioFlasher_RiscVDebug* handle, 609 | uint32_t addr, 610 | uint32_t data) { 611 | uint32_t offset = addr & 3; 612 | uint32_t addr_lo = (addr + 0) & ~3; 613 | uint32_t addr_hi = (addr + 4) & ~3; 614 | 615 | if(offset == 0) { 616 | //set_mem_u32_aligned(addr_lo, data); 617 | CHECK_ERR(set_mem_u32_aligned(handle, addr_lo, data)); 618 | return WchSwioFlasher_Ok; 619 | } 620 | 621 | uint32_t data_lo; 622 | CHECK_ERR(get_mem_u32_aligned(handle, addr_lo, &data_lo)); 623 | uint32_t data_hi; 624 | CHECK_ERR(get_mem_u32_aligned(handle, addr_hi, &data_hi)); 625 | 626 | if(offset == 1) { 627 | data_lo &= 0x000000FF; 628 | data_hi &= 0xFFFFFF00; 629 | data_lo |= data << 8; 630 | data_hi |= data >> 24; 631 | } else if(offset == 2) { 632 | data_lo &= 0x0000FFFF; 633 | data_hi &= 0xFFFF0000; 634 | data_lo |= data << 16; 635 | data_hi |= data >> 16; 636 | } else if(offset == 3) { 637 | data_lo &= 0x00FFFFFF; 638 | data_hi &= 0xFF000000; 639 | data_lo |= data << 24; 640 | data_hi |= data >> 8; 641 | } 642 | 643 | //set_mem_u32_aligned(addr_lo, data_lo); 644 | //set_mem_u32_aligned(addr_hi, data_hi); 645 | CHECK_ERR(set_mem_u32_aligned(handle, addr_lo, data_lo)); 646 | CHECK_ERR(set_mem_u32_aligned(handle, addr_hi, data_hi)); 647 | 648 | return WchSwioFlasher_Ok; 649 | } 650 | 651 | WchSwioFlasher_Error WchSwioFlasher_RiscVDebug_get_block_aligned( 652 | WchSwioFlasher_RiscVDebug* handle, 653 | uint32_t addr, 654 | void* dst, 655 | int size_bytes) { 656 | // Address must be aligned to 4 bytes 657 | if(addr & 0x03) { 658 | return LOG_ERR_M( 659 | WchSwioFlasher_Error_InvalidArgument, "bad address alignment" FMT_4HEX, _UI(addr)); 660 | } 661 | 662 | // Size must be aligned to 4 bytes 663 | if(size_bytes & 0x03) { 664 | return LOG_ERR_M( 665 | WchSwioFlasher_Error_InvalidArgument, "bad size alignment" FMT_4HEX, _UI(addr)); 666 | } 667 | 668 | CHECK_ERR(WchSwioFlasher_RiscVDebug_load_prog( 669 | handle, &WchSwioFlasher_RiscVDebug_get_block_aligned_program)); 670 | 671 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_data1(handle, addr)); 672 | 673 | int size_dwords = size_bytes / 4; 674 | uint32_t* cursor = (uint32_t*)dst; 675 | 676 | for(int i = 0; i < size_dwords; i++) { 677 | if(i == 0) { 678 | CHECK_ERR( 679 | WchSwioFlasher_RiscVDebug_run_prog(handle, WchSwioFlasher_RiscVDebug_NO_TIMEOUT)); 680 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_abstractauto(handle, 0x00000001)); 681 | } 682 | if(i == size_dwords - 1) { 683 | CHECK_ERR(WchSwioFlasher_RiscVDebug_set_abstractauto(handle, 0x00000000)); 684 | } 685 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_data0(handle, &cursor[i])); 686 | } 687 | 688 | return WchSwioFlasher_Ok; 689 | } 690 | 691 | WchSwioFlasher_Error 692 | WchSwioFlasher_RiscVDebug_machine_isa(WchSwioFlasher_RiscVDebug* handle, uint32_t* result) { 693 | CHECK_ERR(WchSwioFlasher_RiscVDebug_load_prog( 694 | handle, &WchSwioFlasher_RiscVDebug_get_machine_isa_program)); 695 | 696 | CHECK_ERR(WchSwioFlasher_RiscVDebug_run_prog(handle, RVD_WAIT_FOR_CHIPINFO_TIMEOUT)); 697 | 698 | CHECK_ERR(WchSwioFlasher_RiscVDebug_get_data0(handle, result)); 699 | 700 | return WchSwioFlasher_Ok; 701 | } 702 | 703 | WchSwioFlasher_Error 704 | WchSwioFlasher_RiscVDebug_get_status(WchSwioFlasher_RiscVDebug* handle, uint32_t* result) { 705 | return get_dmstatus(handle, result); 706 | } --------------------------------------------------------------------------------