0.4.0 第三次测试迭代

This commit is contained in:
锦恢 2024-12-01 01:09:28 +08:00
parent a141f5f53c
commit ab24f6c670
13 changed files with 340 additions and 104 deletions

View File

@ -71,5 +71,12 @@
"info.pl.xilinx.no-need-add-files": "Keine Dateien zum Hinzufügen zum Xilinx-Projekt",
"info.pl.xilinx.no-need-del-files": "Es müssen keine Dateien aus Xilinx gelöscht werden.",
"error.pl.launch.not-valid-vivado-path": "Fehler beim Starten des Vivado TCL-Skriptinterpreters: {0}. Bitte überprüfen Sie, ob der Startpfad für Vivado korrekt ist. Derzeit eingestellter Startordnerpfad für Vivado: {1}",
"info.pl.launch.set-vivado-path": "Zur Einstellung des Vivado-Installationspfads gehen"
"info.pl.launch.set-vivado-path": "Zur Einstellung des Vivado-Installationspfads gehen",
"info.monitor.current-mode": "Aktueller Monitor-Modus: {0}",
"info.simulation.create-vvp": "VVP-Datei in {0} erstellen",
"error.simulation.reason": "Grund: {0}",
"info.simulate.vvp.vcd-generate": "vcd-Datei wurde erstellt in {0}",
"error.simluate.icarus.use-primitives": "Es wurde ein Primitiv {0} erkannt, aber Icarus iverilog unterstützt keine Primitiven.",
"error.simluate.icarus.use-ip": "Es wurde die IP {0} verwendet, aber Icarus iverilog unterstützt keine IP.",
"error.simulation.error-happen-run-command": "Fehler bei der Icarus-Simulation:"
}

View File

@ -71,5 +71,12 @@
"info.pl.xilinx.no-need-add-files": "No files need to be added to the Xilinx project",
"info.pl.xilinx.no-need-del-files": "There are no files to be deleted from Xilinx.",
"error.pl.launch.not-valid-vivado-path": "Error encountered while starting the Vivado TCL script interpreter: {0}. Please check if your Vivado startup path is correct. Currently set Vivado startup folder path: {1}",
"info.pl.launch.set-vivado-path": "Go to set the Vivado installation path"
"info.pl.launch.set-vivado-path": "Go to set the Vivado installation path",
"info.monitor.current-mode": "Current monitor mode: {0}",
"info.simulation.create-vvp": "Create VVP file in {0}",
"error.simulation.reason": "Reason: {0}",
"info.simulate.vvp.vcd-generate": "vcd file has been generated to {0}",
"error.simluate.icarus.use-primitives": "Primitive {0} detected, but Icarus iverilog does not support primitives.",
"error.simluate.icarus.use-ip": "Detected the use of IP {0}, but Icarus iverilog does not support IP.",
"error.simulation.error-happen-run-command": "Error during Icarus simulation:"
}

View File

@ -71,5 +71,12 @@
"info.pl.xilinx.no-need-add-files": "Xilinx プロジェクトに追加するファイルはありません",
"info.pl.xilinx.no-need-del-files": "Xilinx から削除するファイルはありません。",
"error.pl.launch.not-valid-vivado-path": "Vivado TCL スクリプトインタプリタの起動中にエラーが発生しました:{0}。Vivado の起動パスが正しいか確認してください。現在設定されている Vivado 起動フォルダパス:{1}",
"info.pl.launch.set-vivado-path": "Vivado インストールパスの設定に移動"
"info.pl.launch.set-vivado-path": "Vivado インストールパスの設定に移動",
"info.monitor.current-mode": "現在のモニターモード:{0}",
"info.simulation.create-vvp": "{0} で VVP ファイルを作成",
"error.simulation.reason": "理由: {0}",
"info.simulate.vvp.vcd-generate": "vcdファイルが生成されました: {0}",
"error.simluate.icarus.use-primitives": "プリミティブ {0} が検出されましたが、Icarus iverilog はプリミティブをサポートしていません。",
"error.simluate.icarus.use-ip": "IP {0} が使用されていますが、Icarus iverilog は IP をサポートしていません。",
"error.simulation.error-happen-run-command": "Icarusシミュレーション中にエラーが発生しました"
}

