Skip to content

Commit

Permalink
Merge pull request #111 from EDAcation/feat/vhdl-support
Browse files Browse the repository at this point in the history
VHDL support
  • Loading branch information
malmeloo authored Oct 28, 2024
2 parents 6583803 + 2db47a7 commit e2f5327
Show file tree
Hide file tree
Showing 14 changed files with 347 additions and 41 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@
"@vscode/codicons": "^0.0.36",
"@vscode/webview-ui-toolkit": "^1.3.1",
"digitaljs": "github:EDAcation/digitaljs#next",
"edacation": "^0.3.9",
"edacation": "^0.3.10",
"nextpnr-viewer": "^0.6.1",
"os-browserify": "^0.3.0",
"path-browserify": "^1.0.1",
Expand Down
15 changes: 15 additions & 0 deletions src/extension/projects/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,21 @@ export class Project extends BaseProject {
if (doSave) await this.save();
}

async setTopLevelModule(targetId: string, module: string) {
const target = this.getTarget(targetId);
if (!target) throw new Error(`Target "${targetId} does not exist!`);

// Ensure the config tree exists
// We don't care about setting missing defaults, as this is target-level configuration,
// so any missing properties will fallback to project-level config.
if (!target.yosys) target.yosys = {};
if (!target.yosys.options) target.yosys.options = {};

target.yosys.options.topLevelModule = module;

await this.save();
}

