From cc451d2d75caf676fb21f27a802cbd788988d331 Mon Sep 17 00:00:00 2001 From: github0null Date: Sun, 31 Aug 2025 20:46:06 +0800 Subject: [PATCH 1/3] not backup file for KEIL_C51 -> SDCC converter --- src/extension.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 2f6ccd00..ea282c59 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1898,30 +1898,19 @@ function c51ToSDCC() { } const file = new File(vscode.window.activeTextEditor.document.uri.fsPath); - const bkFile = new File(file.path + '.bk'); - if (file.IsFile()) { try { - // backup - fs.copyFileSync(file.path, bkFile.path); - const res: string[] = []; sfrMap.clear(); const lines = fs.readFileSync(file.path, 'utf8').split(/\n|\r\n/); - lines.forEach((_line, index) => { res.push(handleLine(_line, index + 1)); }); - fs.writeFileSync(file.path, res.join(os.EOL)); - - GlobalEvent.emit('msg', newMessage('Info', 'Convert finished !')); - + GlobalEvent.emit('msg', newMessage('Info', 'Convertion finished !')); } catch (error) { GlobalEvent.emit('msg', ExceptionToMessage(error, 'Warning')); } - } else { - GlobalEvent.emit('msg', newMessage('Warning', 'not found file: \'' + file.path + '\'')); } } } From 808d494464b8cd3553404b02c6bd4b69780e65a5 Mon Sep 17 00:00:00 2001 From: github0null Date: Mon, 1 Sep 2025 00:55:18 +0800 Subject: [PATCH 2/3] new flasher 'probe-rs' --- src/EIDEProjectModules.ts | 145 +++++++++++++++++++++++++++++++- src/HexUploader.ts | 170 ++++++++++++++++++++++++++++++++++++-- src/StringTable.ts | 7 +- src/utility.ts | 84 +++++++++++++++++++ 4 files changed, 397 insertions(+), 9 deletions(-) diff --git a/src/EIDEProjectModules.ts b/src/EIDEProjectModules.ts index 7264c731..60c95d7a 100644 --- a/src/EIDEProjectModules.ts +++ b/src/EIDEProjectModules.ts @@ -33,14 +33,21 @@ import { view_str$flasher$resetMode, view_str$flasher$other_cmds, view_str$flasher$stcgalOptions, - view_str$compile$archExtensions + view_str$compile$archExtensions, + view_str$flasher$eraseAll } from "./StringTable"; import { ResManager } from "./ResManager"; import { ArrayDelRepetition } from "../lib/node-utility/Utility"; import { GlobalEvent } from "./GlobalEvents"; import { ExceptionToMessage, newMessage } from "./Message"; import { ToolchainName, IToolchian, ToolchainManager } from './ToolchainManager'; -import { HexUploaderType, STLinkOptions, STVPFlasherOptions, StcgalFlashOption, JLinkOptions, JLinkProtocolType, PyOCDFlashOptions, OpenOCDFlashOptions, STLinkProtocolType, CustomFlashOptions } from "./HexUploader"; +import { + HexUploaderType, STLinkOptions, STVPFlasherOptions, + StcgalFlashOption, JLinkOptions, JLinkProtocolType, + PyOCDFlashOptions, OpenOCDFlashOptions, STLinkProtocolType, + ProbeRSFlashOptions, + CustomFlashOptions +} from "./HexUploader"; import { AbstractProject } from "./EIDEProject"; import { SettingManager } from "./SettingManager"; import { WorkspaceManager } from "./WorkspaceManager"; @@ -1932,6 +1939,8 @@ export abstract class UploadConfigModel extends ConfigModel { return new OpenOCDUploadModel(api); case 'Custom': return new CustomUploadModel(api); + case 'probe-rs': + return new ProbeRSUploadModel(api); default: throw new Error('Invalid uploader type !'); } @@ -1955,6 +1964,9 @@ export abstract class UploadConfigModel extends ConfigModel { } } + /** + * @description 用于获取该选项的值(仅用于在UI中进行可读性显示) + */ getKeyValue(key: string): string { switch (key) { case 'bin': @@ -2841,6 +2853,135 @@ class OpenOCDUploadModel extends UploadConfigModel { } } +class ProbeRSUploadModel extends UploadConfigModel { + + uploader: HexUploaderType = 'probe-rs'; + + GetKeyDescription(key: string): string { + switch (key) { + case 'target': + return view_str$flasher$targetName; + case 'protocol': + return view_str$flasher$interfaceType; + case 'baseAddr': + return view_str$flasher$baseAddr; + case 'speed': + return view_str$flasher$downloadSpeed; + case 'allowEraseAll': + return view_str$flasher$eraseAll; + case 'otherOptions': + return view_str$flasher$other_cmds; + default: + return super.GetKeyDescription(key); + } + } + + getKeyValue(key: string): string { + switch (key) { + case 'bin': + return (this.data)[key] || '${ExecutableName}.elf'; + case 'speed': + return (this.data)[key] > 0 ? `${(this.data)[key]} KHz` : 'default'; + case 'allowEraseAll': + return `${(this.data)[key]}`; + default: + return super.getKeyValue(key); + } + } + + isKeyEnable(key: string): boolean { + switch (key) { + case 'baseAddr': + return /\.bin\b/i.test(this.data.bin); + case 'allowEraseAll': + return false; + default: + return true; + } + } + + getKeyIcon(key: string): KeyIcon | undefined { + switch (key) { + case 'target': + return 'CPU_16x.svg'; + case 'protocol': + return 'ConnectUnplugged_16x.svg'; + case 'baseAddr': + return 'Property_16x.svg'; + case 'otherOptions': + return 'ImmediateWindow_16x.svg'; + default: + return super.getKeyIcon(key); + } + } + + protected GetKeyType(key: string): FieldType { + switch (key) { + case 'target': + case 'protocol': + case 'allowEraseAll': + return 'SELECTION'; + case 'baseAddr': + case 'otherOptions': + return 'INPUT'; + case 'speed': + return 'INPUT_INTEGER'; + default: + return super.GetKeyType(key); + } + } + + protected VerifyString(key: string, input: string): string | undefined { + switch (key) { + case 'baseAddr': + return /^0x[0-9a-f]{1,8}$/i.test(input) ? undefined : 'must be a hex number, like: 0x08000000'; + default: + return super.VerifyString(key, input); + } + } + + protected GetSelectionList(key: string): CompileConfigPickItem[] | undefined { + switch (key) { + case 'target': + return utility.probers_listchips().map(inf => { + return { + label: inf.name, + description: inf.series + } + }); + case 'protocol': + return [ + { label: 'SWD', val: 'swd' }, + { label: 'JTAG', val: 'jtag' } + ]; + case 'allowEraseAll': + return [ + { label: 'true', val: true }, + { label: 'false', val: false } + ]; + default: + return super.GetSelectionList(key); + } + } + + protected getEventData(key: string): EventData | undefined { + return super.getEventData(key); + } + + GetDefault(): ProbeRSFlashOptions { + return { + bin: '', + target: 'STM32F103C8', + protocol: 'swd', + speed: 0, + baseAddr: '0x08000000', + allowEraseAll: false, + otherOptions: '' + }; + } +} + + class CustomUploadModel extends UploadConfigModel { uploader: HexUploaderType = 'Custom'; diff --git a/src/HexUploader.ts b/src/HexUploader.ts index 69ff0e57..37837fa3 100644 --- a/src/HexUploader.ts +++ b/src/HexUploader.ts @@ -31,9 +31,10 @@ import { CmdLineHandler } from "./CmdLineHandler"; import { gotoSet_text, view_str$download_software } from "./StringTable"; import { EncodingConverter } from "./EncodingConverter"; import { ToolchainName, ToolchainManager } from "./ToolchainManager"; -import { runShellCommand } from './utility'; +import { runShellCommand, deepCloneObject, probers_install } from './utility'; import { WorkspaceManager } from "./WorkspaceManager"; +import * as child_process from "child_process"; import * as vscode from "vscode"; import * as NodePath from 'path'; import * as os from "os"; @@ -41,12 +42,12 @@ import * as fs from 'fs'; import * as ini from 'ini'; import { ResInstaller } from "./ResInstaller"; import { newMessage } from "./Message"; -import { concatSystemEnvPath, exeSuffix, prependToSysEnv, osType } from "./Platform"; +import { concatSystemEnvPath, exeSuffix, prependToSysEnv, osType, appendToSysEnv, userhome } from "./Platform"; import { StatusBarManager } from "./StatusBarManager"; let _mInstance: HexUploaderManager | undefined; -export type HexUploaderType = 'JLink' | 'STVP' | 'STLink' | 'stcgal' | 'pyOCD' | 'OpenOCD' | 'Custom'; +export type HexUploaderType = 'JLink' | 'STVP' | 'STLink' | 'stcgal' | 'pyOCD' | 'OpenOCD' | 'probe-rs' | 'Custom'; export interface UploadOption { // program file path @@ -91,6 +92,12 @@ export class HexUploaderManager { description: 'for Cortex-M chips', filters: arm_toolchains.concat(riscv_toolchains, ['ANY_GCC']) }, + { + type: 'probe-rs', + description: 'for ARM, RISC-V devices', + filters: arm_toolchains.concat(riscv_toolchains, ['ANY_GCC']) + }, + // 8/16 bits mcus { type: 'stcgal', description: 'for STC chips', @@ -101,10 +108,11 @@ export class HexUploaderManager { description: 'for STM8 chips, only STLink interface', filters: ['IAR_STM8', 'SDCC', 'COSMIC_STM8'] }, + // custom { type: 'Custom', - label: 'Shell', - description: 'download program by custom shell command' + label: 'Custom CLI', + description: 'download program by custom command-line' } ]; } @@ -142,6 +150,8 @@ export class HexUploaderManager { return new PyOCDUploader(prj); case 'OpenOCD': return new OpenOCDUploader(prj); + case 'probe-rs': + return new ProbeRSUploader(prj); case 'Custom': return new CustomUploader(prj); default: @@ -267,7 +277,7 @@ export abstract class HexUploader { } }); - return commandLine + return commandLine; } getAllProgramFiles(): FlashProgramFile[] { @@ -1087,6 +1097,154 @@ class OpenOCDUploader extends HexUploader { this.executeShellCommand(this.toolType, commandLine); } } + +export interface ProbeRSFlashOptions extends UploadOption { + + /* + --chip + [env: PROBE_RS_CHIP=] + */ + target: string; + + /* + --protocol + Protocol used to connect to chip. Possible options: [swd, jtag] + */ + protocol: string; + + /* + --speed + The protocol speed in kHz + */ + speed: number; + + /* + --base-address + The address in memory where the binary will be put at. This is only considered when `bin` is selected as the format + */ + baseAddr: string; + + /* + --allow-erase-all + Use this flag to allow all memory, including security keys and 3rd party firmware, to be erased even when it has read-only protection + */ + allowEraseAll: boolean; + + /* + other user options pass to cli + */ + otherOptions: string; +} + +class ProbeRSUploader extends HexUploader { + + toolType: HexUploaderType = 'probe-rs'; + + private _getEnv(): any { + const env = deepCloneObject(process.env); + prependToSysEnv(env, [ + NodePath.join(`${userhome()}`, '.cargo', 'bin') // $HOME/.cargo/bin/ + ]); + return env; + } + + protected parseProgramFiles(options: T): FlashProgramFile[] { + + const result: FlashProgramFile[] = []; + const matcher = /(?[^,]+)(?:,(?0x[a-f0-9]+))?/i; + + // if 'bin' path is empty, use default program path + if (options.bin.trim() === '') { + const elfPath = ['.', this.project.getOutputDir(), this.project.getProjectName() + '.elf'].join(File.sep); + return [ + { path: elfPath } + ]; + } + + options.bin.split(';').forEach((path) => { + const m = matcher.exec(path); + if (m && m.groups && m.groups['path']) { + result.push({ + path: this.project.resolveEnvVar(m.groups['path']), + addr: m.groups['addr'] + }); + } + }); + + return result; + } + + protected async _prepare(eraseAll?: boolean): Promise> { + + try { + // PS C:\Users\Administrator> cargo-flash --version + // cargo flash 0.29.1 (git commit: 1cf182e) + child_process.execSync(`cargo-flash --version`, { env: this._getEnv() }); + } catch (error) { + GlobalEvent.log_warn(error); + vscode.window.showWarningMessage( + `Not found 'cargo-flash' command. Install it now ?`, 'Yes', 'No').then(opt => { + if (opt === 'Yes') + probers_install(); + }); + return { isOk: false }; + } + + if (eraseAll) { + GlobalEvent.emit('msg', newMessage('Warning', `not support 'Erase Chip' for '${this.toolType}' flasher`)); + return { isOk: false }; + } + + const option = this.getUploadOptions(); + const programs = this.parseProgramFiles(option); + + if (programs.length == 0) { + throw new Error(`no any program files !`); + } + + const commands: string[] = [ + `--chip ${option.target}`, + `--protocol ${option.protocol}` + ]; + + const wsFolder = WorkspaceManager.getInstance().getWorkspaceRoot(); + if (wsFolder) { + commands.push( + `--work-dir "${wsFolder.path}"` + ); + } + + // just support one file + commands.push(`--path "${programs[0].path}"`); + // if (programs[0].path.endsWith('.bin')) { + // if (programs[0].addr || option.baseAddr) { + // const baseAddr = programs[0].addr || option.baseAddr; + // commands.push(`--base-address ${baseAddr}`); + // } + // } + commands.push(`--verify`); + + if (option.speed) + commands.push(`--speed ${option.speed}`); + // if (option.allowEraseAll) + // commands.push(`--allow-erase-all`); + + // place otherOptions at the last, maybe user want to override some options. + if (option.otherOptions) + commands.push(option.otherOptions); + + return { + isOk: true, + params: commands + }; + } + + protected _launch(commands: string[]): void { + const commandLine = `cargo-flash ${commands.join(' ')}`; + this.executeShellCommand(this.toolType, commandLine, this._getEnv()); + } +} + /** * Custom flasher */ diff --git a/src/StringTable.ts b/src/StringTable.ts index 734b0f27..ababa2cf 100644 --- a/src/StringTable.ts +++ b/src/StringTable.ts @@ -112,6 +112,11 @@ export const view_str$flasher$downloadSpeed = [ 'Download Speed' ][langIndex]; +export const view_str$flasher$eraseAll = [ + '擦除所有', + 'Erase All' +][langIndex]; + export const view_str$flasher$binPath = [ '程序文件', 'Program File' @@ -336,7 +341,7 @@ export const source_list_desc = [ ][langIndex]; export const definition_list_desc = [ - '预处理宏定义', + '预处理器定义', 'Preprocessor Definitions' ][langIndex]; diff --git a/src/utility.ts b/src/utility.ts index 062e42d2..e691245b 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -49,6 +49,90 @@ export const TIME_ONE_MINUTE = 60 * 1000; export const TIME_ONE_HOUR = 3600 * 1000; export const TIME_ONE_DAY = 24 * 3600 * 1000; +export async function probers_install(cwd?: string) { + + let commandLine: string; + if (platform.osType() === 'win32') { + commandLine = `irm https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.ps1 | iex`; + } else if (platform.osType() === 'linux' || platform.osType() === 'darwin') { + commandLine = `curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.sh | sh`; + } else { + GlobalEvent.emit('msg', newMessage('Warning', + `Not support installer for '${platform.osType()}' platform ! Please goto https://probe.rs/docs/getting-started/installation/`)); + return; + } + + // clean old terminal + const title = 'install probe-rs'; + const index = vscode.window.terminals.findIndex((t) => t.name === title); + if (index !== -1) + vscode.window.terminals[index].dispose(); + // new terminal + const tOpts: vscode.TerminalOptions = { + name: title, + cwd: cwd + }; + if (os.platform() == 'win32') + tOpts.shellPath = 'powershell.exe'; + const terminal = vscode.window.createTerminal(tOpts); + // show terminal before sendtext + terminal.show(true); + terminal.sendText(commandLine); +} + +export function probers_listchips(): { name: string, series?: string }[] { + + const result: { name: string, series?: string }[] = []; + + try { + const cmdEnv = deepCloneObject(process.env); + platform.prependToSysEnv(cmdEnv, [ + NodePath.join(`${platform.userhome()}`, '.cargo', 'bin') // $HOME/.cargo/bin/ + ]); + const lines = child_process.execSync(`probe-rs chip list`, { + env: cmdEnv, + maxBuffer: 10 * 1024 * 1024 + }).toString().split(/\r\n|\n/); + let curSeries: string | undefined; + let variantsStarted: boolean = false; + for (const line of lines) { + if (line.startsWith(' ') || line.startsWith('\t')) { + if (line.trim() === 'Variants:') { + variantsStarted = true; + } else if (variantsStarted) { + result.push({ + name: line.trim(), + series: curSeries + }); + } + } else { + curSeries = line.trimEnd(); + variantsStarted = false; + } + } + } catch (error) { + try { + // PS C:\Users\Administrator> cargo-flash --version + // cargo flash 0.29.1 (git commit: 1cf182e) + const env = deepCloneObject(process.env); + platform.prependToSysEnv(env, [ + NodePath.join(`${platform.userhome()}`, '.cargo', 'bin') // $HOME/.cargo/bin/ + ]); + child_process.execSync(`cargo-flash --version`, { env }); + GlobalEvent.log_warn(error); + GlobalEvent.log_show(); + } catch (error) { + vscode.window.showWarningMessage( + `Not found 'cargo-flash' command. Install it now ?`, 'Yes', 'No').then(opt => { + if (opt === 'Yes') + probers_install(); + }); + } + } + + return result; +} + /** * @param len len必须是2的整数倍 */ From a0c4d2884cabdff7efb6ece6cbdedb92c83795f9 Mon Sep 17 00:00:00 2001 From: github0null Date: Mon, 1 Sep 2025 01:01:25 +0800 Subject: [PATCH 3/3] v3.25.0 update --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30aae668..21c9be10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable version changes will be recorded in this file. *** +### [v3.25.0] update + +**New**: + - `Flasher`: Support new flasher [probe-rs](https://probe.rs/docs/overview/about-probe-rs/). Used for 'ARM', 'RISCV' chips. + +*** + ### [v3.24.2] update **New**: diff --git a/package.json b/package.json index b44ab66b..7a5eb2d9 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "homepage": "https://em-ide.com", "license": "MIT", "description": "A mcu development environment for 8051/AVR/STM8/Cortex-M/MIPS/RISC-V", - "version": "3.24.2", + "version": "3.25.0", "preview": false, "engines": { "vscode": "^1.67.0"