View File

@ -71,5 +71,12 @@
"info.pl.xilinx.no-need-add-files": "没有需要添加到 Xilinx 工程的文件",
"info.pl.xilinx.no-need-del-files": "没有需要从 Xilinx 中删除的文件",
"error.pl.launch.not-valid-vivado-path": "启动 Vivado TCL 脚本解释器遇到错误:{0} 。请检查你的 Vivado 启动路径是否正确,当前设置的 Vivado 启动文件夹路径:{1}",
"info.pl.launch.set-vivado-path": "前往设置 Vivado 安装路径"
"info.pl.launch.set-vivado-path": "前往设置 Vivado 安装路径",
"info.monitor.current-mode": "当前监视器模式:{0}",
"info.simulation.create-vvp": "在 {0} 创建 VVP 文件",
"error.simulation.reason": "原因: {0}",
"info.simulate.vvp.vcd-generate": "vcd 文件已经生成至 {0}",
"error.simluate.icarus.use-primitives": "检测到使用了原语 {0},但是 Icarus iverilog 并不支持原语",
"error.simluate.icarus.use-ip": "检测到使用了 IP {0},但是 Icarus iverilog 并不支持 IP",
"error.simulation.error-happen-run-command": "Icarus 仿真时,出现错误:"
}

View File

@ -71,5 +71,12 @@
"info.pl.xilinx.no-need-add-files": "沒有需要添加到 Xilinx 工程的文件",
"info.pl.xilinx.no-need-del-files": "沒有需要從 Xilinx 中刪除的檔案。",
"error.pl.launch.not-valid-vivado-path": "啟動 Vivado TCL 腳本解釋器遇到錯誤:{0} 。請檢查你的 Vivado 啟動路徑是否正確,目前設定的 Vivado 啟動資料夾路徑:{1}",
"info.pl.launch.set-vivado-path": "前往設定 Vivado 安裝路徑"
"info.pl.launch.set-vivado-path": "前往設定 Vivado 安裝路徑",
"info.monitor.current-mode": "目前監視器模式:{0}",
"info.simulation.create-vvp": "在 {0} 建立 VVP 檔案",
"error.simulation.reason": "原因: {0}",
"info.simulate.vvp.vcd-generate": "vcd 檔案已生成至 {0}",
"error.simluate.icarus.use-primitives": "偵測到使用了原語 {0},但是 Icarus iverilog 並不支援原語。",
"error.simluate.icarus.use-ip": "偵測到使用了 IP {0},但是 Icarus iverilog 並不支援 IP。",
"error.simulation.error-happen-run-command": "Icarus 模擬時,出現錯誤:"
}

View File