async removeInputFiles(filePaths: string[]): Promise<void> {
if (!filePaths.length) return;

Expand Down
7 changes: 7 additions & 0 deletions src/extension/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ interface ViewMessageCommand {
args?: [];
}

interface ViewMessageChangeTLM {
type: 'changeTlm';
module: string;
targetId: string;
}

interface ViewMessageRequestSave {
type: 'requestSave';
data: {
Expand All @@ -44,6 +50,7 @@ interface ViewMessageRequestSave {
export type ViewMessage =
| ViewMessageReady
| ViewMessageCommand
| ViewMessageChangeTLM
| ViewMessageChange
| MessageBroadcast
| ViewMessageRequestSave;
Expand Down
6 changes: 2 additions & 4 deletions src/extension/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {FILE_EXTENSIONS_HDL} from 'edacation';
import path from 'path';
import * as vscode from 'vscode';

Expand Down Expand Up @@ -52,12 +53,9 @@ export const encodeJSON = (input: unknown, pretty = false) =>
export const decodeText = (input: BufferSource): string => textDecoder.decode(input);
export const decodeJSON = (input: BufferSource): unknown => JSON.parse(decodeText(input));

export const FILE_EXTENSIONS_VERILOG = ['v', 'vh', 'sv', 'svh'];
export const FILE_EXTENSIONS_VHDL = ['vhd'];

export const FILE_FILTERS_HDL = {
/* eslint-disable-next-line @typescript-eslint/naming-convention */
'HDL (*.v, *.vh, *.sv, *.svh, *.vhd)': [...FILE_EXTENSIONS_VERILOG, ...FILE_EXTENSIONS_VHDL]
'HDL (*.v, *.vh, *.sv, *.svh, *.vhd, *.vhdl)': FILE_EXTENSIONS_HDL
};

export const asWorkspaceRelativeFolderPath = (folderUri: vscode.Uri) =>
Expand Down
19 changes: 14 additions & 5 deletions src/extension/webviews/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,20 @@ export class ActionsProvider extends BaseWebviewViewProvider {

console.log('[actions]', 'ready project', project.getUri());

if (project) {
await webview.postMessage({
type: 'project',
project: Project.serialize(project)
});
await webview.postMessage({
type: 'project',
project: Project.serialize(project)
});
} else if (message.type === 'changeTlm') {
const project = this.projects.getCurrent();
if (!project) return;

console.log('[actions] updating TLM');

try {
await project.setTopLevelModule(message.targetId, message.module);
} catch (err) {
console.log(`[actions] Error while updating TLM: ${err}`);
}
} else if (message.type === 'command') {
await vscode.commands.executeCommand(message.command, ...(message.args ?? []));
Expand Down
17 changes: 15 additions & 2 deletions src/views/actions/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
<script lang="ts">
import {provideVSCodeDesignSystem, vsCodeButton, vsCodeDivider, vsCodeDropdown, vsCodeOption} from '@vscode/webview-ui-toolkit';
import {
provideVSCodeDesignSystem,
vsCodeButton,
vsCodeDivider,
vsCodeDropdown,
vsCodeOption,
vsCodeTextField
} from '@vscode/webview-ui-toolkit';
import {vscode} from '../../vscode';
import EDAProjectActions from './components/EDAProjectActions.vue';
import {state} from './state';
provideVSCodeDesignSystem().register(vsCodeButton(), vsCodeDropdown(), vsCodeOption(), vsCodeDivider());
provideVSCodeDesignSystem().register(
vsCodeButton(),
vsCodeDropdown(),
vsCodeOption(),
vsCodeDivider(),
vsCodeTextField()
);
export default {
components: {
Expand Down
18 changes: 10 additions & 8 deletions src/views/actions/src/components/EDAProjectActions.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<script lang="ts">
import type {TargetConfiguration} from 'edacation';
import {defineComponent} from 'vue';
import * as vscode from '../../../vscode';
import {state as globalState} from '../state';
import EDATargetSelector from './EDATargetSelector.vue';
import type { TargetConfiguration } from 'edacation';
import EDATargetTLM from './EDATargetTLM.vue';
export default defineComponent({
components: {
EDATargetSelector
EDATargetSelector,
EDATargetTLM
},
data() {
return {
Expand All @@ -30,7 +33,7 @@ export default defineComponent({
if (!this.selectedTarget) {
return this.executeCommand(command);
}
return this.executeCommand(command, this.selectedTarget.id)
return this.executeCommand(command, this.selectedTarget.id);
}
},
computed: {
Expand All @@ -40,20 +43,19 @@ export default defineComponent({
selectedTarget(): TargetConfiguration | null {
return this.targets[this.state.selectedTargetIndex] ?? null;
},
}
}
});
</script>

<template>
<div style="display: flex; flex-direction: column; align-items: stretch; gap: 0.75rem; margin: 0.5rem">
<vscode-button @click="executeCommand('openProjectConfiguration')">
Open Project Configuration
</vscode-button>
<vscode-button @click="executeCommand('openProjectConfiguration')"> Open Project Configuration </vscode-button>

<template v-if="targets.length !== 0">
<vscode-divider></vscode-divider>
<EDATargetSelector v-if="targets.length > 1"/>
<EDATargetSelector v-if="targets.length > 1" />
<EDATargetTLM :targetIndex="state.selectedTargetIndex" />

<vscode-button @click="executeTargetCommand('runRTL')">Show RTL</vscode-button>
<vscode-button @click="executeTargetCommand('runYosys')">Synthesize using Yosys</vscode-button>
Expand Down
66 changes: 66 additions & 0 deletions src/views/actions/src/components/EDATargetTLM.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script lang="ts">
import {TextField} from '@vscode/webview-ui-toolkit';
import {type TargetConfiguration, type YosysOptions, getYosysDefaultOptions, getYosysOptions} from 'edacation';
import {defineComponent} from 'vue';
import * as vscode from '../../../vscode';
import {state as globalState} from '../state';
export default defineComponent({
props: {
targetIndex: {
type: Number
}
},
data() {
return {
state: globalState
};
},
methods: {
handleTLMChange(event: Event) {
if (!this.target) return;
const newTlm = (event.target as TextField).currentValue;
vscode.vscode.postMessage({
type: 'changeTlm',
module: newTlm,
targetId: this.target.id
});
}
},
computed: {
target(): TargetConfiguration | null {
if (this.targetIndex === undefined) return null;
const targets = this.state.project?.configuration.targets ?? [];
return targets[this.targetIndex] || null;
},
effectiveOptions(): YosysOptions | null {
if (!this.state.project) return null;
const projectConfig = this.state.project.configuration;
const targetId = this.target?.id;
if (!targetId) {
// Default configuration
return getYosysDefaultOptions(projectConfig);
} else {
// Target configuration
return getYosysOptions(projectConfig, targetId);
}
},
topLevelModule(): string | null {
return this.effectiveOptions?.topLevelModule ?? null;
}
}
});
</script>

<template>
<vscode-text-field placeholder="Automatic (Verilog only)" :value="topLevelModule" @input="handleTLMChange">
Top-level module
</vscode-text-field>
</template>

<style scoped></style>
26 changes: 20 additions & 6 deletions src/views/project/src/components/EDATargetNextpnr.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {NextpnrConfiguration, NextpnrTargetConfiguration, TargetConfigurati
import {defineComponent} from 'vue';
import {state as globalState} from '../state';
import {type PotentialError} from '../util';
import EDATargetValueList from './EDATargetValueList.vue';
Expand All @@ -28,10 +29,22 @@ export default defineComponent({
console.log('nextpnr target', this.target, this.targetIndex, nextpnr, nextpnr ?? {});
return nextpnr ?? {};
},
generated(): ReturnType<typeof generateNextpnrWorkerOptions> | null {
if (!this.target) return null;
generated(): PotentialError<ReturnType<typeof generateNextpnrWorkerOptions> | null> {
if (!this.target || !this.state.project) return {status: 'ok', res: null};
return generateNextpnrWorkerOptions(this.state.project!.configuration, this.target.id);
try {
const options = generateNextpnrWorkerOptions(this.state.project.configuration, this.target.id);
return {status: 'ok', res: options};
} catch (err: any) {
console.trace(`Error generating Nextpnr worker options: ${err}`);
return {status: 'error', err: err as Error};
}
},
generatedError(): Error | null {
return this.generated.status === 'error' ? this.generated.err : null;
},
generatedOptions(): ReturnType<typeof generateNextpnrWorkerOptions> | null {
return this.generated.status === 'ok' ? this.generated.res : null;
}
},
data() {
Expand All @@ -48,9 +61,10 @@ export default defineComponent({
<template>
<template v-if="nextpnr">
<div style="width: 100%; display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem">
<code v-if="generatedError" style="color: red; grid-column: span 2">{{ generatedError }}</code>
<EDATargetValueList
:targetIndex="targetIndex"
:generated="generated?.arguments ?? []"
:generated="generatedOptions?.arguments ?? []"
:parse="parseNextpnrArguments"
workerId="nextpnr"
workerName="nextpnr"
Expand All @@ -63,7 +77,7 @@ export default defineComponent({
<EDATargetValueList
:targetIndex="targetIndex"
:generated="generated?.inputFiles ?? []"
:generated="generatedOptions?.inputFiles ?? []"
workerId="nextpnr"
workerName="nextpnr"
configId="inputFiles"
Expand All @@ -76,7 +90,7 @@ export default defineComponent({
<EDATargetValueList
:targetIndex="targetIndex"
:generated="generated?.outputFiles ?? []"
:generated="generatedOptions?.outputFiles ?? []"
workerId="nextpnr"
workerName="nextpnr"
configId="outputFiles"
Expand Down
28 changes: 27 additions & 1 deletion src/views/project/src/components/EDATargetSynthesis.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
<script lang="ts">
import type {TargetConfiguration} from 'edacation';
import {defineComponent} from 'vue';
import {state as globalState} from '../state';
import EDATargetCheckbox from './EDATargetCheckbox.vue';
import EDATargetTextfield from './EDATargetTextfield.vue';
export default defineComponent({
components: {
EDATargetCheckbox
EDATargetCheckbox,
EDATargetTextfield
},
props: {
targetIndex: {
type: Number
}
},
computed: {
target(): TargetConfiguration | undefined {
if (this.targetIndex === undefined) {
return undefined;
}
return this.state.project!.configuration.targets[this.targetIndex];
}
},
data() {
return {
state: globalState
};
}
});
</script>
Expand All @@ -23,5 +41,13 @@ export default defineComponent({
configId="optimize"
configName="Enable Yosys optimization"
/>

<EDATargetTextfield
:targetIndex="targetIndex"
workerId="yosys"
configId="topLevelModule"
configName="Top-level module name"
placeholder="Automatic (Verilog only)"
/>
</div>
</template>
Loading

0 comments on commit e2f5327

Please sign in to comment.