Skip to content

Commit

Permalink
Added additional details to the hover tooltips for Register and `Fi…
Browse files Browse the repository at this point in the history
…eld` nodes in the peripheral register viewer.

Currently displays the following:

1) Field Node:
   * Name, computed memory address (with bit range), current value in selected format (also displayed directly in node)
   * Description from SVD file
   * Current value in the following formats: Enumeration value (if the SVD file defines enumeration values), hex, decimal and binary
2) Register Node:
   * Name, computed memory address, current value in selected format (also displayed directly in node)
   * Description from SVD file
   * Current value in hex, decimal and binary formats
   * Summary of fields if defined
  • Loading branch information
Marus committed Jan 28, 2021
1 parent f68fdff commit 20ffba5
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 51 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

New Features:
* Enable ChibiOS RTOS support for the J-Link server

* Added additional details to the the register and field level hover tooltips in the peripheral register view.
# V0.3.10
This feature upgrades our VSCode dependency for the extension - as a result V0.3.10 will only support the Visual Studio Code version 1.52 and above.

Expand Down
142 changes: 108 additions & 34 deletions src/frontend/views/nodes/peripheralfieldnode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TreeItem, TreeItemCollapsibleState, window, debug } from 'vscode';
import { TreeItem, TreeItemCollapsibleState, window, debug, MarkdownString, TreeItemLabel } from 'vscode';
import { PeripheralBaseNode } from './basenode';
import { AccessType } from '../../svd';
import { PeripheralRegisterNode } from './peripheralregisternode';
Expand Down Expand Up @@ -74,49 +74,123 @@ export class PeripheralFieldNode extends PeripheralBaseNode {
}

public getTreeItem(): TreeItem | Promise<TreeItem> {
const value = this.parent.extractBits(this.offset, this.width);
const isReserved = this.name.toLowerCase() === 'reserved';

const context = isReserved ? 'field-res' : (this.parent.accessType === AccessType.ReadOnly ? 'field-ro' : 'field');

const rangestart = this.offset;
const rangeend = this.offset + this.width - 1;

const item = new TreeItem(`${this.name} [${rangeend}:${rangestart}]`, TreeItemCollapsibleState.None);

item.contextValue = context;
item.tooltip = this.generateTooltipMarkdown(isReserved);
item.description = this.getFormattedValue(this.getFormat());

const label = `${this.name} [${rangeend}:${rangestart}]`;
return item;
}