@ -1,5 +1,6 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as fspath from 'path';
import * as child_process from 'child_process';
import { hdlParam } from '../../hdlParser';
@ -10,6 +11,7 @@ import { HdlLangID, ToolChainType } from '../../global/enum';
import { HdlFile, HdlModule } from '../../hdlParser/core';
import { ModuleDataItem } from '../treeView/tree';
import { defaultMacro, doFastApi } from '../../hdlParser/util';
import { t } from '../../i18n';
type Path = string;
@ -185,17 +187,35 @@ class IcarusSimulate extends Simulate {
return args.join(' ').trim();
}
/**
* @description 仿
* @param dependences
* @returns
*/
private makeDependenceArguments(dependences: string[]): string {
// 去重
const visitedPath = new Set<Path>;
const args = [];
for (const dep of dependences) {
// 去重
if (visitedPath.has(dep)) {
continue;
}
// icarus 不支持 原语
if (dep === 'xilinx-primitives') {
MainOutput.report(t('error.simluate.icarus.use-primitives', dep), { level: ReportType.Error });
continue;
}
// icarus 不支持 IP
if (dep.startsWith(opeParam.prjInfo.ipPath)) {
MainOutput.report(t('error.simluate.icarus.use-ip', dep), { level: ReportType.Error });
continue;
}
args.push(makeSafeArgPath(dep));
visitedPath.add(dep);
}
return args.join(' ').trim();
}
@ -215,13 +235,15 @@ class IcarusSimulate extends Simulate {
}
/**
* generate acutal iverlog simulation command
* @description iverilog 仿
* @param name name of top module
* @param path path of the simulated file
* @param dependences dependence that not specified in `include macro
* @returns
*/
private getCommand(name: string, path: AbsPath, dependences: string[]): string | undefined {
MainOutput.clear();
const simConfig = this.getConfig(path, 'iverilog');
if (!simConfig) {
return;
@ -252,22 +274,14 @@ class IcarusSimulate extends Simulate {
const iverilogPath = simConfig.iverilogPath;
// default is -g2012
const argu = '-g' + iverilogCompileOptions.standard;
const outVvpPath = makeSafeArgPath(hdlPath.join(simConfig.simulationHome, 'out.vvp'));
const outVvpPath = makeSafeArgPath(hdlPath.join(simConfig.simulationHome, name + '.vvp'));
const mainPath = makeSafeArgPath(path);
// console.log(macroIncludeArgs);
// console.log(thirdLibraryDirArgs);
// console.log(dependenceArgs);
// console.log(thirdLibraryFileArgs);
const cmd = `${iverilogPath} ${argu} -o ${outVvpPath} -s ${name} ${macroIncludeArgs} ${thirdLibraryDirArgs} ${mainPath} ${dependenceArgs} ${thirdLibraryFileArgs}`;
MainOutput.report(cmd, {
level: ReportType.Run
});
return cmd;
}
private execInTerminal(command: string, cwd: AbsPath) {
private execInTerminal(command: string, cwd: AbsPath, hdlModule: HdlModule) {
// let vvp: vscode.Terminal;
// const targetTerminals = vscode.window.terminals.filter(t => t.name === 'vvp');
// if (targetTerminals.length > 0) {
@ -291,56 +305,136 @@ class IcarusSimulate extends Simulate {
// }
}
private execInOutput(command: string, cwd: AbsPath) {
/**
* @description Digital IDE iverilog 仿
* @param command
* @param cwd
* @param hdlModule
* @returns
*/
private execInOutput(command: string, cwd: AbsPath, hdlModule: HdlModule) {
const simConfig = this.simConfig;
if (!simConfig) {
return;
}
child_process.exec(command, { cwd }, (error, stdout, stderr) => {
if (error) {
MainOutput.report('Error took place when run ' + command, {
level: ReportType.Error
});
MainOutput.report('Reason: ' + stderr, {
level: ReportType.Error
});
} else {
MainOutput.report(stdout, {
level: ReportType.Info
});
const vvpOutFile = hdlPath.join(simConfig.simulationHome, 'out.vvp');
MainOutput.report("Create vvp to " + vvpOutFile, {
level: ReportType.Run
});
const outVvpPath = hdlPath.join(simConfig.simulationHome, 'out.vvp');
const vvpPath = simConfig.vvpPath;
this.runIverilog(simConfig, command, cwd, hdlModule);
}
// run vvp to interrupt script
const vvpCommand = `${vvpPath} ${outVvpPath}`;
MainOutput.report(vvpCommand, {
level: ReportType.Run
});
child_process.exec(vvpCommand, { cwd }, (error, stdout, stderr) => {
if (error) {
MainOutput.report('Error took place when run ' + vvpCommand, {
level: ReportType.Error
});
MainOutput.report('Reason: ' + stderr, {
level: ReportType.Error
});
} else {
MainOutput.report(stdout, {
level: ReportType.Info
});
}
});
}
private reportCommandError(command: string, stderr: string) {
MainOutput.report(t('error.simulation.error-happen-run-command') + command, {
level: ReportType.Error
});
MainOutput.report(t('error.simulation.reason', stderr), {
level: ReportType.Error
});
}
private exec(command: string, cwd: AbsPath) {
/**
* @description iverilog xxx
* @param simConfig
* @param command
* @param cwd
* @param hdlModule
*/
private runIverilog(simConfig: SimulateConfig, command: string, cwd: string, hdlModule: HdlModule) {
child_process.exec(command, (error, stdout, stderr) => {
if (error) {
this.reportCommandError(command, stderr);
return;
}
// 准备执行 vvp
MainOutput.report(stdout);
const generateVvpName = hdlModule.name + '.vvp';
const outVvpPath = hdlPath.join(simConfig.simulationHome, generateVvpName);
MainOutput.report(t('info.simulation.create-vvp', outVvpPath), {
level: ReportType.Run
});
const vvpPath = simConfig.vvpPath;
// 运行 vvp 文件,执行目录在生成的 vcd 的同级目录
// 对于 vvp 执行的 cwd为了方便用户可以调用 $readmemb $fopen $dumpfile
// 这些系统调用进行 IO所以选择 {workspace} 作为执行 vvp 的 cwd
const vvpCwd = opeParam.openMode === 'file' ? cwd: opeParam.workspacePath;
const vvpCommand = `${vvpPath} ${outVvpPath}`;
MainOutput.report(vvpCommand, { level: ReportType.Run });
this.runVvp(vvpCommand, vvpCwd);
});
}
/**
* @description vvp xxx
* @param command
* @param cwd
*/
private runVvp(command: string, cwd: string) {
child_process.exec(command, { cwd }, (error, stdout, stderr) => {
if (error) {
this.reportCommandError(command, stderr);
return;
}
// 对于 vvp 的输出结果,特殊处理
this.handleVvpStdOutput(stdout, command, cwd);
});
}
/**
* @description vvp xxx.vvp
* vvp
* @param stdoutMessage vvp
* @param command
* @param cwd
*/
private handleVvpStdOutput(stdoutMessage: string, command: string, cwd: string) {
for (const line of stdoutMessage.split('\n').map(line => line.trim())) {
if (line.startsWith('WARNING:')) {
// 运行时警告
MainOutput.report(line.slice(8).trim(), { level: ReportType.Warn });
} else if (line.startsWith('ERROR:')) {
// 运行时错误,比如用户 $readmemb 读取的文件并不存在
MainOutput.report(line.slice(6).trim(), { level: ReportType.Error });
} else if (line.startsWith('VCD info:')) {
// 导出 VCD 的信息,用于输出
// 此处尝试提取 vcd 导出的信息,然后转换内部信息导出到屏幕上
const match = line.match(/dumpfile (.+) opened for output/);
if (match) {
const vcdPath = match[1];
const absVcdPath = hdlPath.resolve(cwd, vcdPath);
MainOutput.report(t('info.simulate.vvp.vcd-generate', absVcdPath));
} else {
MainOutput.report(line.slice(9).trim());
}
} else if (line.startsWith('VCD Error:')) {
// 出现 VCD Error 可能是因为生成地点的目录不存在,创建目录,然后再运行
const match = line.match(/Unable to open (.+) for output\./);
if (match) {
const vcdPath = match[1];
const absVcdPath = hdlPath.resolve(cwd, vcdPath);
const parentFolderPath = fspath.dirname(absVcdPath);
hdlDir.mkdir(parentFolderPath);
// 清除输出,准备第二次运行
MainOutput.clear();
this.runVvp(command, cwd);
} else {
// 没有匹配到,说明是其他错误,直接按照错误输出
MainOutput.report(line.slice(10).trim(), { level: ReportType.Error });
}
} else {
MainOutput.report(line, { level: ReportType.Info });
}
}
}
private exec(command: string, cwd: AbsPath, hdlModule: HdlModule) {
const simConfig = this.simConfig;
if (!simConfig) {
MainOutput.report('this.simConfig is empty when exec');
@ -350,10 +444,10 @@ class IcarusSimulate extends Simulate {
const runInTerminal = vscode.workspace.getConfiguration().get('digital-ide.function.simulate.runInTerminal');
if (runInTerminal) {
this.execInTerminal(command, cwd);
this.execInTerminal(command, cwd, hdlModule);
} else {
MainOutput.show();
this.execInOutput(command, cwd);
this.execInOutput(command, cwd, hdlModule);
}
}
@ -375,11 +469,12 @@ class IcarusSimulate extends Simulate {
// MainOutput.report(warningMsg, ReportType.Warn, true);
// return;
// }
const dependences = this.getAllOtherDependences(path, name);
const simulationCommand = this.getCommand(name, path, dependences);
if (simulationCommand) {
const cwd = hdlPath.resolve(path, '..');
this.exec(simulationCommand, cwd);
this.exec(simulationCommand, cwd, hdlModule);
} else {
const errorMsg = 'Fail to generate command';
MainOutput.report(errorMsg, {

View File

@ -133,7 +133,7 @@ async function askUserToSaveFilelist(filelist: string[]) {
if (uri === undefined) {
return;
}
const filePath = uri.path;
const filePath = uri.fsPath;
const fileContent = filelist.join('\n');
try {

View File

@ -67,9 +67,6 @@ export function isSystemVerilogFile(path: AbsPath): boolean {
}
export function isHDLFile(path: AbsPath): boolean {
if (!isFile(path)) {
return false;
}
const ext = hdlPath.extname(path, false);
return hdlExts.includes(ext);
}

View File

@ -103,6 +103,7 @@ function toEscapePath(path: AbsPath): AbsPath {
}
function toPureRelativePath(path: RelPath): RelPath {
if (path.startsWith('./') || path.startsWith('.\\')) {
return path.slice(2);
}

View File

@ -169,6 +169,10 @@ class HdlParam {
return undefined;
}
if (this.isTopModule(path, name)) {
console.log(module);
}
const dependencies : common.HdlDependence = {
current: [],
include: [],
@ -497,15 +501,93 @@ class HdlParam {
const hdlParam = new HdlParam();
class HdlInstance {
name: string; // name of the instance
type: string; // type
/**
* @description
*
* name `u_tool`
* @example
* module hello()
* tool u_tool();
* endmodule
*
*/
name: string;
/**
* @description
*
* type `tool`
* @example
* module hello()
* tool u_tool();
* endmodule
*
*/
type: string;
/**
* @description range
*/
range: common.Range; // range of instance
instModPath: AbsPath | undefined; // path of the definition
instModPathStatus: common.InstModPathStatus; // status of the instance (current, include, others)
instparams: common.InstRange; // range of params
instports: common.InstRange; // range of ports
parentMod: HdlModule; // 例化模块例化地点的外层 module
module: HdlModule | undefined; // 例化模块的定义模块
/**
* @description
*
* instModPath `tool` `module tool`
* @example
* module hello()
* tool u_tool();
* endmodule
*
*/
instModPath: AbsPath | undefined;
/**
* @description
* - current: 例化对应的模块就在当前文件中
* - include: 通过 `include
* - others: 其他
*/
instModPathStatus: common.InstModPathStatus;
/**
* @description params range.
* vhdl generic map range
*/
instparams: common.InstRange;
/**
* @description ports range.
* vhdl port map range
*/
instports: common.InstRange;
/**
* @description module
*
* `u_tool` parentMod `hello`
* @example
* module hello()
* tool u_tool();
* endmodule
*
*/
parentMod: HdlModule;
/**
* @description
*
* `u_tool` `module` tool
* @example
* module hello();
* tool u_tool();
* endmodule
*
* module tool();
* ...
* endmodule
*/
module: HdlModule | undefined;
constructor(name: string,
type: string,
@ -611,7 +693,34 @@ class HdlInstance {
this.instports = newInstance.instports;
this.instModPath = this.module?.path || '';
this.instModPathStatus = this.parentMod.solveInstModPathStatus();
this.updateInstModPathStatus();
}
/**
* @description A u_A1 u_A2
* 使 u_A1 u_A2 u_A1 u_A1.updateInstModPathStatus()
* u_A1 instModPathStatus
*/
public updateInstModPathStatus() {
const module = this.module;
if (module) {
const userModule = this.parentMod;
if (userModule.path === module.path) {
this.instModPathStatus = common.InstModPathStatus.Current;
} else {
const userIncludePaths = userModule.file.macro.includes.map(
include => hdlPath.rel2abs(userModule.path, include.path)
);
if (userIncludePaths.includes(module.path)) {
this.instModPathStatus = common.InstModPathStatus.Include;
} else {
this.instModPathStatus = common.InstModPathStatus.Others;
}
}
} else {
this.instModPathStatus = common.InstModPathStatus.Unknown;
}
}
public get getDoFastFileType(): DoFastFileType | undefined {
@ -882,28 +991,6 @@ class HdlModule {
}
}
public solveInstModPathStatus(): common.InstModPathStatus {
// TODO: 修改这套系统,因为现在只是拿第一个例化来判断的,这是不合理的
// 应该把 common.InstModPathStatus 修改成一个可以通过析取来表示的变量
const inst = hdlParam.getUnhandleInstancesByModuleName(this.name)[0];
if (!inst) {
return common.InstModPathStatus.Unknown;
}
const userModule = inst.parentMod;
if (userModule.path === this.path) {
return common.InstModPathStatus.Current;
} else {
const userIncludePaths = userModule.file.macro.includes.map(
include => hdlPath.rel2abs(userModule.path, include.path));
if (userIncludePaths.includes(this.path)) {
return common.InstModPathStatus.Include;
} else {
return common.InstModPathStatus.Others;
}
}
}
/**
* @description module
*/
@ -918,7 +1005,7 @@ class HdlModule {
// 解决
instance.instModPath = this.path;
instance.instModPathStatus = this.solveInstModPathStatus();
instance.updateInstModPathStatus();
// 找寻这个 instance 对应的真正的 module也有可能是原语
// 并将这个 instance 加入这个 module 的计数器中

View File

@ -1,8 +1,10 @@
import * as vscode from 'vscode';
import { AbsPath, opeParam } from '../global';
import { hdlPath } from '../hdlFs';
import { hdlFile, hdlPath } from '../hdlFs';
import * as fs from 'fs';
import * as fspath from 'path';
import { minimatch } from 'minimatch';
import { toPureRelativePath } from '../hdlFs/path';
class HdlIgnore {
@ -19,9 +21,8 @@ class HdlIgnore {
const workspace = opeParam.workspacePath;
// 转换成相对于 ws 的相对路径,形如 ./src/test.py
let relativePath = hdlPath.toPureRelativePath(hdlPath.relative(workspace, path));
console.log('current path:', relativePath);
for (const pattern of this.patterns.map(p => hdlPath.toPureRelativePath(p))) {
for (const pattern of this.patterns) {
const matched = minimatch(relativePath, pattern);
if (matched) {
return true;
@ -55,11 +56,26 @@ class HdlIgnore {
}
}
this.patterns = [...validGlobStrings];
this.makeClearPattern();
} else {
// .dideignore 不存在直接赋值为空
this.patterns = [];
}
}
/**
* @description 使 patterns
*
*/
private makeClearPattern() {
for (let i = 0; i < this.patterns.length; ++ i) {
let pattern = this.patterns[i];
if (fspath.isAbsolute(pattern)) {
pattern = hdlPath.relative(opeParam.workspacePath, pattern);
}
this.patterns[i] = toPureRelativePath(pattern);
}
}
}
const hdlIgnore = new HdlIgnore();

View File

@ -44,7 +44,6 @@ class HdlMonitor{
*/
public getHdlMonitor() {
const prjInfo = opeParam.prjInfo;
const monitorPathSet = new PathSet();
// 在输出中展示当前的监视路径
@ -57,8 +56,14 @@ class HdlMonitor{
level: ReportType.Launch
});
// chokidar 4.0.0 开始不支持 glob需要在每一个入口自己判断
return this.makeMonitor([opeParam.workspacePath, prjInfo.libCommonPath]);
MainOutput.report(t('info.monitor.current-mode', opeParam.openMode));
if (opeParam.openMode === 'file') {
return this.makeMonitor([prjInfo.libCommonPath]);
} else {
// chokidar 4.0.0 开始不支持 glob需要在每一个入口自己判断
return this.makeMonitor([opeParam.workspacePath, prjInfo.libCommonPath]);
}
}
public getIgnoreMonitor() {