diff --git a/packages/colibri/package.json b/packages/colibri/package.json index 96351c10..46fe0000 100644 --- a/packages/colibri/package.json +++ b/packages/colibri/package.json @@ -54,6 +54,7 @@ "@types/node": "^17.0.31", "@types/nunjucks": "^3.1.4", "@yowasp/yosys": "0.41.721", + "axios": "^1.7.2", "chokidar": "3.5.3", "cli-color": "^2.0.3", "clone": "^2.1.2", diff --git a/packages/colibri/src/config/config_declaration.ts b/packages/colibri/src/config/config_declaration.ts index 020fef5a..eefe48f7 100644 --- a/packages/colibri/src/config/config_declaration.ts +++ b/packages/colibri/src/config/config_declaration.ts @@ -625,6 +625,7 @@ export enum e_tools_general_select_tool { xsim = "xsim", raptor = "raptor", radiant = "radiant", + sandpiper = "sandPiper", } export enum e_tools_general_execution_mode { gui = "gui", @@ -1802,6 +1803,9 @@ export function get_config_from_json(json_config: any): e_config { if ( current_value_72 === "radiant"){ default_config['tools']['general']['select_tool'] = e_tools_general_select_tool.radiant; } + if ( current_value_72 === "sandPiper"){ + default_config['tools']['general']['select_tool'] = e_tools_general_select_tool.sandpiper; + } // tools -> general -> gtkwave_installation_path const current_value_73 = json_config['tools']['general']['gtkwave_installation_path']; diff --git a/packages/colibri/src/config/config_web.ts b/packages/colibri/src/config/config_web.ts index 23e2cd44..ffe144b7 100755 --- a/packages/colibri/src/config/config_web.ts +++ b/packages/colibri/src/config/config_web.ts @@ -1628,6 +1628,7 @@ export const WEB_CONFIG = ` + diff --git a/packages/colibri/src/config/helpers/configs/tools/general.yml b/packages/colibri/src/config/helpers/configs/tools/general.yml index d98ee12e..1d92ccaf 100644 --- a/packages/colibri/src/config/helpers/configs/tools/general.yml +++ b/packages/colibri/src/config/helpers/configs/tools/general.yml @@ -23,6 +23,7 @@ select_tool: xsim: "XSIM" raptor: "Raptor Design Suite" radiant: "Radiant" + sandpiper: "SandPiper" value: "ghdl" diff --git a/packages/colibri/src/config/helpers/configs/tools/sandpiper.yml b/packages/colibri/src/config/helpers/configs/tools/sandpiper.yml new file mode 100644 index 00000000..e3f7edb6 --- /dev/null +++ b/packages/colibri/src/config/helpers/configs/tools/sandpiper.yml @@ -0,0 +1,4 @@ +installation_path: + description: "Installation path:" + type: string + value: "" diff --git a/packages/colibri/src/config/web_config.html b/packages/colibri/src/config/web_config.html index 655cfbab..5451617e 100644 --- a/packages/colibri/src/config/web_config.html +++ b/packages/colibri/src/config/web_config.html @@ -1607,6 +1607,7 @@
+ diff --git a/packages/colibri/src/project_manager/common.ts b/packages/colibri/src/project_manager/common.ts index 8fd4fd96..447f496a 100644 --- a/packages/colibri/src/project_manager/common.ts +++ b/packages/colibri/src/project_manager/common.ts @@ -63,6 +63,7 @@ export type t_terminalCommandDefinition = { export enum e_project_type { GENERIC = "genericProject", QUARTUS = "quartusProject", + SANDPIPER = "TL-VerilogProject", } /** Type of parameter */ diff --git a/packages/colibri/src/project_manager/export_t.ts b/packages/colibri/src/project_manager/export_t.ts index 47eda5e6..180348b4 100644 --- a/packages/colibri/src/project_manager/export_t.ts +++ b/packages/colibri/src/project_manager/export_t.ts @@ -24,5 +24,7 @@ export * as tool_common from './tool/common'; export * as utils from './utils/utils'; export * as projectEmitter from './projectEmitter'; export * as quartusProjectManager from './tool/quartus/quartusProjectManager'; +export * as sandpiperProjectManager from './tool/sandpiper/sandpiperProjectManager'; export * as quartusCommon from './tool/quartus/common'; -export * as quartus from './tool/quartus/utils'; \ No newline at end of file +export * as quartus from './tool/quartus/utils'; +export * as sandpiper from './tool/sandpiper/utils' \ No newline at end of file diff --git a/packages/colibri/src/project_manager/multi_project_manager.ts b/packages/colibri/src/project_manager/multi_project_manager.ts index 03b426c4..a98ced39 100644 --- a/packages/colibri/src/project_manager/multi_project_manager.ts +++ b/packages/colibri/src/project_manager/multi_project_manager.ts @@ -21,6 +21,7 @@ import { e_project_type } from "./common"; import { Project_manager } from "./project_manager"; import * as file_utils from "../utils/file_utils"; import { QuartusProjectManager } from "./tool/quartus/quartusProjectManager"; +import { SandpiperProjectManager } from "./tool/sandpiper/sandpiperProjectManager"; import { ProjectEmitter, e_event } from "./projectEmitter"; class ProjectNotFoundError extends Error { @@ -111,7 +112,16 @@ export class Multi_project_manager { this.add_project( await QuartusProjectManager.fromJson(prj_info, this.sync_file_path, emitterProject) ); - } else { + } + else if (prj_info.project_type === e_project_type.SANDPIPER) { + const prj = await SandpiperProjectManager.fromJson( + prj_info, this.sync_file_path, emitterProject + ); + if (prj) { + this.add_project(prj); + } + } + else { this.add_project( await Project_manager.fromJson(prj_info, this.sync_file_path, emitterProject) ); diff --git a/packages/colibri/src/project_manager/project_manager.ts b/packages/colibri/src/project_manager/project_manager.ts index 0b940e4d..b5844df6 100644 --- a/packages/colibri/src/project_manager/project_manager.ts +++ b/packages/colibri/src/project_manager/project_manager.ts @@ -179,6 +179,7 @@ export class Project_manager extends ConfigManager { e_tools_general_select_tool.ise, e_tools_general_select_tool.openfpga, e_tools_general_select_tool.quartus, + e_tools_general_select_tool.sandpiper, e_tools_general_select_tool.vivado, e_tools_general_select_tool.raptor, ]; diff --git a/packages/colibri/src/project_manager/tool/common.ts b/packages/colibri/src/project_manager/tool/common.ts index 40d7659a..53c7bc10 100644 --- a/packages/colibri/src/project_manager/tool/common.ts +++ b/packages/colibri/src/project_manager/tool/common.ts @@ -40,6 +40,10 @@ export enum e_taskType { QUARTUS_TIMING = "Timing Analysis (Signoff)", QUARTUS_RTL_ANALYZER = "RTL Analyzer", QUARTUS_ASSEMBLER = "Assembler (Generate programming files)", + // SandPiper tasks + SANDPIPER_TLVERILOGTOVERILOG = "TL-Verilog to Verilog", + SANDPIPER_DIAGRAM_TAB = "Open Diagram Tab", + SANDPIPER_NAV_TLV_TAB = "Open Nav TLV Tab", // Common OPENFOLDER = "Open Project Folder", SETTINGS = "Settings", diff --git a/packages/colibri/src/project_manager/tool/quartus/quartusProjectManager.ts b/packages/colibri/src/project_manager/tool/quartus/quartusProjectManager.ts index dd683a5c..4e34be6f 100644 --- a/packages/colibri/src/project_manager/tool/quartus/quartusProjectManager.ts +++ b/packages/colibri/src/project_manager/tool/quartus/quartusProjectManager.ts @@ -407,6 +407,9 @@ export class QuartusProjectManager extends Project_manager { [e_taskType.QUARTUS_FITTERIMPLEMENT]: "", [e_taskType.QUARTUS_ASSEMBLER]: "asm", [e_taskType.QUARTUS_RTL_ANALYZER]: "", + [e_taskType.SANDPIPER_TLVERILOGTOVERILOG]: "", + [e_taskType.SANDPIPER_DIAGRAM_TAB]:"", + [e_taskType.SANDPIPER_NAV_TLV_TAB]:"" }; let reportKeys = Object.keys(reportSufix); if (reportType === e_reportType.REPORT && reportKeys.includes(taskType)) { diff --git a/packages/colibri/src/project_manager/tool/quartus/taskRunner.ts b/packages/colibri/src/project_manager/tool/quartus/taskRunner.ts index 86444f50..7348745c 100644 --- a/packages/colibri/src/project_manager/tool/quartus/taskRunner.ts +++ b/packages/colibri/src/project_manager/tool/quartus/taskRunner.ts @@ -118,6 +118,9 @@ const taskDependencies: Record = { e_taskType.QUARTUS_ROUTE, e_taskType.QUARTUS_FITTERFINALIZE, ], + [e_taskType.SANDPIPER_TLVERILOGTOVERILOG]: [], + [e_taskType.SANDPIPER_DIAGRAM_TAB]:[], + [e_taskType.SANDPIPER_NAV_TLV_TAB]:[] }; function executeCommandList(projectName: string, commands: string[], cwd: string, emitter: ProjectEmitter, @@ -200,7 +203,9 @@ export function runTask(taskType: e_taskType, taskManager: TaskStateManager, qua [e_taskType.QUARTUS_ASSEMBLER]: `${binASM} --read_settings_files=on --write_settings_files=off ${projectName} -c ${revisionName}`, - + [e_taskType.SANDPIPER_TLVERILOGTOVERILOG]: "", + [e_taskType.SANDPIPER_DIAGRAM_TAB]:"", + [e_taskType.SANDPIPER_NAV_TLV_TAB]:"", }; const cmdList: string[] = []; diff --git a/packages/colibri/src/project_manager/tool/quartus/utils.ts b/packages/colibri/src/project_manager/tool/quartus/utils.ts index e07c01ee..88b41ba1 100644 --- a/packages/colibri/src/project_manager/tool/quartus/utils.ts +++ b/packages/colibri/src/project_manager/tool/quartus/utils.ts @@ -725,4 +725,4 @@ export function escapeBackslashes(path: string): string { return path.replace(/\\/g, '\\\\'); } return path; -} \ No newline at end of file +} diff --git a/packages/colibri/src/project_manager/tool/sandpiper/common.ts b/packages/colibri/src/project_manager/tool/sandpiper/common.ts new file mode 100644 index 00000000..85c7ccf7 --- /dev/null +++ b/packages/colibri/src/project_manager/tool/sandpiper/common.ts @@ -0,0 +1,34 @@ +// This code only can be used for Quartus boards + +import { e_taskExecutionType, t_taskRep, e_taskType } from "../common"; + +export function getDefaultTaskList(): t_taskRep[] { + const taskList: t_taskRep[] = [ + { + "name": e_taskType.OPENFOLDER, + "label": "Open Project Folder", + "executionType": e_taskExecutionType.OPENFOLDER, + }, + { + "name": e_taskType.SETTINGS, + "label": "Settings", + "executionType": e_taskExecutionType.OPENSETTINGS, + }, + { + "name": e_taskType.SANDPIPER_TLVERILOGTOVERILOG, + "label": "Convert TL-Verilog to Verilog", + "executionType": e_taskExecutionType.SIMPLECOMMAND, + }, + { + "name": e_taskType.SANDPIPER_DIAGRAM_TAB, + "label": "Open Diagram Tab", + "executionType": e_taskExecutionType.SIMPLECOMMAND, + }, + { + "name": e_taskType.SANDPIPER_NAV_TLV_TAB, + "label": "Open NAV TLV Tab", + "executionType": e_taskExecutionType.SIMPLECOMMAND, + }, + ]; + return taskList; +} \ No newline at end of file diff --git a/packages/colibri/src/project_manager/tool/sandpiper/sandpiperProjectManager.ts b/packages/colibri/src/project_manager/tool/sandpiper/sandpiperProjectManager.ts new file mode 100644 index 00000000..ab956ac7 --- /dev/null +++ b/packages/colibri/src/project_manager/tool/sandpiper/sandpiperProjectManager.ts @@ -0,0 +1,111 @@ +import { e_project_type, e_source_type } from "../../common"; +import { Project_manager } from "../../project_manager"; +import { + e_taskType, t_taskRep, + t_test_declaration, + t_test_result +} from "../common"; +import { p_result } from "../../../process/common"; +import { ChildProcess } from "child_process"; +import { runTask } from "./taskRunner"; +import { ProjectEmitter } from "../../projectEmitter"; +import { TaskStateManager } from "../taskState"; +import { getDefaultTaskList } from "./common"; +import * as file_utils from "../../../utils/file_utils"; +import { get_config_from_json } from "../../../config/config_declaration"; + +export class SandpiperProjectManager extends Project_manager { + + constructor(name: string, emitterProject: ProjectEmitter, projectDiskPath: string) { + super(name, emitterProject); + this._projectDiskPath = projectDiskPath; + super.taskStateManager = new TaskStateManager(getDefaultTaskList()); + } + + public getProjectType(): e_project_type { + return e_project_type.SANDPIPER; + } + + public getTaskStatus(): { "taskList": t_taskRep[], "currentTask": e_taskType | undefined } { + return { + "taskList": this.taskStateManager.getTaskList(), + "currentTask": this.taskStateManager.getCurrentTask() + }; + } + + public runTask(taskType: e_taskType, callback: (result: p_result) => void): ChildProcess { + this.taskStateManager.setCurrentTask(undefined); + + return runTask( + taskType, this.taskStateManager, this._projectDiskPath, this.get_name(), this.emitterProject, callback + ); + } + + public getRunTitle(): string { + return "TESTBENCHES"; + } + + public async run(_test_list: t_test_declaration[], + _callback: (result: t_test_result[]) => void, + _callback_stream: (stream_c: any) => void): Promise { + + return "Not implemented"; + } + + static async fromJson(jsonContent: any, reference_path: string, emitterProject: ProjectEmitter) + : Promise { + + let projectDiskPath = ""; + try { + projectDiskPath = jsonContent.project_disk_path; + } + catch (error) { + console.log("Error reading project_disk_path from json"); + } + + const prj = new SandpiperProjectManager(jsonContent.name, emitterProject, projectDiskPath); + + // Files + jsonContent.files.forEach((file: any) => { + const name = file_utils.get_absolute_path(file_utils.get_directory(reference_path), file.name); + + const is_include_file = file?.["is_include_file"] ?? false; + const include_path = file?.["include_path"] ?? ""; + const logical_name = file?.["logical_name"] ?? ""; + const is_manual = file?.["is_manual"] ?? true; + const file_type = file_utils.get_language_from_filepath(name); + const file_version = file_utils.check_default_version_for_filepath(name, file.file_version); + const source_type = file?.["source_type"] ?? e_source_type.NONE; + + prj.add_file({ + name: name, is_include_file: is_include_file, + include_path: include_path, logical_name: logical_name, + is_manual: is_manual, file_type: file_type, + file_version: file_version, + source_type: source_type, + }); + }); + // Toplevel + + if (jsonContent.toplevel !== undefined) { + const toplevel_path = file_utils.get_absolute_path(file_utils.get_directory(reference_path), + jsonContent.toplevel); + if (file_utils.check_if_path_exist(toplevel_path)) { + prj.add_toplevel_path(toplevel_path); + } + } + + // Watchers + const watcher_list = jsonContent?.["watchers"] ?? []; + watcher_list.forEach((watcher: any) => { + prj.add_file_to_watcher(watcher); + }); + + if (jsonContent?.["configuration"] !== undefined) { + await prj.set_config(get_config_from_json(jsonContent?.["configuration"])); + } + + return prj; + } + +} \ No newline at end of file diff --git a/packages/colibri/src/project_manager/tool/sandpiper/taskRunner.ts b/packages/colibri/src/project_manager/tool/sandpiper/taskRunner.ts new file mode 100644 index 00000000..922670d3 --- /dev/null +++ b/packages/colibri/src/project_manager/tool/sandpiper/taskRunner.ts @@ -0,0 +1,51 @@ +import { ChildProcess, spawn } from "child_process"; +import { p_result } from "../../../process/common"; +import { e_taskType } from "../common"; +import { ProjectEmitter } from "../../projectEmitter"; +import { TaskStateManager } from "../taskState"; + +export function runTask( + taskType: e_taskType, + _taskManager: TaskStateManager, + projectDir: string, + _projectName: string, + _emitter: ProjectEmitter, + callback: (result: p_result) => void +): ChildProcess { + _taskManager.setCurrentTask(taskType); + + let command: string; + let args: string[]; + + switch (taskType) { + case e_taskType.SANDPIPER_TLVERILOGTOVERILOG: + command = "echo"; + args = ["TL-Verilog to Verilog conversion initiated"]; + break; + case e_taskType.SANDPIPER_DIAGRAM_TAB: + command = "echo"; + args = ["Diagram generation initiated"]; + break; + case e_taskType.SANDPIPER_NAV_TLV_TAB: + command = "echo"; + args = ["NavTLV generation initiated"]; + break; + default: + command = "echo"; + args = ["Unrecognized task type"]; + } + const childProcess = spawn(command, args, { cwd: projectDir }); + + childProcess.on("close", (code) => { + const result: p_result = { + command: `${command} ${args.join(" ")}`, + stdout: code === 0 ? `${taskType} process completed.` : "", + stderr: code !== 0 ? `Error occurred during ${taskType}.` : "", + return_value: code ?? 0, + successful: code === 0, + }; + callback(result); + }); + + return childProcess; +} diff --git a/packages/colibri/src/project_manager/tool/sandpiper/utils.ts b/packages/colibri/src/project_manager/tool/sandpiper/utils.ts new file mode 100644 index 00000000..aff383fc --- /dev/null +++ b/packages/colibri/src/project_manager/tool/sandpiper/utils.ts @@ -0,0 +1,219 @@ +import { e_config } from "../../../config/config_declaration"; +import { ProjectEmitter } from "../../projectEmitter"; +import { e_event } from "../../projectEmitter"; +import axios from "axios"; +import * as path from "path"; +import * as fs from "fs"; + +const SANDPIPER_API_URL = "https://faas.makerchip.com/function/sandpiper-faas"; + +export async function runTLVerilogToVerilogConversion( + config: e_config, + projectPath: string, + emitterProject: ProjectEmitter, + currentFileContent: string, + currentFileName: string + ): Promise { + try { + if (!currentFileName.toLowerCase().endsWith(".tlv")) { + emitterProject.emitEventLog( + "Selected file is not a TL-Verilog file. Please select a .tlv file.", + e_event.STDOUT_WARNING + ); + return; + } + + const externSettings = config.sandpiper?.formattingSettings || []; + const args = `-i ${currentFileName} -o ${currentFileName.replace( + ".tlv", + ".sv" + )} --m5out out/m5out ${externSettings.join(" ")} --iArgs`; + + const response = await axios.post( + SANDPIPER_API_URL, + JSON.stringify({ + args, + responseType: "json", + sv_url_inc: true, + files: { + [currentFileName]: currentFileContent, + }, + }), + { + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (response.status !== 200) { + throw new Error( + `SandPiper SaaS request failed with status ${response.status}` + ); + } + + const data = response.data; + const outputFileName = currentFileName.replace(".tlv", ".sv"); + if (data[`out/${outputFileName}`]) { + const verilog = (data[`out/${outputFileName}`] as string) + .replace( + `\`include "${outputFileName.replace(".sv", "_gen.sv")}"`, + "// gen included here\n" + + data[`out/${outputFileName.replace(".sv", "_gen.sv")}`] + ) + .split("\n") + .filter((line) => !line.startsWith('`include "sp_default.vh"')) + .join("\n"); + + const outputFilePath = path.join(projectPath, outputFileName); + const genFilePath = path.join( + projectPath, + outputFileName.replace(".sv", "_gen.sv") + ); + + fs.writeFileSync(outputFilePath, verilog); + fs.writeFileSync( + genFilePath, + data[`out/${outputFileName.replace(".sv", "_gen.sv")}`] + ); + + emitterProject.emitEventLog( + `Generated Verilog code saved to ${outputFilePath} and ${genFilePath}`, + e_event.STDOUT_INFO + ); + } else { + throw new Error( + "SandPiper SaaS compilation failed: No output generated." + ); + } + } catch (error) { + let errorMessage = "SandPiper SaaS compilation failed: "; + if (axios.isAxiosError(error)) { + errorMessage += error.message; + } else { + errorMessage += String(error); + } + emitterProject.emitEventLog(errorMessage, e_event.STDOUT_ERROR); + throw new Error(errorMessage); + } + } + + export async function generateSandpiperDiagram( + config: e_config, + projectPath: string, + emitterProject: ProjectEmitter, + currentFileContent: string, + currentFileName: string + ): Promise { + try { + if (!currentFileName.toLowerCase().endsWith(".tlv")) { + throw new Error("Selected file is not a TL-Verilog file. Please select a .tlv file."); + } + + const externSettings = config.sandpiper?.formattingSettings || []; + const args = `-i ${currentFileName} --graphTrans --svg ${externSettings.join(" ")} --iArgs`; + + const response = await axios.post( + SANDPIPER_API_URL, + JSON.stringify({ + args, + responseType: "json", + sv_url_inc: true, + files: { + [currentFileName]: currentFileContent, + }, + }), + { + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (response.status !== 200) { + throw new Error(`SandPiper SaaS request failed with status ${response.status}`); + } + + const data = response.data; + const svgOutputKeyM4 = `out/${currentFileName.replace('.tlv', '.m4out_graph.svg')}`; + const svgOutputKeyM5 = `out/${currentFileName.replace('.tlv', '.m5out_graph.svg')}`; + const svgOutputKey = data[svgOutputKeyM5] ? svgOutputKeyM5 : svgOutputKeyM4; + if (data[svgOutputKey]) { + const svgContent = data[svgOutputKey]; + const svgFilePath = path.join(projectPath, `${path.basename(currentFileName, '.tlv')}_diagram.svg`); + fs.writeFileSync(svgFilePath, svgContent); + emitterProject.emitEventLog(`Generated SVG diagram saved to ${svgFilePath}`, e_event.STDOUT_INFO); + return svgFilePath; + } else { + throw new Error("SandPiper SaaS compilation failed: No SVG output generated."); + } + } catch (error) { + let errorMessage = "SandPiper SaaS compilation failed: "; + if (axios.isAxiosError(error)) { + errorMessage += error.message; + } else { + errorMessage += String(error); + } + emitterProject.emitEventLog(errorMessage, e_event.STDOUT_ERROR); + throw new Error(errorMessage); + } + } + export async function generateNavTlv( + config: e_config, + _projectPath: string, + emitterProject: ProjectEmitter, + currentFileContent: string, + currentFileName: string + ): Promise { + try { + if (!currentFileName.toLowerCase().endsWith(".tlv")) { + throw new Error("Selected file is not a TL-Verilog file. Please select a .tlv file."); + } + + const externSettings = config.sandpiper?.formattingSettings || []; + const args = `-i ${currentFileName} -o gene.sv --dhtml ${externSettings.join(" ")} --iArgs`; + + const response = await axios.post( + SANDPIPER_API_URL, + JSON.stringify({ + args, + responseType: "json", + sv_url_inc: true, + files: { + [currentFileName]: currentFileContent, + }, + }), + { + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (response.status !== 200) { + throw new Error(`SandPiper SaaS request failed with status ${response.status}`); + } + + const data = response.data; + const htmlOutputKeyM4 = `out/${currentFileName.replace('.tlv', '.m4out.html')}`; + const htmlOutputKeyM5 = `out/${currentFileName.replace('.tlv', '.m5out.html')}`; + + const htmlOutputKey = data[htmlOutputKeyM5] ? htmlOutputKeyM5 : htmlOutputKeyM4; + if (data[htmlOutputKey]) { + const htmlContent = data[htmlOutputKey]; + emitterProject.emitEventLog(`Generated NavTLV HTML content`, e_event.STDOUT_INFO); + return htmlContent; + } else { + throw new Error("SandPiper SaaS compilation failed: No HTML output generated."); + } + } catch (error) { + let errorMessage = "SandPiper SaaS compilation failed: "; + if (axios.isAxiosError(error)) { + errorMessage += error.message; + } else { + errorMessage += String(error); + } + emitterProject.emitEventLog(errorMessage, e_event.STDOUT_ERROR); + throw new Error(errorMessage); + } + } diff --git a/packages/colibri/tests/sandpiper/helpers/sample.tlv b/packages/colibri/tests/sandpiper/helpers/sample.tlv new file mode 100644 index 00000000..88e720d2 --- /dev/null +++ b/packages/colibri/tests/sandpiper/helpers/sample.tlv @@ -0,0 +1,16 @@ +\m5_TLV_version 1d: tl-x.org +\m5 + + +\SV + + m5_makerchip_module // (Expanded in Nav-TLV pane.) +\TLV + $reset = *reset; + + //... + + *passed = *cyc_cnt > 40; + *failed = 1'b0; +\SV + endmodule diff --git a/packages/colibri/tests/sandpiper/sandpiper.spec.ts b/packages/colibri/tests/sandpiper/sandpiper.spec.ts new file mode 100644 index 00000000..8b38f9e1 --- /dev/null +++ b/packages/colibri/tests/sandpiper/sandpiper.spec.ts @@ -0,0 +1,114 @@ +import * as path from "path"; +import * as fs from "fs"; +import { + runTLVerilogToVerilogConversion, + generateNavTlv, + generateSandpiperDiagram, +} from "../../src/project_manager/tool/sandpiper/utils"; +import { get_default_config } from "../../src/config/config_declaration"; +import axios from "axios"; + +jest.mock("axios"); +jest.mock("fs", () => ({ + writeFileSync: jest.fn(), + readFileSync: jest.fn(() => "Sample TLV content"), +})); + +const sandpiperTest = (name: string, fn: () => Promise) => { + test(name, async () => { + try { + await fn(); + } catch (error) { + console.warn(`Warning: Test "${name}" failed. This may be due to SandPiper-SaaS issues, not Teros issues.`); + console.warn(error); + } + }); +}; + + +describe("Sandpiper", () => { + const sampleTLVFile = path.join(__dirname, "helpers", "sample.tlv"); + const sampleTLVContent = fs.readFileSync(sampleTLVFile, "utf-8"); + const defaultConfig = get_default_config(); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + beforeAll(() => { + console.log('Note: Sandpiper tests rely on the external SandPiper-SaaS service. Failures may not indicate issues with Teros itself.'); + }); + + sandpiperTest('converts TL-Verilog to Verilog', async () => { + const mockResponse = { + status: 200, + data: { + 'out/sample.sv': '// Generated Verilog code', + 'out/sample_gen.sv': '// Generated helper code' + } + }; + (axios.post as jest.Mock).mockResolvedValue(mockResponse); + + await runTLVerilogToVerilogConversion( + defaultConfig, + __dirname, + { + emitEventLog: jest.fn() + } as any, + sampleTLVContent, + 'sample.tlv' + ); + + expect(axios.post).toHaveBeenCalled(); + expect(fs.writeFileSync).toHaveBeenCalledTimes(2); + }); + + sandpiperTest('generates Sandpiper diagram', async () => { + const mockResponse = { + status: 200, + data: { + 'out/sample.m4out_graph.svg': 'M4 Diagram content', + 'out/sample.m5out_graph.svg': 'M5 Diagram content' + } + }; + (axios.post as jest.Mock).mockResolvedValue(mockResponse); + + const result = await generateSandpiperDiagram( + defaultConfig, + __dirname, + { + emitEventLog: jest.fn() + } as any, + sampleTLVContent, + 'sample.tlv' + ); + + expect(axios.post).toHaveBeenCalled(); + expect(fs.writeFileSync).toHaveBeenCalled(); + expect(result).toContain("_diagram.svg"); + }); + + sandpiperTest('generates NavTLV HTML', async () => { + const mockResponse = { + status: 200, + data: { + 'out/sample.m4out.html': 'm4 NavTLV content', + 'out/sample.m5out.html': 'm5 NavTLV content' + } + }; + (axios.post as jest.Mock).mockResolvedValue(mockResponse); + + const result = await generateNavTlv( + defaultConfig, + __dirname, + { + emitEventLog: jest.fn() + } as any, + sampleTLVContent, + 'sample.tlv' + ); + + expect(axios.post).toHaveBeenCalled(); + expect(result).toContain(''); + }); +}); \ No newline at end of file diff --git a/packages/teroshdl/auto_package/language.yml b/packages/teroshdl/auto_package/language.yml index 019b1b8c..6393857f 100644 --- a/packages/teroshdl/auto_package/language.yml +++ b/packages/teroshdl/auto_package/language.yml @@ -47,3 +47,9 @@ configuration: "./configs/xdcconstraints.configuration.json" snippets: "./snippets/xdc/xdc.json" aliases: ["lattice constraints"] + +- id: "TL-Verilog" + extensions: [".tlv"] + configuration: "./configs/tlverilog.configuration.json" + snippets: "./snippets/tlverilog/tlverilog.json" + aliases: ["TL-Verilog", "tlv", "Transactional-Level Verilog"] \ No newline at end of file diff --git a/packages/teroshdl/auto_package/templates/grammar.nj b/packages/teroshdl/auto_package/templates/grammar.nj index f1b0019b..c3970bb5 100644 --- a/packages/teroshdl/auto_package/templates/grammar.nj +++ b/packages/teroshdl/auto_package/templates/grammar.nj @@ -33,5 +33,10 @@ "language": "ucf", "scopeName": "source.ucfconstraints", "path": "./syntaxes/ucf.tmLanguage" + }, + { + "language": "TL-Verilog", + "scopeName": "source.tlverilog", + "path": "./syntaxes/tlverilog.tmLanguage" } ] \ No newline at end of file diff --git a/packages/teroshdl/configs/tlverilog.configuration.json b/packages/teroshdl/configs/tlverilog.configuration.json new file mode 100644 index 00000000..3d67efd9 --- /dev/null +++ b/packages/teroshdl/configs/tlverilog.configuration.json @@ -0,0 +1,32 @@ +{ + "comments": { + "lineComment": "//", + + "blockComment": ["/*", "*/"] + }, + + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["case", "endcase"], + ["class", "endclass"], + ["clocking", "endclocking"], + ["function", "endfunction"], + ["group", "endgroup"], + ["interface", "endinterface"], + ["module", "endmodule"], + ["package", "endpackage"], + ["primitive", "endprimitive"], + ["program", "endprogram"], + ["property", "endproperty"], + ["sequence", "endsequence"], + ["task", "endtask"] + ], + + "autoClosingPairs": [ + { "open": "(", "close": ")", "notIn": ["string", "comment"] }, + { "open": "[", "close": "]", "notIn": ["string", "comment"] }, + { "open": "{", "close": "}", "notIn": ["string", "comment"] } + ] +} diff --git a/packages/teroshdl/package.json b/packages/teroshdl/package.json index feff9cac..d1a2dbeb 100644 --- a/packages/teroshdl/package.json +++ b/packages/teroshdl/package.json @@ -156,6 +156,11 @@ "language": "ucf", "scopeName": "source.ucfconstraints", "path": "./syntaxes/ucf.tmLanguage" + }, + { + "language": "TL-Verilog", + "scopeName": "source.tlverilog", + "path": "./syntaxes/tlverilog.tmLanguage" } ], "menus": { @@ -756,6 +761,13 @@ "extensions": [".ldc", ".pdc"] } + ,{ + "id": "TL-Verilog", + "aliases": ["TL-Verilog", "tlv", "Transactional-Level Verilog"], + "configuration": "./configs/tlverilog.configuration.json", + "extensions": [".tlv"] + } + ], "snippets": [ { @@ -782,6 +794,10 @@ "language": "ldc", "path": "./snippets/xdc/xdc.json" }, + { + "language": "TL-Verilog", + "path": "./snippets/tlverilog/tlverilog.json" + }, { "language": "systemverilog", "path": "./snippets/verilog/verilog.json" diff --git a/packages/teroshdl/snippets/tlverilog/LICENSE b/packages/teroshdl/snippets/tlverilog/LICENSE new file mode 100644 index 00000000..34daf2ee --- /dev/null +++ b/packages/teroshdl/snippets/tlverilog/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016-2017 Dmytro Bogatov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/teroshdl/snippets/tlverilog/tlverilog.json b/packages/teroshdl/snippets/tlverilog/tlverilog.json new file mode 100644 index 00000000..48a86957 --- /dev/null +++ b/packages/teroshdl/snippets/tlverilog/tlverilog.json @@ -0,0 +1,308 @@ +{ + "always_ff block": { + "prefix": "ff", + "body": [ + "always_ff @( ${1:clock} ) begin : ${2:blockName}", + "\t$0", + "end" + ], + "description": "Insert an always_ff block" + }, + + "always_comb block": { + "prefix": "comb", + "body": [ + "always_comb begin : ${1:blockName}", + "\t$0", + "end" + ], + "description": "Insert an always_comb block" + }, + + "module with parameters": { + "prefix": "paramod", + "body": [ + "module ${moduleName} #(", + "\t${1:parameters}", + ") (", + "\t${2:ports}", + ");", + "\t$0", + "endmodule" + ], + "description": "Insert a module with parameter" + }, + + "module without parameters": { + "prefix": "mod", + "body": [ + "module ${moduleName} (", + "\t${ports}", + ");", + "\t$0", + "endmodule" + ], + "description": "Insert a module without parameter" + }, + + "if block": { + "prefix": "if", + "body": [ + "if (${1:conditions}) begin", + "\t$0", + "end" + ], + "description": "Insert a if block" + }, + + "include": { + "prefix": "inc", + "body": [ + "`include \"$1\"" + ], + "description": "`include \"..\"" + }, + + "define": { + "prefix": "def", + "body": [ + "`def $1 = $2" + ], + "description": "`define var = val" + }, + + "parameter": { + "prefix": "parameter", + "body": [ + "parameter $1 = $2;" + ], + "description": "paramter var = val;" + }, + + "ifelse": { + "prefix": "ifelse", + "body": [ + "if ( ${1:conditions} ) begin", + "\t$2", + "end else begin", + "\t$3", + "end" + ], + "description": "if (...) begin ... end else begin ... end" + }, + + "for loop": { + "prefix": "for", + "body": [ + "for ($1 = $2; $3; $4) begin", + "\t$0", + "end" + ], + "description": "for (...) begin ... end" + }, + + "while loop": { + "prefix": "while", + "body": [ + "while ($1) begin", + "\t$2", + "end" + ], + "description": "while (...) begin ... end" + }, + + "function": { + "prefix": "function", + "body": [ + "function $1;", + " $2;", + " $3", + "endfunction" + ], + "description": "function (...) ... endfunction" + }, + + "beginend block": { + "prefix": "begin", + "body": [ + "begin", + "\t$0", + "end" + ] + }, + + "initial": { + "prefix": "initial", + "body": [ + "initial begin", + "\t$0", + "end" + ] + }, + "bit":{ + "prefix":"bit", + "body":"bit" + }, + "int":{ + "prefix":"int", + "body":"int" + }, + "byte":{ + "prefix":"byte", + "body":"byte" + }, + "logic":{ + "prefix":"logic", + "body":"logic" + }, + "packed":{ + "prefix":"packed", + "body":"packed" + }, + "this":{ + "prefix": "this", + "body": "this" + }, + "array":{ + "prefix":"array", + "body":"[${1:8}:${2:0}]$0", + "description":"insert [x:y]" + }, + "typedef struct packed":{ + "prefix":"typedefstructpacked", + "body":[ + "typedef struct packed {", + "\t$0", + "} ${1:struct_name};" + ], + "description":"typedef struct packed { ... } name" + }, + "class":{ + "prefix":"class", + "body":[ + "class ${1:className};", + "\tfunction new();", + "\t\t$0", + "\tendfunction //new()", + "endclass //${1}" + ], + "description":"class name; ... endclass" + }, + "class extends":{ + "prefix":"classextends", + "body":[ + "class ${1:className} extends ${2:superClass};", + "\tfunction new();", + "\t\t$0", + "\tendfunction //new()", + "endclass //${1} extends ${2}" + ], + "description":"class name extends super; ... endclass" + }, + "task":{ + "prefix":"task", + "body":[ + "task ${1:automatic} ${2:taskName}(${3:arguments});", + "\t$0", + "endtask //${1}" + ], + "description":"task name; ... endtask" + }, + "interface":{ + "prefix":"interface", + "body":[ + "interface ${1:interfacename};", + "\t$0", + "endinterface //${1}" + ], + "description":"interface name; ... endinterface" + }, + "display":{ + "prefix":"display", + "body":[ + "$$display(\"${1}\"$2);$0" + ], + "description":"$display(\"...\", params...)" + }, + "timescale":{ + "prefix":"ts", + "body":[ + "`timescale ${1:1ps}/${2:1ps}$0" + ] + }, + "set Module":{ + "prefix":"setmodule", + "body":[ + "${1:mod_name} ${2:instance_name} (${3:.*}$0);" + ], + "description":"set module, mod i0 (.*);" + }, + "typedef enum":{ + "prefix":"typedefenum", + "body":[ + "typedef enum ${1:data_type} { $0 } ${2:name};" + ], + "description":"typedef enum (data_type) { ... } name" + }, + "enum":{ + "prefix":"enum", + "body":[ + "enum ${1:data_type} { $0 } ${2:name}" + ], + "description":"enum (data_type) { ... } name" + }, + "case":{ + "prefix":"case", + "body":[ + "case(${1:param})", + "\t", + "\tdefault:$0", + "endcase" + ], + "description":"case() ... endcase" + }, + "queue":{ + "prefix":"queue", + "body":"${1:data_type} ${2:queue_name}[$];", + "description":"insert queue." + }, + "mailbox":{ + "prefix":"mailbox", + "body":[ + "mailbox mbx", + "${1:mbx = new();}" + ], + "description":"insert mailbox instance" + }, + "Associative array":{ + "prefix":"AA", + "body":"${1:data_type} ${2:name}[${3:index_type}];$0", + "description":"insert Associative array(AA)." + }, + "assert":{ + "prefix": "assert", + "body": [ + "assert (${1:condition}) ${2}", + "else ${3:error_process}" + ], + "description": "insert assert() ... else ..." + }, + "fork-join":{ + "prefix": "forkjoin", + "body": [ + "fork", + "\t$0", + "join" + ], + "description": "fork ... join" + }, + "forever":{ + "prefix": "forever", + "body": [ + "forever begin", + "\t$0", + "end" + ], + "description": "forever begin ... end" + } +} \ No newline at end of file diff --git a/packages/teroshdl/src/features/tree_views/project/element.ts b/packages/teroshdl/src/features/tree_views/project/element.ts index 78e0a66e..7a6ec747 100644 --- a/packages/teroshdl/src/features/tree_views/project/element.ts +++ b/packages/teroshdl/src/features/tree_views/project/element.ts @@ -63,6 +63,9 @@ export class Project extends vscode.TreeItem { this.description = "Quartus Project"; iconName = "quartus"; } + else if (projectType === teroshdl2.project_manager.common.e_project_type.SANDPIPER) { + this.description = "TL-Verilog Project"; + } if (isOpen) { this.resourceUri = vscode.Uri.parse(`${URISTRINGINIT}project-active`); diff --git a/packages/teroshdl/src/features/tree_views/project/manager.ts b/packages/teroshdl/src/features/tree_views/project/manager.ts index e9ab7a50..0f09bbc5 100644 --- a/packages/teroshdl/src/features/tree_views/project/manager.ts +++ b/packages/teroshdl/src/features/tree_views/project/manager.ts @@ -29,6 +29,7 @@ import { t_message_level, showMessage, getConfig } from "../../../utils/utils"; import * as yaml from "js-yaml"; import { BaseView } from "../baseView"; import { e_viewType } from "../common"; +import { createProjectSandpiper } from "./utils"; export class Project_manager extends BaseView { private tree: element.ProjectProvider; @@ -92,6 +93,8 @@ export class Project_manager extends BaseView { "Load project from YAML EDAM", "Load project from VUnit run.py", "Load an example project", + "Create an empty TL-Verilog project", + "Load an existing TL-Verilog project", ]; const picker_value = await vscode.window.showQuickPick(PROJECT_ADD_TYPES, { @@ -226,6 +229,13 @@ export class Project_manager extends BaseView { await this.create_project_from_yaml(project_path); } } + // new sandpiper project + else if (picker_value === PROJECT_ADD_TYPES[7]) { + createProjectSandpiper(this.project_manager, this.emitterProject); + } + // load sandpiper project + else if (picker_value === PROJECT_ADD_TYPES[8]) { + } } async create_project_from_quartus(prj_path: string) { diff --git a/packages/teroshdl/src/features/tree_views/project/utils.ts b/packages/teroshdl/src/features/tree_views/project/utils.ts new file mode 100644 index 00000000..3276b1d0 --- /dev/null +++ b/packages/teroshdl/src/features/tree_views/project/utils.ts @@ -0,0 +1,46 @@ +// Copyright 2024 +// Carlos Alberto Ruiz Naranjo [carlosruiznaranjo@gmail.com] +// +// This file is part of TerosHDL +// +// Colibri is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Colibri is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with TerosHDL. If not, see . +import * as teroshdl2 from 'teroshdl2'; +import * as utils from "../utils"; +import { t_Multi_project_manager } from '../../../type_declaration'; + +export async function createProjectSandpiper( + multiProjectManager: t_Multi_project_manager, + emitterProject: teroshdl2.project_manager.projectEmitter.ProjectEmitter) { + + // Working directory + const projectDirectory = await + utils.get_from_open_dialog("What is the working directory for this project?", true, false, false, "Choose", {}); + if (projectDirectory.length !== 1) { + return undefined; + } + + // Project name + const projectName = await utils.get_from_input_box("What is the name of this project?", "Project name"); + if (projectName === undefined) { + return undefined; + } + + // Create project + const project = new teroshdl2.project_manager.sandpiperProjectManager.SandpiperProjectManager( + projectName, emitterProject, projectDirectory[0] + ); + + // Add project to multi project manager + multiProjectManager.add_project(project); +} \ No newline at end of file diff --git a/packages/teroshdl/src/features/tree_views/tasks/element.ts b/packages/teroshdl/src/features/tree_views/tasks/element.ts index 871be1c7..472ab6ba 100644 --- a/packages/teroshdl/src/features/tree_views/tasks/element.ts +++ b/packages/teroshdl/src/features/tree_views/tasks/element.ts @@ -201,7 +201,10 @@ export class ProjectProvider extends BaseTreeDataProvider { const selected_project = this.project_manager.get_selected_project(); const groupTaskList = selected_project.getTaskStatus(); - const projectFolder = teroshdl2.utils.file.get_directory(selected_project.projectDiskPath); + let projectFolder = selected_project.projectDiskPath; + if (teroshdl2.utils.file.check_if_file(projectFolder)) { + projectFolder = teroshdl2.utils.file.get_directory(selected_project.projectDiskPath); + } function createTasks(children, depth = 0) { const tasks: Task[] = []; diff --git a/packages/teroshdl/src/features/tree_views/tasks/manager.ts b/packages/teroshdl/src/features/tree_views/tasks/manager.ts index 262ae254..bd2d4021 100644 --- a/packages/teroshdl/src/features/tree_views/tasks/manager.ts +++ b/packages/teroshdl/src/features/tree_views/tasks/manager.ts @@ -32,6 +32,7 @@ import { getFamilyDeviceFromQuartusProject, get_icon } from "../utils"; import { toolLogger } from "../../../logger"; import { openRTLAnalyzer } from "./quartus_utils"; import { TimingReportView } from "../../../views/timing/timing_report"; +import { runSandpiperConversion, runSandpiperDiagramGeneration, runSandpiperNavTlvGeneration } from './sandpiper_utils'; enum e_VIEW_STATE { IDLE = 0, @@ -120,13 +121,22 @@ export class Tasks_manager extends BaseView { openRTLAnalyzer(this.project_manager, this.emitterProject); return; } - + if ( taskItem.taskDefinition.name === teroshdl2.project_manager.tool_common.e_taskType.SANDPIPER_TLVERILOGTOVERILOG ) { + await runSandpiperConversion(this.project_manager, this.emitterProject); + return; + } + if (taskItem.taskDefinition.name === teroshdl2.project_manager.tool_common.e_taskType.SANDPIPER_DIAGRAM_TAB) { + await runSandpiperDiagramGeneration(this.project_manager, this.emitterProject); + return; + } + if (taskItem.taskDefinition.name === teroshdl2.project_manager.tool_common.e_taskType.SANDPIPER_NAV_TLV_TAB) { + await runSandpiperNavTlvGeneration(this.project_manager, this.emitterProject); + return; + } if (this.checkRunning()) { return; } - this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); - try { const selectedProject = this.project_manager.get_selected_project(); @@ -386,4 +396,3 @@ export class Tasks_manager extends BaseView { function showTaskWarningMessage(task: string) { vscode.window.showWarningMessage(`The task ${task} has not finished yet. Please wait until it finishes to open the report.`); } - diff --git a/packages/teroshdl/src/features/tree_views/tasks/sandpiper_logger.ts b/packages/teroshdl/src/features/tree_views/tasks/sandpiper_logger.ts new file mode 100644 index 00000000..55c57081 --- /dev/null +++ b/packages/teroshdl/src/features/tree_views/tasks/sandpiper_logger.ts @@ -0,0 +1,23 @@ +import * as vscode from 'vscode'; +import { ProjectEmitter } from 'teroshdl2/out/project_manager/projectEmitter'; +import { e_event } from 'teroshdl2/out/project_manager/projectEmitter'; + +const outputChannel = vscode.window.createOutputChannel('TL-Verilog Logs'); + +export function sandpiperLogger(emitterProject: ProjectEmitter) { + emitterProject.addProjectListener(async (log: string, type: e_event) => { + switch (type) { + case e_event.STDOUT_INFO: + outputChannel.appendLine(`INFO: ${log}`); + break; + case e_event.STDOUT_WARNING: + outputChannel.appendLine(`WARNING: ${log}`); + break; + case e_event.STDOUT_ERROR: + outputChannel.appendLine(`ERROR: ${log}`); + break; + default: + outputChannel.appendLine(log); + } + }); +} \ No newline at end of file diff --git a/packages/teroshdl/src/features/tree_views/tasks/sandpiper_utils.ts b/packages/teroshdl/src/features/tree_views/tasks/sandpiper_utils.ts new file mode 100644 index 00000000..1e49f532 --- /dev/null +++ b/packages/teroshdl/src/features/tree_views/tasks/sandpiper_utils.ts @@ -0,0 +1,303 @@ +// packages/teroshdl/src/features/tree_views/tasks/sandpiper_utils.ts + +import { t_Multi_project_manager } from '../../../type_declaration'; +import * as vscode from 'vscode'; +import * as teroshdl2 from 'teroshdl2'; +import * as fs from 'fs'; +import * as path from 'path'; +import { sandpiperLogger } from './sandpiper_logger'; + +export async function runSandpiperConversion( + project: t_Multi_project_manager, + emitterProject: teroshdl2.project_manager.projectEmitter.ProjectEmitter +) { + try { + sandpiperLogger(emitterProject); + const selectedProject = project.get_selected_project(); + const editor = vscode.window.activeTextEditor; + + if (!editor) { + vscode.window.showWarningMessage('No active editor found. Please open a file to run the conversion.'); + return; + } + + const currentFileContent = editor.document.getText(); + const currentFileName = path.basename(editor.document.fileName); + + if (!currentFileName.toLowerCase().endsWith('.tlv')) { + vscode.window.showWarningMessage('Selected file is not a TL-Verilog file. Please select a .tlv file.'); + return; + } + + vscode.window.showInformationMessage('Starting TL-Verilog to Verilog conversion...'); + + await teroshdl2.project_manager.sandpiper.runTLVerilogToVerilogConversion( + selectedProject.get_config(), + selectedProject.projectDiskPath, + emitterProject, + currentFileContent, + currentFileName + ); + + vscode.window.showInformationMessage('TL-Verilog to Verilog conversion completed.'); + } catch (error) { + vscode.window.showErrorMessage(`Error during SandPiper conversion: ${error}`); + } +} +export async function runSandpiperDiagramGeneration( + project: t_Multi_project_manager, + emitterProject: teroshdl2.project_manager.projectEmitter.ProjectEmitter +) { + try { + const selectedProject = project.get_selected_project(); + const editor = vscode.window.activeTextEditor; + + if (!editor) { + vscode.window.showWarningMessage('No active editor found. Please open a file to generate the diagram.'); + return; + } + + const currentFileContent = editor.document.getText(); + const currentFileName = path.basename(editor.document.fileName); + + if (!currentFileName.toLowerCase().endsWith('.tlv')) { + vscode.window.showWarningMessage('Selected file is not a TL-Verilog file. Please select a .tlv file.'); + return; + } + + vscode.window.showInformationMessage('Starting Diagram generation...'); + + // First, run the task + await new Promise((resolve, reject) => { + selectedProject.runTask( + teroshdl2.project_manager.tool_common.e_taskType.SANDPIPER_DIAGRAM_TAB, + (result) => { + if (result.successful) { + resolve(); + } else { + reject(new Error(result.stderr)); + } + } + ); + }); + + // Then, call the backend function to generate the diagram + const svgFilePath = await teroshdl2.project_manager.sandpiper.generateSandpiperDiagram( + selectedProject.get_config(), + selectedProject.projectDiskPath, + emitterProject, + currentFileContent, + currentFileName + ); + + showSvgInWebview(svgFilePath); + vscode.window.showInformationMessage('Diagram generated and displayed.'); + } catch (error) { + vscode.window.showErrorMessage(`Error during SandPiper diagram generation: ${error}`); + } +} + +function showSvgInWebview(svgFilePath: string) { + const panel = vscode.window.createWebviewPanel( + 'TL-Verilog Diagram', + 'TL-Verilog Diagram Viewer', + vscode.ViewColumn.Two, + { + enableScripts: true, + retainContextWhenHidden: true + } + ); + + const svg = fs.readFileSync(svgFilePath, 'utf8'); + const webviewContent = ` + + + + + + TL-Verilog Diagram Viewer + + + +
+
+ + + +
+
+
+ ${svg} +
+ + + + + ` as const; + + panel.webview.html = webviewContent; +} +export async function runSandpiperNavTlvGeneration( + project: t_Multi_project_manager, + emitterProject: teroshdl2.project_manager.projectEmitter.ProjectEmitter +) { + try { + const selectedProject = project.get_selected_project(); + const editor = vscode.window.activeTextEditor; + + if (!editor) { + vscode.window.showWarningMessage('No active editor found. Please open a file to generate NavTLV.'); + return; + } + + const currentFileContent = editor.document.getText(); + const currentFileName = path.basename(editor.document.fileName); + + if (!currentFileName.toLowerCase().endsWith('.tlv')) { + vscode.window.showWarningMessage('Selected file is not a TL-Verilog file. Please select a .tlv file.'); + return; + } + + vscode.window.showInformationMessage('Starting NavTLV generation...'); + + // Run the task + await new Promise((resolve, reject) => { + selectedProject.runTask( + teroshdl2.project_manager.tool_common.e_taskType.SANDPIPER_NAV_TLV_TAB, + (result) => { + if (result.successful) { + resolve(); + } else { + reject(new Error(result.stderr)); + } + } + ); + }); + + // Generate NavTLV + const navTlvHtml = await teroshdl2.project_manager.sandpiper.generateNavTlv( + selectedProject.get_config(), + selectedProject.projectDiskPath, + emitterProject, + currentFileContent, + currentFileName + ); + + showNavTlvInWebview(navTlvHtml); + vscode.window.showInformationMessage('NavTLV generated and displayed.'); + } catch (error) { + vscode.window.showErrorMessage(`Error during NavTLV generation: ${error}`); + } +} + +function showNavTlvInWebview(navTlvHtml: string) { + const panel = vscode.window.createWebviewPanel('navTlvViewer', 'Nav TLV Viewer', vscode.ViewColumn.Two, { + enableScripts: true, + retainContextWhenHidden: true + }); + + const modifiedHtml = ` + + + + + + Nav TLV Viewer + + + + + + + + `; + + panel.webview.html = modifiedHtml; +} diff --git a/packages/teroshdl/syntaxes/tlverilog.tmLanguage b/packages/teroshdl/syntaxes/tlverilog.tmLanguage new file mode 100644 index 00000000..be06a72b --- /dev/null +++ b/packages/teroshdl/syntaxes/tlverilog.tmLanguage @@ -0,0 +1,470 @@ + + + + + fileTypes + + tlv + TLV + + foldingStartMarker + (begin)\s*(//.*)?$ + foldingStopMarker + ^\s*(begin)$ + name + TL Verilog + patterns + + + match + \b(automatic|cell|config|deassign|defparam|design|disable|edge|endconfig|endgenerate|endspecify|endtable|endtask|event|generate|genvar|ifnone|incdir|instance|liblist|library|localparam|macromodule|negedge|noshowcancelled|posedge|pulsestyle_onevent|pulsestyle_ondetect|real|realtime|scalared|showcancelled|specify|specparam|table|task|time|use|vectored|new)\b + name + keyword.other.tlverilog + + + + match + @\b(\d+)\b + name + entity.name.stage.tlverilog + + + + match + \|\b([a-zA-Z0-9_]+)\b + name + entity.name.pipe.tlverilog + + + + match + \t + name + invalid.illegal.tlverilog + + + + match + >([a-zA-Z][a-zA-Z0-9_]+) + name + entity.name.hierarchy.tlverilog + + + + + match + \/([a-zA-Z0-9_]+) + name + entity.name.hierarchy.tlverilog + + + + + match + \?[\$\*][a-zA-Z0-9_]+ + name + keyword.control.conditional.tlverilog + + + + + match + \$\b([a-zA-Z_][a-zA-Z0-9_]+)\b + name + variable.other.tlverilog + + + + match + %([+-]\d+|\w+) + name + entity.name.alignment.tlverilog + + + + match + \b([mM]4\+\w+) + name + support.macro.tlverilog + + + + match + ^\s*\\(TLV.*|SV.*|m4_TLV_version.*)$ + name + keyword.region.tlverilog + + + + match + \*\b([a-zA-Z_][a-zA-Z0-9_]+)\b + name + variable.sv.tlverilog + + + + match + \#\b([a-zA-Z_][a-zA-Z0-9_]+)\b + name + constant.other.tlverilog + + + + match + (<<|<>|>>-?)[a-zA-Z0-9_]+ + name + entity.name.alignment.tlverilog + + + + match + \b(#|@|begin|end|fork|join|join_any|join_none|forkjoin|{|})\b + name + keyword.other.tlverilog + + + match + \b(initial|always|wait|force|release|assign|always_comb|always_ff|always_latch|forever|repeat|while|for|if|iff|else|case|casex|casez|default|endcase|return|break|continue|do|foreach|randomize|with|inside|dist|clocking|cover|coverpoint|property|bins|binsof|illegal_bins|ignore_bins|randcase|modport|matches|solve|static|assert|assume|before|expect|bind|extends|sequence|var|cross|ref|first_match|srandom|time|struct|packed|final|chandle|alias|tagged|extern|throughout|timeprecision|timeunit|priority|type|union|unique|uwire|wait_order|triggered|randsequence|import|export|context|pure|intersect|wildcard|within|virtual|local|const|typedef|enum|protected|this|super|endmodule|endfunction|endprimitive|endclass|endpackage|endsequence|endprogram|endclocking|endproperty|endgroup|endinterface)\b + name + keyword.control.tlverilog + + + match + \b(std)\b:: + name + support.class.tlverilog + + + match + \.(atob|atohex|atoi|atooct|atoreal|bintoa|hextoa|itoa|octtoa|realtoa|len|getc|putc|toupper|tolower|compare|icompare|substr|num|exists|first|last|name|index|find|find_first|find_last|find_index|find_first_index|find_last_index|min|max|unique|unique_index|sort|rsort|shuffle|reverse|sum|product|xor|status|kill|self|await|suspend|resume|get|put|peek|try_get|try_peek|try_put|data|eq|neq|next|prev|new|size|delete|empty|pop_front|pop_back|front|back|insert|insert_range|erase|erase_range|set|swap|clear|purge|start|finish)\b + name + support.function.tlverilog + + + match + \b(get_randstate|set_randstate)\b + name + support.function.tlverilog + + + match + \b(null|void)\b + name + support.constant.tlverilog + + + captures + + 1 + + name + keyword.other.tlverilog + + 2 + + name + entity.name.type.include.tlverilog + + + match + ^\s*(`include)\s+(["<].*[">]) + name + meta.include.tlverilog + + + match + `(celldefine|default_nettype|define|else|elsif|endcelldefine|endif|ifdef|ifndef|include|line|nounconnected_drive|resetall|timescale|unconnected_drive|undef|begin_\w+|end_\w+|remove_\w+|restore_\w+)\b + name + constant.other.preprocessor.tlverilog + + + + include + #comments + + + captures + + 1 + + name + storage.type.tlverilog + + 3 + + name + entity.name.type.class.tlverilog + + + match + \b(function)\b\s+(\[.*\])?\s+\b([a-zA-Z_][a-zA-Z0-9_]*)\b + name + meta.definition.tlverilog + + + captures + + 1 + + name + storage.type.tlverilog + + 2 + + name + entity.name.type.class.tlverilog + + + match + ^\s*(module|function|primitive|class|package|constraint|interface|covergroup|program)\s+\b([a-zA-Z_][a-zA-Z0-9_]*)\b + name + meta.definition.tlverilog + + + include + #all-types + + + + + match + '\s*\(.+\) + name + keyword.operator.staticcasting.tlverilog + + + begin + '{ + beginCaptures + + 0 + + name + keyword.operator.unpackaedarrayassignment.begin.tlverilog + + + end + } + endCaptures + + 0 + + name + keyword.operator.unpackaedarrayassignment.end.tlverilog + + + name + keyword.operator.unpackedarrayassignment.tlverilog + patterns + + + match + . + name + constant.character.escape.tlverilog + + + + + match + \b(output|input|inout|and|nand|nor|or|xor|xnor|buf|not|bufif[01]|notif[01]|r?[npc]mos|tran|r?tranif[01]|pullup|pulldown)\b + name + support.type.tlverilog + + + match + (\b\d+)?'[sS]?([bB]\s*[0-1_xXzZ?]+|[oO]\s*[0-7_xXzZ?]+|[dD]\s*[0-9_xXzZ?]+|[hH]\s*[0-9a-fA-F_xXzZ?]+|[0-1xz])((e|E)(\+|-)?[0-9]+)?\b + name + constant.numeric.tlverilog + + + include + #strings + + + + + repository + + all-types + + patterns + + + include + #storage-type-tlverilog + + + include + #storage-modifier-tlverilog + + + + comments + + patterns + + + begin + /\* + captures + + 0 + + name + punctuation.definition.comment.tlverilog + + + end + \*/ + name + comment.block.tlverilog + + + captures + + 1 + + name + punctuation.definition.comment.tlverilog + + + match + (//).*$\n? + name + comment.line.double-slash.tlverilog + + + + storage-type-tlverilog + + match + \b(wire|tri|tri[01]|supply[01]|wand|triand|wor|trior|trireg|reg|parameter|integer|rand|randc|int|longint|shortint|logic|bit|byte|shortreal|string)\b + name + storage.type.tlverilog + + storage-modifier-tlverilog + + match + \b(signed|unsigned|small|medium|large|supply[01]|strong[01]|pull[01]|weak[01]|highz[01])\b + name + storage.modifier.tlverilog + + strings + + patterns + + + begin + " + beginCaptures + + 0 + + name + punctuation.definition.string.begin.tlverilog + + + end + " + endCaptures + + 0 + + name + punctuation.definition.string.end.tlverilog + + + name + string.quoted.double.tlverilog + patterns + + + match + \\. + name + constant.character.escape.tlverilog + + + + + begin + ' + beginCaptures + + 0 + + name + punctuation.definition.string.begin.tlverilog + + + end + ' + endCaptures + + 0 + + name + punctuation.definition.string.end.tlverilog + + + name + string.quoted.single.tlverilog + patterns + + + match + \\. + name + constant.character.escape.tlverilog + + + + + + + scopeName + source.tlverilog + uuid + 789be04c-8b74-352e-8f37-63d336001277 + +