1014 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable @typescript-eslint/naming-convention */
import * as vscode from 'vscode';
import { ChildProcessWithoutNullStreams, exec, spawn } from 'child_process';
import * as fspath from 'path';
import * as fs from 'fs';
import { AbsPath, opeParam, PrjInfo } from '../../global';
import { hdlParam } from '../../hdlParser/core';
import { hdlFile, hdlDir, hdlPath } from '../../hdlFs';
import { PropertySchema } from '../../global/propertySchema';
import { XilinxIP } from '../../global/enum';
import { HardwareOutput, MainOutput, ReportType } from '../../global/outputChannel';
import { debounce } from '../../global/util';
import { t } from '../../i18n';
import { HdlFileProjectType } from '../../hdlParser/common';
interface XilinxCustom {
ipRepo: AbsPath,
bdRepo: AbsPath
};
interface TopMod {
src: string,
sim: string
};
// Programmable Logic Context for short
interface PLContext {
// 保留启动上下文
terminal? : vscode.Terminal,
// 目前使用的启动上下文
process?: ChildProcessWithoutNullStreams,
// 工具类型
tool? : string,
// 第三方工具运行路径
path? : string,
// 操作类
ope : XilinxOperation,
};
interface PLPrjInfo {
path : AbsPath,
name : string,
device : string
};
interface BootInfo {
outsidePath : AbsPath,
insidePath : AbsPath,
outputPath : AbsPath,
elfPath : AbsPath,
bitPath : AbsPath,
fsblPath : AbsPath
};
/**
* xilinx operation under PL
*/
class XilinxOperation {
guiLaunched: boolean;
constructor() {
this.guiLaunched = false;
}
public get xipRepo(): XilinxIP[] {
return opeParam.prjInfo.IP_REPO;
}
public get xipPath(): AbsPath {
return hdlPath.join(opeParam.extensionPath, 'IP_repo');
}
public get xbdPath(): AbsPath {
return hdlPath.join(opeParam.extensionPath, 'lib', 'xilinx', 'bd');
}
public get xilinxPath(): AbsPath {
return hdlPath.join(opeParam.extensionPath, 'resources', 'script', 'xilinx');
}
public get prjPath(): AbsPath {
return opeParam.prjInfo.arch.prjPath;
}
public get srcPath(): AbsPath {
return opeParam.prjInfo.arch.hardware.src;
}
public get simPath(): AbsPath {
return opeParam.prjInfo.arch.hardware.sim;
}
public get datPath(): AbsPath {
return opeParam.prjInfo.arch.hardware.data;
}
public get softSrc(): AbsPath {
return opeParam.prjInfo.arch.software.src;
}
public get HWPath(): AbsPath {
return fspath.dirname(this.srcPath);
}
public get extensionPath(): AbsPath {
return opeParam.extensionPath;
}
public get prjConfig(): PrjInfo {
return opeParam.prjInfo;
}
public get custom(): XilinxCustom {
return {
ipRepo: vscode.workspace.getConfiguration().get('digital-ide.prj.xilinx.IP.repo.path', ''),
bdRepo: vscode.workspace.getConfiguration().get('digital-ide.prj.xilinx.BD.repo.path', '')
};
}
public get topMod(): TopMod {
return {
src : opeParam.firstSrcTopModule.name,
sim : opeParam.firstSimTopModule.name,
};
}
public get prjInfo(): PLPrjInfo {
return {
path : hdlPath.join(this.prjPath, 'xilinx'),
name : opeParam.prjInfo.prjName.PL,
device : opeParam.prjInfo.device
};
}
/**
* xilinx下的launch运行打开存在的工程或者再没有工程时进行新建
* @param context
*/
public async launch(context: PLContext): Promise<string | undefined> {
this.guiLaunched = false;
let scripts: string[] = [];
let prjFilePath = this.prjPath as AbsPath;
// 找到所有的 xilinx 工程文件
const prjFiles = hdlFile.pickFileRecursive(prjFilePath,
filePath => filePath.endsWith('.xpr')
);
if (prjFiles.length) {
if (prjFiles.length > 1) {
const selection = await vscode.window.showQuickPick(prjFiles, {
placeHolder : t('info.pl.xilinx.launch.pick-project-placeholder'),
canPickMany: false
});
if (selection) {
this.open(selection, scripts);
}
} else {
prjFilePath = prjFiles[0];
this.open(prjFilePath, scripts);
}
} else {
if (!hdlDir.mkdir(this.prjInfo.path)) {
vscode.window.showErrorMessage(`mkdir ${this.prjInfo.path} failed`);
return undefined;
}
this.create(scripts);
}
const tclPath = hdlPath.join(this.xilinxPath, 'launch.tcl');
scripts.push(this.getRefreshXprDesignSourceCommand());
scripts.push(`file delete ${tclPath} -force`);
const tclCommands = scripts.join('\n') + '\n';
hdlFile.writeFile(tclPath, tclCommands);
const argu = `-notrace -nolog -nojournal`;
context.path = this.updateVivadoPath();
const cmd = `${context.path} -mode tcl -s ${tclPath} ${argu}`;
const _this = this;
const onVivadoClose = debounce(() => {
_this.onVivadoClose();
}, 100);
function launchScript(): Promise<ChildProcessWithoutNullStreams | undefined> {
if (!opeParam.workspacePath) {
return Promise.resolve(undefined);
}
// 执行 cmd 启动
const vivadoProcess = spawn(cmd, [], { shell: true, stdio: 'pipe', cwd: opeParam.workspacePath });
let status: 'pending' | 'fulfilled' = 'pending';
vivadoProcess.on('close', () => {
onVivadoClose();
});
vivadoProcess.on('exit', () => {
onVivadoClose();
});
vivadoProcess.on('disconnect', () => {
onVivadoClose();
});
return new Promise(resolve => {
vivadoProcess.stdout.on('data', data => {
const message: string = _this.handleMessage(data.toString(), status);
if (status === 'pending') {
HardwareOutput.clear();
HardwareOutput.show();
resolve(vivadoProcess);
}
HardwareOutput.report(message, {
level: ReportType.Info
});
status = 'fulfilled';
});
vivadoProcess.stderr.on('data', async data => {
HardwareOutput.report(data.toString(), {
level: ReportType.Error
});
HardwareOutput.show();
if (status === 'pending') {
// pending 阶段就出现 stderr 说明启动失败
resolve(undefined);
const vivadoInstallPath = vscode.workspace.getConfiguration('digital-ide').get<string>('prj.vivado.install.path') || '';
const res = await vscode.window.showErrorMessage(
t('error.pl.launch.not-valid-vivado-path', data.toString(), vivadoInstallPath.toString()),
{
title: t('info.pl.launch.set-vivado-path'),
value: true
}
);
if (res?.value) {
await vscode.commands.executeCommand('workbench.action.openSettings', 'digital-ide.prj.vivado.install.path');
}
}
});
});
}
const process = await vscode.window.withProgress({
title: t('info.pl.launch.progress.launch-tcl.title'),
location: vscode.ProgressLocation.Notification,
cancellable: true
}, async () => {
return await launchScript();
});
context.process = process;
}
private handleMessage(message: string, status: 'pending' | 'fulfilled'): string {
if (status === 'fulfilled') {
return message.trim();
} else {
const messageBuffer: string[] = [];
for (const line of message.trim().split('\n')) {
if (line.startsWith('source') && line.includes('.tcl')) {
continue;
}
messageBuffer.push(line);
}
const launchInfo = t('info.pl.launch.launch-info');
messageBuffer.unshift(launchInfo);
return messageBuffer.join("\n");
}
}
private onVivadoClose() {
const workspacePath = opeParam.workspacePath;
const plName = opeParam.prjInfo.prjName.PL;
const targetPath = fspath.dirname(opeParam.prjInfo.arch.hardware.src);
const sourceIpPath = `${workspacePath}/prj/xilinx/${plName}.srcs/sources_1/ip`;
const sourceBdPath = `${workspacePath}/prj/xilinx/${plName}.srcs/sources_1/bd`;
hdlDir.mvdir(sourceIpPath, targetPath, true);
HardwareOutput.report("move dir from " + sourceIpPath + " to " + targetPath);
hdlDir.mvdir(sourceBdPath, targetPath, true);
HardwareOutput.report("move dir from " + sourceBdPath + " to " + targetPath);
}
public create(scripts: string[]) {
scripts.push(`set_param general.maxThreads 8`);
scripts.push(`create_project ${this.prjInfo.name} ${this.prjInfo.path} -part ${this.prjInfo.device} -force`);
scripts.push(`set_property SOURCE_SET sources_1 [get_filesets sim_1]`);
scripts.push(`set_property top_lib xil_defaultlib [get_filesets sim_1]`);
scripts.push(`update_compile_order -fileset sim_1 -quiet`);
}
public open(path: AbsPath, scripts: string[]) {
scripts.push(`set_param general.maxThreads 8`);
scripts.push(`open_project ${path} -quiet`);
}
/**
* @description 更新 xpr 设计源的命令
* @returns
*/
private getRefreshXprDesignSourceCommand(): string {
const scripts: string[] = [];
// 清除所有源文件
scripts.push(`remove_files -quiet [get_files]`);
// 导入 IP_repo_paths
scripts.push(`set xip_repo_paths {}`);
if (fs.existsSync(this.custom.ipRepo)) {
scripts.push(`lappend xip_repo_paths ${this.custom.ipRepo}`);
}
this.xipRepo.forEach(
ip => scripts.push(`lappend xip_repo_paths ${this.xipPath}/${ip}`));
scripts.push(`set_property ip_repo_paths $xip_repo_paths [current_project] -quiet`);
scripts.push(`update_ip_catalog -quiet`);
// 导入bd设计源文件
if (hdlFile.isHasAttr(this.prjConfig, "SOC.bd")) {
const bd = this.prjConfig.soc.bd;
const bdFile = bd + '.bd';
let bdSrcPath = hdlPath.join(this.xbdPath, bdFile);
if (!hdlFile.isFile(bdSrcPath)) {
bdSrcPath = hdlPath.join(this.custom.bdRepo, bdFile);
}
if (!hdlFile.isFile(bdSrcPath)) {
vscode.window.showErrorMessage(`can not find ${bd}.bd in ${this.xbdPath} and ${this.custom.bdRepo}`);
} else {
if (hdlFile.copyFile(
bdSrcPath,
hdlPath.join(this.HWPath, 'bd', bd, bdFile)
)) {
vscode.window.showErrorMessage(`cp ${bd} failed, can not find ${bdSrcPath}`);
}
}
const bdPaths = [
hdlPath.join(this.HWPath, 'bd'),
hdlPath.join(this.prjInfo.path, this.prjInfo.name + '.src', 'sources_1', 'bd')
];
hdlFile.pickFileRecursive(bdPaths, filePath => {
if (filePath.endsWith('.bd')) {
scripts.push(`add_files ${filePath} -quiet`);
scripts.push(`add_files ${fspath.dirname(filePath)}/hdl -quiet`);
}
});
if (bd) {
const loadBdPath = hdlPath.join(this.HWPath, 'bd', bd, bdFile);
scripts.push(`generate_target all [get_files ${loadBdPath}] -quiet`);
scripts.push(`make_wrapper -files [get_files ${loadBdPath}] -top -quiet`);
scripts.push(`open_bd_design ${loadBdPath} -quiet`);
}
}
const mrefPath = hdlPath.join(this.HWPath, 'bd', 'mref');
hdlFile.pickFileRecursive(mrefPath, filePath => {
if (filePath.endsWith('.tcl')) {
scripts.push(`source ${filePath}`);
}
});
// 导入ip设计源文件
const ipPaths = [
hdlPath.join(this.HWPath, 'ip'),
hdlPath.join(this.prjInfo.path, this.prjInfo.name + '.src', 'sources_1', 'ip')
];
hdlFile.pickFileRecursive(ipPaths, filePath => {
if (filePath.endsWith('.xci')) {
scripts.push(`add_files ${filePath} -quiet`);
}
});
// 导入非本地的设计源文件
for (const hdlFile of hdlParam.getAllHdlFiles()) {
switch (hdlFile.projectType) {
case HdlFileProjectType.Src:
case HdlFileProjectType.LocalLib:
case HdlFileProjectType.RemoteLib:
// src 和 library 加入 source_1 设计源
scripts.push(`add_file ${hdlFile.path} -quiet`);
break;
case HdlFileProjectType.Sim:
// sim 加入 sim_1 设计源
scripts.push(`add_file -fileset sim_1 ${hdlFile.path} -quiet`);
break;
case HdlFileProjectType.IP:
case HdlFileProjectType.Primitive:
// IP 和 原语不用管
break;
default:
break;
}
}
scripts.push(`add_files -fileset constrs_1 ${this.datPath} -quiet`);
if (this.topMod.src !== '') {
scripts.push(`set_property top ${this.topMod.src} [current_fileset]`);
}
if (this.topMod.sim !== '') {
scripts.push(`set_property top ${this.topMod.sim} [get_filesets sim_1]`);
}
let script = '';
for (let i = 0; i < scripts.length; i++) {
const content = scripts[i];
script += content + '\n';
}
const scriptPath = `${this.xilinxPath}/refresh.tcl`;
script += `file delete ${scriptPath} -force\n`;
hdlFile.writeFile(scriptPath, script);
const cmd = `source ${scriptPath} -quiet`;
return cmd;
}
/**
* @description 【Xilinx Vivado 操作】更新 xpr 文件
* @param context
*/
public refresh(context: PLContext) {
const cmd = this.getRefreshXprDesignSourceCommand();
context.process?.stdin.write(cmd + '\n');
}
public simulate(context: PLContext) {
this.simulateCli(context);
}
public simulateGui(context: PLContext) {
const scriptPath = `${this.xilinxPath}/simulate.tcl`;
const script = `
if {[current_sim] != ""} {
relaunch_sim -quiet
} else {
launch_simulation -quiet
}
set curr_wave [current_wave_config]
if { [string length $curr_wave] == 0 } {
if { [llength [get_objects]] > 0} {
add_wave /
set_property needs_save false [current_wave_config]
} else {
send_msg_id Add_Wave-1 WARNING "No top level signals found. Simulator will start without a wave window. If you want to open a wave window go to 'File->New Waveform Configuration' or type 'create_wave_config' in the TCL console."
}
}
run 1us
start_gui -quiet
file delete ${scriptPath} -force\n`;
hdlFile.writeFile(scriptPath, script);
const cmd = `source ${scriptPath} -quiet`;
HardwareOutput.report('simulateGui');
context.process?.stdin.write(cmd + '\n');
}
public simulateCli(context: PLContext) {
const scriptPath = hdlPath.join(this.xilinxPath, 'simulate.tcl');
const script = `
if {[current_sim] != ""} {
relaunch_sim -quiet
} else {
launch_simulation -quiet
}
set curr_wave [current_wave_config]
if { [string length $curr_wave] == 0 } {
if { [llength [get_objects]] > 0} {
add_wave /
set_property needs_save false [current_wave_config]
} else {
send_msg_id Add_Wave-1 WARNING "No top level signals found. Simulator will start without a wave window. If you want to open a wave window go to 'File->New Waveform Configuration' or type 'create_wave_config' in the TCL console."
}
}
run 1us
file delete ${scriptPath} -force\n`;
hdlFile.writeFile(scriptPath, script);
const cmd = `source ${scriptPath} -quiet`;
HardwareOutput.report('simulateCli');
context.process?.stdin.write(cmd + '\n');
}
public synth(context: PLContext) {
let quietArg = '';
if (opeParam.prjInfo.enableShowLog) {
quietArg = '-quiet';
}
let script = '';
script += `reset_run synth_1 ${quietArg};`;
script += `launch_runs synth_1 ${quietArg} -jobs 4;`;
script += `wait_on_run synth_1 ${quietArg}`;
context.process?.stdin.write(script + '\n');
}
impl(context: PLContext) {
let quietArg = '';
if (opeParam.prjInfo.enableShowLog) {
quietArg = '-quiet';
}
let script = '';
script += `reset_run impl_1 ${quietArg};`;
script += `launch_runs impl_1 ${quietArg} -jobs 4;`;
script += `wait_on_run impl_1 ${quietArg};`;
script += `open_run impl_1 ${quietArg};`;
script += `report_timing_summary ${quietArg}`;
context.process?.stdin.write(script + '\n');
}
build(context: PLContext) {
let quietArg = '';
if (this.prjConfig.enableShowLog) {
quietArg = '-quiet';
}
let script = '';
script += `reset_run synth_1 ${quietArg}\n`;
script += `launch_runs synth_1 ${quietArg} -jobs 4\n`;
script += `wait_on_run synth_1 ${quietArg}\n`;
script += `reset_run impl_1 ${quietArg}\n`;
script += `launch_runs impl_1 ${quietArg} -jobs 4\n`;
script += `wait_on_run impl_1 ${quietArg}\n`;
script += `open_run impl_1 ${quietArg}\n`;
script += `report_timing_summary ${quietArg}\n`;
this.generateBit(context);
const scriptPath = `${this.xilinxPath}/build.tcl`;
script += `source ${scriptPath} -notrace\n`;
script += `file delete ${scriptPath} -force\n`;
hdlFile.writeFile(scriptPath, script);
const cmd = `source ${scriptPath} -quiet`;
context.process?.stdin.write(cmd + '\n');
}
generateBit(context: PLContext) {
let scripts: string[] = [];
let core = this.prjConfig.soc.core;
let sysdefPath = `${this.prjInfo.path}/${this.prjInfo.name}.runs` +
`/impl_1/${this.prjInfo.name}.sysdef`;
if (core && (core !== "none")) {
if (fs.existsSync(sysdefPath)) {
scripts.push(`file copy -force ${sysdefPath} ${this.softSrc}/[current_project].hdf`);
} else {
scripts.push(`write_hwdef -force -file ${this.softSrc}/[current_project].hdf`);
}
// TODO: 是否专门设置输出文件路径的参数
scripts.push(`write_bitstream ./[current_project].bit -force -quiet`);
} else {
scripts.push(`write_bitstream ./[current_project].bit -force -quiet -bin_file`);
}
let script = '';
for (let i = 0; i < scripts.length; i++) {
const content = scripts[i];
script += content + '\n';
}
let scriptPath = `${this.xilinxPath}/bit.tcl`;
script += `file delete ${scriptPath} -force\n`;
hdlFile.writeFile(scriptPath, script);
const cmd = `source ${scriptPath} -quiet`;
context.process?.stdin.write(cmd + '\n');
}
program(context: PLContext) {
let scriptPath = `${this.xilinxPath}/program.tcl`;
let script = `
open_hw -quiet
connect_hw_server -quiet
set found 0
foreach hw_target [get_hw_targets] {
current_hw_target $hw_target
open_hw_target -quiet
foreach hw_device [get_hw_devices] {
if { [string equal -length 6 [get_property PART $hw_device] ${this.prjInfo.device}] == 1 } {
puts "------Successfully Found Hardware Target with a ${this.prjInfo.device} device------ "
current_hw_device $hw_device
set found 1
}
}
if {$found == 1} {break}
close_hw_target
}
#download the hw_targets
if {$found == 0 } {
puts "******ERROR : Did not find any Hardware Target with a ${this.prjInfo.device} device****** "
} else {
set_property PROGRAM.FILE ./[current_project].bit [current_hw_device]
program_hw_devices [current_hw_device] -quiet
disconnect_hw_server -quiet
}
file delete ${scriptPath} -force\n`;
hdlFile.writeFile(scriptPath, script);
const cmd = `source ${scriptPath} -quiet`;
context.process?.stdin.write(cmd + '\n');
}
public async gui(context: PLContext) {
if (context.process === undefined) {
await this.launch(context);
}
if (context.process) {
context.process.stdin.write('start_gui -quiet\n');
HardwareOutput.report(t('info.pl.gui.report-title'), {
level: ReportType.Info
});
HardwareOutput.show();
this.guiLaunched = true;
}
}
public addFiles(files: string[], context: PLContext) {
if (!this.guiLaunched && files.length > 0) {
const filesString = files.join("\n");
HardwareOutput.report(t('info.pl.add-files.title') + '\n' + filesString);
this.execCommandToFilesInTclInterpreter(files, context, "add_file");
}
}
public delFiles(files: string[], context: PLContext) {
if (!this.guiLaunched && files.length > 0) {
const filesString = files.join("\n");
HardwareOutput.report(t('info.pl.del-files.title') + '\n' + filesString);
this.execCommandToFilesInTclInterpreter(files, context, "remove_files");
}
}
/**
* @description 设置为 src 顶层文件
* @param name
* @param context
*/
public setSrcTop(name: string, context: PLContext) {
const cmd = `set_property top ${name} [current_fileset]`;
context.process?.stdin.write(cmd + '\n');
}
/**
* @description 设置为 sim 顶层文件
* @param name
* @param context
*/
public setSimTop(name: string, context: PLContext) {
const cmd = `set_property top ${name} [get_filesets sim_1]`;
context.process?.stdin.write(cmd + '\n');
}
/**
* @description 为输入的每一个文件在 TCL 解释器中执行 command
* @param files
* @param context
* @param command
*/
public execCommandToFilesInTclInterpreter(files: string[], context: PLContext, command: string) {
if (context.process === undefined) {
return;
}
for (const file of files) {
context.process.stdin.write(command + ' ' + file + '\n');
}
}
public xExecShowLog(logPath: AbsPath) {
let logPathList = ["runme", "xvlog", "elaborate"];
let fileName = fspath.basename(logPath, ".log");
if (!logPathList.includes(fileName)) {
return null;
}
let content = hdlFile.readFile(logPath);
if (!content) {
return null;
}
if (content.indexOf("INFO: [Common 17-206] Exiting Vivado") === -1) {
return null;
}
let log = '';
var regExp = /(?<head>CRITICAL WARNING:|ERROR:)(?<content>[\w\W]*?)(INFO:|WARNING:)/g;
while (true) {
let match = regExp.exec(content);
if (match === null) {
break;
}
if (match.groups) {
log += match.groups.head.replace("ERROR:", "[error] :");
log += match.groups.content;
}
}
MainOutput.report(log);
}
public updateVivadoPath(): string {
const vivadoBinFolder = vscode.workspace.getConfiguration('digital-ide.prj.vivado.install').get<string>('path') || '';
if (hdlFile.isDir(vivadoBinFolder)) {
let vivadoPath = hdlPath.join(hdlPath.toSlash(vivadoBinFolder), 'vivado');
if (opeParam.os === 'win32') {
vivadoPath += '.bat';
}
return vivadoPath;
} else {
// 没有设置 vivado bin 文件夹,就认为用户已经把对应的路径加入环境变量了
return 'vivado';
}
}
}
class XilinxBd {
setting : vscode.WorkspaceConfiguration;
extensionPath: AbsPath;
xbdPath: AbsPath;
schemaPath: AbsPath;
schemaCont: PropertySchema;
bdEnum: string[];
bdRepo: AbsPath;
constructor() {
this.setting = vscode.workspace.getConfiguration();
this.extensionPath = opeParam.extensionPath;
this.xbdPath = hdlPath.join(this.extensionPath, 'lib', 'bd', 'xilinx');
this.schemaPath = opeParam.propertySchemaPath;
this.schemaCont = hdlFile.readJSON(this.schemaPath) as PropertySchema;
this.bdEnum = this.schemaCont.properties.soc.properties.bd.enum;
this.bdRepo = this.setting.get('digital-ide.prj.xilinx.BD.repo.path', '');
}
public getConfig() {
this.extensionPath = opeParam.extensionPath;
this.xbdPath = hdlPath.join(this.extensionPath, 'lib', 'bd', 'xilinx');
this.schemaPath = opeParam.propertySchemaPath;
this.schemaCont = hdlFile.readJSON(this.schemaPath) as PropertySchema;
this.bdEnum = this.schemaCont.properties?.soc.properties.bd.enum;
this.bdRepo = this.setting.get('digital-ide.prj.xilinx.BD.repo.path', '');
}
public async overwrite(uri: vscode.Uri): Promise<void> {
this.getConfig();
// 获取当前bd file的路径
const select = await vscode.window.showQuickPick(this.bdEnum);
// the user canceled the select
if (!select) {
return;
}
let bdSrcPath = `${this.xbdPath}/${select}.bd`;
if (!hdlFile.isFile(bdSrcPath)) {
bdSrcPath = `${this.bdRepo}/${select}.bd`;
}
if (!hdlFile.isFile(bdSrcPath)) {
vscode.window.showErrorMessage(`can not find ${select}.bd in ${this.xbdPath} and ${this.bdRepo}, please load again.`);
} else {
const docPath = hdlPath.toSlash(uri.fsPath);
const doc = hdlFile.readFile(docPath);
if (doc) {
hdlFile.writeFile(bdSrcPath, doc);
}
}
}
public add(uri: vscode.Uri) {
this.getConfig();
// 获取当前bd file的路径
let docPath = hdlPath.toSlash(uri.fsPath);
let bd_name = hdlPath.basename(docPath);
// 检查是否重复
if (this.bdEnum.includes(bd_name)) {
vscode.window.showWarningMessage(`The file already exists.`);
return null;
}
// 获取存放路径
let storePath = this.setting.get('digital-ide.prj.xilinx.BD.repo.path', '');
if (!fs.existsSync(storePath)) {
vscode.window.showWarningMessage(`This bd file will be added into extension folder.We don't recommend doing this because it will be cleared in the next update.`);
storePath = this.xbdPath;
}
// 写入
const bd_path = `${storePath}/${bd_name}.bd`;
const doc = hdlFile.readFile(docPath);
if (doc) {
hdlFile.writeFile(bd_path, doc);
}
this.schemaCont.properties.soc.properties.bd.enum.push(bd_name);
hdlFile.writeJSON(this.schemaPath, this.schemaCont);
}
public delete() {
this.getConfig();
vscode.window.showQuickPick(this.bdEnum).then(select => {
// the user canceled the select
if (!select) {
return;
}
let bdSrcPath = `${this.xbdPath}/${select}.bd`;
if (!hdlFile.isFile(bdSrcPath)) {
bdSrcPath = `${this.bdRepo}/${select}.bd`;
}
if (!hdlFile.isFile(bdSrcPath)) {
vscode.window.showErrorMessage(`can not find ${select}.bd in ${this.xbdPath} and ${this.bdRepo}, please load again.`);
} else {
hdlFile.removeFile(bdSrcPath);
}
});
}
public load() {
this.getConfig();
if (hdlFile.isDir(this.bdRepo)) {
for (const file of fs.readdirSync(this.bdRepo)) {
if (file.endsWith('.bd')) {
let basename = hdlPath.basename(file);
if (this.bdEnum.includes(basename)) {
return;
}
this.schemaCont.properties.soc.properties.bd.enum.push(basename);
}
}
}
hdlFile.writeJSON(this.schemaPath, this.schemaCont);
}
};
const tools = {
async boot() {
// 声明变量
const bootInfo: BootInfo = {
outsidePath : hdlPath.join(fspath.dirname(opeParam.prjStructure.prjPath), 'boot'),
insidePath : hdlPath.join(opeParam.extensionPath, 'resources', 'boot', 'xilinx'),
outputPath : hdlPath.join(opeParam.extensionPath, 'resources', 'boot', 'xilinx', 'output.bif'),
elfPath : '',
bitPath : '',
fsblPath : ''
};
if (opeParam.prjInfo.INSIDE_BOOT_TYPE) {
bootInfo.insidePath = hdlPath.join(bootInfo.insidePath, opeParam.prjInfo.INSIDE_BOOT_TYPE);
} else {
bootInfo.insidePath = hdlPath.join(bootInfo.insidePath, 'microphase');
}
let output_context = "//arch = zynq; split = false; format = BIN\n";
output_context += "the_ROM_image:\n";
output_context += "{\n";
bootInfo.fsblPath = await this.getfsblPath(bootInfo.outsidePath, bootInfo.insidePath);
if (!bootInfo.fsblPath) {
return null;
}
output_context += bootInfo.fsblPath;
bootInfo.bitPath = await this.getBitPath(opeParam.workspacePath);
if (bootInfo.bitPath) {
output_context += bootInfo.bitPath;
}
bootInfo.elfPath = await this.getElfPath(bootInfo);
if (!bootInfo.elfPath) {
return null;
}
output_context += bootInfo.elfPath;
output_context += "}";
let result = hdlFile.writeFile(bootInfo.outputPath, output_context);
if (!result) {
return null;
}
let command = `bootgen -arch zynq -image ${bootInfo.outputPath} -o ${opeParam.workspacePath}/BOOT.bin -w on`;
exec(command, function (error, stdout, stderr) {
if (error) {
vscode.window.showErrorMessage(`${error}`);
vscode.window.showErrorMessage(`stderr: ${stderr}`);
return;
} else {
vscode.window.showInformationMessage("write boot file successfully!!");
}
});
},
async getfsblPath(outsidePath: AbsPath, insidePath: AbsPath): Promise<string> {
const paths: AbsPath[] = hdlFile.pickFileRecursive(outsidePath,
filePath => filePath.endsWith('fsbl.elf'));
if (paths.length) {
if (paths.length === 1) {
return `\t[bootloader]${outsidePath}/${paths[0]}\n`;
}
let selection = await vscode.window.showQuickPick(paths);
if (!selection) {
return '';
}
return `\t[bootloader]${outsidePath}/${selection}\n`;
}
return `\t[bootloader]${insidePath}/fsbl.elf\n`;
},
async getBitPath(bitPath: AbsPath): Promise<string> {
let bitList = hdlFile.pickFileRecursive(bitPath,
filePath => filePath.endsWith('.bit'));
if (bitList.length === 0) {
vscode.window.showInformationMessage("Generated only from elf file");
}
else if (bitList.length === 1) {
return"\t" + bitPath + bitList[0] + "\n";
}
else {
let selection = await vscode.window.showQuickPick(bitList);
if (!selection) {
return '';
}
return "\t" + bitPath + selection + "\n";
}
return '';
},
async getElfPath(bootInfo: BootInfo): Promise<string> {
// 优先在外层寻找elf文件
let elfs = this.pickElfFile(bootInfo.outsidePath);
if (elfs.length) {
if (elfs.length === 1) {
return `\t${bootInfo.outsidePath}/${elfs[0]}\n`;
}
let selection = await vscode.window.showQuickPick(elfs);
if (!selection) {
return '';
}
return `\t${bootInfo.outsidePath}/${selection}\n`;
}
// 如果外层找不到文件则从内部调用
elfs = this.pickElfFile(bootInfo.insidePath);
if (elfs.length) {
if (elfs.length === 1) {
return `\t${bootInfo.insidePath}/${elfs[0]}\n`;
}
let selection = await vscode.window.showQuickPick(elfs);
if (!selection) {
return '';
}
return `\t${bootInfo.insidePath}/${selection}\n`;
}
// 如果内层也没有则直接退出
vscode.window.showErrorMessage("The elf file was not found\n");
return '';
},
pickElfFile(path: AbsPath): AbsPath[] {
return hdlFile.pickFileRecursive(path,
filePath => filePath.endsWith('.elf') && !filePath.endsWith('fsbl.elf'));
}
};
export {
XilinxOperation,
tools,
XilinxBd,
PLContext
};