From b13f33aa4082dac94f7c9154db5df7b2af08f25a Mon Sep 17 00:00:00 2001 From: thegecko Date: Fri, 27 Jan 2023 23:18:23 +0000 Subject: [PATCH 01/10] Load svd from asset service --- package.json | 6 ++ src/browser/extension.ts | 10 ++- src/cmsis-pack/pack-utils.ts | 53 +++++++++++ src/cmsis-pack/pdsc.ts | 168 +++++++++++++++++++++++++++++++++++ src/debug-tracker.ts | 18 +--- src/desktop/extension.ts | 10 ++- src/manifest.ts | 20 +---- src/svd-parser.ts | 6 +- src/svd-resolver.ts | 97 ++++++++++++++++++++ src/utils.ts | 13 +++ src/views/peripheral.ts | 75 +++++----------- src/vscode-utils.ts | 54 +++++++++++ yarn.lock | 31 ++++++- 13 files changed, 468 insertions(+), 93 deletions(-) create mode 100644 src/cmsis-pack/pack-utils.ts create mode 100644 src/cmsis-pack/pdsc.ts create mode 100644 src/svd-resolver.ts create mode 100644 src/vscode-utils.ts diff --git a/package.json b/package.json index 4dbc776..57df278 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "serve": "serve --cors -p 9000" }, "dependencies": { + "jszip": "^3.10.1", "node-fetch": "^2.6.7", "xml2js": "^0.4.23" }, @@ -240,6 +241,11 @@ "minimum": -1, "maximum": 32, "description": "If the gap between registers is less than this threshold (multiple of 8), combine into a single read from device. -1 means never combine registers and is very slow" + }, + "svd-viewer.packAssetUrl": { + "type": "string", + "default": "https://pack-asset-service.keil.arm.com", + "description": "Base URL for CMSIS pack assets" } } } diff --git a/src/browser/extension.ts b/src/browser/extension.ts index df00867..48cf516 100644 --- a/src/browser/extension.ts +++ b/src/browser/extension.ts @@ -1,13 +1,19 @@ +/** + * Copyright (C) 2023 Arm Limited + */ + import * as vscode from 'vscode'; import { PeripheralTreeProvider } from '../views/peripheral'; import { Commands } from '../commands'; import { DebugTracker } from '../debug-tracker'; import { SvdRegistry } from '../svd-registry'; +import { SvdResolver } from '../svd-resolver'; export const activate = async (context: vscode.ExtensionContext): Promise => { - const registry = new SvdRegistry(); const tracker = new DebugTracker(); - const peripheralTree = new PeripheralTreeProvider(tracker, registry); + const registry = new SvdRegistry(); + const resolver = new SvdResolver(registry); + const peripheralTree = new PeripheralTreeProvider(tracker, resolver); const commands = new Commands(peripheralTree); await tracker.activate(context); diff --git a/src/cmsis-pack/pack-utils.ts b/src/cmsis-pack/pack-utils.ts new file mode 100644 index 0000000..01d86b8 --- /dev/null +++ b/src/cmsis-pack/pack-utils.ts @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2023 Arm Limited + */ + +import * as vscode from 'vscode'; + +const MAIN_DELIMITER = '::'; +const VERSION_DELIMITER = '@'; + +export interface Pack { + vendor: string; + pack: string; + version?: string; +} + +export const parsePackString = (packString: string): Pack | undefined => { + let parts = packString.split(MAIN_DELIMITER); + + if (parts.length < 2) { + return undefined; + } + + const vendor = parts[0]; + let pack = parts[1]; + + parts = pack.split(VERSION_DELIMITER); + let version: string | undefined; + + if (parts.length > 1) { + pack = parts[0]; + version = parts[1]; + } + + return { + vendor, + pack, + version + }; +}; + +export const pdscFromPack = (basePath: string, pack: Pack): vscode.Uri => { + const pdscFile = `${pack.vendor}.${pack.pack}.pdsc`; + return fileFromPack(basePath, pack, pdscFile); +}; + +export const fileFromPack = (basePath: string, pack: Pack, file: string): vscode.Uri => { + if (!pack.version) { + throw new Error('CMSIS pack version is required'); + } + + const baseUri = vscode.Uri.parse(basePath); + return vscode.Uri.joinPath(baseUri, pack.vendor, pack.pack, pack.version, file); +}; diff --git a/src/cmsis-pack/pdsc.ts b/src/cmsis-pack/pdsc.ts new file mode 100644 index 0000000..ebe24ec --- /dev/null +++ b/src/cmsis-pack/pdsc.ts @@ -0,0 +1,168 @@ +/** + * Copyright (C) 2023 Arm Limited + */ + +/** + * Interface describing raw PDSC data returned from xml2js + */ +export interface PDSC { + package: { + devices?: Array<{ + family: DeviceFamily[]; + }>; + }; +} + +export interface DeviceProperties { + processor?: Array<{ + $: { + Pname: string; + Punits: string; + Dclock: string; + DcoreVersion: string; + }; + }>; + debug?: Array<{ + $?: { + __dp?: string; + __ap?: string; + __apid?: string; + address?: string; + svd?: string; + Pname?: string; + Punit?: string; + defaultResetSequence?: string; + }; + }>; +} + +export interface DeviceVariant extends DeviceProperties { + $: { + Dvariant: string; + Dname: string; + }; +} + +export interface Device extends DeviceProperties { + $: { + Dname: string; + }; + variant?: DeviceVariant[]; +} + +export interface DeviceSubFamily extends DeviceProperties { + $: { + DsubFamily: string; + }; + device?: Device[]; +} + +export interface DeviceFamily extends DeviceProperties { + $: { + Dfamily: string; + Dvendor: string; + }; + subFamily?: DeviceSubFamily[]; + device?: Device[]; +} + +// Return whether an item is an object +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const isObject = (item: any): boolean => item && typeof item === 'object' && !Array.isArray(item); + +// Merge two objects recursively, with source overwriting target when there's a conflict +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const deepMerge = (target: { [key: string]: any }, source: T): T => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const output = Object.assign({} as any, target); + + Object.keys(source).forEach(key => { + if (isObject(source[key]) && key in target) { + output[key] = deepMerge(target[key], source[key]); + } else { + Object.assign(output, { [key]: source[key] }); + } + }); + + return output; +}; + +// Recurse DeviceFamily and DeviceSubFamily to find Devices and DeviceVariants, merging them as we go +export const getDevices = (pack: PDSC): Array => { + const result: Device[] = []; + + const addDevice = (device: Device, parent: DeviceProperties = {}) => { + const entry = deepMerge(parent, device); + result.push(entry); + }; + + const walkDevices = (devices?: Device[], parent: DeviceProperties = {}) => { + if (devices) { + for (const device of devices) { + if (device.variant) { + // If there are variants, add them instead of the parent device + for (const variant of device.variant) { + // Merge in device + const variantParent = deepMerge(parent, device); + addDevice(variant, variantParent); + } + } else { + addDevice(device, parent); + } + } + } + }; + + // Walk the DeviceFamily array + if (pack.package.devices) { + for (const device of pack.package.devices) { + for (const family of device.family) { + walkDevices(family.device, family); + + // Walk the DeviceSubFamily array + if (family.subFamily) { + for (const sub of family.subFamily) { + const parent = deepMerge(family, sub); + walkDevices(sub.device, parent); + } + } + } + } + } + + return result; +}; + +/** + * Return svd path (or undefined) for specified device + * If processorName specified, matching svd file is returned, else the first one + */ +export const getSvdPath = (device: Device, processorName?: string): string | undefined => { + if (device.debug) { + const filtered = filterByProcessor(device.debug, processorName); + for (const debug of filtered) { + if (debug.$ && debug.$.svd) { + return debug.$.svd; + } + } + } + + return undefined; +}; + +const filterByProcessor = (theArray: T[], processorName: string | undefined): T[] => { + // If processorName not specified, return all items + if (!processorName) { + return theArray; + } + + return filter(theArray, item => item.$?.Pname === processorName) as T[]; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const filter = (theArray: T[], predicate: (item: T) => boolean): T[] => { + const filtered = theArray.filter(item => item.$ && predicate(item)); + + // If no items match, return them all + return filtered.length > 0 ? filtered as T[] : theArray; +}; diff --git a/src/debug-tracker.ts b/src/debug-tracker.ts index 00f8b79..5e6ef87 100644 --- a/src/debug-tracker.ts +++ b/src/debug-tracker.ts @@ -1,19 +1,5 @@ -/* - * Copyright 2017-2019 Marcel Ball - * https://github.com/Marus/cortex-debug - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without - * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. +/** + * Copyright (C) 2023 Arm Limited */ import * as vscode from 'vscode'; diff --git a/src/desktop/extension.ts b/src/desktop/extension.ts index df00867..48cf516 100644 --- a/src/desktop/extension.ts +++ b/src/desktop/extension.ts @@ -1,13 +1,19 @@ +/** + * Copyright (C) 2023 Arm Limited + */ + import * as vscode from 'vscode'; import { PeripheralTreeProvider } from '../views/peripheral'; import { Commands } from '../commands'; import { DebugTracker } from '../debug-tracker'; import { SvdRegistry } from '../svd-registry'; +import { SvdResolver } from '../svd-resolver'; export const activate = async (context: vscode.ExtensionContext): Promise => { - const registry = new SvdRegistry(); const tracker = new DebugTracker(); - const peripheralTree = new PeripheralTreeProvider(tracker, registry); + const registry = new SvdRegistry(); + const resolver = new SvdResolver(registry); + const peripheralTree = new PeripheralTreeProvider(tracker, resolver); const commands = new Commands(peripheralTree); await tracker.activate(context); diff --git a/src/manifest.ts b/src/manifest.ts index 3b8aa90..911c52a 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -1,19 +1,5 @@ -/* - * Copyright 2017-2019 Marcel Ball - * https://github.com/Marus/cortex-debug - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without - * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. +/** + * Copyright (C) 2023 Arm Limited */ export const PACKAGE_NAME = 'svd-viewer'; @@ -23,3 +9,5 @@ export const CONFIG_DEVICE = 'deviceConfig'; export const DEFAULT_DEVICE = 'device'; export const CONFIG_ADDRGAP = 'svdAddrGapThreshold'; export const DEFAULT_ADDRGAP = 16; +export const CONFIG_ASSET_PATH = 'packAssetUrl'; +export const DEFAULT_ASSET_PATH = 'https://pack-asset-service.keil.arm.com'; diff --git a/src/svd-parser.ts b/src/svd-parser.ts index e81342a..b6b6802 100644 --- a/src/svd-parser.ts +++ b/src/svd-parser.ts @@ -48,11 +48,11 @@ const accessTypeFromString = (type: string): AccessType => { } }; -interface Peripheral { +export interface Peripheral { name: string[]; } -interface Device { +export interface Device { resetValue: string[]; size: string[]; access: string[]; @@ -61,7 +61,7 @@ interface Device { }[]; } -interface SvdData { +export interface SvdData { device: Device; } diff --git a/src/svd-resolver.ts b/src/svd-resolver.ts new file mode 100644 index 0000000..2450497 --- /dev/null +++ b/src/svd-resolver.ts @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2023 Arm Limited + */ + +import * as vscode from 'vscode'; +import * as manifest from './manifest'; +import { isAbsolute, join, normalize } from 'path'; +import { parseStringPromise } from 'xml2js'; +import { SvdRegistry } from './svd-registry'; +import { parsePackString, pdscFromPack, fileFromPack } from './cmsis-pack/pack-utils'; +import { PDSC, Device, DeviceVariant, getDevices, getSvdPath } from './cmsis-pack/pdsc'; +import { readFromUrl } from './utils'; +import { getSelection } from './vscode-utils'; + +export class SvdResolver { + public constructor(protected registry: SvdRegistry) { + } + + public async resolve(svdPath: string | undefined, device: string | undefined, wsFolderPath?: vscode.Uri): Promise { + if (!svdPath && !device) { + return undefined; + } + + try { + if (svdPath) { + const pack = parsePackString(svdPath); + + if (pack) { + const assetBase = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_ASSET_PATH) || manifest.DEFAULT_ASSET_PATH; + const pdscPath = pdscFromPack(assetBase, pack); + const pdscBuffer = await readFromUrl(pdscPath.toString()); + + if (!pdscBuffer) { + throw new Error(`No data loaded from ${pdscPath.toString()}`); + } + + const decoder = new TextDecoder(); + const pdscString = decoder.decode(pdscBuffer); + + const pdsc = await parseStringPromise(pdscString, { + explicitCharkey: true + }) as PDSC; + + // Load devices from pack + const devices = getDevices(pdsc); + const deviceMap = new Map(); + for (const dev of devices) { + const deviceName = (dev as DeviceVariant).$.Dvariant || dev.$.Dname; + deviceMap.set(deviceName, device); + } + + let packDevice: Device | undefined; + + if (device && deviceMap.has(device)) { + packDevice = deviceMap.get(device); + } else { + // Ask user which device to use + const items = [...deviceMap.keys()]; + const selected = await getSelection('Select a device', items, device); + if (selected) { + if (!deviceMap.has(selected)) { + throw new Error(`Device not found: ${selected}`); + } + + packDevice = deviceMap.get(selected); + } + } + + if (!packDevice) { + return undefined; + } + + const svdFile = getSvdPath(packDevice); + if (svdFile) { + const svdUri = fileFromPack(assetBase, pack, svdFile); + svdPath = svdUri.toString(); + } + } else if (vscode.env.uiKind === vscode.UIKind.Desktop && !svdPath.startsWith('http')) { + // On desktop, ensure full path + if (!isAbsolute(svdPath) && wsFolderPath) { + svdPath = normalize(join(wsFolderPath.fsPath, svdPath)); + } + } + } else if (device) { + svdPath = this.registry.getSVDFile(device); + if (!svdPath) { + svdPath = await this.registry.getSVDFileFromCortexDebug(device); + } + } + } catch(e) { + // eslint-disable-next-line no-console + console.warn(e); + } + + return svdPath; + } +} diff --git a/src/utils.ts b/src/utils.ts index 45e734b..fc4106e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -118,3 +118,16 @@ export function parseDimIndex(spec: string, count: number): string[] { return []; } + +export const readFromUrl = async (url: string): Promise => { + // Download using fetch + const response = await fetch(url); + if (!response.ok) { + const body = await response.text(); + const msg = `Request to ${url} failed. Status="${response.status}". Body="${body}".`; + throw new Error(msg); + } + + const buffer = await response.arrayBuffer(); + return buffer; +}; diff --git a/src/views/peripheral.ts b/src/views/peripheral.ts index 00b469b..24d7fb3 100644 --- a/src/views/peripheral.ts +++ b/src/views/peripheral.ts @@ -17,7 +17,6 @@ */ import * as vscode from 'vscode'; -import * as path from 'path'; import * as manifest from '../manifest'; import { parseStringPromise } from 'xml2js'; import { BaseNode, PeripheralBaseNode } from './nodes/basenode'; @@ -27,7 +26,9 @@ import { NodeSetting } from '../common'; import { SVDParser } from '../svd-parser'; import { AddrRange } from '../addrranges'; import { DebugTracker } from '../debug-tracker'; -import { SvdRegistry } from '../svd-registry'; +import { SvdResolver } from '../svd-resolver'; +import { readFromUrl } from '../utils'; +import { uriExists } from '../vscode-utils'; const STATE_FILENAME = '.svd-viewer.state.json'; @@ -39,32 +40,18 @@ const pathToUri = (path: string): vscode.Uri => { } }; -const readFromUrl = async (url: string): Promise => { - // Download using fetch - const response = await fetch(url); - if (!response.ok) { - const body = await response.text(); - const msg = `Request to ${url} failed. Status="${response.status}". Body="${body}".`; - throw new Error(msg); - } - - return response; -}; - export class PeripheralTreeForSession extends PeripheralBaseNode { public myTreeItem: vscode.TreeItem; private peripherials: PeripheralNode[] = []; private loaded = false; private errMessage = 'No SVD file loaded'; - private wsFolderPath: vscode.Uri | undefined; constructor( public session: vscode.DebugSession, public state: vscode.TreeItemCollapsibleState, + private wsFolderPath: vscode.Uri | undefined, private fireCb: () => void) { super(); - // Remember the path as it may not be available when session ends - this.wsFolderPath = this.session.workspaceFolder ? this.session.workspaceFolder.uri : vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0].uri; this.myTreeItem = new vscode.TreeItem(this.session.name, this.state); } @@ -79,14 +66,13 @@ export class PeripheralTreeForSession extends PeripheralBaseNode { private async loadSvdState(): Promise { const stateUri = this.getSvdStateUri(); if (stateUri) { - try { + const exists = await uriExists(stateUri); + if (exists) { await vscode.workspace.fs.stat(stateUri); const data = await vscode.workspace.fs.readFile(stateUri); const decoder = new TextDecoder(); const text = decoder.decode(data); return JSON.parse(text); - } catch { - // File may not exist } } @@ -116,24 +102,16 @@ export class PeripheralTreeForSession extends PeripheralBaseNode { return state; } - private async loadSVD(svd: string, gapThreshold: number): Promise { + private async createPeripherals(svdPath: string, gapThreshold: number): Promise { let svdData: string | undefined; try { let contents: ArrayBuffer | undefined; - if (svd.startsWith('http')) { - const response = await readFromUrl(svd); - contents = await response.arrayBuffer(); + if (svdPath.startsWith('http')) { + contents = await readFromUrl(svdPath); } else { - if (vscode.env.uiKind === vscode.UIKind.Desktop) { - // On desktop, ensure full path - if (!path.isAbsolute(svd) && this.wsFolderPath) { - svd = path.normalize(path.join(this.wsFolderPath.fsPath, svd)); - } - } - - const uri = pathToUri(svd); + const uri = pathToUri(svdPath); contents = await vscode.workspace.fs.readFile(uri); } @@ -151,7 +129,7 @@ export class PeripheralTreeForSession extends PeripheralBaseNode { return; } - this.errMessage = `Loading ${svd}`; + this.errMessage = `Loading ${svdPath}`; try { this.peripherials = await SVDParser.parseSVD(this.session, JSON.parse(svdData), gapThreshold); @@ -219,7 +197,7 @@ export class PeripheralTreeForSession extends PeripheralBaseNode { return undefined; } - public async sessionStarted(svd: string, thresh: number): Promise { // Never rejects + public async sessionStarted(svdPath: string, thresh: number): Promise { // Never rejects if (((typeof thresh) === 'number') && (thresh < 0)) { thresh = -1; // Never merge register reads even if adjacent } else { @@ -231,7 +209,7 @@ export class PeripheralTreeForSession extends PeripheralBaseNode { this.fireCb(); try { - await this.loadSVD(svd, thresh); + await this.createPeripherals(svdPath, thresh); const settings = await this.loadSvdState(); settings.forEach((s: NodeSetting) => { @@ -247,7 +225,7 @@ export class PeripheralTreeForSession extends PeripheralBaseNode { this.peripherials.sort(PeripheralNode.compare); this.fireCb(); } catch(e) { - this.errMessage = `Unable to parse SVD file ${svd}: ${(e as Error).message}`; + this.errMessage = `Unable to parse SVD file ${svdPath}: ${(e as Error).message}`; vscode.window.showErrorMessage(this.errMessage); if (vscode.debug.activeDebugConsole) { vscode.debug.activeDebugConsole.appendLine(this.errMessage); @@ -274,7 +252,7 @@ export class PeripheralTreeProvider implements vscode.TreeDataProvider(); protected oldState = new Map (); - constructor(tracker: DebugTracker, protected registry: SvdRegistry) { + constructor(tracker: DebugTracker, protected resolver: SvdResolver) { tracker.onWillStartSession(session => this.debugSessionStarted(session)); tracker.onDidStopSession(session => this.debugSessionTerminated(session)); } @@ -320,22 +298,15 @@ export class PeripheralTreeProvider implements vscode.TreeDataProvider { const svdConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_SVD_PATH) || manifest.DEFAULT_SVD_PATH; - let svd = session.configuration[svdConfig]; + const svd = session.configuration[svdConfig]; - if (!svd) { - // Try loading from device support pack - const deviceConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_DEVICE) || manifest.DEFAULT_DEVICE; - const device = session.configuration[deviceConfig]; + const deviceConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_DEVICE) || manifest.DEFAULT_DEVICE; + const device = session.configuration[deviceConfig]; - if (device) { - svd = this.registry.getSVDFile(device); - if (!svd) { - svd = await this.registry.getSVDFileFromCortexDebug(device); - } - } - } + const wsFolderPath = session.workspaceFolder ? session.workspaceFolder.uri : vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0].uri; + const svdPath = await this.resolver.resolve(svd, device, wsFolderPath); - if (!svd) { + if (!svdPath) { return; } @@ -349,7 +320,7 @@ export class PeripheralTreeProvider implements vscode.TreeDataProvider { + const regs = new PeripheralTreeForSession(session, state, wsFolderPath, () => { this._onDidChangeTreeData.fire(undefined); }); @@ -361,7 +332,7 @@ export class PeripheralTreeProvider implements vscode.TreeDataProvider => { + try { + await vscode.workspace.fs.stat(uri); + return true; + } catch { + return false; + } +}; + +interface QuickPickItem extends vscode.QuickPickItem { + value?: string; +} + +export const getSelection = async (title: string, items: QuickPickItem[], value?: string): Promise => { + const disposables: vscode.Disposable[] = []; + try { + return await new Promise(resolve => { + const input = vscode.window.createQuickPick(); + input.title = title; + input.items = items; + if (value) { + input.value = value; + } + + for (const item of items) { + if (item.picked === true) { + input.value = item.label; + break; + } + } + + disposables.push( + input.onDidChangeSelection(items => { + const item = items[0] as QuickPickItem; + resolve(item.value || item.label); + input.hide(); + }), + input.onDidHide(() => { + resolve(undefined); + input.dispose(); + }) + ); + input.show(); + }); + } finally { + disposables.forEach(d => d.dispose()); + } +}; diff --git a/yarn.lock b/yarn.lock index 29df196..8ba0d04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1741,6 +1741,11 @@ ignore@^5.1.8, ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -1929,6 +1934,16 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= +jszip@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + keygrip@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" @@ -2036,6 +2051,13 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + linkify-it@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" @@ -2375,6 +2397,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -2600,7 +2627,7 @@ read@^1.0.7: dependencies: mute-stream "~0.0.4" -readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.5: +readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -2800,7 +2827,7 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -setimmediate@^1.0.4: +setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== From 7dbaaa95081981a1b2ec9c575baebe03b4e9f06a Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 10:16:36 +0000 Subject: [PATCH 02/10] Rename device --- package.json | 2 +- src/manifest.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 57df278..a8e2bd2 100644 --- a/package.json +++ b/package.json @@ -231,7 +231,7 @@ }, "svd-viewer.deviceConfig": { "type": "string", - "default": "device", + "default": "deviceName", "description": "Debug configuration key to use to get the device" }, "svd-viewer.svdAddrGapThreshold": { diff --git a/src/manifest.ts b/src/manifest.ts index 911c52a..1a50d0f 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -6,7 +6,7 @@ export const PACKAGE_NAME = 'svd-viewer'; export const CONFIG_SVD_PATH = 'svdPathConfig'; export const DEFAULT_SVD_PATH = 'svdPath'; export const CONFIG_DEVICE = 'deviceConfig'; -export const DEFAULT_DEVICE = 'device'; +export const DEFAULT_DEVICE = 'deviceName'; export const CONFIG_ADDRGAP = 'svdAddrGapThreshold'; export const DEFAULT_ADDRGAP = 16; export const CONFIG_ASSET_PATH = 'packAssetUrl'; From b2112fb723c295134be1b91e5fff3698be5b19e5 Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 10:31:37 +0000 Subject: [PATCH 03/10] Fixed stop event --- src/debug-tracker.ts | 6 +++--- src/views/peripheral.ts | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/debug-tracker.ts b/src/debug-tracker.ts index 5e6ef87..d451c73 100644 --- a/src/debug-tracker.ts +++ b/src/debug-tracker.ts @@ -14,8 +14,8 @@ export class DebugTracker { private _onWillStopSession: vscode.EventEmitter = new vscode.EventEmitter(); public readonly onWillStopSession: vscode.Event = this._onWillStopSession.event; - private _onDidStopSession: vscode.EventEmitter = new vscode.EventEmitter(); - public readonly onDidStopSession: vscode.Event = this._onDidStopSession.event; + private _onDidStopDebug: vscode.EventEmitter = new vscode.EventEmitter(); + public readonly onDidStopDebug: vscode.Event = this._onDidStopDebug.event; public async activate(context: vscode.ExtensionContext): Promise { const createDebugAdapterTracker = (session: vscode.DebugSession): vscode.DebugAdapterTracker => { @@ -24,7 +24,7 @@ export class DebugTracker { onWillStopSession: () => this._onWillStopSession.fire(session), onDidSendMessage: message => { if (message.type === 'event' && message.event === 'stopped') { - this._onDidStopSession.fire(session); + this._onDidStopDebug.fire(session); } } }; diff --git a/src/views/peripheral.ts b/src/views/peripheral.ts index 24d7fb3..11634cf 100644 --- a/src/views/peripheral.ts +++ b/src/views/peripheral.ts @@ -23,7 +23,7 @@ import { BaseNode, PeripheralBaseNode } from './nodes/basenode'; import { PeripheralNode } from './nodes/peripheralnode'; import { MessageNode } from './nodes/messagenode'; import { NodeSetting } from '../common'; -import { SVDParser } from '../svd-parser'; +import { SvdData, SVDParser } from '../svd-parser'; import { AddrRange } from '../addrranges'; import { DebugTracker } from '../debug-tracker'; import { SvdResolver } from '../svd-resolver'; @@ -103,7 +103,7 @@ export class PeripheralTreeForSession extends PeripheralBaseNode { } private async createPeripherals(svdPath: string, gapThreshold: number): Promise { - let svdData: string | undefined; + let svdData: SvdData | undefined; try { let contents: ArrayBuffer | undefined; @@ -132,7 +132,7 @@ export class PeripheralTreeForSession extends PeripheralBaseNode { this.errMessage = `Loading ${svdPath}`; try { - this.peripherials = await SVDParser.parseSVD(this.session, JSON.parse(svdData), gapThreshold); + this.peripherials = await SVDParser.parseSVD(this.session, svdData, gapThreshold); this.loaded = true; } catch(e) { this.peripherials = []; @@ -254,7 +254,8 @@ export class PeripheralTreeProvider implements vscode.TreeDataProvider this.debugSessionStarted(session)); - tracker.onDidStopSession(session => this.debugSessionTerminated(session)); + tracker.onWillStopSession(session => this.debugSessionTerminated(session)); + tracker.onDidStopDebug(session => this.debugStopped(session)); } public async activate(context: vscode.ExtensionContext): Promise { @@ -355,6 +356,13 @@ export class PeripheralTreeProvider implements vscode.TreeDataProvider 0); } + public debugStopped(session: vscode.DebugSession) { + const regs = this.sessionPeripheralsMap.get(session.id); + if (regs) { // We are called even before the session has started, as part of reset + regs.updateData(); + } + } + public togglePinPeripheral(node: PeripheralBaseNode): void { const session = vscode.debug.activeDebugSession; if (session) { From 395a2ff5388c2e66d233c4baaeb596ec8abf33ec Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 10:45:04 +0000 Subject: [PATCH 04/10] Rename state file --- src/views/peripheral.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/peripheral.ts b/src/views/peripheral.ts index 11634cf..404c752 100644 --- a/src/views/peripheral.ts +++ b/src/views/peripheral.ts @@ -30,7 +30,7 @@ import { SvdResolver } from '../svd-resolver'; import { readFromUrl } from '../utils'; import { uriExists } from '../vscode-utils'; -const STATE_FILENAME = '.svd-viewer.state.json'; +const STATE_FILENAME = '.svd-viewer.json'; const pathToUri = (path: string): vscode.Uri => { try { From 61047be1cb4f9eb2b4c154f9518e64dd0d2a89b5 Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 10:45:19 +0000 Subject: [PATCH 05/10] Fix asset service access --- src/svd-resolver.ts | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/svd-resolver.ts b/src/svd-resolver.ts index 2450497..454dacb 100644 --- a/src/svd-resolver.ts +++ b/src/svd-resolver.ts @@ -26,6 +26,7 @@ export class SvdResolver { const pack = parsePackString(svdPath); if (pack) { + const getDeviceName = (device: Device) => (device as DeviceVariant).$.Dvariant || device.$.Dname; const assetBase = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_ASSET_PATH) || manifest.DEFAULT_ASSET_PATH; const pdscPath = pdscFromPack(assetBase, pack); const pdscBuffer = await readFromUrl(pdscPath.toString()); @@ -45,36 +46,41 @@ export class SvdResolver { const devices = getDevices(pdsc); const deviceMap = new Map(); for (const dev of devices) { - const deviceName = (dev as DeviceVariant).$.Dvariant || dev.$.Dname; - deviceMap.set(deviceName, device); + deviceMap.set(getDeviceName(dev), dev); } let packDevice: Device | undefined; if (device && deviceMap.has(device)) { packDevice = deviceMap.get(device); + } else if (!device && devices.length == 1) { + packDevice = devices[0]; } else { // Ask user which device to use const items = [...deviceMap.keys()]; const selected = await getSelection('Select a device', items, device); - if (selected) { - if (!deviceMap.has(selected)) { - throw new Error(`Device not found: ${selected}`); - } - - packDevice = deviceMap.get(selected); + if (!selected) { + return; + } + + if (!deviceMap.has(selected)) { + throw new Error(`Device not found: ${selected}`); } + + packDevice = deviceMap.get(selected); } if (!packDevice) { - return undefined; + return; } const svdFile = getSvdPath(packDevice); - if (svdFile) { - const svdUri = fileFromPack(assetBase, pack, svdFile); - svdPath = svdUri.toString(); + if (!svdFile) { + throw new Error(`Unable to load device ${getDeviceName(packDevice)}`); } + + const svdUri = fileFromPack(assetBase, pack, svdFile); + svdPath = svdUri.toString(); } else if (vscode.env.uiKind === vscode.UIKind.Desktop && !svdPath.startsWith('http')) { // On desktop, ensure full path if (!isAbsolute(svdPath) && wsFolderPath) { From 445aa975e31471fc1fed7c42061469e1ea2f0188 Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 10:53:30 +0000 Subject: [PATCH 06/10] Add processorName --- package.json | 7 ++++++- src/manifest.ts | 2 ++ src/svd-resolver.ts | 33 +++++++++++++++++++++------------ src/views/peripheral.ts | 8 +------- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index a8e2bd2..66b0a7a 100644 --- a/package.json +++ b/package.json @@ -232,7 +232,12 @@ "svd-viewer.deviceConfig": { "type": "string", "default": "deviceName", - "description": "Debug configuration key to use to get the device" + "description": "Debug configuration key to use to get the device name" + }, + "svd-viewer.processorConfig": { + "type": "string", + "default": "processorName", + "description": "Debug configuration key to use to get the procerssor name" }, "svd-viewer.svdAddrGapThreshold": { "type": "number", diff --git a/src/manifest.ts b/src/manifest.ts index 1a50d0f..c724934 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -7,6 +7,8 @@ export const CONFIG_SVD_PATH = 'svdPathConfig'; export const DEFAULT_SVD_PATH = 'svdPath'; export const CONFIG_DEVICE = 'deviceConfig'; export const DEFAULT_DEVICE = 'deviceName'; +export const CONFIG_PROCESSOR = 'processorConfig'; +export const DEFAULT_PROCESSOR = 'processorName'; export const CONFIG_ADDRGAP = 'svdAddrGapThreshold'; export const DEFAULT_ADDRGAP = 16; export const CONFIG_ASSET_PATH = 'packAssetUrl'; diff --git a/src/svd-resolver.ts b/src/svd-resolver.ts index 454dacb..06441f7 100644 --- a/src/svd-resolver.ts +++ b/src/svd-resolver.ts @@ -16,8 +16,17 @@ export class SvdResolver { public constructor(protected registry: SvdRegistry) { } - public async resolve(svdPath: string | undefined, device: string | undefined, wsFolderPath?: vscode.Uri): Promise { - if (!svdPath && !device) { + public async resolve(session: vscode.DebugSession, wsFolderPath?: vscode.Uri): Promise { + const svdConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_SVD_PATH) || manifest.DEFAULT_SVD_PATH; + let svdPath = session.configuration[svdConfig]; + + const deviceConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_DEVICE) || manifest.DEFAULT_DEVICE; + const deviceName = session.configuration[deviceConfig]; + + const processorConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_PROCESSOR) || manifest.DEFAULT_PROCESSOR; + const processorName = session.configuration[processorConfig]; + + if (!svdPath && !deviceName) { return undefined; } @@ -45,20 +54,20 @@ export class SvdResolver { // Load devices from pack const devices = getDevices(pdsc); const deviceMap = new Map(); - for (const dev of devices) { - deviceMap.set(getDeviceName(dev), dev); + for (const device of devices) { + deviceMap.set(getDeviceName(device), device); } let packDevice: Device | undefined; - if (device && deviceMap.has(device)) { - packDevice = deviceMap.get(device); - } else if (!device && devices.length == 1) { + if (deviceName && deviceMap.has(deviceName)) { + packDevice = deviceMap.get(deviceName); + } else if (!deviceName && devices.length == 1) { packDevice = devices[0]; } else { // Ask user which device to use const items = [...deviceMap.keys()]; - const selected = await getSelection('Select a device', items, device); + const selected = await getSelection('Select a device', items, deviceName); if (!selected) { return; } @@ -74,7 +83,7 @@ export class SvdResolver { return; } - const svdFile = getSvdPath(packDevice); + const svdFile = getSvdPath(packDevice, processorName); if (!svdFile) { throw new Error(`Unable to load device ${getDeviceName(packDevice)}`); } @@ -87,10 +96,10 @@ export class SvdResolver { svdPath = normalize(join(wsFolderPath.fsPath, svdPath)); } } - } else if (device) { - svdPath = this.registry.getSVDFile(device); + } else if (deviceName) { + svdPath = this.registry.getSVDFile(deviceName); if (!svdPath) { - svdPath = await this.registry.getSVDFileFromCortexDebug(device); + svdPath = await this.registry.getSVDFileFromCortexDebug(deviceName); } } } catch(e) { diff --git a/src/views/peripheral.ts b/src/views/peripheral.ts index 404c752..b570039 100644 --- a/src/views/peripheral.ts +++ b/src/views/peripheral.ts @@ -298,14 +298,8 @@ export class PeripheralTreeProvider implements vscode.TreeDataProvider { - const svdConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_SVD_PATH) || manifest.DEFAULT_SVD_PATH; - const svd = session.configuration[svdConfig]; - - const deviceConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_DEVICE) || manifest.DEFAULT_DEVICE; - const device = session.configuration[deviceConfig]; - const wsFolderPath = session.workspaceFolder ? session.workspaceFolder.uri : vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0].uri; - const svdPath = await this.resolver.resolve(svd, device, wsFolderPath); + const svdPath = await this.resolver.resolve(session, wsFolderPath); if (!svdPath) { return; From 3e5ab678ba72fb78d4fabd06e599c06352f95fc3 Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 11:06:23 +0000 Subject: [PATCH 07/10] Fix device selection --- src/svd-resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svd-resolver.ts b/src/svd-resolver.ts index 06441f7..3fa3a06 100644 --- a/src/svd-resolver.ts +++ b/src/svd-resolver.ts @@ -66,7 +66,7 @@ export class SvdResolver { packDevice = devices[0]; } else { // Ask user which device to use - const items = [...deviceMap.keys()]; + const items = [...deviceMap.keys()].map(label => ({ label })); const selected = await getSelection('Select a device', items, deviceName); if (!selected) { return; From ae057618f03cde4acaa323443ab846896420860b Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 11:16:35 +0000 Subject: [PATCH 08/10] Processor selection --- src/cmsis-pack/pdsc.ts | 17 +++++++++++++++++ src/svd-resolver.ts | 28 ++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/cmsis-pack/pdsc.ts b/src/cmsis-pack/pdsc.ts index ebe24ec..85b4e74 100644 --- a/src/cmsis-pack/pdsc.ts +++ b/src/cmsis-pack/pdsc.ts @@ -133,6 +133,23 @@ export const getDevices = (pack: PDSC): Array => { return result; }; +/** + * Return list of processor names available for specified device + */ +export const getProcessors = (device: Device): string[] => { + const processors: string[] = []; + + if (device.processor) { + for (const processor of device.processor) { + if (processor.$ && processor.$.Pname) { + processors.push(processor.$.Pname); + } + } + } + + return processors; +}; + /** * Return svd path (or undefined) for specified device * If processorName specified, matching svd file is returned, else the first one diff --git a/src/svd-resolver.ts b/src/svd-resolver.ts index 3fa3a06..e2660e2 100644 --- a/src/svd-resolver.ts +++ b/src/svd-resolver.ts @@ -8,7 +8,7 @@ import { isAbsolute, join, normalize } from 'path'; import { parseStringPromise } from 'xml2js'; import { SvdRegistry } from './svd-registry'; import { parsePackString, pdscFromPack, fileFromPack } from './cmsis-pack/pack-utils'; -import { PDSC, Device, DeviceVariant, getDevices, getSvdPath } from './cmsis-pack/pdsc'; +import { PDSC, Device, DeviceVariant, getDevices, getSvdPath, getProcessors } from './cmsis-pack/pdsc'; import { readFromUrl } from './utils'; import { getSelection } from './vscode-utils'; @@ -24,7 +24,7 @@ export class SvdResolver { const deviceName = session.configuration[deviceConfig]; const processorConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_PROCESSOR) || manifest.DEFAULT_PROCESSOR; - const processorName = session.configuration[processorConfig]; + let processorName = session.configuration[processorConfig]; if (!svdPath && !deviceName) { return undefined; @@ -58,6 +58,7 @@ export class SvdResolver { deviceMap.set(getDeviceName(device), device); } + // Select device let packDevice: Device | undefined; if (deviceName && deviceMap.has(deviceName)) { @@ -83,6 +84,29 @@ export class SvdResolver { return; } + // Load processors for device + const processors = getProcessors(packDevice); + + // Select processor + if (processorName && processors.includes(processorName)) { + // Keep existing processor name + } else if (!processorName && processors.length == 1) { + processorName = processors[0]; + } else { + // Ask user which processor to use + const items = processors.map(label => ({ label })); + const selected = await getSelection('Select a processor', items, processorName); + if (!selected) { + return; + } + + if (!processors.includes(selected)) { + throw new Error(`Processor not found: ${selected}`); + } + + processorName = selected; + } + const svdFile = getSvdPath(packDevice, processorName); if (!svdFile) { throw new Error(`Unable to load device ${getDeviceName(packDevice)}`); From 6f36bcec1a09cd3a70471048c4cd9771f02da899 Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 11:21:55 +0000 Subject: [PATCH 09/10] Refactor --- src/svd-resolver.ts | 168 +++++++++++++++++++++------------------- src/views/peripheral.ts | 2 +- 2 files changed, 88 insertions(+), 82 deletions(-) diff --git a/src/svd-resolver.ts b/src/svd-resolver.ts index e2660e2..7f2888b 100644 --- a/src/svd-resolver.ts +++ b/src/svd-resolver.ts @@ -7,7 +7,7 @@ import * as manifest from './manifest'; import { isAbsolute, join, normalize } from 'path'; import { parseStringPromise } from 'xml2js'; import { SvdRegistry } from './svd-registry'; -import { parsePackString, pdscFromPack, fileFromPack } from './cmsis-pack/pack-utils'; +import { parsePackString, pdscFromPack, fileFromPack, Pack } from './cmsis-pack/pack-utils'; import { PDSC, Device, DeviceVariant, getDevices, getSvdPath, getProcessors } from './cmsis-pack/pdsc'; import { readFromUrl } from './utils'; import { getSelection } from './vscode-utils'; @@ -24,7 +24,7 @@ export class SvdResolver { const deviceName = session.configuration[deviceConfig]; const processorConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_PROCESSOR) || manifest.DEFAULT_PROCESSOR; - let processorName = session.configuration[processorConfig]; + const processorName = session.configuration[processorConfig]; if (!svdPath && !deviceName) { return undefined; @@ -35,85 +35,7 @@ export class SvdResolver { const pack = parsePackString(svdPath); if (pack) { - const getDeviceName = (device: Device) => (device as DeviceVariant).$.Dvariant || device.$.Dname; - const assetBase = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_ASSET_PATH) || manifest.DEFAULT_ASSET_PATH; - const pdscPath = pdscFromPack(assetBase, pack); - const pdscBuffer = await readFromUrl(pdscPath.toString()); - - if (!pdscBuffer) { - throw new Error(`No data loaded from ${pdscPath.toString()}`); - } - - const decoder = new TextDecoder(); - const pdscString = decoder.decode(pdscBuffer); - - const pdsc = await parseStringPromise(pdscString, { - explicitCharkey: true - }) as PDSC; - - // Load devices from pack - const devices = getDevices(pdsc); - const deviceMap = new Map(); - for (const device of devices) { - deviceMap.set(getDeviceName(device), device); - } - - // Select device - let packDevice: Device | undefined; - - if (deviceName && deviceMap.has(deviceName)) { - packDevice = deviceMap.get(deviceName); - } else if (!deviceName && devices.length == 1) { - packDevice = devices[0]; - } else { - // Ask user which device to use - const items = [...deviceMap.keys()].map(label => ({ label })); - const selected = await getSelection('Select a device', items, deviceName); - if (!selected) { - return; - } - - if (!deviceMap.has(selected)) { - throw new Error(`Device not found: ${selected}`); - } - - packDevice = deviceMap.get(selected); - } - - if (!packDevice) { - return; - } - - // Load processors for device - const processors = getProcessors(packDevice); - - // Select processor - if (processorName && processors.includes(processorName)) { - // Keep existing processor name - } else if (!processorName && processors.length == 1) { - processorName = processors[0]; - } else { - // Ask user which processor to use - const items = processors.map(label => ({ label })); - const selected = await getSelection('Select a processor', items, processorName); - if (!selected) { - return; - } - - if (!processors.includes(selected)) { - throw new Error(`Processor not found: ${selected}`); - } - - processorName = selected; - } - - const svdFile = getSvdPath(packDevice, processorName); - if (!svdFile) { - throw new Error(`Unable to load device ${getDeviceName(packDevice)}`); - } - - const svdUri = fileFromPack(assetBase, pack, svdFile); - svdPath = svdUri.toString(); + svdPath = await this.loadFromPack(pack, deviceName, processorName); } else if (vscode.env.uiKind === vscode.UIKind.Desktop && !svdPath.startsWith('http')) { // On desktop, ensure full path if (!isAbsolute(svdPath) && wsFolderPath) { @@ -133,4 +55,88 @@ export class SvdResolver { return svdPath; } + + protected async loadFromPack(pack: Pack, deviceName: string | undefined, processorName: string | undefined): Promise { + + const getDeviceName = (device: Device) => (device as DeviceVariant).$.Dvariant || device.$.Dname; + + const assetBase = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_ASSET_PATH) || manifest.DEFAULT_ASSET_PATH; + const pdscPath = pdscFromPack(assetBase, pack); + const pdscBuffer = await readFromUrl(pdscPath.toString()); + + if (!pdscBuffer) { + throw new Error(`No data loaded from ${pdscPath.toString()}`); + } + + const decoder = new TextDecoder(); + const pdscString = decoder.decode(pdscBuffer); + + const pdsc = await parseStringPromise(pdscString, { + explicitCharkey: true + }) as PDSC; + + // Load devices from pack + const devices = getDevices(pdsc); + const deviceMap = new Map(); + for (const device of devices) { + deviceMap.set(getDeviceName(device), device); + } + + // Select device + let packDevice: Device | undefined; + + if (deviceName && deviceMap.has(deviceName)) { + packDevice = deviceMap.get(deviceName); + } else if (!deviceName && devices.length == 1) { + packDevice = devices[0]; + } else { + // Ask user which device to use + const items = [...deviceMap.keys()].map(label => ({ label })); + const selected = await getSelection('Select a device', items, deviceName); + if (!selected) { + return; + } + + if (!deviceMap.has(selected)) { + throw new Error(`Device not found: ${selected}`); + } + + packDevice = deviceMap.get(selected); + } + + if (!packDevice) { + return; + } + + // Load processors for device + const processors = getProcessors(packDevice); + + // Select processor + if (processorName && processors.includes(processorName)) { + // Keep existing processor name + } else if (!processorName && processors.length == 1) { + processorName = processors[0]; + } else { + // Ask user which processor to use + const items = processors.map(label => ({ label })); + const selected = await getSelection('Select a processor', items, processorName); + if (!selected) { + return; + } + + if (!processors.includes(selected)) { + throw new Error(`Processor not found: ${selected}`); + } + + processorName = selected; + } + + const svdFile = getSvdPath(packDevice, processorName); + if (!svdFile) { + throw new Error(`Unable to load device ${getDeviceName(packDevice)}`); + } + + const svdUri = fileFromPack(assetBase, pack, svdFile); + return svdUri.toString(); + } } diff --git a/src/views/peripheral.ts b/src/views/peripheral.ts index b570039..869c2d5 100644 --- a/src/views/peripheral.ts +++ b/src/views/peripheral.ts @@ -350,7 +350,7 @@ export class PeripheralTreeProvider implements vscode.TreeDataProvider 0); } - public debugStopped(session: vscode.DebugSession) { + public debugStopped(session: vscode.DebugSession): void { const regs = this.sessionPeripheralsMap.get(session.id); if (regs) { // We are called even before the session has started, as part of reset regs.updateData(); From 51f2ade8c363f2e12500567b020a83c3864e443a Mon Sep 17 00:00:00 2001 From: thegecko Date: Mon, 30 Jan 2023 11:37:57 +0000 Subject: [PATCH 10/10] Tweak --- src/svd-resolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/svd-resolver.ts b/src/svd-resolver.ts index 7f2888b..b833806 100644 --- a/src/svd-resolver.ts +++ b/src/svd-resolver.ts @@ -57,7 +57,6 @@ export class SvdResolver { } protected async loadFromPack(pack: Pack, deviceName: string | undefined, processorName: string | undefined): Promise { - const getDeviceName = (device: Device) => (device as DeviceVariant).$.Dvariant || device.$.Dname; const assetBase = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get(manifest.CONFIG_ASSET_PATH) || manifest.DEFAULT_ASSET_PATH;