private generateTooltipMarkdown(isReserved: boolean): MarkdownString | null {
const mds = new MarkdownString('', true);
mds.isTrusted = true;

const context = this.name.toLowerCase() === 'reserved' ? 'field-res' : (this.parent.accessType === AccessType.ReadOnly ? 'field-ro' : 'field');
const address = `${ hexFormat(this.parent.getAddress()) }${ this.getFormattedRange() }`;

const item = new TreeItem(label, TreeItemCollapsibleState.None);
item.contextValue = context;
item.tooltip = this.description;
if (isReserved) {
mds.appendMarkdown(`| ${this.name }@${address} | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | *Reserved* |\n`);
mds.appendMarkdown('|:---|:---:|---:|');
return mds;
}

const formattedValue = this.getFormattedValue(this.getFormat(), true);

if (this.name.toLowerCase() !== 'reserved') {
if (this.accessType === AccessType.WriteOnly) {
item.description = '<Write Only>';
mds.appendMarkdown(`| ${this.name }@${address} | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | *${formattedValue}* |\n`);
mds.appendMarkdown('|:---|:---:|---:|');

mds.appendMarkdown('\n____\n\n');
mds.appendMarkdown(this.description);

mds.appendMarkdown('\n_____\n\n');

// Don't try to display current value table for write only fields
if (this.accessType === AccessType.WriteOnly) {
return mds;
}

const value = this.parent.extractBits(this.offset, this.width);
const hex = hexFormat(value, Math.ceil(this.width / 4), true);
const decimal = value.toString();
const binary = binaryFormat(value, this.width);

if (this.enumeration) {
mds.appendMarkdown('| Enumeration Value &nbsp;&nbsp; | Hex &nbsp;&nbsp; | Decimal &nbsp;&nbsp; | Binary &nbsp;&nbsp; |\n');
mds.appendMarkdown('|:---|:---|:---|:---|\n');
let ev = 'Unknown';
if (this.enumeration[value]) {
ev = this.enumeration[value].name;
}
else {
let formatted = '';
switch (this.getFormat()) {
case NumberFormat.Decimal:
formatted = value.toString();
break;
case NumberFormat.Binary:
formatted = binaryFormat(value, this.width);
break;
case NumberFormat.Hexidecimal:
formatted = hexFormat(value, Math.ceil(this.width / 4), true);
break;
default:
formatted = this.width >= 4 ? hexFormat(value, Math.ceil(this.width / 4), true) : binaryFormat(value, this.width);
break;
}

if (this.enumeration && this.enumeration[value]) {
item.description = `${this.enumeration[value].name} (${formatted})`;
}
else {
item.description = formatted;
}

mds.appendMarkdown(`| ${ ev } &nbsp;&nbsp; | ${ hex } &nbsp;&nbsp; | ${ decimal } &nbsp;&nbsp; | ${ binary } &nbsp;&nbsp; |\n\n`);
if (this.enumeration[value].description) {
mds.appendMarkdown(this.enumeration[value].description);
}
} else {
mds.appendMarkdown('| Hex &nbsp;&nbsp; | Decimal &nbsp;&nbsp; | Binary &nbsp;&nbsp; |\n');
mds.appendMarkdown('|:---|:---|:---|\n');
mds.appendMarkdown(`| ${ hex } &nbsp;&nbsp; | ${ decimal } &nbsp;&nbsp; | ${ binary } &nbsp;&nbsp; |\n`);
}

return item;
return mds;
}

public getFormattedRange(): string {
const rangestart = this.offset;
const rangeend = this.offset + this.width - 1;
return `[${rangeend}:${rangestart}]`;
}

public getFormattedValue(format: NumberFormat, includeEnumeration: boolean = true): string {
if (this.accessType === AccessType.WriteOnly) {
return '<Write Only>';
}

let formatted = '';
const value = this.parent.extractBits(this.offset, this.width);

switch (format) {
case NumberFormat.Decimal:
formatted = value.toString();
break;
case NumberFormat.Binary:
formatted = binaryFormat(value, this.width);
break;
case NumberFormat.Hexidecimal:
formatted = hexFormat(value, Math.ceil(this.width / 4), true);
break;
default:
formatted = this.width >= 4 ? hexFormat(value, Math.ceil(this.width / 4), true) : binaryFormat(value, this.width);
break;
}

if (includeEnumeration && this.enumeration) {
if (this.enumeration[value]) {
formatted = `${this.enumeration[value].name} (${formatted})`;
} else {
formatted = `Unkown Enumeration Value (${formatted})`;
}
}

return formatted;
}

public getEnumerationValue(value: number): string | null {
if (!this.enumeration) {
return null;
}

if (this.enumeration[value]) {
return this.enumeration[value].name;
}
}

public getChildren(): PeripheralBaseNode[] | Promise<PeripheralBaseNode[]> {
Expand Down
80 changes: 64 additions & 16 deletions src/frontend/views/nodes/peripheralregisternode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TreeItem, TreeItemCollapsibleState, window, debug } from 'vscode';
import { TreeItem, TreeItemCollapsibleState, window, debug, MarkdownString } from 'vscode';
import { PeripheralNode } from './peripheralnode';
import { PeripheralClusterNode } from './peripheralclusternode';
import { PeripheralBaseNode } from './basenode';
Expand All @@ -7,6 +7,7 @@ import { AccessType } from '../../svd';
import { extractBits, createMask, hexFormat, binaryFormat } from '../../utils';
import { NumberFormat, NodeSetting } from '../../../common';
import { AddressRangesInUse } from '../../addrranges';
import { MessageChannel } from 'worker_threads';

export interface PeripheralRegisterOptions {
name: string;
Expand Down Expand Up @@ -83,26 +84,69 @@ export class PeripheralRegisterNode extends PeripheralBaseNode {

const item = new TreeItem(label, collapseState);
item.contextValue = this.accessType === AccessType.ReadWrite ? 'registerRW' : (this.accessType === AccessType.ReadOnly ? 'registerRO' : 'registerWO');
item.tooltip = this.description;
item.tooltip = this.generateTooltipMarkdown();
item.description = this.getFormattedValue(this.getFormat());

return item;
}

private generateTooltipMarkdown(): MarkdownString | null {
const mds = new MarkdownString('', true);
mds.isTrusted = true;

const address = `${ hexFormat(this.getAddress()) }`;

const formattedValue = this.getFormattedValue(this.getFormat());

mds.appendMarkdown(`| ${this.name }@${address} | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | *${formattedValue}* |\n`);
mds.appendMarkdown('|:---|:---:|---:|\n');

mds.appendMarkdown('\n____\n\n');
mds.appendMarkdown(this.description);

mds.appendMarkdown('\n_____\n\n');

// Don't try to display current value table for write only fields
if (this.accessType === AccessType.WriteOnly) {
item.description = '<Write Only>';
return mds;
}
else {
switch (this.getFormat()) {
case NumberFormat.Decimal:
item.description = this.currentValue.toString();
break;
case NumberFormat.Binary:
item.description = binaryFormat(this.currentValue, this.hexLength * 4, false, true);
break;
default:
item.description = hexFormat(this.currentValue, this.hexLength);
break;
}

const hex = this.getFormattedValue(NumberFormat.Hexidecimal);
const decimal = this.getFormattedValue(NumberFormat.Decimal);
const binary = this.getFormattedValue(NumberFormat.Binary);

mds.appendMarkdown('| Hex &nbsp;&nbsp; | Decimal &nbsp;&nbsp; | Binary &nbsp;&nbsp; |\n');
mds.appendMarkdown('|:---|:---|:---|\n');
mds.appendMarkdown(`| ${ hex } &nbsp;&nbsp; | ${ decimal } &nbsp;&nbsp; | ${ binary } &nbsp;&nbsp; |\n\n`);

const children = this.getChildren();
if (children.length === 0) { return mds; }

mds.appendMarkdown('**Fields**\n\n');
mds.appendMarkdown('| Field | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Bit-Range | Value |\n');
mds.appendMarkdown('|:---|:---:|:---|:---|\n');

children.forEach((field) => {
mds.appendMarkdown(`| ${ field.name } | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | ${ field.getFormattedRange() } | ${ field.getFormattedValue(field.getFormat(), true) } |\n`);
});

return mds;
}

public getFormattedValue(format: NumberFormat): string {
if (this.accessType === AccessType.WriteOnly) {
return '<Write Only>';
}
const value = this.currentValue;

return item;
switch (format) {
case NumberFormat.Decimal:
return value.toString();
case NumberFormat.Binary:
return binaryFormat(value, this.hexLength * 4);
default:
return hexFormat(value, this.hexLength, true);
}
}

public getChildren(): PeripheralFieldNode[] {
Expand Down Expand Up @@ -157,6 +201,10 @@ export class PeripheralRegisterNode extends PeripheralBaseNode {
});
}

public getAddress(): number {
return this.parent.getAddress(this.offset);
}

private updateValueInternal(value: number): Thenable<boolean> {
const address = this.parent.getAddress(this.offset);
const bytes = [];
Expand Down

0 comments on commit 20ffba5

Please sign in to comment.