From daf23cbee8e386f11abe5bffa4e0369f96a1ae88 Mon Sep 17 00:00:00 2001 From: Hendrik Mennen Date: Fri, 8 Mar 2024 16:32:21 +0100 Subject: [PATCH] quartus settings --- .github/workflows/publish.yml | 2 +- Extension.md | 7 +- oneware-extension.json | 8 -- src/OneWare.Quartus/Assets/Quartus_prime.png | Bin 0 -> 9338 bytes src/OneWare.Quartus/Helper/QsfFile.cs | 119 ++++++++++++++++++ src/OneWare.Quartus/Helper/QsfHelper.cs | 24 ++++ src/OneWare.Quartus/OneWare.Quartus.csproj | 8 +- src/OneWare.Quartus/QuartusLoader.cs | 8 +- src/OneWare.Quartus/QuartusToolchain.cs | 105 ++++------------ .../Services/QuartusService.cs | 27 +++- .../QuartusCompileSettingsViewModel.cs | 88 +++++++++++++ .../QuartusCompileWindowExtensionViewModel.cs | 32 ++++- .../Views/QuartusCompileSettingsView.axaml | 29 +++++ .../Views/QuartusCompileSettingsView.axaml.cs | 14 +++ .../QuartusCompileWindowExtensionView.axaml | 16 ++- src/OneWare.Quartus/oneware.json | 8 +- 16 files changed, 380 insertions(+), 115 deletions(-) create mode 100644 src/OneWare.Quartus/Assets/Quartus_prime.png create mode 100644 src/OneWare.Quartus/Helper/QsfFile.cs create mode 100644 src/OneWare.Quartus/Helper/QsfHelper.cs create mode 100644 src/OneWare.Quartus/ViewModels/QuartusCompileSettingsViewModel.cs create mode 100644 src/OneWare.Quartus/Views/QuartusCompileSettingsView.axaml create mode 100644 src/OneWare.Quartus/Views/QuartusCompileSettingsView.axaml.cs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5c76368..e80a719 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,7 +10,7 @@ jobs: build: strategy: matrix: - target: ["win-x64", "linux-x64", "osx-x64", "osx-arm64"] + target: ["win-x64", "linux-x64"] runs-on: ubuntu-latest diff --git a/Extension.md b/Extension.md index 666062b..1626020 100644 --- a/Extension.md +++ b/Extension.md @@ -1,5 +1,8 @@ ![Icon](https://raw.githubusercontent.com/one-ware/OneWare.Quartus/main/Icon.png) -### Get Started +### Get Started with Quartus -> This text will be visible in the builtin package manager +1. Install this Extension +2. Download and Install [Quartus for Linux](https://download.altera.com/akdlm/software/acdsinst/18.1std/625/ib_installers/QuartusLiteSetup-18.1.0.625-linux.run) or for [Quartus for Windows](https://download.altera.com/akdlm/software/acdsinst/18.1std/625/ib_installers/QuartusLiteSetup-18.1.0.625-windows.exe) +3. Go to Settings -> Tools and set the correct installation path +4. Select Quartus as your toolchain in the compile drop-down menu or create a new project selecting quartus \ No newline at end of file diff --git a/oneware-extension.json b/oneware-extension.json index 3902eab..89000d1 100644 --- a/oneware-extension.json +++ b/oneware-extension.json @@ -33,14 +33,6 @@ { "target": "linux-x64", "url": "https://github.com/one-ware/OneWare.Quartus/releases/download/0.1/OneWare.QuartusExtension_0.1_linux-x64.zip" - }, - { - "target": "osx-x64", - "url": "https://github.com/one-ware/OneWare.Quartus/releases/download/0.1/OneWare.QuartusExtension_0.1_osx-x64.zip" - }, - { - "target": "osx-arm64", - "url": "https://github.com/one-ware/OneWare.Quartus/releases/download/0.1/OneWare.QuartusExtension_0.1_osx-arm64.zip" } ] } diff --git a/src/OneWare.Quartus/Assets/Quartus_prime.png b/src/OneWare.Quartus/Assets/Quartus_prime.png new file mode 100644 index 0000000000000000000000000000000000000000..6e894d30478badc5a6eb8e372da83eb408900f28 GIT binary patch literal 9338 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 literal 0 HcmV?d00001 diff --git a/src/OneWare.Quartus/Helper/QsfFile.cs b/src/OneWare.Quartus/Helper/QsfFile.cs new file mode 100644 index 0000000..770be71 --- /dev/null +++ b/src/OneWare.Quartus/Helper/QsfFile.cs @@ -0,0 +1,119 @@ +using System.Text.RegularExpressions; +using OneWare.Essentials.Extensions; +using OneWare.Essentials.Models; + +namespace OneWare.Quartus.Helper; + +public partial class QsfFile(string[] lines) +{ + [GeneratedRegex(@"set_location_assignment\s*PIN_(\w+)\s+-to\s+(\w+)")] + private static partial Regex LocationAssignmentRegex(); + + [GeneratedRegex(@"set_location_assignment\s")] + private static partial Regex RemoveLocationAssignmentRegex(); + + public List Lines { get; private set; } = lines.ToList(); + + public string? GetGlobalAssignment(string propertyName) + { + var regex = new Regex(@"set_global_assignment\s*-name\s" + propertyName + @"\s(.+)"); + foreach (var line in Lines) + { + var match = regex.Match(line); + if (match is { Success: true, Groups.Count: > 1 }) + { + var value = match.Groups[1].Value; + if(value.Length > 0 && value[0] == '"' && value[^1] == '"') return value[1..^1]; + return value; + } + } + return null; + } + + public void SetGlobalAssignment(string name, string value) + { + var regex = new Regex(@"set_global_assignment\s*-name\s*" + name); + var line= Lines.FindIndex(x => regex.IsMatch(x)); + var newAssignment = $"set_global_assignment -name {name} \"{value}\""; + + if(line != -1) + { + Lines[line] = newAssignment; + } + else + { + Lines.Add(newAssignment); + } + } + + public void RemoveGlobalAssignment(string name) + { + var regex = new Regex(@"set_global_assignment\s*-name\s*" + name); + Lines = Lines.Where(x => !regex.IsMatch(x)).ToList(); + } + + public IEnumerable<(string,string)> GetLocationAssignments() + { + foreach (var line in lines) + { + var match = LocationAssignmentRegex().Match(line); + if (!match.Success) continue; + + var pin = match.Groups[1].Value; + var node = match.Groups[2].Value; + + yield return (pin, node); + } + } + + public void AddLocationAssignment(string pin, string node) + { + Lines.Add($"set_location_assignment PIN_{pin} -to {node}"); + } + + public void RemoveLocationAssignments() + { + var regex = RemoveLocationAssignmentRegex(); + Lines = Lines.Where(x => !regex.IsMatch(x)).ToList(); + } + + public void AddFile(IProjectFile file) + { + switch (file.Extension) + { + case ".vhd" or ".vhdl": + Lines.Add($"set_global_assignment -name VHDL_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".v": + Lines.Add($"set_global_assignment -name VERILOG_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".sv": + Lines.Add($"set_global_assignment -name SYSTEMVERILOG_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".qip": + Lines.Add($"set_global_assignment -name QIP_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".qsys": + Lines.Add($"set_global_assignment -name QSYS_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".bdf": + Lines.Add($"set_global_assignment -name BDF_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".ahdl": + Lines.Add($"set_global_assignment -name AHDL_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".smf": + Lines.Add($"set_global_assignment -name SMF_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".tcl": + Lines.Add($"set_global_assignment -name TCL_SCRIPT_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".hex": + Lines.Add($"set_global_assignment -name HEX_FILE {file.RelativePath.ToLinuxPath()}"); + break; + case ".mif": + Lines.Add($"set_global_assignment -name MIF_FILE {file.RelativePath.ToLinuxPath()}"); + break; + } + } +} \ No newline at end of file diff --git a/src/OneWare.Quartus/Helper/QsfHelper.cs b/src/OneWare.Quartus/Helper/QsfHelper.cs new file mode 100644 index 0000000..9c306be --- /dev/null +++ b/src/OneWare.Quartus/Helper/QsfHelper.cs @@ -0,0 +1,24 @@ +using System.Text.RegularExpressions; +using OneWare.UniversalFpgaProjectSystem.Models; + +namespace OneWare.Quartus.Helper; + +public static partial class QsfHelper +{ + public static string GetQsfPath(UniversalFpgaProjectRoot project) + { + return Path.Combine(project.RootFolderPath, Path.GetFileNameWithoutExtension(project.TopEntity?.FullPath ?? throw new Exception("TopEntity not set!")) + ".qsf"); + } + + public static QsfFile ReadQsf(string path) + { + var qsf = File.Exists(path) ? File.ReadAllText(path) : string.Empty; + + return new QsfFile(qsf.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); + } + + public static void WriteQsf(string path, QsfFile file) + { + File.WriteAllLines(path, file.Lines); + } +} \ No newline at end of file diff --git a/src/OneWare.Quartus/OneWare.Quartus.csproj b/src/OneWare.Quartus/OneWare.Quartus.csproj index 0b0e635..2e1c278 100644 --- a/src/OneWare.Quartus/OneWare.Quartus.csproj +++ b/src/OneWare.Quartus/OneWare.Quartus.csproj @@ -16,12 +16,8 @@ - - - - - - + + diff --git a/src/OneWare.Quartus/QuartusLoader.cs b/src/OneWare.Quartus/QuartusLoader.cs index 410c2b3..664f684 100644 --- a/src/OneWare.Quartus/QuartusLoader.cs +++ b/src/OneWare.Quartus/QuartusLoader.cs @@ -30,8 +30,12 @@ public async Task DownloadAsync(UniversalFpgaProjectRoot project) var fpga = project.Properties["Fpga"]; if (fpga == null) return; var fpgaModel = fpga.ToString(); + + var cableSetting = "Auto"; + var cableName = cableSetting == "Auto" + ? "1" + : "\"" + cableSetting + "\""; - var cableName = "Auto"; var sofFile = Path.GetFileName(FirstFileInPath(project.FullPath, ".sof") ?? ""); if (string.IsNullOrEmpty(sofFile)) @@ -41,6 +45,6 @@ public async Task DownloadAsync(UniversalFpgaProjectRoot project) } await childProcessService.ExecuteShellAsync("quartus_pgm", $"-c {cableName} -m JTAG -o P;{sofFile}", - project.FullPath, "Running OpenFPGALoader"); + project.FullPath, "Running Quartus programmer (Short-Term)..."); } } \ No newline at end of file diff --git a/src/OneWare.Quartus/QuartusToolchain.cs b/src/OneWare.Quartus/QuartusToolchain.cs index 5ba38b3..73ab4b3 100644 --- a/src/OneWare.Quartus/QuartusToolchain.cs +++ b/src/OneWare.Quartus/QuartusToolchain.cs @@ -1,49 +1,29 @@ -using System.Text.RegularExpressions; -using DynamicData; -using OneWare.Essentials.Extensions; -using OneWare.Essentials.Services; +using OneWare.Essentials.Services; +using OneWare.Quartus.Helper; using OneWare.Quartus.Services; using OneWare.UniversalFpgaProjectSystem.Models; using OneWare.UniversalFpgaProjectSystem.Services; namespace OneWare.Quartus; -public partial class QuartusToolchain(QuartusService quartusService, ILogger logger) : IFpgaToolchain +public class QuartusToolchain(QuartusService quartusService, ILogger logger) : IFpgaToolchain { public string Name => "Quartus"; - - - [GeneratedRegex(@"set_location_assignment\s*PIN_(\w+)\s+-to\s+(\w+)")] - private static partial Regex AssignmentRegex(); - - [GeneratedRegex(@"(set_global_assignment\s*-name\s*((VHDL|VERILOG|SYSTEMVERILOG|QIP|QSYS|BDF|AHDL|SMF|TCL_SCRIPT|HEX|MIF)_FILE|FAMILY|DEVICE|TOP_LEVEL_ENTITY)|set_location_assignment)")] - private static partial Regex RemoveLinesFromQsfRegex(); public void LoadConnections(UniversalFpgaProjectRoot project, FpgaModel fpga) { try { - var files = Directory.GetFiles(project.RootFolderPath); - var qsfPath = files.FirstOrDefault(x => Path.GetExtension(x) == ".qsf"); - if (qsfPath != null) + var qsfPath = QsfHelper.GetQsfPath(project); + var qsf = QsfHelper.ReadQsf(qsfPath); + + foreach (var (pin, node) in qsf.GetLocationAssignments()) { - var pcf = File.ReadAllText(qsfPath); - var lines = pcf.Split('\n'); - foreach (var line in lines) - { - var regex = AssignmentRegex(); - - var match = regex.Match(line); - if (!match.Success) continue; - - var pin = match.Groups[1].Value; - var node = match.Groups[2].Value; - if(!fpga.PinModels.TryGetValue(pin, out var pinModel)) return; - if(!fpga.NodeModels.TryGetValue(node, out var nodeModel)) return; + if(!fpga.PinModels.TryGetValue(pin, out var pinModel)) return; + if(!fpga.NodeModels.TryGetValue(node, out var nodeModel)) return; - fpga.Connect(pinModel, nodeModel); - } + fpga.Connect(pinModel, nodeModel); } } catch (Exception e) @@ -58,73 +38,34 @@ public void SaveConnections(UniversalFpgaProjectRoot project, FpgaModel fpga) { var topEntity = project.TopEntity?.Header ?? throw new Exception("No TopEntity set!"); topEntity = Path.GetFileNameWithoutExtension(topEntity); - - var qsfPath = Path.Combine(project.RootFolderPath, topEntity + ".qsf"); - - var qsf = File.Exists(qsfPath) ? File.ReadAllText(qsfPath) : string.Empty; - - var lines = qsf.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - .Where(x => !RemoveLinesFromQsfRegex().IsMatch(x)) - .ToList(); + + var qsfPath = QsfHelper.GetQsfPath(project); + var qsf = QsfHelper.ReadQsf(qsfPath); //Add Family - lines.Add($"set_global_assignment -name FAMILY \"{fpga.Fpga.Family}\""); + qsf.SetGlobalAssignment("FAMILY", fpga.Fpga.Family); //Add Device - lines.Add($"set_global_assignment -name DEVICE {fpga.Fpga.Model}"); + qsf.SetGlobalAssignment("DEVICE", fpga.Fpga.Model); //Add toplevel - lines.Add($"set_global_assignment -name TOP_LEVEL_ENTITY {topEntity}"); + qsf.SetGlobalAssignment("TOP_LEVEL_ENTITY", topEntity); //Add Files foreach (var file in project.Files) { - switch (file.Extension) - { - case ".vhd" or ".vhdl": - lines.Add($"set_global_assignment -name VHDL_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".v": - lines.Add($"set_global_assignment -name VERILOG_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".sv": - lines.Add($"set_global_assignment -name SYSTEMVERILOG_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".qip": - lines.Add($"set_global_assignment -name QIP_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".qsys": - lines.Add($"set_global_assignment -name QSYS_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".bdf": - lines.Add($"set_global_assignment -name BDF_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".ahdl": - lines.Add($"set_global_assignment -name AHDL_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".smf": - lines.Add($"set_global_assignment -name SMF_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".tcl": - lines.Add($"set_global_assignment -name TCL_SCRIPT_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".hex": - lines.Add($"set_global_assignment -name HEX_FILE {file.RelativePath.ToLinuxPath()}"); - break; - case ".mif": - lines.Add($"set_global_assignment -name MIF_FILE {file.RelativePath.ToLinuxPath()}"); - break; - } + qsf.AddFile(file); } - foreach (var (key, pinModel) in fpga.PinModels) + qsf.RemoveLocationAssignments(); + foreach (var (_, pinModel) in fpga.PinModels) { if(pinModel.Connection == null) continue; - - lines.Add($"set_location_assignment PIN_{pinModel.Pin.Name} -to {pinModel.Connection.Node.Name}"); + + qsf.AddLocationAssignment(pinModel.Pin.Name, pinModel.Connection.Node.Name); } - File.WriteAllLines(qsfPath, lines); + QsfHelper.WriteQsf(qsfPath, qsf); } catch (Exception e) { @@ -134,6 +75,6 @@ public void SaveConnections(UniversalFpgaProjectRoot project, FpgaModel fpga) public void StartCompile(UniversalFpgaProjectRoot project, FpgaModel fpga) { - _ = quartusService.SynthAsync(project, fpga); + _ = quartusService.CompileAsync(project, fpga); } } \ No newline at end of file diff --git a/src/OneWare.Quartus/Services/QuartusService.cs b/src/OneWare.Quartus/Services/QuartusService.cs index aff8676..045940f 100644 --- a/src/OneWare.Quartus/Services/QuartusService.cs +++ b/src/OneWare.Quartus/Services/QuartusService.cs @@ -1,21 +1,40 @@ -using OneWare.Essentials.Models; +using Avalonia.Media; +using Avalonia.Threading; +using OneWare.Essentials.Enums; +using OneWare.Essentials.Models; using OneWare.Essentials.Services; using OneWare.UniversalFpgaProjectSystem.Models; namespace OneWare.Quartus.Services; -public class QuartusService(IChildProcessService childProcessService, ILogger logger) +public class QuartusService(IChildProcessService childProcessService, ILogger logger, IOutputService outputService) { - public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpga) + public async Task CompileAsync(UniversalFpgaProjectRoot project, FpgaModel fpga) { if (project.TopEntity == null) { logger.Error("No TopEntity set"); return; } + + var start = DateTime.Now; + outputService.WriteLine("Compiling...\n==================", Brushes.CornflowerBlue); await childProcessService.ExecuteShellAsync("quartus_sh", $"--flow compile {Path.GetFileNameWithoutExtension(project.TopEntity.Header)}", - project.FullPath, "Starting Quartus Prime Shell..."); + project.FullPath, "Running Quartus Prime Shell...", AppState.Loading, true, (x) => + { + var output = x.TrimStart(); + Dispatcher.UIThread.Post(() => + { + if(output.StartsWith("Error (")) outputService.WriteLine(x, Brushes.Red); + else if(output.StartsWith("Warning (") || output.StartsWith("Critical Warning (")) outputService.WriteLine(x, Brushes.Orange); + else outputService.WriteLine(x); + }); + }); + + var compileTime = DateTime.Now - start; + + outputService.WriteLine($"==================\n\nCompilation finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); } } \ No newline at end of file diff --git a/src/OneWare.Quartus/ViewModels/QuartusCompileSettingsViewModel.cs b/src/OneWare.Quartus/ViewModels/QuartusCompileSettingsViewModel.cs new file mode 100644 index 0000000..c5d8a2a --- /dev/null +++ b/src/OneWare.Quartus/ViewModels/QuartusCompileSettingsViewModel.cs @@ -0,0 +1,88 @@ +using OneWare.Essentials.Controls; +using OneWare.Essentials.ViewModels; +using OneWare.ProjectSystem.Models; +using OneWare.Quartus.Helper; +using OneWare.Settings; +using OneWare.Settings.ViewModels; +using OneWare.Settings.ViewModels.SettingTypes; +using OneWare.UniversalFpgaProjectSystem.Models; + +namespace OneWare.Quartus.ViewModels; + +public class QuartusCompileSettingsViewModel : FlexibleWindowViewModelBase +{ + private string _qsfPath; + private QsfFile _qsfFile; + + public SettingsCollectionViewModel SettingsCollection { get; } = new("Quartus Settings") + { + ShowTitle = false + }; + + private ComboBoxSetting _processorCountSetting; + private ComboBoxSetting _configurationModeSetting; + private ComboBoxSetting _optimizationModeSetting; + + private Dictionary AvailableConfigurationModes { get; } = new() + { + { "DEFAULT", "Default" }, + { "DUAL IMAGES", "Dual Compressed Images (256Kbits UFM)" }, + { "SINGLE COMP IMAGE", "Single Compressed Image (1376Kbits UFM)" }, + { "SINGLE COMP IMAGE WITH ERAM", "Single Compressed Image with Memory Initialization (256Kbits UFM)" }, + { "SINGLE IMAGE", "Single Uncompressed Image (912Kbits UFM)" }, + { "SINGLE IMAGE WITH ERAM", "Single Uncompressed Image with Memory Initialization (256Kbits UFM)" } + }; + + private Dictionary AvailableOptimizationModes { get; } = new() + { + { "BALANCED", "Balanced" }, + { "HIGH PERFORMANCE EFFORT", "Performance (High effort - increases runtime)" }, + { "AGGRESSIVE PERFORMANCE", "Performance (Aggressive - increases runtime and area)" }, + { "HIGH POWER EFFORT", "Power (High effort - increases runtime)" }, + { "AGGRESSIVE POWER", "Power (Aggressive - increases runtime, reduces performance)" }, + { "AGGRESSIVE AREA", "Area (Aggressive - reduces performance)" } + }; + + + public QuartusCompileSettingsViewModel(UniversalFpgaProjectRoot fpgaProjectRoot) + { + Title = "Quartus Compile Settings"; + Id = "QuartusCompileSettings"; + + _qsfPath = QsfHelper.GetQsfPath(fpgaProjectRoot); + _qsfFile = QsfHelper.ReadQsf(_qsfPath); + + _processorCountSetting = new ComboBoxSetting("Parallel Compilation Processors", + "Max processors Quartus will use to compile", + Environment.ProcessorCount, Enumerable.Range(1, Environment.ProcessorCount).Cast()); + + _configurationModeSetting = new ComboBoxSetting("Configuration Mode", "Quartus Configuration Mode", + AvailableConfigurationModes["DEFAULT"], AvailableConfigurationModes.Values); + + _optimizationModeSetting = new ComboBoxSetting("Optimization Mode", "Quartus Optimization Mode", + AvailableOptimizationModes["BALANCED"], AvailableOptimizationModes.Values); + + SettingsCollection.SettingModels.Add(new ComboBoxSettingViewModel(_processorCountSetting)); + SettingsCollection.SettingModels.Add(new ComboBoxSettingViewModel(_configurationModeSetting)); + SettingsCollection.SettingModels.Add(new ComboBoxSettingViewModel(_optimizationModeSetting)); + + var processorCount = _qsfFile.GetGlobalAssignment("NUM_PARALLEL_PROCESSORS"); + if(!string.IsNullOrWhiteSpace(processorCount) && int.TryParse(processorCount, out var processorCountInt)) _processorCountSetting.Value = processorCountInt; + + var configurationMode = _qsfFile.GetGlobalAssignment("INTERNAL_FLASH_UPDATE_MODE"); + if(!string.IsNullOrWhiteSpace(configurationMode)) _configurationModeSetting.Value = configurationMode; + + var optimizationMode = _qsfFile.GetGlobalAssignment("OPTIMIZATION_MODE"); + if(!string.IsNullOrWhiteSpace(optimizationMode)) _optimizationModeSetting.Value = optimizationMode; + } + + public void Save(FlexibleWindow flexibleWindow) + { + _qsfFile.SetGlobalAssignment("NUM_PARALLEL_PROCESSORS", _processorCountSetting.Value.ToString()!); + _qsfFile.SetGlobalAssignment("INTERNAL_FLASH_UPDATE_MODE", _configurationModeSetting.Value.ToString()!); + _qsfFile.SetGlobalAssignment("OPTIMIZATION_MODE", _optimizationModeSetting.Value.ToString()!); + + QsfHelper.WriteQsf(_qsfPath, _qsfFile); + Close(flexibleWindow); + } +} \ No newline at end of file diff --git a/src/OneWare.Quartus/ViewModels/QuartusCompileWindowExtensionViewModel.cs b/src/OneWare.Quartus/ViewModels/QuartusCompileWindowExtensionViewModel.cs index 8e0adda..f544aa7 100644 --- a/src/OneWare.Quartus/ViewModels/QuartusCompileWindowExtensionViewModel.cs +++ b/src/OneWare.Quartus/ViewModels/QuartusCompileWindowExtensionViewModel.cs @@ -1,12 +1,18 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; using DynamicData.Binding; using OneWare.Essentials.Services; +using OneWare.Quartus.Views; using OneWare.UniversalFpgaProjectSystem.Models; +using Prism.Ioc; namespace OneWare.Quartus.ViewModels; public class QuartusCompileWindowExtensionViewModel : ObservableObject { + private readonly IWindowService _windowService; + private readonly IProjectExplorerService _projectExplorerService; + private bool _isVisible = false; public bool IsVisible @@ -15,8 +21,11 @@ public bool IsVisible set => SetProperty(ref _isVisible, value); } - public QuartusCompileWindowExtensionViewModel(IProjectExplorerService projectExplorerService) + public QuartusCompileWindowExtensionViewModel(IProjectExplorerService projectExplorerService, IWindowService windowService) { + _windowService = windowService; + _projectExplorerService = projectExplorerService; + IDisposable? disposable = null; projectExplorerService.WhenValueChanged(x => x.ActiveProject).Subscribe(x => { @@ -28,4 +37,23 @@ public QuartusCompileWindowExtensionViewModel(IProjectExplorerService projectExp }); }); } + + public async Task OpenCompileSettingsAsync() + { + await Dispatcher.UIThread.InvokeAsync(async () => + { + try + { + if (_projectExplorerService.ActiveProject is UniversalFpgaProjectRoot fpgaProjectRoot) + { + await _windowService.ShowDialogAsync(new QuartusCompileSettingsView() + { DataContext = new QuartusCompileSettingsViewModel(fpgaProjectRoot) }); + } + } + catch (Exception e) + { + ContainerLocator.Container.Resolve().Error(e.Message, e); + } + }); + } } \ No newline at end of file diff --git a/src/OneWare.Quartus/Views/QuartusCompileSettingsView.axaml b/src/OneWare.Quartus/Views/QuartusCompileSettingsView.axaml new file mode 100644 index 0000000..a65242d --- /dev/null +++ b/src/OneWare.Quartus/Views/QuartusCompileSettingsView.axaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/src/OneWare.Quartus/Views/QuartusCompileSettingsView.axaml.cs b/src/OneWare.Quartus/Views/QuartusCompileSettingsView.axaml.cs new file mode 100644 index 0000000..4fdeded --- /dev/null +++ b/src/OneWare.Quartus/Views/QuartusCompileSettingsView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using OneWare.Essentials.Controls; + +namespace OneWare.Quartus.Views; + +public partial class QuartusCompileSettingsView : FlexibleWindow +{ + public QuartusCompileSettingsView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/OneWare.Quartus/Views/QuartusCompileWindowExtensionView.axaml b/src/OneWare.Quartus/Views/QuartusCompileWindowExtensionView.axaml index 87b68fb..5fda7b3 100644 --- a/src/OneWare.Quartus/Views/QuartusCompileWindowExtensionView.axaml +++ b/src/OneWare.Quartus/Views/QuartusCompileWindowExtensionView.axaml @@ -7,7 +7,15 @@ x:Class="OneWare.Quartus.Views.QuartusCompileWindowExtensionView" x:DataType="viewModels:QuartusCompileWindowExtensionViewModel" IsVisible="{Binding IsVisible}"> - - - - + + + + + + \ No newline at end of file diff --git a/src/OneWare.Quartus/oneware.json b/src/OneWare.Quartus/oneware.json index 8d2f251..d6bc0b2 100644 --- a/src/OneWare.Quartus/oneware.json +++ b/src/OneWare.Quartus/oneware.json @@ -2,13 +2,13 @@ "Dependencies": [ { "Name": "OneWare.Essentials", - "MinVersion": "0.2.0.0", - "MaxVersion": "0.2.0.0" + "MinVersion": "0.3.0.0", + "MaxVersion": "0.3.0.0" }, { "Name": "OneWare.UniversalFpgaProjectSystem", - "MinVersion": "0.15.0.0", - "MaxVersion": "0.15.0.0" + "MinVersion": "0.15.1.0", + "MaxVersion": "0.15.1.0" } ] } \ No newline at end of file