diff --git a/src/OneWare.Core/App.axaml.cs b/src/OneWare.Core/App.axaml.cs index 5380aa124..de1920caf 100644 --- a/src/OneWare.Core/App.axaml.cs +++ b/src/OneWare.Core/App.axaml.cs @@ -401,6 +401,19 @@ Select the target OpenVINO device. InputGesture = new KeyGesture(Key.S, PlatformHelper.ControlKey | KeyModifiers.Shift), Icon = new IconModel("VsImageLib.SaveAll16X") }); + windowService.RegisterMenuItem("MainWindow_MainMenu/File", new MenuItemModel("exit") + { + Command = new RelayCommand(() => + { + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow?.Close(); + } + }), + Header = "Exit", + InputGesture = new KeyGesture(Key.X, PlatformHelper.ControlKey | KeyModifiers.Shift), + Icon = new IconModel("VsImageLib.Exit") + }); var applicationCommandService = Services.Resolve(); diff --git a/src/OneWare.OssCadSuiteIntegration/Loaders/OpenFpgaLoader.cs b/src/OneWare.OssCadSuiteIntegration/Loaders/OpenFpgaLoader.cs index 5161f847e..771bdde49 100644 --- a/src/OneWare.OssCadSuiteIntegration/Loaders/OpenFpgaLoader.cs +++ b/src/OneWare.OssCadSuiteIntegration/Loaders/OpenFpgaLoader.cs @@ -9,8 +9,7 @@ namespace OneWare.OssCadSuiteIntegration.Loaders; -public class OpenFpgaLoader(IChildProcessService childProcess, - ISettingsService settingsService, ILogger logger, IOutputService outputService, +public class OpenFpgaLoader(ISettingsService settingsService, ILogger logger, IOutputService outputService, IToolExecutionDispatcherService toolExecutionDispatcherService) : IFpgaLoader { @@ -44,9 +43,19 @@ public async Task DownloadAsync(UniversalFpgaProjectRoot project) return; } - if (longTerm) openFpgaLoaderArguments.Add("-f"); + if (longTerm) + { + if (properties.GetValueOrDefault("openFpgaLoaderLongTermFlags") is { } longFlags) + openFpgaLoaderArguments.Add(longFlags); + + openFpgaLoaderArguments.Add("-f"); + } + else if (properties.GetValueOrDefault("openFpgaLoaderShortTermFlags") is { } shortFlags) + { + openFpgaLoaderArguments.Add(shortFlags); + } - openFpgaLoaderArguments.AddRange(properties.GetValueOrDefault("OpenFpgaLoader_Flags")?.Split(' ', + openFpgaLoaderArguments.AddRange(properties.GetValueOrDefault("OpenFpgaLoaderFlags")?.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) ?? []); var bitstreamFormat = properties @@ -66,16 +75,28 @@ public async Task DownloadAsync(UniversalFpgaProjectRoot project) } var path = settingsService.GetSettingValue(OssCadSuiteIntegrationModule.OpenFpgaLoaderPathSetting); - var command = ToolCommand.FromShellParams(path, openFpgaLoaderArguments, + outputService.WriteLine("Starting OpenFpgaLoader ..."); + var command = ToolCommand.FromShellParams(path, openFpgaLoaderArguments!, project.FullPath, $"Running {path}...", AppState.Loading, true, null, s => { Dispatcher.UIThread.Post(() => { outputService.WriteLine(s); }); return true; }); - await toolExecutionDispatcherService.ExecuteAsync(command); - - //await childProcess.ExecuteShellAsync(path, openFpgaLoaderArguments, - // project.FullPath, "Running OpenFPGALoader...", AppState.Loading, true); + try + { + await toolExecutionDispatcherService.ExecuteAsync(command); + } + catch (Exception ex) + { + Dispatcher.UIThread.Post(() => + { + outputService.WriteLine($"Error in ExecuteAsync: {ex.Message}"); + if (ex.InnerException != null) + { + outputService.WriteLine($"Details: {ex.InnerException.Message}"); + } + }); + } } } \ No newline at end of file diff --git a/src/OneWare.OssCadSuiteIntegration/OssCadSuiteIntegrationModule.cs b/src/OneWare.OssCadSuiteIntegration/OssCadSuiteIntegrationModule.cs index 3e4bb5433..f0a38f13f 100644 --- a/src/OneWare.OssCadSuiteIntegration/OssCadSuiteIntegrationModule.cs +++ b/src/OneWare.OssCadSuiteIntegration/OssCadSuiteIntegrationModule.cs @@ -367,6 +367,7 @@ public override void Initialize(IServiceProvider serviceProvider) var windowService = serviceProvider.Resolve(); var projectExplorerService = serviceProvider.Resolve(); var fpgaService = serviceProvider.Resolve(); + var outputService = serviceProvider.Resolve(); fpgaService.RegisterNodeProvider(); @@ -545,8 +546,13 @@ await windowService.ShowDialogAsync( Path.Combine(x, "lib", $"python3{PlatformHelper.ExecutableExtension}")); //environmentService.SetEnvironmentVariable("VERILATOR_ROOT", // Path.Combine(x, "share", $"verilator")); - environmentService.SetEnvironmentVariable("GHDL_PREFIX", - Path.Combine(x, "lib", $"ghdl")); + // GHDL is not provided in the Windows version + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + environmentService.SetEnvironmentVariable("GHDL_PREFIX", + Path.Combine(x, "lib", $"ghdl")); + } + environmentService.SetEnvironmentVariable("GTK_EXE_PREFIX", x); environmentService.SetEnvironmentVariable("GTK_DATA_PREFIX", x); environmentService.SetEnvironmentVariable("GDK_PIXBUF_MODULEDIR", @@ -598,6 +604,25 @@ await serviceProvider.Resolve().OpenInGtkWaveAsync(wave.FullPath } } } + if (x is [IProjectFile { Extension: ".ccf" } ccf]) + { + if (ccf.Root is UniversalFpgaProjectRoot universalFpgaProjectRoot) + { + l.Add(new MenuItemModel("ccf") + { + Header = "Convert to pcf", + Command = new AsyncRelayCommand(() => + { + var absoluteCcfPath = Path.Combine(universalFpgaProjectRoot.RootFolderPath, ccf.FullPath); + var absolutePcfPath = Path.ChangeExtension(absoluteCcfPath, ".pcf"); + outputService.WriteLine($"Converting {absoluteCcfPath} to CCF File"); + if (Path.Exists(absolutePcfPath)) + ConstraintFileHelper.Convert(absoluteCcfPath, absolutePcfPath); + return Task.CompletedTask; + }), + }); + } + } }); serviceProvider.Resolve().RegisterFileOpenOverwrite(x => diff --git a/src/OneWare.OssCadSuiteIntegration/Yosys/YosysService.cs b/src/OneWare.OssCadSuiteIntegration/Yosys/YosysService.cs index d971aa658..0b5146f0f 100644 --- a/src/OneWare.OssCadSuiteIntegration/Yosys/YosysService.cs +++ b/src/OneWare.OssCadSuiteIntegration/Yosys/YosysService.cs @@ -74,6 +74,14 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f var includedFiles = project.GetFiles("*.v").Concat(project.GetFiles("*.sv")) .Where(x => !project.IsCompileExcluded(x)) .Where(x => !project.IsTestBench(x)); + + var genVerilogPath = Path.Combine(project.RootFolderPath, "build", "gen_verilog"); + if (Directory.Exists(genVerilogPath)) + { + var generatedFiles = Directory.EnumerateFiles(genVerilogPath, "*.v", + SearchOption.AllDirectories); + includedFiles = includedFiles.Concat(generatedFiles); + } var yosysSynthTool = properties.GetValueOrDefault("yosysToolchainYosysSynthTool") ?? throw new Exception("Yosys Tool not set!"); @@ -91,7 +99,7 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f } else { - yosysCommand = yosysCommand.Replace("$TOP", top.Split(".")[0]); + yosysCommand = yosysCommand.Replace("$TOP", Path.GetFileNameWithoutExtension(top)); yosysCommand = yosysCommand.Replace("$SYNTH_TOOL", yosysSynthTool); yosysCommand = yosysCommand.Replace("$OUTPUT", "build/synth.json"); diff --git a/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs b/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs index b8be0159a..96cf68bf4 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/UniversalFpgaProjectManager.cs @@ -1,11 +1,6 @@ -using Avalonia; -using Avalonia.Controls; -using CommunityToolkit.Mvvm.Input; -using OneWare.Essentials.Extensions; -using OneWare.Essentials.Helpers; +using CommunityToolkit.Mvvm.Input; using OneWare.Essentials.Models; using OneWare.Essentials.Services; -using OneWare.Essentials.ViewModels; using OneWare.UniversalFpgaProjectSystem.Models; using OneWare.UniversalFpgaProjectSystem.Services; using OneWare.UniversalFpgaProjectSystem.ViewModels; @@ -19,14 +14,16 @@ public class UniversalFpgaProjectManager : IProjectManager private readonly IMainDockService _mainDockService; private readonly IProjectExplorerService _projectExplorerService; private readonly IWindowService _windowService; + private readonly IOutputService _outputService; public UniversalFpgaProjectManager(IProjectExplorerService projectExplorerService, IMainDockService mainDockService, - IWindowService windowService, FpgaService fpgaService) + IWindowService windowService, FpgaService fpgaService, IOutputService outputService) { _projectExplorerService = projectExplorerService; _mainDockService = mainDockService; _windowService = windowService; _fpgaService = fpgaService; + _outputService = outputService; _projectExplorerService.RegisterConstructContextMenu(ConstructContextMenu); } @@ -112,6 +109,12 @@ private void ConstructContextMenu(IReadOnlyList selected, Command = new AsyncRelayCommand(() => _projectExplorerService.ReloadProjectAsync(root)), Icon = new IconModel("VsImageLib.RefreshGrey16X") }); + menuItems.Add(new MenuItemModel("Clean") + { + Header = "Clean", + Command = new AsyncRelayCommand(() => + CleanBuildFoldersAsync(root)) + }); menuItems.Add(new MenuItemModel("ProjectSettings") { Header = "Project Settings", @@ -211,4 +214,18 @@ await _windowService.ShowDialogAsync(new UniversalFpgaProjectSettingsEditorView typeof(UniversalFpgaProjectRoot), root)) }); } + + private async Task CleanBuildFoldersAsync(UniversalFpgaProjectRoot root) + { + var buildFolderPath = Path.Combine(root.RootFolderPath, "build"); + _outputService.WriteLine($"Cleaning build folders: {buildFolderPath}"); + if (Directory.Exists(buildFolderPath)) + { + await Task.Run(() => + { + Directory.Delete(buildFolderPath, true); + Directory.CreateDirectory(buildFolderPath); + }); + } + } }