Skip to content

An alternative way to load peripherals information (with peripherals) #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ Once you have the SVD file, specify the location of it in your `launch.json` usi
}
```

### Extending MCU Peripheral Viewer

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.

```json
{
...
"peripherals": "command:myExtension.command.to.get.peripherals"
...
}
```

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.

## Settings

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.
Expand Down
91 changes: 91 additions & 0 deletions docs/appendix-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Appendix: Types of `peripheral-viewer`

Types could be imported from `peripheral-viewer` like the code below:

```js
import {
AccessType,
PeripheralOptions,
PeripheralRegisterOptions,
ClusterOptions,
FieldOptions
} from "peripheral-viewer/src/types";
```

## Enum: AccessType

AccessType enum defines the type of the access to the related peripheral item.

```js
enum AccessType {
ReadOnly = 1,
ReadWrite,
WriteOnly
}
```

## Interface: PeripheralOptions

The definition of the PeripheralOptions interface is shown below:

```js
interface PeripheralOptions {
name: string;
baseAddress: number;
totalLength: number;
description: string;
groupName?: string;
accessType?: AccessType;
size?: number;
resetValue?: number;
registers?: PeripheralRegisterOptions[];
clusters?: ClusterOptions[];
}
```
## Interface: PeripheralRegisterOptions

The definition of the PeripheralRegisterOptions interface is shown below:

```js
interface PeripheralRegisterOptions {
name: string;
description?: string;
addressOffset: number;
accessType?: AccessType;
size?: number;
resetValue?: number;
fields?: FieldOptions[];
}
```

## Interface: ClusterOptions Interface

The definition of the ClusterOptions interface is shown below:

```js
interface ClusterOptions {
name: string;
description?: string;
addressOffset: number;
accessType?: AccessType;
size?: number;
resetValue?: number;
registers?: PeripheralRegisterOptions[];
clusters?: ClusterOptions[];
}
```

## Interface: FieldOptions Interface

The definition of the FieldOptions interface is shown below:

```js
interface FieldOptions {
name: string;
description: string;
offset: number;
width: number;
enumeration?: EnumerationMap;
accessType?: AccessType;
}
```
81 changes: 81 additions & 0 deletions docs/extending-peripheral-viewer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Extending MCU Peripheral Viewer

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.

```json
{
...
"peripherals": "command:myExtension.command.to.get.peripherals"
...
}
```

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.

## Building your VSCode Extension to extend MCU Peripheral Viewer

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.

### Adding peripheral-viewer to your VSCode extension

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:

Using with npm:
```bash
npm install github:mcu-debug/peripheral-viewer
```
Using with yarn:
```bash
yarn add github:mcu-debug/peripheral-viewer
```


### Developing your extension

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[]`.

You can find the example command implementation below:

```js
import { commands, DebugSession } from 'vscode';
import {
PeripheralOptions,
PeripheralRegisterOptions,
ClusterOptions,
FieldOptions,
AccessType
} from "peripheral-viewer/src/types";
export async function activate(context: ExtensionContext) {
...
commands.registerCommand(
'myExtension.command.to.get.peripherals',
async (session: DebugSession) => {
// Load your peripherals data
const peripherals: PeripheralOptions[] = ...
return peripherals;
}
)
...
}
```

You can check the type definitions (`PeripheralOptions`, `PeripheralRegisterOptions`, `ClusterOptions`, `FieldOptions`, `AccessType`) from [this document](./appendix-types.md).

### Modifying your package.json

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.

You need to add your command to `activationEvents` in the package.json and define MCU Peripheral Viewer in the `extensionDependencies` as shown below:

```json
{
...
"activationEvents": [
"onCommand:myExtension.command.to.get.peripherals"
],
"extensionDependencies": [
"mcu-debug.peripheral-viewer"
],
...
}
```
6 changes: 3 additions & 3 deletions src/browser/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import { PeripheralTreeProvider } from '../views/peripheral';
import { Commands } from '../commands';
import { DebugTrackerWrapper } from '../debug-tracker-wrapper';
import { SvdRegistry } from '../svd-registry';
import { SvdResolver } from '../svd-resolver';
import { logToOutputWindow } from '../vscode-utils';
export * from '../types';

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

const tracker = new DebugTrackerWrapper();
const registry = new SvdRegistry();
const resolver = new SvdResolver(registry);
const peripheralTree = new PeripheralTreeProvider(tracker, resolver, context);

const peripheralTree = new PeripheralTreeProvider(tracker, context);
const commands = new Commands(peripheralTree);

await tracker.activate(context);
Expand Down
5 changes: 2 additions & 3 deletions src/desktop/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ import { PeripheralTreeProvider } from '../views/peripheral';
import { Commands } from '../commands';
import { DebugTrackerWrapper } from '../debug-tracker-wrapper';
import { SvdRegistry } from '../svd-registry';
import { SvdResolver } from '../svd-resolver';
import { logToOutputWindow } from '../vscode-utils';
export * from '../types';

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

const tracker = new DebugTrackerWrapper();
const registry = new SvdRegistry();
const resolver = new SvdResolver(registry);
const peripheralTree = new PeripheralTreeProvider(tracker, resolver, context);
const peripheralTree = new PeripheralTreeProvider(tracker, context);
const commands = new Commands(peripheralTree);

