Skip to content

Commit b51a311

Browse files
committed
2 parents 0c8d9cc + 483820a commit b51a311

File tree

13 files changed

+409
-97
lines changed

13 files changed

+409
-97
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,20 @@ Once you have the SVD file, specify the location of it in your `launch.json` usi
7171
}
7272
```
7373

74+
### Extending MCU Peripheral Viewer
75+
76+
It is possible to extend the MCU Peripheral Viewer with your VSCode extension and provide peripherals information to the MCU Peripheral Viewer without passing an SVD file. In this method, `peripherals` variable passed to the debug launch configuration in `launch.json` file.
77+
78+
```json
79+
{
80+
...
81+
"peripherals": "command:myExtension.command.to.get.peripherals"
82+
...
83+
}
84+
```
85+
86+
The `peripherals` variable will define the source to load the peripherals information. Peripherals could be loaded from VSCode command which is defined after `command:` prefix in `peripherals` variable. For more details about the implementation, please check the [Extending MCU Peripheral Viewer](./docs/extending-peripheral-viewer.md) document.
87+
7488
## Settings
7589

7690
All variable key names used to extract data from debug launch configurations can be modified. This allows variable name clashes to be avoided as well as the need to duplicate configuration entries.

docs/appendix-types.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Appendix: Types of `peripheral-viewer`
2+
3+
Types could be imported from `peripheral-viewer` like the code below:
4+
5+
```js
6+
import {
7+
AccessType,
8+
PeripheralOptions,
9+
PeripheralRegisterOptions,
10+
ClusterOptions,
11+
FieldOptions
12+
} from "peripheral-viewer/src/types";
13+
```
14+
15+
## Enum: AccessType
16+
17+
AccessType enum defines the type of the access to the related peripheral item.
18+
19+
```js
20+
enum AccessType {
21+
ReadOnly = 1,
22+
ReadWrite,
23+
WriteOnly
24+
}
25+
```
26+
27+
## Interface: PeripheralOptions
28+
29+
The definition of the PeripheralOptions interface is shown below:
30+
31+
```js
32+
interface PeripheralOptions {
33+
name: string;
34+
baseAddress: number;
35+
totalLength: number;
36+
description: string;
37+
groupName?: string;
38+
accessType?: AccessType;
39+
size?: number;
40+
resetValue?: number;
41+
registers?: PeripheralRegisterOptions[];
42+
clusters?: ClusterOptions[];
43+
}
44+
```
45+
## Interface: PeripheralRegisterOptions
46+
47+
The definition of the PeripheralRegisterOptions interface is shown below:
48+
49+
```js
50+
interface PeripheralRegisterOptions {
51+
name: string;
52+
description?: string;
53+
addressOffset: number;
54+
accessType?: AccessType;
55+
size?: number;
56+
resetValue?: number;
57+
fields?: FieldOptions[];
58+
}
59+
```
60+
61+
## Interface: ClusterOptions Interface
62+
63+
The definition of the ClusterOptions interface is shown below:
64+
65+
```js
66+
interface ClusterOptions {
67+
name: string;
68+
description?: string;
69+
addressOffset: number;
70+
accessType?: AccessType;
71+
size?: number;
72+
resetValue?: number;
73+
registers?: PeripheralRegisterOptions[];
74+
clusters?: ClusterOptions[];
75+
}
76+
```
77+
78+
## Interface: FieldOptions Interface
79+
80+
The definition of the FieldOptions interface is shown below:
81+
82+
```js
83+
interface FieldOptions {
84+
name: string;
85+
description: string;
86+
offset: number;
87+
width: number;
88+
enumeration?: EnumerationMap;
89+
accessType?: AccessType;
90+
}
91+
```
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Extending MCU Peripheral Viewer
2+
3+
It is possible to extend the MCU Peripheral Viewer with your VSCode extension and provide peripherals information to the MCU Peripheral Viewer without passing an SVD file. In this method, `peripherals` variable passed to the debug launch configuration in `launch.json` file.
4+
5+
```json
6+
{
7+
...
8+
"peripherals": "command:myExtension.command.to.get.peripherals"
9+
...
10+
}
11+
```
12+
13+
The `peripherals` variable will define the source to load the peripherals information. Peripherals could be loaded from VSCode command which is defined after `command:` prefix in `peripherals` variable.
14+
15+
## Building your VSCode Extension to extend MCU Peripheral Viewer
16+
17+
This is a guide about how you can inject peripherals information to MCU Peripheral Viewer in your VSCode extension. Please refer to [VSCode Extension API](https://code.visualstudio.com/api) for more information about developing VSCode extensions.
18+
19+
### Adding peripheral-viewer to your VSCode extension
20+
21+
You need to install mcu-debug/peripheral-viewer to access the types information (`PeripheralOptions`, `PeripheralRegisterOptions`, `ClusterOptions`, `FieldOptions`, `AccessType`). You can use `npm` or `yarn` with the following arguments described below:
22+
23+
Using with npm:
24+
```bash
25+
npm install github:mcu-debug/peripheral-viewer
26+
```
27+
Using with yarn:
28+
```bash
29+
yarn add github:mcu-debug/peripheral-viewer
30+
```
31+
32+
33+
### Developing your extension
34+
35+
To provide the peripherals information to MCU Peripheral Viewer on debug session time, you need register your command which is going construct the peripherals information. The command will receive `DebugSession` object as an input parameter and expects to return array of type `PeripheralOptions[]`.
36+
37+
You can find the example command implementation below:
38+
39+
```js
40+
import { commands, DebugSession } from 'vscode';
41+
import {
42+
PeripheralOptions,
43+
PeripheralRegisterOptions,
44+
ClusterOptions,
45+
FieldOptions,
46+
AccessType
47+
} from "peripheral-viewer/src/types";
48+
export async function activate(context: ExtensionContext) {
49+
...
50+
commands.registerCommand(
51+
'myExtension.command.to.get.peripherals',
52+
async (session: DebugSession) => {
53+
// Load your peripherals data
54+
const peripherals: PeripheralOptions[] = ...
55+
return peripherals;
56+
}
57+
)
58+
...
59+
}
60+
```
61+
62+
You can check the type definitions (`PeripheralOptions`, `PeripheralRegisterOptions`, `ClusterOptions`, `FieldOptions`, `AccessType`) from [this document](./appendix-types.md).
63+
64+
### Modifying your package.json
65+
66+
In `package.json` of your VSCode extension project, you need to define the command as an activator and provide the dependency between svd-viewer and your extension.
67+
68+
You need to add your command to `activationEvents` in the package.json and define MCU Peripheral Viewer in the `extensionDependencies` as shown below:
69+
70+
```json
71+
{
72+
...
73+
"activationEvents": [
74+
"onCommand:myExtension.command.to.get.peripherals"
75+
],
76+
"extensionDependencies": [
77+
"mcu-debug.peripheral-viewer"
78+
],
79+
...
80+
}
81+
```

src/browser/extension.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ import { PeripheralTreeProvider } from '../views/peripheral';
77
import { Commands } from '../commands';
88
import { DebugTrackerWrapper } from '../debug-tracker-wrapper';
99
import { SvdRegistry } from '../svd-registry';
10-
import { SvdResolver } from '../svd-resolver';
1110
import { logToOutputWindow } from '../vscode-utils';
11+
export * from '../types';
1212

1313
export const activate = async (context: vscode.ExtensionContext): Promise<SvdRegistry> => {
1414
logToOutputWindow('Activating browser version');
1515

1616
const tracker = new DebugTrackerWrapper();
1717
const registry = new SvdRegistry();
18-
const resolver = new SvdResolver(registry);
19-
const peripheralTree = new PeripheralTreeProvider(tracker, resolver, context);
18+
19+
const peripheralTree = new PeripheralTreeProvider(tracker, context);
2020
const commands = new Commands(peripheralTree);
2121

2222
await tracker.activate(context);

src/desktop/extension.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,15 @@ import { PeripheralTreeProvider } from '../views/peripheral';
77
import { Commands } from '../commands';
88
import { DebugTrackerWrapper } from '../debug-tracker-wrapper';
99
import { SvdRegistry } from '../svd-registry';
10-
import { SvdResolver } from '../svd-resolver';
1110
import { logToOutputWindow } from '../vscode-utils';
11+
export * from '../types';
1212

1313
export const activate = async (context: vscode.ExtensionContext): Promise<SvdRegistry> => {
1414
logToOutputWindow('Activating desktop version');
1515

1616
const tracker = new DebugTrackerWrapper();
1717
const registry = new SvdRegistry();
18-
const resolver = new SvdResolver(registry);
19-
const peripheralTree = new PeripheralTreeProvider(tracker, resolver, context);
18+
const peripheralTree = new PeripheralTreeProvider(tracker, context);
2019
const commands = new Commands(peripheralTree);
2120

2221
await tracker.activate(context);

src/manifest.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@ export const CONFIG_ASSET_PATH = 'packAssetUrl';
1515
export const DEFAULT_ASSET_PATH = 'https://pack-content.cmsis.io';
1616
export const CONFIG_SAVE_LAYOUT = 'saveLayout';
1717
export const DEBUG_LEVEL = 'debugLevel';
18+
export const CONFIG_PERIPHERALS = 'peripheralsConfig';
19+
export const DEFAULT_PERIPHERALS = 'peripherals';
20+
export const CONFIG_PERIPHERALS_CACHE_KEY = 'peripheralsCacheKeyConfig';
21+
export const DEFAULT_PERIPHERALS_CACHE_KEY = 'peripheralsCacheKey';

src/peripherals-provider.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import * as vscode from 'vscode';
2+
import * as manifest from './manifest';
3+
import { SvdResolver } from './svd-resolver';
4+
import { PeripheralNode, PeripheralOptions } from './views/nodes/peripheralnode';
5+
import { SvdData, SVDParser } from './svd-parser';
6+
import { parseStringPromise } from 'xml2js';
7+
import { readFromUrl } from './utils';
8+
import { SvdRegistry } from './svd-registry';
9+
10+
const pathToUri = (path: string): vscode.Uri => {
11+
try {
12+
return vscode.Uri.file(path);
13+
} catch (e) {
14+
return vscode.Uri.parse(path);
15+
}
16+
};
17+
18+
const getData = async <T>(definition: string, ...params: unknown[]) : Promise<T | undefined> => {
19+
if(definition.startsWith('command:')) {
20+
const command = definition.substring('command:'.length);
21+
return vscode.commands.executeCommand(command, ...params) as Promise<T | undefined>;
22+
}
23+
return definition as T;
24+
};
25+
26+
export class PeripheralsProvider {
27+
readonly svdResolver: SvdResolver;
28+
constructor(protected session: vscode.DebugSession, protected context: vscode.ExtensionContext) {
29+
const registry = new SvdRegistry();
30+
this.svdResolver = new SvdResolver(registry);
31+
}
32+
33+
public async cacheKey() : Promise<string | vscode.Uri | undefined> {
34+
const getPeripheralsCacheKeyConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<string>(manifest.CONFIG_PERIPHERALS_CACHE_KEY) || manifest.DEFAULT_PERIPHERALS_CACHE_KEY;
35+
const getPeripheralsCacheKey = this.session.configuration[getPeripheralsCacheKeyConfig];
36+
37+
if(getPeripheralsCacheKey) {
38+
return getPeripheralsCacheKey;
39+
}
40+
41+
const wsFolderPath = this.session.workspaceFolder ? this.session.workspaceFolder.uri : vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0].uri;
42+
const svdPath = await this.svdResolver.resolve(this.session, wsFolderPath);
43+
return svdPath;
44+
}
45+
46+
public async getPeripherals(): Promise<PeripheralNode[] | undefined> {
47+
const getPeripheralsConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<string>(manifest.CONFIG_PERIPHERALS) || manifest.DEFAULT_PERIPHERALS;
48+
const getPeripherals = this.session.configuration[getPeripheralsConfig];
49+
50+
let thresh = this.session.configuration[manifest.CONFIG_ADDRGAP];
51+
if (!thresh) {
52+
thresh = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<number>(manifest.CONFIG_ADDRGAP) || manifest.DEFAULT_ADDRGAP;
53+
}
54+
55+
if (((typeof thresh) === 'number') && (thresh < 0)) {
56+
thresh = -1; // Never merge register reads even if adjacent
57+
} else {
58+
// Set the threshold between 0 and 32, with a default of 16 and a mukltiple of 8
59+
thresh = ((((typeof thresh) === 'number') ? Math.max(0, Math.min(thresh, 32)) : 16) + 7) & ~0x7;
60+
}
61+
62+
if(getPeripherals) {
63+
return this.getPeripheralsDynamic(thresh, getPeripherals);
64+
} else {
65+
return this.getPeripheralsFromSVD(thresh);
66+
}
67+
}
68+
69+
private async getPeripheralsDynamic(thresh: number, command: string): Promise<PeripheralNode[] | undefined> {
70+
const poptions = await getData<PeripheralOptions[]>(command, this.session);
71+
if(!poptions?.length) {
72+
return undefined;
73+
}
74+
const peripherials = poptions.map((options) => new PeripheralNode(thresh, options));
75+
const enumTypeValuesMap = {};
76+
for (const p of peripherials) {
77+
p.resolveDeferedEnums(enumTypeValuesMap); // This can throw an exception
78+
p.collectRanges();
79+
}
80+
return peripherials;
81+
}
82+
83+
private async getPeripheralsFromSVD(thresh: number): Promise<PeripheralNode[] | undefined> {
84+
const wsFolderPath = this.session.workspaceFolder ? this.session.workspaceFolder.uri : vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0].uri;
85+
86+
const svdPath = await this.svdResolver.resolve(this.session, wsFolderPath);
87+
88+
if (!svdPath) {
89+
return undefined;
90+
}
91+
92+
let svdData: SvdData | undefined;
93+
94+
try {
95+
let contents: ArrayBuffer | undefined;
96+
97+
if (svdPath.startsWith('http')) {
98+
contents = await readFromUrl(svdPath);
99+
} else {
100+
const uri = pathToUri(svdPath);
101+
contents = await vscode.workspace.fs.readFile(uri);
102+
}
103+
104+
if (contents) {
105+
const decoder = new TextDecoder();
106+
const xml = decoder.decode(contents);
107+
svdData = await parseStringPromise(xml);
108+
}
109+
} catch(e) {
110+
// eslint-disable-next-line no-console
111+
console.warn(e);
112+
}
113+
114+
if (!svdData) {
115+
return;
116+
}
117+
118+
try {
119+
const parser = new SVDParser();
120+
return parser.parseSVD(svdData, thresh);
121+
} catch(e) {
122+
return undefined;
123+
}
124+
}
125+
}

src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export { PeripheralOptions } from './views/nodes/peripheralnode';
2+
export { PeripheralRegisterOptions } from './views/nodes/peripheralregisternode';
3+
export { ClusterOptions } from './views/nodes/peripheralclusternode';
4+
export { FieldOptions } from './views/nodes/peripheralfieldnode';
5+
export { AccessType } from './svd-parser';

0 commit comments

Comments
 (0)