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': '',
+ 'out/sample.m5out_graph.svg': ''
+ }
+ };
+ (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
+
+
+
+ ${navTlvHtml}
+
+
+
+ `;
+
+ 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
+
+