await tracker.activate(context);
Expand Down
4 changes: 4 additions & 0 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ export const CONFIG_ASSET_PATH = 'packAssetUrl';
export const DEFAULT_ASSET_PATH = 'https://pack-asset-service.keil.arm.com';
export const CONFIG_SAVE_LAYOUT = 'saveLayout';
export const DEBUG_LEVEL = 'debugLevel';
export const CONFIG_PERIPHERALS = 'peripheralsConfig';
export const DEFAULT_PERIPHERALS = 'peripherals';
export const CONFIG_PERIPHERALS_CACHE_KEY = 'peripheralsCacheKeyConfig';
export const DEFAULT_PERIPHERALS_CACHE_KEY = 'peripheralsCacheKey';
125 changes: 125 additions & 0 deletions src/peripherals-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import * as vscode from 'vscode';
import * as manifest from './manifest';
import { SvdResolver } from './svd-resolver';
import { PeripheralNode, PeripheralOptions } from './views/nodes/peripheralnode';
import { SvdData, SVDParser } from './svd-parser';
import { parseStringPromise } from 'xml2js';
import { readFromUrl } from './utils';
import { SvdRegistry } from './svd-registry';

const pathToUri = (path: string): vscode.Uri => {
try {
return vscode.Uri.file(path);
} catch (e) {
return vscode.Uri.parse(path);
}
};

const getData = async <T>(definition: string, ...params: unknown[]) : Promise<T | undefined> => {
if(definition.startsWith('command:')) {
const command = definition.substring('command:'.length);
return vscode.commands.executeCommand(command, ...params) as Promise<T | undefined>;
}
return definition as T;
};

export class PeripheralsProvider {
readonly svdResolver: SvdResolver;
constructor(protected session: vscode.DebugSession, protected context: vscode.ExtensionContext) {
const registry = new SvdRegistry();
this.svdResolver = new SvdResolver(registry);
}

public async cacheKey() : Promise<string | vscode.Uri | undefined> {
const getPeripheralsCacheKeyConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<string>(manifest.CONFIG_PERIPHERALS_CACHE_KEY) || manifest.DEFAULT_PERIPHERALS_CACHE_KEY;
const getPeripheralsCacheKey = this.session.configuration[getPeripheralsCacheKeyConfig];

if(getPeripheralsCacheKey) {
return getPeripheralsCacheKey;
}

const wsFolderPath = this.session.workspaceFolder ? this.session.workspaceFolder.uri : vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0].uri;
const svdPath = await this.svdResolver.resolve(this.session, wsFolderPath);
return svdPath;
}

public async getPeripherals(): Promise<PeripheralNode[] | undefined> {
const getPeripheralsConfig = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<string>(manifest.CONFIG_PERIPHERALS) || manifest.DEFAULT_PERIPHERALS;
const getPeripherals = this.session.configuration[getPeripheralsConfig];

let thresh = this.session.configuration[manifest.CONFIG_ADDRGAP];
if (!thresh) {
thresh = vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<number>(manifest.CONFIG_ADDRGAP) || manifest.DEFAULT_ADDRGAP;
}

if (((typeof thresh) === 'number') && (thresh < 0)) {
thresh = -1; // Never merge register reads even if adjacent
} else {
// Set the threshold between 0 and 32, with a default of 16 and a mukltiple of 8
thresh = ((((typeof thresh) === 'number') ? Math.max(0, Math.min(thresh, 32)) : 16) + 7) & ~0x7;
}

if(getPeripherals) {
return this.getPeripheralsDynamic(thresh, getPeripherals);
} else {
return this.getPeripheralsFromSVD(thresh);
}
}

private async getPeripheralsDynamic(thresh: number, command: string): Promise<PeripheralNode[] | undefined> {
const poptions = await getData<PeripheralOptions[]>(command, this.session);
if(!poptions?.length) {
return undefined;
}
const peripherials = poptions.map((options) => new PeripheralNode(thresh, options));
const enumTypeValuesMap = {};
for (const p of peripherials) {
p.resolveDeferedEnums(enumTypeValuesMap); // This can throw an exception
p.collectRanges();
}
return peripherials;
}

private async getPeripheralsFromSVD(thresh: number): Promise<PeripheralNode[] | undefined> {
const wsFolderPath = this.session.workspaceFolder ? this.session.workspaceFolder.uri : vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0].uri;

const svdPath = await this.svdResolver.resolve(this.session, wsFolderPath);

if (!svdPath) {
return undefined;
}

let svdData: SvdData | undefined;

try {
let contents: ArrayBuffer | undefined;

if (svdPath.startsWith('http')) {
contents = await readFromUrl(svdPath);
} else {
const uri = pathToUri(svdPath);
contents = await vscode.workspace.fs.readFile(uri);
}

if (contents) {
const decoder = new TextDecoder();
const xml = decoder.decode(contents);
svdData = await parseStringPromise(xml);
}
} catch(e) {
// eslint-disable-next-line no-console
console.warn(e);
}

if (!svdData) {
return;
}

try {
const parser = new SVDParser();
return parser.parseSVD(svdData, thresh);
} catch(e) {
return undefined;
}
}
}
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { PeripheralOptions } from './views/nodes/peripheralnode';
export { PeripheralRegisterOptions } from './views/nodes/peripheralregisternode';
export { ClusterOptions } from './views/nodes/peripheralclusternode';
export { FieldOptions } from './views/nodes/peripheralfieldnode';
export { AccessType } from './svd-parser';
Loading