Skip to content

Commit 2ce1c44

Browse files
authored
Parse interrupt tables and expose through extension API (#77)
* Extension API: get interrupt table (types and docs) * Extends parser and API implementation * Normalize paths through URI conversion, move pathToUri to central location * Test command: Requires the following in settings.json of workspace "peripheral-inspector.testCommandsEnabled": true
1 parent 4609bcd commit 2ce1c44

10 files changed

Lines changed: 220 additions & 28 deletions

File tree

docs/extending-peripheral-inspector.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ yarn add github:eclipse-cdt-cloud/vscode-peripheral-inspector
2121

2222
### Developing your extension
2323

24-
To provide the peripherals information to Peripheral Inspector on debug session time, you need register your command which is going to construct the peripherals information. The command will receive `DebugSession` object as an input parameter and expects to return array of type `PeripheralOptions[]`.
24+
To provide the peripherals information to Peripheral Inspector on debug session time, you need register your command which is going to construct the peripherals information.
25+
The command will receive `DebugSession` object as an input parameter and expects to return array of type `PeripheralOptions[]`.
2526

2627
You can find the example command implementation below:
2728

@@ -41,11 +42,11 @@ class MyExtensionProvider implements api.IPeripheralsProvider {
4142
export async function activate(context: ExtensionContext) {
4243
...
4344
// Get the eclipse-cdt.peripheral-inspector extension
44-
const peripheralInspectorExtention = extensions.getExtension<api.IPeripheralInspectorAPI>('eclipse-cdt.peripheral-inspector');
45+
const peripheralInspectorExtension = extensions.getExtension<api.IPeripheralInspectorAPI>('eclipse-cdt.peripheral-inspector');
4546

4647
// Check if the eclipse-cdt.peripheral-inspector extension is installed
47-
if (peripheralInspectorExtention) {
48-
const peripheralInspectorAPI = await peripheralInspectorExtention.activate();
48+
if (peripheralInspectorExtension) {
49+
const peripheralInspectorAPI = await peripheralInspectorExtension.activate();
4950

5051
// Invoke registerPeripheralsProvider method in eclipse-cdt.peripheral-inspector extension api
5152
// Register 'MyExtensionProvider' for files *.myext
@@ -55,6 +56,10 @@ export async function activate(context: ExtensionContext) {
5556
}
5657
```
5758

59+
Alternatively, you also use the extension API to extract information from parsed SVD files:
60+
- See the implementation of the [Print Interrupt Table (Extension API)](../src/test-commands/print-api-interrupt-table.ts) test command for an example how to get the
61+
interrupt table for an SVD.
62+
5863
For further information about the api definitions, review the [Peripheral Inspector API Definitions](../src/api-types.ts).
5964

6065
### Modifying your package.json

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@
162162
"command": "peripheral-inspector.svd.periodicRefreshInterval",
163163
"title": "Set Periodic Refresh Interval",
164164
"icon": "$(calendar)"
165+
},
166+
{
167+
"command": "peripheral-inspector.printInterruptTable",
168+
"title": "Print Interrupt Table (Extension API)",
169+
"category": "Peripherals",
170+
"enablement": "inDebugMode && config.peripheral-inspector.testCommandsEnabled"
165171
}
166172
],
167173
"menus": {
@@ -213,6 +219,10 @@
213219
{
214220
"command": "peripheral-inspector.svd.clearIgnoredPeripherals",
215221
"when": "false"
222+
},
223+
{
224+
"command": "peripheral-inspector.printInterruptTable",
225+
"when": "inDebugMode && config.peripheral-inspector.testCommandsEnabled"
216226
}
217227
],
218228
"touchBar": [

src/api-types.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ export interface IPeripheralInspectorAPI {
44
getSVDFile: (device: string) => string | undefined;
55
getSVDFileFromCortexDebug: (device: string) => Promise<string | undefined>;
66
registerPeripheralsProvider: (fileExtension: string, provider: IPeripheralsProvider) => void;
7+
/**
8+
* Get interrupt table for provided SVD file path.
9+
* IMPORTANT: The input path must be absolute and normalized on desktop.
10+
*
11+
* @param svdPath The SVD file path.
12+
* @returns The interrupt table for the SVD file, or undefined if the file was not loaded.
13+
*/
14+
getInterruptTable?: (svdPath: string) => InterruptTable | undefined;
715
}
816

917
export interface IPeripheralsProvider {
@@ -25,6 +33,7 @@ export interface PeripheralOptions {
2533
resetValue?: number;
2634
registers?: PeripheralRegisterOptions[];
2735
clusters?: ClusterOptions[];
36+
interrupt?: InterruptEntry[];
2837
}
2938

3039
export interface PeripheralRegisterOptions {
@@ -49,6 +58,12 @@ export interface ClusterOptions {
4958
clusters?: ClusterOptions[];
5059
}
5160

61+
export interface InterruptEntry {
62+
name: string;
63+
description?: string;
64+
value: number;
65+
}
66+
5267
export interface FieldOptions {
5368
name: string;
5469
description: string;
@@ -60,14 +75,6 @@ export interface FieldOptions {
6075
readAction?: ReadActionType;
6176
}
6277

63-
export interface IGetPeripheralsArguments {
64-
gapThreshold: number;
65-
}
66-
67-
export interface IPeripheralsProvider {
68-
getPeripherals: (data: string, options: IGetPeripheralsArguments) => Promise<PeripheralOptions[]>;
69-
}
70-
7178
export interface PeripheralsConfiguration {
7279
gapThreshold: number;
7380
peripheralOptions: Record<string, PeripheralOptions>;
@@ -97,3 +104,16 @@ export interface IEnumeratedValue {
97104
description: string;
98105
value: number;
99106
}
107+
108+
/**
109+
* Interrupt table structure to represent the interrupts defined in the SVD file's
110+
* <peripheral> elements.
111+
*/
112+
export interface InterruptTable {
113+
/**
114+
* Mapping of interrupt numbers to their corresponding interrupt information.
115+
* The keys are the interrupt numbers, and the values are objects containing
116+
* the name, value, and optional description of each interrupt.
117+
*/
118+
interrupts: Record<number, InterruptEntry>;
119+
}

src/entry-points/desktop/extension.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { SvdResolver } from '../../svd-resolver';
1515
import { PeripheralConfigurationProvider } from '../../model/peripheral/tree/peripheral-configuration-provider';
1616
import { PeripheralTreeDataProvider } from '../../views/peripheral/peripheral-data-provider';
1717
import { PeripheralsTreeTableWebView } from '../../views/peripheral/peripheral-view-provider';
18+
import { CONFIG_ENABLE_TEST_COMMANDS } from '../../manifest';
19+
import { PrintApiInterruptTable } from '../../test-commands/print-api-interrupt-table';
1820
export * as api from '../../api-types';
1921

2022
export const activate = async (context: vscode.ExtensionContext): Promise<IPeripheralInspectorAPI> => {
@@ -33,6 +35,12 @@ export const activate = async (context: vscode.ExtensionContext): Promise<IPerip
3335
await dataProvider.activate(webView);
3436
await webView.activate(context);
3537

38+
const enableTestCommands = vscode.workspace.getConfiguration().get<boolean>(CONFIG_ENABLE_TEST_COMMANDS, false);
39+
if (enableTestCommands) {
40+
const printApiInterruptTable = new PrintApiInterruptTable();
41+
await printApiInterruptTable.activate(context);
42+
}
43+
3644
return api;
3745
};
3846

src/fileUtils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,11 @@ export async function getFilePath(): Promise<vscode.Uri | undefined> {
1414
});
1515
return fileUri;
1616
}
17+
18+
export const pathToUri = (path: string): vscode.Uri => {
19+
try {
20+
return vscode.Uri.file(path);
21+
} catch {
22+
return vscode.Uri.parse(path);
23+
}
24+
};

src/manifest.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import { CommandDefinition } from '@eclipse-cdt-cloud/vscode-ui-components/lib/vscode/webview-types';
99

10+
export const PUBLISHER_NAME = 'eclipse-cdt';
1011
export const PACKAGE_NAME = 'peripheral-inspector';
1112
export const CONFIG_SVD_PATH = 'definitionPathConfig';
1213
export const DEFAULT_SVD_PATH = 'definitionPath';
@@ -32,6 +33,9 @@ export const DEFAULT_PERIODIC_REFRESH_MODE: PeriodicRefreshMode = 'always';
3233
export const CONFIG_PERIODIC_REFRESH_INTERVAL = 'periodicRefreshInterval';
3334
export const DEFAULT_PERIODIC_REFRESH_INTERVAL = 500;
3435

36+
// Hidden configs, not exposed to users via package.json
37+
export const CONFIG_ENABLE_TEST_COMMANDS = `${PACKAGE_NAME}.testCommandsEnabled`;
38+
3539
// Commands
3640
export namespace Commands {
3741
// Commands used only within VSCode

src/model/peripheral/tree/peripheral-session-tree.ts

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,24 @@
77

88
import * as vscode from 'vscode';
99
import { AddrRange } from '../../../addrranges';
10-
import { IPeripheralsProvider, PeripheralOptions, PeripheralsConfiguration } from '../../../api-types';
10+
import { InterruptTable, IPeripheralsProvider, PeripheralOptions, PeripheralsConfiguration } from '../../../api-types';
1111
import { NodeSetting, PERIPHERAL_ID_SEP, PeripheralNodeSort, PeripheralSessionNodeDTO } from '../../../common';
1212
import * as manifest from '../../../manifest';
1313
import { PeripheralInspectorAPI } from '../../../peripheral-inspector-api';
1414
import { SVDParser } from '../../../svd-parser';
1515
import { readFromUrl } from '../../../utils';
16+
import { pathToUri } from '../../../fileUtils';
1617
import { MessageNode, PeripheralBaseNode, PeripheralNode, UpdateDataContext } from '../nodes';
1718
import { PeripheralConfigurationProvider } from './peripheral-configuration-provider';
1819
import { DebugSessionStatus } from '../../../debug-tracker';
1920
import { clearTimeout, setTimeout } from 'timers';
2021

21-
const pathToUri = (path: string): vscode.Uri => {
22-
try {
23-
return vscode.Uri.file(path);
24-
} catch {
25-
return vscode.Uri.parse(path);
26-
}
27-
};
28-
2922
interface CachedSVDFile {
3023
svdUri: vscode.Uri;
3124
mtime: number;
3225
peripherals: PeripheralNode[],
3326
configuration: PeripheralsConfiguration;
27+
interruptTable?: InterruptTable;
3428
}
3529

3630
export class PeripheralTreeForSession extends PeripheralBaseNode {
@@ -40,6 +34,7 @@ export class PeripheralTreeForSession extends PeripheralBaseNode {
4034

4135
private peripherals: PeripheralNode[] = [];
4236
private peripheralsConfiguration?: PeripheralsConfiguration;
37+
private interruptTable?: InterruptTable;
4338

4439
private loaded = false;
4540
private errMessage = 'No SVD file loaded';
@@ -97,21 +92,28 @@ export class PeripheralTreeForSession extends PeripheralBaseNode {
9792
return state;
9893
}
9994

100-
private static async addToCache(uri: vscode.Uri, peripherals: PeripheralNode[], configuration: PeripheralsConfiguration) {
95+
private static async addToCache(
96+
uri: vscode.Uri,
97+
peripherals: PeripheralNode[],
98+
configuration: PeripheralsConfiguration,
99+
interruptTable?: InterruptTable
100+
): Promise<CachedSVDFile | undefined> {
101101
try {
102102
const stat = await vscode.workspace.fs.stat(uri);
103103
if (stat && stat.mtime) {
104104
const tmp: CachedSVDFile = {
105105
svdUri: uri,
106106
mtime: stat.mtime,
107107
peripherals,
108-
configuration
108+
configuration,
109+
interruptTable
109110
};
110111
PeripheralTreeForSession.svdCache[uri.toString()] = tmp;
112+
return tmp;
111113
}
112114
} catch {
113115
delete PeripheralTreeForSession.svdCache[uri.toString()];
114-
return;
116+
return undefined;
115117
}
116118
}
117119

@@ -135,6 +137,7 @@ export class PeripheralTreeForSession extends PeripheralBaseNode {
135137
this.errMessage = `Loading ${svdPath} ...`;
136138
let parsedConfiguration: PeripheralsConfiguration | undefined;
137139
let parsedPeripherals: PeripheralNode[] | undefined;
140+
const parsedInterruptTable: InterruptTable = { interrupts: {} };
138141

139142
const ignoredPeripherals = this.config.ignorePeripherals();
140143

@@ -146,6 +149,9 @@ export class PeripheralTreeForSession extends PeripheralBaseNode {
146149
} else {
147150
this.svdUri = pathToUri(svdPath);
148151
const cached = await PeripheralTreeForSession.getFromCache(this.svdUri);
152+
if (!cached) {
153+
this.api.updateLoadedSVDInfo(this.svdUri, undefined);
154+
}
149155
if (cached && manifest.IgnorePeripherals.isEqual(cached.configuration.ignoredPeripherals, ignoredPeripherals)) {
150156
this.peripherals = cached.peripherals;
151157
this.peripheralsConfiguration = cached.configuration;
@@ -174,6 +180,13 @@ export class PeripheralTreeForSession extends PeripheralBaseNode {
174180
parsedConfiguration = await this.parseWithSVDParser(data, gapThreshold, ignoredPeripherals);
175181
}
176182

183+
// Ignored peripherals not applied yet, collect interrupt information before filtering out peripherals
184+
Object.values(parsedConfiguration.peripheralOptions).forEach((options) => {
185+
options.interrupt?.forEach((interrupt) => {
186+
parsedInterruptTable.interrupts[interrupt.value] = { name: interrupt.name, value: interrupt.value, description: interrupt.description };
187+
});
188+
});
189+
177190
const poptions = Array.from(Object.values(parsedConfiguration.peripheralOptions)).filter(p => !manifest.IgnorePeripherals.includes(ignoredPeripherals, p.name));
178191
parsedPeripherals = poptions.map((options) => new PeripheralNode(gapThreshold, options, this));
179192
parsedPeripherals.sort(PeripheralNodeSort.compare);
@@ -195,15 +208,18 @@ export class PeripheralTreeForSession extends PeripheralBaseNode {
195208
try {
196209
this.peripherals = parsedPeripherals;
197210
this.peripheralsConfiguration = parsedConfiguration;
211+
this.interruptTable = parsedInterruptTable;
198212

199213
this.loaded = true;
200214
await this.setSession(this.session);
201215
if (this.svdUri) {
202-
await PeripheralTreeForSession.addToCache(this.svdUri, this.peripherals, this.peripheralsConfiguration);
216+
const cachedSvd = await PeripheralTreeForSession.addToCache(this.svdUri, this.peripherals, this.peripheralsConfiguration, this.interruptTable);
217+
this.api.updateLoadedSVDInfo(this.svdUri, cachedSvd ? { interruptTable: cachedSvd.interruptTable } : undefined);
203218
}
204219
} catch (e) {
205220
this.peripherals = [];
206221
this.peripheralsConfiguration = undefined;
222+
this.interruptTable = undefined;
207223
this.loaded = false;
208224
throw e;
209225
}
@@ -264,7 +280,8 @@ export class PeripheralTreeForSession extends PeripheralBaseNode {
264280
this.refresh();
265281

266282
if (this.svdUri) {
267-
await PeripheralTreeForSession.addToCache(this.svdUri, this.peripherals, this.peripheralsConfiguration);
283+
const cachedSvd = await PeripheralTreeForSession.addToCache(this.svdUri, this.peripherals, this.peripheralsConfiguration, this.interruptTable);
284+
this.api.updateLoadedSVDInfo(this.svdUri, cachedSvd ? { interruptTable: cachedSvd.interruptTable } : undefined);
268285
}
269286
}
270287

src/peripheral-inspector-api.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
********************************************************************************/
77

88
import * as vscode from 'vscode';
9-
import { IPeripheralsProvider, IPeripheralInspectorAPI } from './api-types';
9+
import {
10+
IPeripheralsProvider,
11+
IPeripheralInspectorAPI,
12+
InterruptTable
13+
} from './api-types';
14+
import { pathToUri } from './fileUtils';
1015

1116
const CORTEX_EXTENSION = 'marus25.cortex-debug';
1217

@@ -15,9 +20,16 @@ interface SVDInfo {
1520
path: string;
1621
}
1722

23+
interface LoadedSVDInfo {
24+
interruptTable?: InterruptTable;
25+
}
26+
1827
export class PeripheralInspectorAPI implements IPeripheralInspectorAPI {
1928
private SVDDirectory: SVDInfo[] = [];
2029
private PeripheralProviders: Record<string, IPeripheralsProvider> = {};
30+
private LoadedSVDInfos: Record<string, LoadedSVDInfo> = {};
31+
32+
/** IPeripheralInspectorAPI implementation */
2133

2234
public registerSVDFile(expression: RegExp | string, path: string): void {
2335
if (typeof expression === 'string') {
@@ -58,8 +70,26 @@ export class PeripheralInspectorAPI implements IPeripheralInspectorAPI {
5870
this.PeripheralProviders[fileExtension] = provider;
5971
}
6072

73+
public getInterruptTable(svdPath: string): InterruptTable | undefined {
74+
const normalizedPath = pathToUri(svdPath).toString();
75+
return this.LoadedSVDInfos[normalizedPath]?.interruptTable;
76+
}
77+
78+
/** Locally used methods */
79+
6180
public getPeripheralsProvider(svdPath: string): IPeripheralsProvider | undefined {
6281
const ext = Object.keys(this.PeripheralProviders).filter((extension) => svdPath.endsWith(`.${extension}`))[0];
6382
return ext ? this.PeripheralProviders[ext] : undefined;
6483
}
84+
85+
public updateLoadedSVDInfo(svdPath: string | vscode.Uri, svdInfo?: LoadedSVDInfo): void {
86+
// Normalize path by converting to URI and back to string.
87+
const svdUri = typeof svdPath === 'string' ? pathToUri(svdPath) : svdPath;
88+
const svdUriString = svdUri.toString();
89+
if (svdInfo) {
90+
this.LoadedSVDInfos[svdUriString] = svdInfo;
91+
} else {
92+
delete this.LoadedSVDInfos[svdUriString];
93+
}
94+
}
6595
}

0 commit comments

Comments
 (0)