From 5a22db29ffaa8da14ff59eb4bfa21ae9e804d322 Mon Sep 17 00:00:00 2001 From: Sebastian Wittlich Date: Fri, 22 Aug 2025 12:49:51 +0200 Subject: [PATCH 1/7] Version to 0.8-SNAPSHOT --- src/OneWare.CologneChip/OneWare.CologneChip.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OneWare.CologneChip/OneWare.CologneChip.csproj b/src/OneWare.CologneChip/OneWare.CologneChip.csproj index 4b45ee1..7176b6b 100644 --- a/src/OneWare.CologneChip/OneWare.CologneChip.csproj +++ b/src/OneWare.CologneChip/OneWare.CologneChip.csproj @@ -1,7 +1,7 @@  - 0.7 + 0.8-SNAPSHOT net9.0 enable enable From d757379af34c3a0c0d34abd1969354a85c17935e Mon Sep 17 00:00:00 2001 From: Sebastian Wittlich Date: Wed, 27 Aug 2025 12:48:30 +0200 Subject: [PATCH 2/7] WIP: Possibility to use different toolchains --- .../OneWareCologneChipModule.cs | 8 +- .../Services/CcNextpnrCompileStrategy.cs | 16 ++ .../Services/CcProprietaryCompileStrategy.cs | 144 ++++++++++++++++++ .../Services/CologneChipConstantService.cs | 6 + .../Services/CologneChipService.cs | 136 +++-------------- .../Services/ICologneChipCompileStrategy.cs | 10 ++ 6 files changed, 205 insertions(+), 115 deletions(-) create mode 100644 src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs create mode 100644 src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs create mode 100644 src/OneWare.CologneChip/Services/ICologneChipCompileStrategy.cs diff --git a/src/OneWare.CologneChip/OneWareCologneChipModule.cs b/src/OneWare.CologneChip/OneWareCologneChipModule.cs index 9d9e641..7d228ba 100644 --- a/src/OneWare.CologneChip/OneWareCologneChipModule.cs +++ b/src/OneWare.CologneChip/OneWareCologneChipModule.cs @@ -22,7 +22,8 @@ public class OneWareCologneChipModule : IModule { public void RegisterTypes(IContainerRegistry containerRegistry) { - + containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); } public void OnInitialized(IContainerProvider containerProvider) @@ -84,6 +85,9 @@ public void OnInitialized(IContainerProvider containerProvider) settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.CcPathSetting, new FolderPathSetting("CologneChip Toolchain Path", defaultCologneChipPath, null, null, IsCologneChipPathValid)); + settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.ToolChainSettingsKey, + new ComboBoxSetting("Place & Route", CologneChipConstantService.ToolChainDefault, CologneChipConstantService.Toolchains)); + settingsService.GetSettingObservable(CologneChipConstantService.CcPathSetting).Subscribe(x => { if (string.IsNullOrEmpty(x)) return; @@ -141,7 +145,7 @@ public void OnInitialized(IContainerProvider containerProvider) Command = new AsyncRelayCommand(async () => { // await projectExplorerService.SaveOpenFilesForProjectAsync(root); - await cologneChipService.PRAysnc(root, new FpgaModel(fpga!)); + await cologneChipService.PrAysnc(root, new FpgaModel(fpga!)); }, () => fpga != null) }, } diff --git a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs new file mode 100644 index 0000000..26ba851 --- /dev/null +++ b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs @@ -0,0 +1,16 @@ +using OneWare.UniversalFpgaProjectSystem.Models; + +namespace OneWare.CologneChip.Services; + +public class CcNextpnrCompileStrategy : ICologneChipCompileStrategy +{ + public Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + throw new NotImplementedException(); + } + + public Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs new file mode 100644 index 0000000..69bb659 --- /dev/null +++ b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs @@ -0,0 +1,144 @@ +using Avalonia.Media; +using Avalonia.Threading; +using OneWare.CologneChip.Helpers; +using OneWare.Essentials.Enums; +using OneWare.Essentials.Services; +using OneWare.UniversalFpgaProjectSystem.Models; +using OneWare.UniversalFpgaProjectSystem.Parser; +using Prism.Ioc; + +namespace OneWare.CologneChip.Services; + +public class CcProprietaryCompileStrategy : ICologneChipCompileStrategy +{ + private readonly IDockService _dockService; + private readonly IChildProcessService _childProcessService; + private readonly ILogger _logger; + private readonly IOutputService _outputService; + + public CcProprietaryCompileStrategy() + { + _dockService = ContainerLocator.Container.Resolve(); + _childProcessService = ContainerLocator.Container.Resolve(); + _logger = ContainerLocator.Container.Resolve(); + _outputService = ContainerLocator.Container.Resolve(); + } + + public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + try + { + var properties = FpgaSettingsParser.LoadSettings(project, fpgaModel.Fpga.Name); + var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); + + var buildDir = Path.Combine(project.FullPath, "build"); + Directory.CreateDirectory(buildDir); + + _dockService.Show(); + + var start = DateTime.Now; + + var yosysSynthTool = properties.GetValueOrDefault("yosysToolchainYosysSynthTool") ?? + throw new Exception("Yosys Tool not set!"); + + var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); + + List yosysArguments = []; + List includedExtensions = []; + + switch (topLanguage) + { + case "vhd": + _outputService.WriteLine("VHDL Synthesis...\n==============="); + yosysArguments = ["-q","-l ./../synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{top} -e {topName}; {yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; + includedExtensions = []; + break; + case "v": + _outputService.WriteLine("Verilog Synthesis...\n=============="); + yosysArguments = ["-ql", "./../log/synth.log", "-p", $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; + includedExtensions = [".v", ".sv"]; + break; + } + + var includedFiles = project.Files + .Where(x => includedExtensions.Contains(x.Extension)) + .Where(x => !project.CompileExcluded.Contains(x)) + .Where(x => !project.TestBenches.Contains(x)) + .Select(x => $"./../{x.RelativePath}"); + + yosysArguments.AddRange(properties.GetValueOrDefault("yosysToolchainYosysFlags")?.Split(' ', + StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) ?? []); + yosysArguments.AddRange(includedFiles); + + var (success, _) = await _childProcessService.ExecuteShellAsync("yosys", yosysArguments, $"{project.FullPath}/build", + "Running yosys...", AppState.Loading, true, x => + { + if (x.StartsWith("Error:")) + { + _logger.Error(x); + return false; + } + + _outputService.WriteLine(x); + return true; + }); + + + if (!success) { + var ignoreSynthExitCode = ContainerLocator.Container.Resolve().GetSettingValue(CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode); + if (ignoreSynthExitCode) + { + ContainerLocator.Container.Resolve().Warning("The synthesis was terminated with an exit code other than zero"); + ContainerLocator.Container.Resolve().Warning($"Setting '{CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode}' to true"); + ContainerLocator.Container.Resolve().Warning($"Because of this setting, the route and placing tool is started anyway"); + success = true; + } + } + + + var compileTime = DateTime.Now - start; + if (success) + _outputService.WriteLine( + $"==================\n\nSynthesis finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); + else + _outputService.WriteLine( + $"==================\n\nSynthesis failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", + Brushes.Red); + + return success; + } + catch (Exception e) + { + _logger.Error(e.Message, e); + return false; + } + } + + public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + var start = DateTime.Now; + var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); + var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); + var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); + + List prArguments = ["-i", $"{topName}_synth.v", "-o", topName, $"-ccf ./../{ccfFile} -cCP"]; + + var success = (await _childProcessService.ExecuteShellAsync("p_r", prArguments, + $"{project.FullPath}/build", $"Running P_R...", AppState.Loading, true, null, s => + { + Dispatcher.UIThread.Post(() => { _outputService.WriteLine(s); }); + return true; + })).success; + + var compileTime = DateTime.Now - start; + if (success) + _outputService.WriteLine( + $"==================\n\nPlace and Route finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); + else + _outputService.WriteLine( + $"==================\n\nPlace and Route failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", + Brushes.Red); + + return success; + } +} \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs index c4e23ea..1ceec86 100644 --- a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs +++ b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs @@ -21,6 +21,12 @@ private CologneChipConstantService() {} public static string CologneChipDefaultConstraintFile => "project.ccf"; + public const string ToolChainSettingsKey = "cologneChipToolchain"; + + public static readonly string[] Toolchains = ["p_r", "nextpnr"]; + + public const string ToolChainDefault = "nextpnr"; + public const string CcfTemplate = @"# # ##### Important ##### # Currently, the pin constraints must be added manually in this file diff --git a/src/OneWare.CologneChip/Services/CologneChipService.cs b/src/OneWare.CologneChip/Services/CologneChipService.cs index e03ae4f..120e18d 100644 --- a/src/OneWare.CologneChip/Services/CologneChipService.cs +++ b/src/OneWare.CologneChip/Services/CologneChipService.cs @@ -14,124 +14,33 @@ public class CologneChipService( IChildProcessService childProcessService, ILogger logger, IOutputService outputService, - IDockService dockService) + IDockService dockService, + ISettingsService settingsService) { public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) { - try + var toolchain = settingsService.GetSettingValue(CologneChipConstantService.ToolChainSettingsKey); + return toolchain switch { - var properties = FpgaSettingsParser.LoadSettings(project, fpgaModel.Fpga.Name); - var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); - - var buildDir = Path.Combine(project.FullPath, "build"); - Directory.CreateDirectory(buildDir); - - dockService.Show(); - - var start = DateTime.Now; - - var yosysSynthTool = properties.GetValueOrDefault("yosysToolchainYosysSynthTool") ?? - throw new Exception("Yosys Tool not set!"); - - var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); - - List yosysArguments = []; - List includedExtensions = []; - - switch (topLanguage) - { - case "vhd": - outputService.WriteLine("VHDL Synthesis...\n==============="); - yosysArguments = ["-q","-l ./../synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{top} -e {topName}; {yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; - includedExtensions = []; - break; - case "v": - outputService.WriteLine("Verilog Synthesis...\n=============="); - yosysArguments = ["-ql", "./../log/synth.log", "-p", $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; - includedExtensions = [".v", ".sv"]; - break; - } - - var includedFiles = project.Files - .Where(x => includedExtensions.Contains(x.Extension)) - .Where(x => !project.CompileExcluded.Contains(x)) - .Where(x => !project.TestBenches.Contains(x)) - .Select(x => $"./../{x.RelativePath}"); - - yosysArguments.AddRange(properties.GetValueOrDefault("yosysToolchainYosysFlags")?.Split(' ', - StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) ?? []); - yosysArguments.AddRange(includedFiles); - - var (success, _) = await childProcessService.ExecuteShellAsync("yosys", yosysArguments, $"{project.FullPath}/build", - "Running yosys...", AppState.Loading, true, x => - { - if (x.StartsWith("Error:")) - { - logger.Error(x); - return false; - } - - outputService.WriteLine(x); - return true; - }); - - - if (!success) { - var ignoreSynthExitCode = ContainerLocator.Container.Resolve().GetSettingValue(CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode); - if (ignoreSynthExitCode) - { - ContainerLocator.Container.Resolve().Warning("The synthesis was terminated with an exit code other than zero"); - ContainerLocator.Container.Resolve().Warning($"Setting '{CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode}' to true"); - ContainerLocator.Container.Resolve().Warning($"Because of this setting, the route and placing tool is started anyway"); - success = true; - } - } - - - var compileTime = DateTime.Now - start; - if (success) - outputService.WriteLine( - $"==================\n\nSynthesis finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); - else - outputService.WriteLine( - $"==================\n\nSynthesis failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", - Brushes.Red); - - return success; - } - catch (Exception e) - { - logger.Error(e.Message, e); - return false; - } + "p_r" => await ContainerLocator.Container.Resolve() + .SynthAsync(project, fpgaModel), + "nextpnr" => await ContainerLocator.Container.Resolve() + .SynthAsync(project, fpgaModel), + _ => throw new Exception($"Unknown toolchain: {toolchain}") + }; } - public async Task PRAysnc(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + public async Task PrAysnc(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) { - var start = DateTime.Now; - var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); - var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); - var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); - - List prArguments = ["-i", $"{topName}_synth.v", "-o", topName, $"-ccf ./../{ccfFile} -cCP"]; - - var success = (await childProcessService.ExecuteShellAsync("p_r", prArguments, - $"{project.FullPath}/build", $"Running P_R...", AppState.Loading, true, null, s => - { - Dispatcher.UIThread.Post(() => { outputService.WriteLine(s); }); - return true; - })).success; - - var compileTime = DateTime.Now - start; - if (success) - outputService.WriteLine( - $"==================\n\nPlace and Route finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); - else - outputService.WriteLine( - $"==================\n\nPlace and Route failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", - Brushes.Red); - - return success; + var toolchain = settingsService.GetSettingValue(CologneChipConstantService.ToolChainSettingsKey); + return toolchain switch + { + "p_r" => await ContainerLocator.Container.Resolve() + .PrAsync(project, fpgaModel), + "nextpnr" => await ContainerLocator.Container.Resolve() + .PrAsync(project, fpgaModel), + _ => throw new Exception($"Unknown toolchain: {toolchain}") + }; } public async Task CreateNetListJsonAsync(IProjectFile verilog) @@ -219,10 +128,11 @@ public void SaveConnections(UniversalFpgaProjectRoot project, FpgaModel fpga) public async Task CompileAsync(UniversalFpgaProjectRoot project, FpgaModel fpga) { var start = DateTime.Now; - outputService.WriteLine("Starting CC Toolchain...\n==============="); + outputService.WriteLine($"Starting CC Toolchain... ({ + settingsService.GetSettingValue(CologneChipConstantService.ToolChainSettingsKey)})\n==============="); var success = await SynthAsync(project, fpga); - success &= await PRAysnc(project, fpga); + success &= await PrAysnc(project, fpga); var endTime = DateTime.Now - start; if (success) diff --git a/src/OneWare.CologneChip/Services/ICologneChipCompileStrategy.cs b/src/OneWare.CologneChip/Services/ICologneChipCompileStrategy.cs new file mode 100644 index 0000000..271bbe4 --- /dev/null +++ b/src/OneWare.CologneChip/Services/ICologneChipCompileStrategy.cs @@ -0,0 +1,10 @@ +using OneWare.UniversalFpgaProjectSystem.Models; + +namespace OneWare.CologneChip.Services; + +public interface ICologneChipCompileStrategy +{ + public Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel); + + public Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel); +} \ No newline at end of file From ff38ec90f8ca320fa0de7cad521360adcdeb25ae Mon Sep 17 00:00:00 2001 From: Sebastian Wittlich Date: Wed, 27 Aug 2025 17:42:08 +0200 Subject: [PATCH 3/7] WIP: nextpnr support complete. Poject settings override tbd --- src/OneWare.CologneChip/CologneChipLoader.cs | 34 +++- .../OneWareCologneChipModule.cs | 31 ++- .../Services/CcNextpnrCompileStrategy.cs | 179 +++++++++++++++++- .../Services/CcProprietaryCompileStrategy.cs | 26 ++- .../Services/CologneChipConstantService.cs | 7 +- .../Services/CologneChipService.cs | 13 ++ .../Services/ICologneChipCompileStrategy.cs | 2 + 7 files changed, 276 insertions(+), 16 deletions(-) diff --git a/src/OneWare.CologneChip/CologneChipLoader.cs b/src/OneWare.CologneChip/CologneChipLoader.cs index dbbbdff..78f72c7 100644 --- a/src/OneWare.CologneChip/CologneChipLoader.cs +++ b/src/OneWare.CologneChip/CologneChipLoader.cs @@ -111,8 +111,19 @@ public async Task DownloadAsync(UniversalFpgaProjectRoot project) var state = GetProgrammerState(properties); + var toolchain = settingsService.GetSettingValue(CologneChipConstantService.ToolChainSettingsKey); + var bitStreamPath = ""; + switch (toolchain) + { + case "p_r": + bitStreamPath = $"{CologneChipConstantService.Instance.GetBuildPath(project.RelativePath)}{topName}_00.cfg.bit"; + break; + case "nextpnr": + bitStreamPath = $"{CologneChipConstantService.Instance.GetBuildPath(project.RelativePath)}{topName}.bit"; + break; + } + List fpgaArgs = []; - var bitStreamPath = $"{CologneChipConstantService.Instance.GetBuildPath(project.RelativePath)}{topName}_00.cfg.bit"; switch (state) { @@ -151,9 +162,24 @@ public async Task DownloadAsync(UniversalFpgaProjectRoot project) throw new Exception("IllegalState"); } - if (!useWsl) { - - await childProcessService.ExecuteShellAsync("openFPGALoader", fpgaArgs, + if (!useWsl) + { + // C:\Users\sebas\OneWareStudio\Packages\NativeTools\colognechip\cc-toolchain-win + var execPath = ""; + + switch (settingsService.GetSettingValue(CologneChipConstantService.OpenFPGALoaderSourceSettingsKey)) + { + case "CologneChip": + var path = settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); + execPath = $"{path}/bin/openFPGALoader/openFPGALoader"; + break; + default: + execPath = "openFPGALoader"; + break; + } + + + await childProcessService.ExecuteShellAsync(execPath, fpgaArgs, outputDir, "Running OpenFPGALoader (Short-Term)...", AppState.Loading, true); } else diff --git a/src/OneWare.CologneChip/OneWareCologneChipModule.cs b/src/OneWare.CologneChip/OneWareCologneChipModule.cs index 7d228ba..c59bb2e 100644 --- a/src/OneWare.CologneChip/OneWareCologneChipModule.cs +++ b/src/OneWare.CologneChip/OneWareCologneChipModule.cs @@ -8,6 +8,7 @@ using OneWare.CologneChip.Services; using OneWare.CologneChip.ViewModels; using OneWare.CologneChip.Views; +using OneWare.Essentials.Helpers; using OneWare.Essentials.Models; using OneWare.Essentials.Services; using OneWare.Essentials.ViewModels; @@ -88,6 +89,12 @@ public void OnInitialized(IContainerProvider containerProvider) settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.ToolChainSettingsKey, new ComboBoxSetting("Place & Route", CologneChipConstantService.ToolChainDefault, CologneChipConstantService.Toolchains)); + settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.OpenFPGALoaderSourceSettingsKey, + new ComboBoxSetting("openFPGALoader Source", CologneChipConstantService.OpenFPGALoaderSourceDefault, CologneChipConstantService.BinarySources)); + + settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.YosysSourceSettingsKey, + new ComboBoxSetting("Yosys Source", CologneChipConstantService.OpenFPGALoaderSourceDefault, CologneChipConstantService.BinarySources)); + settingsService.GetSettingObservable(CologneChipConstantService.CcPathSetting).Subscribe(x => { if (string.IsNullOrEmpty(x)) return; @@ -102,10 +109,19 @@ public void OnInitialized(IContainerProvider containerProvider) var pr = Path.Combine(x, "bin/p_r"); var openFpgaLoader = Path.Combine(x, "bin/openFPGALoader"); - ContainerLocator.Container.Resolve().SetPath("CC_yosys", yosys); + // ContainerLocator.Container.Resolve().SetPath("CC_yosys", yosys); ContainerLocator.Container.Resolve().SetPath("CC_p_r", pr); - ContainerLocator.Container.Resolve().SetPath("CC_openFPGALoader", openFpgaLoader); + // ContainerLocator.Container.Resolve().SetPath("CC_openFPGALoader", openFpgaLoader); }); + + var projectSettingsService = containerProvider.Resolve(); + projectSettingsService.AddProjectSetting(new ProjectSettingBuilder() + .WithSetting(new ComboBoxSetting("Place & Route", CologneChipConstantService.ToolChainDefault, + CologneChipConstantService.Toolchains)) + .WithCategory("CologneChip") + .WithKey("CCP_Toolchain") + .Build()); + containerProvider.Resolve().RegisterSetting("Tools", "CologneChip", CologneChipConstantService.CologneChipSettingsIgnoreGuiKey, new CheckBoxSetting("Ignore UI for HardwarePin Mapping", false)); @@ -148,6 +164,15 @@ public void OnInitialized(IContainerProvider containerProvider) await cologneChipService.PrAysnc(root, new FpgaModel(fpga!)); }, () => fpga != null) }, + new MenuItem() + { + Header = "Run Packing", + Command = new AsyncRelayCommand(async () => + { + // await projectExplorerService.SaveOpenFilesForProjectAsync(root); + await cologneChipService.PackAysnc(root, new FpgaModel(fpga!)); + }, () => fpga != null) + }, } }; })); @@ -158,7 +183,7 @@ private static bool IsCologneChipPathValid(string path) { if (!Directory.Exists(path)) return false; - if (!File.Exists(Path.Combine(path, "bin", "VERSION"))) return false; + if (!File.Exists(Path.Combine(path, "VERSION"))) return false; if (!Directory.Exists(Path.Combine(path, "bin", "yosys"))) return false; if (!Directory.Exists(Path.Combine(path, "bin", "p_r"))) return false; if (!Directory.Exists(Path.Combine(path, "bin", "openFPGALoader"))) return false; diff --git a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs index 26ba851..4ac7692 100644 --- a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs @@ -1,16 +1,187 @@ +using Avalonia.Media; +using Avalonia.Threading; +using OneWare.CologneChip.Helpers; +using OneWare.Essentials.Enums; +using OneWare.Essentials.Services; using OneWare.UniversalFpgaProjectSystem.Models; +using OneWare.UniversalFpgaProjectSystem.Parser; +using Prism.Ioc; namespace OneWare.CologneChip.Services; public class CcNextpnrCompileStrategy : ICologneChipCompileStrategy { - public Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + private readonly IDockService _dockService; + private readonly IChildProcessService _childProcessService; + private readonly ILogger _logger; + private readonly IOutputService _outputService; + private readonly ISettingsService _settingsService; + + public CcNextpnrCompileStrategy() { - throw new NotImplementedException(); + _dockService = ContainerLocator.Container.Resolve(); + _childProcessService = ContainerLocator.Container.Resolve(); + _logger = ContainerLocator.Container.Resolve(); + _outputService = ContainerLocator.Container.Resolve(); + _settingsService = ContainerLocator.Container.Resolve(); } + + public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + try + { + var properties = FpgaSettingsParser.LoadSettings(project, fpgaModel.Fpga.Name); + var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); + + var buildDir = Path.Combine(project.FullPath, "build"); + Directory.CreateDirectory(buildDir); + + _dockService.Show(); + + var start = DateTime.Now; + + var yosysSynthTool = properties.GetValueOrDefault("yosysToolchainYosysSynthTool") ?? + throw new Exception("Yosys Tool not set!"); + + var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); + + List yosysArguments = []; + List includedExtensions = []; + + switch (topLanguage) + { + // TODO: synth for vhdl + case "vhd": + _outputService.WriteLine("VHDL Synthesis...\n==============="); + yosysArguments = ["-q","-l","./synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{top} -e {topName}; {yosysSynthTool} -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;"]; + includedExtensions = []; + break; + case "v": + _outputService.WriteLine("Verilog Synthesis...\n=============="); + yosysArguments = ["-p", $"synth_gatemate -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;"]; + includedExtensions = [".v", ".sv"]; + break; + } + + var includedFiles = project.Files + .Where(x => includedExtensions.Contains(x.Extension)) + .Where(x => !project.CompileExcluded.Contains(x)) + .Where(x => !project.TestBenches.Contains(x)) + .Select(x => $"./../{x.RelativePath}"); + + yosysArguments.AddRange(properties.GetValueOrDefault("yosysToolchainYosysFlags")?.Split(' ', + StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) ?? []); + yosysArguments.AddRange(includedFiles); + + var execPath = ""; + switch (_settingsService.GetSettingValue(CologneChipConstantService.OpenFPGALoaderSourceSettingsKey)) + { + case "CologneChip": + var path = _settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); + execPath = $"{path}/bin/yosys/yosys"; + break; + default: + execPath = "yosys"; + break; + } + + var (success, _) = await _childProcessService.ExecuteShellAsync(execPath, yosysArguments, $"{project.FullPath}/build", + "Running yosys...", AppState.Loading, true, x => + { + if (x.StartsWith("Error:")) + { + _logger.Error(x); + return false; + } + + _outputService.WriteLine(x); + return true; + }); + + + if (!success) { + var ignoreSynthExitCode = ContainerLocator.Container.Resolve().GetSettingValue(CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode); + if (ignoreSynthExitCode) + { + ContainerLocator.Container.Resolve().Warning("The synthesis was terminated with an exit code other than zero"); + ContainerLocator.Container.Resolve().Warning($"Setting '{CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode}' to true"); + ContainerLocator.Container.Resolve().Warning($"Because of this setting, the route and placing tool is started anyway"); + success = true; + } + } + + + var compileTime = DateTime.Now - start; + if (success) + _outputService.WriteLine( + $"==================\n\nSynthesis finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); + else + _outputService.WriteLine( + $"==================\n\nSynthesis failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", + Brushes.Red); - public Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + return success; + } + catch (Exception e) + { + _logger.Error(e.Message, e); + return false; + } + } + + public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + var start = DateTime.Now; + var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); + var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); + var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); + + List prArguments = ["--device=CCGM1A1","--json", $"{topName}.json", "-o", $"ccf=./../{ccfFile}", "-o", $"out={topName}_impl.txt", "--router=router2"]; + + var success = (await _childProcessService.ExecuteShellAsync("nextpnr-himbaechel", prArguments, + $"{project.FullPath}/build", $"Running P_R...", AppState.Loading, true, null, s => + { + Dispatcher.UIThread.Post(() => { _outputService.WriteLine(s); }); + return true; + })).success; + + var compileTime = DateTime.Now - start; + if (success) + _outputService.WriteLine( + $"==================\n\nPlace and Route finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); + else + _outputService.WriteLine( + $"==================\n\nPlace and Route failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", + Brushes.Red); + + return success; + } + + public async Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) { - throw new NotImplementedException(); + var start = DateTime.Now; + var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); + var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); + var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); + + List prArguments = [$"{topName}_impl.txt", $"{topName}.bit"]; + + var success = (await _childProcessService.ExecuteShellAsync("gmpack", prArguments, + $"{project.FullPath}/build", $"Running P_R...", AppState.Loading, true, null, s => + { + Dispatcher.UIThread.Post(() => { _outputService.WriteLine(s); }); + return true; + })).success; + + var compileTime = DateTime.Now - start; + if (success) + _outputService.WriteLine( + $"==================\n\nPlace and Route finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); + else + _outputService.WriteLine( + $"==================\n\nPlace and Route failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", + Brushes.Red); + + return success; } } \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs index 69bb659..f2f4a1b 100644 --- a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs @@ -15,6 +15,7 @@ public class CcProprietaryCompileStrategy : ICologneChipCompileStrategy private readonly IChildProcessService _childProcessService; private readonly ILogger _logger; private readonly IOutputService _outputService; + private readonly ISettingsService _settingsService; public CcProprietaryCompileStrategy() { @@ -22,6 +23,7 @@ public CcProprietaryCompileStrategy() _childProcessService = ContainerLocator.Container.Resolve(); _logger = ContainerLocator.Container.Resolve(); _outputService = ContainerLocator.Container.Resolve(); + _settingsService = ContainerLocator.Container.Resolve(); } public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) @@ -50,12 +52,12 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f { case "vhd": _outputService.WriteLine("VHDL Synthesis...\n==============="); - yosysArguments = ["-q","-l ./../synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{top} -e {topName}; {yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; + yosysArguments = ["-q","-l ./synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{top} -e {topName}; {yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; includedExtensions = []; break; case "v": _outputService.WriteLine("Verilog Synthesis...\n=============="); - yosysArguments = ["-ql", "./../log/synth.log", "-p", $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; + yosysArguments = ["-ql", "./synth.log", "-p", $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; includedExtensions = [".v", ".sv"]; break; } @@ -70,7 +72,20 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) ?? []); yosysArguments.AddRange(includedFiles); - var (success, _) = await _childProcessService.ExecuteShellAsync("yosys", yosysArguments, $"{project.FullPath}/build", + var execPath = ""; + + switch (_settingsService.GetSettingValue(CologneChipConstantService.OpenFPGALoaderSourceSettingsKey)) + { + case "CologneChip": + var path = _settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); + execPath = $"{path}/bin/yosys/yosys"; + break; + default: + execPath = "yosys"; + break; + } + + var (success, _) = await _childProcessService.ExecuteShellAsync(execPath, yosysArguments, $"{project.FullPath}/build", "Running yosys...", AppState.Loading, true, x => { if (x.StartsWith("Error:")) @@ -141,4 +156,9 @@ public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpga return success; } + + public Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + return Task.FromResult(true); + } } \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs index 1ceec86..2a67348 100644 --- a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs +++ b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs @@ -22,10 +22,13 @@ private CologneChipConstantService() {} public static string CologneChipDefaultConstraintFile => "project.ccf"; public const string ToolChainSettingsKey = "cologneChipToolchain"; - public static readonly string[] Toolchains = ["p_r", "nextpnr"]; - public const string ToolChainDefault = "nextpnr"; + + public const string OpenFPGALoaderSourceSettingsKey = "cologneChipOpenFPGALoaderSource"; + public const string YosysSourceSettingsKey = "cologneChipYosysSource"; + public static readonly string[] BinarySources = ["CologneChip", "OSSCAD Suite"]; + public const string OpenFPGALoaderSourceDefault = "CologneChip"; public const string CcfTemplate = @"# # ##### Important ##### diff --git a/src/OneWare.CologneChip/Services/CologneChipService.cs b/src/OneWare.CologneChip/Services/CologneChipService.cs index 120e18d..5933901 100644 --- a/src/OneWare.CologneChip/Services/CologneChipService.cs +++ b/src/OneWare.CologneChip/Services/CologneChipService.cs @@ -43,6 +43,18 @@ public async Task PrAysnc(UniversalFpgaProjectRoot project, FpgaModel fpga }; } + public async Task PackAysnc(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + var toolchain = settingsService.GetSettingValue(CologneChipConstantService.ToolChainSettingsKey); + return toolchain switch + { + "p_r" => await ContainerLocator.Container.Resolve().PackAsync(project, fpgaModel), + "nextpnr" => await ContainerLocator.Container.Resolve() + .PackAsync(project, fpgaModel), + _ => throw new Exception($"Unknown toolchain: {toolchain}") + }; + } + public async Task CreateNetListJsonAsync(IProjectFile verilog) { await childProcessService.ExecuteShellAsync("yosys", [ @@ -133,6 +145,7 @@ public async Task CompileAsync(UniversalFpgaProjectRoot project, FpgaModel var success = await SynthAsync(project, fpga); success &= await PrAysnc(project, fpga); + success &= await PackAysnc(project, fpga); var endTime = DateTime.Now - start; if (success) diff --git a/src/OneWare.CologneChip/Services/ICologneChipCompileStrategy.cs b/src/OneWare.CologneChip/Services/ICologneChipCompileStrategy.cs index 271bbe4..d3c8b4b 100644 --- a/src/OneWare.CologneChip/Services/ICologneChipCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/ICologneChipCompileStrategy.cs @@ -7,4 +7,6 @@ public interface ICologneChipCompileStrategy public Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel); public Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel); + + public Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel); } \ No newline at end of file From f8ad865c0975575fc265b7d70b8291767cf0216b Mon Sep 17 00:00:00 2001 From: Sebastian Wittlich Date: Thu, 28 Aug 2025 17:11:23 +0200 Subject: [PATCH 4/7] WIP: Duplicate cleaning and implementation of issue #6 --- .../OneWare.CologneChip.csproj | 1 + .../OneWareCologneChipModule.cs | 22 ++- .../Services/CcCompileStrategyBase.cs | 183 ++++++++++++++++++ .../Services/CcNextpnrCompileStrategy.cs | 2 +- .../Services/CcProprietaryCompileStrategy.cs | 9 +- .../Services/CcSettingsService.cs | 23 +++ .../Services/CologneChipConstantService.cs | 3 + 7 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs create mode 100644 src/OneWare.CologneChip/Services/CcSettingsService.cs diff --git a/src/OneWare.CologneChip/OneWare.CologneChip.csproj b/src/OneWare.CologneChip/OneWare.CologneChip.csproj index 7176b6b..0eabb3c 100644 --- a/src/OneWare.CologneChip/OneWare.CologneChip.csproj +++ b/src/OneWare.CologneChip/OneWare.CologneChip.csproj @@ -17,6 +17,7 @@ + diff --git a/src/OneWare.CologneChip/OneWareCologneChipModule.cs b/src/OneWare.CologneChip/OneWareCologneChipModule.cs index c59bb2e..b3064e3 100644 --- a/src/OneWare.CologneChip/OneWareCologneChipModule.cs +++ b/src/OneWare.CologneChip/OneWareCologneChipModule.cs @@ -25,6 +25,7 @@ public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); } public void OnInitialized(IContainerProvider containerProvider) @@ -116,12 +117,27 @@ public void OnInitialized(IContainerProvider containerProvider) var projectSettingsService = containerProvider.Resolve(); projectSettingsService.AddProjectSetting(new ProjectSettingBuilder() - .WithSetting(new ComboBoxSetting("Place & Route", CologneChipConstantService.ToolChainDefault, - CologneChipConstantService.Toolchains)) + .WithSetting(new ComboBoxSetting("Place & Route", CologneChipConstantService.ProjectOverrideValue, + CologneChipConstantService.BinarySourcesProject)) .WithCategory("CologneChip") - .WithKey("CCP_Toolchain") + .WithKey(CologneChipConstantService.ToolChainSettingsKey) .Build()); + projectSettingsService.AddProjectSetting(new ProjectSettingBuilder() + .WithSetting(new ComboBoxSetting("openFPGALoader Source", + CologneChipConstantService.ProjectOverrideValue, + CologneChipConstantService.BinarySourcesProject)) + .WithCategory("CologneChip") + .WithKey(CologneChipConstantService.OpenFPGALoaderSourceSettingsKey) + .Build()); + + projectSettingsService.AddProjectSetting(new ProjectSettingBuilder() + .WithSetting(new ComboBoxSetting("Yosys Source", + CologneChipConstantService.ProjectOverrideValue, + CologneChipConstantService.BinarySourcesProject)) + .WithCategory("CologneChip") + .WithKey(CologneChipConstantService.YosysSourceSettingsKey) + .Build()); containerProvider.Resolve().RegisterSetting("Tools", "CologneChip", CologneChipConstantService.CologneChipSettingsIgnoreGuiKey, new CheckBoxSetting("Ignore UI for HardwarePin Mapping", false)); diff --git a/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs b/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs new file mode 100644 index 0000000..1b9c33f --- /dev/null +++ b/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs @@ -0,0 +1,183 @@ +namespace OneWare.CologneChip.Services; + +using Avalonia.Media; +using Avalonia.Threading; +using OneWare.CologneChip.Helpers; +using OneWare.Essentials.Enums; +using OneWare.Essentials.Services; +using OneWare.UniversalFpgaProjectSystem.Models; +using OneWare.UniversalFpgaProjectSystem.Parser; +using Prism.Ioc; + +public abstract class CcCompileStrategyBase : ICologneChipCompileStrategy +{ + protected readonly IDockService Dock; + protected readonly IChildProcessService Proc; + protected readonly ILogger Log; + protected readonly IOutputService Out; + protected readonly ISettingsService Settings; + + protected CcCompileStrategyBase() + { + Dock = ContainerLocator.Container.Resolve(); + Proc = ContainerLocator.Container.Resolve(); + Log = ContainerLocator.Container.Resolve(); + Out = ContainerLocator.Container.Resolve(); + Settings = ContainerLocator.Container.Resolve(); + } + + // === Template: Synth === + public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + try + { + var properties = FpgaSettingsParser.LoadSettings(project, fpgaModel.Fpga.Name); + var topHeader = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); + var (topName, topLang) = SplitTop(topHeader); + + var buildDir = Path.Combine(project.FullPath, "build"); + Directory.CreateDirectory(buildDir); + Dock.Show(); + + var start = DateTime.Now; + + // files & flags + var included = project.Files + .Where(f => GetIncludedExtensions(topLang).Contains(f.Extension)) + .Where(f => !project.CompileExcluded.Contains(f)) + .Where(f => !project.TestBenches.Contains(f)) + .Select(f => $"./../{f.RelativePath}"); + + var yosysFlags = (properties.GetValueOrDefault("yosysToolchainYosysFlags") ?? string.Empty) + .Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + + var yosysSynthTool = properties.GetValueOrDefault("yosysToolchainYosysSynthTool") + ?? throw new Exception("Yosys Tool not set!"); + + var yosysArgs = BuildYosysArgs(topName, topLang, topHeader, yosysSynthTool) + .Concat(yosysFlags) + .Concat(included) + .ToList(); + + var yosysPath = ResolveYosysPath(project); + + var (success, _) = await ExecWithOutput( + yosysPath, + yosysArgs, + $"{project.FullPath}/build", + "Running yosys..."); + + success = MaybeIgnoreSynthExitCode(success); + + WritePhaseResult("Synthesis", start, success); + return success; + } + catch (Exception e) + { + Log.Error(e.Message, e); + return false; + } + } + + // === Template: P&R === + public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + var start = DateTime.Now; + var topHeader = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); + var (topName, topLang) = SplitTop(topHeader); + + var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); + var (exe, args) = BuildPrCommand(topName, topLang, ccfFile); + + var success = (await ExecWithOutput( + exe, + args, + $"{project.FullPath}/build", + "Running P_R...")).success; + + WritePhaseResult("Place and Route", start, success); + return success; + } + + // === Template: Pack === + public virtual async Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + { + // default: no-op/true + return await Task.FromResult(true); + } + + // ---------- Hooks to override ---------- + + protected abstract IEnumerable BuildYosysArgs( + string topName, string topLang, string topHeader, string yosysSynthTool); + + protected abstract (string exe, List args) BuildPrCommand( + string topName, string topLang, string ccfFile); + + protected virtual IEnumerable GetIncludedExtensions(string topLang) => + topLang == "v" ? new[] { ".v", ".sv" } : Array.Empty(); + + protected virtual string ResolveYosysPath(UniversalFpgaProjectRoot project) + { + var src = ContainerLocator.Container.Resolve() + .GetSetting(CologneChipConstantService.YosysSourceSettingsKey, project); + + if (src == "CologneChip") + { + var path = Settings.GetSettingValue(CologneChipConstantService.CcPathSetting); + var p = $"{path}/bin/yosys/yosys"; + Log.Log($"Yosys exec path: {p}"); + return p; + } + return "yosys"; + } + + // ---------- Utilities ---------- + + protected static (string name, string lang) SplitTop(string header) + { + var parts = header.Split('.'); + return (parts.First(), parts.Last()); + } + + protected bool MaybeIgnoreSynthExitCode(bool success) + { + if (success) return true; + + var ignore = Settings.GetSettingValue(CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode); + if (ignore) + { + Log.Warning("The synthesis was terminated with a non-zero exit code."); + Log.Warning($"Setting '{CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode}' is true; continuing."); + return true; + } + return false; + } + + protected async Task<(bool success, int exitCode)> ExecWithOutput( + string exe, + IEnumerable args, + string cwd, + string title) + { + return await Proc.ExecuteShellAsync(exe, args, cwd, title, AppState.Loading, true, x => + { + if (x.StartsWith("Error:")) + { + Log.Error(x); + return false; + } + + Out.WriteLine(x); + return true; + }); + } + + protected void WritePhaseResult(string phase, DateTime start, bool success) + { + var t = DateTime.Now - start; + var msg = $"==================\n\n{phase} {(success ? "finished" : "failed")} after {(int)t.TotalMinutes:D2}:{t.Seconds:D2}\n"; + if (success) Out.WriteLine(msg); + else Out.WriteLine(msg, Brushes.Red); + } +} \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs index 4ac7692..a6790e7 100644 --- a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs @@ -74,7 +74,7 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f yosysArguments.AddRange(includedFiles); var execPath = ""; - switch (_settingsService.GetSettingValue(CologneChipConstantService.OpenFPGALoaderSourceSettingsKey)) + switch (_settingsService.GetSettingValue(CologneChipConstantService.YosysSourceSettingsKey)) { case "CologneChip": var path = _settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); diff --git a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs index f2f4a1b..93dcb15 100644 --- a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs @@ -3,6 +3,7 @@ using OneWare.CologneChip.Helpers; using OneWare.Essentials.Enums; using OneWare.Essentials.Services; +using OneWare.GhdlExtension.Services; using OneWare.UniversalFpgaProjectSystem.Models; using OneWare.UniversalFpgaProjectSystem.Parser; using Prism.Ioc; @@ -47,6 +48,8 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f List yosysArguments = []; List includedExtensions = []; + + var ghdlService = ContainerLocator.Container.Resolve(); switch (topLanguage) { @@ -73,8 +76,9 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f yosysArguments.AddRange(includedFiles); var execPath = ""; - - switch (_settingsService.GetSettingValue(CologneChipConstantService.OpenFPGALoaderSourceSettingsKey)) + + switch (ContainerLocator.Container.Resolve() + .GetSetting(CologneChipConstantService.YosysSourceSettingsKey, project)) { case "CologneChip": var path = _settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); @@ -84,6 +88,7 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f execPath = "yosys"; break; } + _logger.Log($"Yosys exec path: {execPath}"); var (success, _) = await _childProcessService.ExecuteShellAsync(execPath, yosysArguments, $"{project.FullPath}/build", "Running yosys...", AppState.Loading, true, x => diff --git a/src/OneWare.CologneChip/Services/CcSettingsService.cs b/src/OneWare.CologneChip/Services/CcSettingsService.cs new file mode 100644 index 0000000..8458c0c --- /dev/null +++ b/src/OneWare.CologneChip/Services/CcSettingsService.cs @@ -0,0 +1,23 @@ +using OneWare.Essentials.Services; +using OneWare.UniversalFpgaProjectSystem.Models; + +namespace OneWare.CologneChip.Services; + +public class CcSettingsService(ISettingsService settingsService) +{ + + public string GetSetting(string key, UniversalFpgaProjectRoot? root = null) + { + if (root != null) + { + if (root.Properties.ContainsKey(key)) + { + var value = root.Properties[key]!.ToString(); + if (value != CologneChipConstantService.ProjectOverrideValue) + return value; + } + } + + return settingsService.GetSettingValue(key); + } +} \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs index 2a67348..9c5e952 100644 --- a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs +++ b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs @@ -23,11 +23,14 @@ private CologneChipConstantService() {} public const string ToolChainSettingsKey = "cologneChipToolchain"; public static readonly string[] Toolchains = ["p_r", "nextpnr"]; + public static readonly string[] ToolchainsGlobal = ["p_r", "nextpnr", ProjectOverrideValue]; public const string ToolChainDefault = "nextpnr"; + public const string ProjectOverrideValue = "Global"; public const string OpenFPGALoaderSourceSettingsKey = "cologneChipOpenFPGALoaderSource"; public const string YosysSourceSettingsKey = "cologneChipYosysSource"; public static readonly string[] BinarySources = ["CologneChip", "OSSCAD Suite"]; + public static readonly string[] BinarySourcesProject = ["CologneChip", "OSSCAD Suite", ProjectOverrideValue]; public const string OpenFPGALoaderSourceDefault = "CologneChip"; public const string CcfTemplate = @"# From 225816d9d6e34681647615dbf1ab65b68587a56c Mon Sep 17 00:00:00 2001 From: Sebastian Wittlich Date: Fri, 29 Aug 2025 15:16:07 +0200 Subject: [PATCH 5/7] fin #6 & f#12 --- src/OneWare.CologneChip/CologneChipLoader.cs | 30 +-- .../OneWare.CologneChip.csproj | 2 +- .../OneWareCologneChipModule.cs | 7 + .../Services/CcCompileStrategyBase.cs | 19 +- .../Services/CcCustomLogger.cs | 27 +++ .../Services/CcNextpnrCompileStrategy.cs | 196 +++--------------- .../Services/CcProprietaryCompileStrategy.cs | 167 ++------------- .../Services/CcUtilsService.cs | 146 +++++++++++++ .../Services/CologneChipConstantService.cs | 2 + .../Services/ICcCustomLogger.cs | 8 + 10 files changed, 270 insertions(+), 334 deletions(-) create mode 100644 src/OneWare.CologneChip/Services/CcCustomLogger.cs create mode 100644 src/OneWare.CologneChip/Services/CcUtilsService.cs create mode 100644 src/OneWare.CologneChip/Services/ICcCustomLogger.cs diff --git a/src/OneWare.CologneChip/CologneChipLoader.cs b/src/OneWare.CologneChip/CologneChipLoader.cs index 78f72c7..78c00bd 100644 --- a/src/OneWare.CologneChip/CologneChipLoader.cs +++ b/src/OneWare.CologneChip/CologneChipLoader.cs @@ -4,6 +4,7 @@ using OneWare.UniversalFpgaProjectSystem.Models; using OneWare.UniversalFpgaProjectSystem.Parser; using OneWare.UniversalFpgaProjectSystem.Services; +using Prism.Ioc; using ILogger = OneWare.Essentials.Services.ILogger; namespace OneWare.CologneChip; @@ -165,19 +166,7 @@ public async Task DownloadAsync(UniversalFpgaProjectRoot project) if (!useWsl) { // C:\Users\sebas\OneWareStudio\Packages\NativeTools\colognechip\cc-toolchain-win - var execPath = ""; - - switch (settingsService.GetSettingValue(CologneChipConstantService.OpenFPGALoaderSourceSettingsKey)) - { - case "CologneChip": - var path = settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); - execPath = $"{path}/bin/openFPGALoader/openFPGALoader"; - break; - default: - execPath = "openFPGALoader"; - break; - } - + var execPath = ResolveOpenFPGALoaderPath(project); await childProcessService.ExecuteShellAsync(execPath, fpgaArgs, outputDir, "Running OpenFPGALoader (Short-Term)...", AppState.Loading, true); @@ -191,4 +180,19 @@ await childProcessService.ExecuteShellAsync("wsl", args, outputDir, "Running openFPGALoader (Short-Term)...", AppState.Loading, true); } } + + protected virtual string ResolveOpenFPGALoaderPath(UniversalFpgaProjectRoot project) + { + var src = ContainerLocator.Container.Resolve() + .GetSetting(CologneChipConstantService.YosysSourceSettingsKey, project); + + if (src == "CologneChip") + { + var path = settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); + var p = $"{path}/bin/openFPGALoader/openFPGALoader"; + logger.Log($"openFPGALoader exec path: {p}"); + return p; + } + return "openFPGALoader"; + } } \ No newline at end of file diff --git a/src/OneWare.CologneChip/OneWare.CologneChip.csproj b/src/OneWare.CologneChip/OneWare.CologneChip.csproj index 0eabb3c..6aeea53 100644 --- a/src/OneWare.CologneChip/OneWare.CologneChip.csproj +++ b/src/OneWare.CologneChip/OneWare.CologneChip.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/OneWare.CologneChip/OneWareCologneChipModule.cs b/src/OneWare.CologneChip/OneWareCologneChipModule.cs index b3064e3..bbfb2bc 100644 --- a/src/OneWare.CologneChip/OneWareCologneChipModule.cs +++ b/src/OneWare.CologneChip/OneWareCologneChipModule.cs @@ -25,7 +25,11 @@ public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); + + containerRegistry.RegisterSingleton(); } public void OnInitialized(IContainerProvider containerProvider) @@ -144,6 +148,9 @@ public void OnInitialized(IContainerProvider containerProvider) containerProvider.Resolve().RegisterSetting("Tools", "CologneChip", CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode, new CheckBoxSetting("Ignore an exit code not equal to 0 after the synthesis", false)); + + containerProvider.Resolve().RegisterSetting("Tools", "CologneChip", + CologneChipConstantService.AutoDownloadBinariesKey, new CheckBoxSetting("Auto Download Binaries", true)); diff --git a/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs b/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs index 1b9c33f..26091c3 100644 --- a/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs +++ b/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs @@ -88,13 +88,14 @@ public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpga var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); var (exe, args) = BuildPrCommand(topName, topLang, ccfFile); - - var success = (await ExecWithOutput( - exe, - args, - $"{project.FullPath}/build", - "Running P_R...")).success; - + + var success = (await Proc.ExecuteShellAsync(exe, args, + $"{project.FullPath}/build", $"Running P_R...", AppState.Loading, true, null, s => + { + Dispatcher.UIThread.Post(() => { Out.WriteLine(s); }); + return true; + })).success; + WritePhaseResult("Place and Route", start, success); return success; } @@ -154,9 +155,9 @@ protected bool MaybeIgnoreSynthExitCode(bool success) return false; } - protected async Task<(bool success, int exitCode)> ExecWithOutput( + protected async Task<(bool success, string output)> ExecWithOutput( string exe, - IEnumerable args, + IReadOnlyCollection args, string cwd, string title) { diff --git a/src/OneWare.CologneChip/Services/CcCustomLogger.cs b/src/OneWare.CologneChip/Services/CcCustomLogger.cs new file mode 100644 index 0000000..caba9c1 --- /dev/null +++ b/src/OneWare.CologneChip/Services/CcCustomLogger.cs @@ -0,0 +1,27 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Media; +using OneWare.Essentials.Services; +using TextMateSharp.Themes; +namespace OneWare.CologneChip.Services; + +public class CcCustomLogger(ILogger logger) : ICcCustomLogger +{ + private const ConsoleColor LogMessageConsoleColor = ConsoleColor.Cyan; + + private static readonly IBrush LogMessageBrush = + (Application.Current!.GetResourceObservable("ThemeAccentBrush") as IBrush)!; + + private readonly string _assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name!; + + public void Log(string message, bool showOutput = false) + { + logger.Log("[" + _assemblyName + "]: " + message, LogMessageConsoleColor, showOutput, LogMessageBrush); + } + + public void Error(string message, bool showOutput = true) + { + logger.Error("[" + _assemblyName + "]: " + message, null, showOutput); + } +} \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs index a6790e7..eac1468 100644 --- a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs @@ -9,179 +9,49 @@ namespace OneWare.CologneChip.Services; -public class CcNextpnrCompileStrategy : ICologneChipCompileStrategy +public class CcNextpnrCompileStrategy : CcCompileStrategyBase { - private readonly IDockService _dockService; - private readonly IChildProcessService _childProcessService; - private readonly ILogger _logger; - private readonly IOutputService _outputService; - private readonly ISettingsService _settingsService; - - public CcNextpnrCompileStrategy() + protected override IEnumerable BuildYosysArgs(string topName, string topLang, string topHeader, string yosysSynthTool) { - _dockService = ContainerLocator.Container.Resolve(); - _childProcessService = ContainerLocator.Container.Resolve(); - _logger = ContainerLocator.Container.Resolve(); - _outputService = ContainerLocator.Container.Resolve(); - _settingsService = ContainerLocator.Container.Resolve(); - } - - public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) - { - try - { - var properties = FpgaSettingsParser.LoadSettings(project, fpgaModel.Fpga.Name); - var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); - - var buildDir = Path.Combine(project.FullPath, "build"); - Directory.CreateDirectory(buildDir); - - _dockService.Show(); - - var start = DateTime.Now; - - var yosysSynthTool = properties.GetValueOrDefault("yosysToolchainYosysSynthTool") ?? - throw new Exception("Yosys Tool not set!"); - - var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); - - List yosysArguments = []; - List includedExtensions = []; - - switch (topLanguage) - { - // TODO: synth for vhdl - case "vhd": - _outputService.WriteLine("VHDL Synthesis...\n==============="); - yosysArguments = ["-q","-l","./synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{top} -e {topName}; {yosysSynthTool} -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;"]; - includedExtensions = []; - break; - case "v": - _outputService.WriteLine("Verilog Synthesis...\n=============="); - yosysArguments = ["-p", $"synth_gatemate -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;"]; - includedExtensions = [".v", ".sv"]; - break; - } - - var includedFiles = project.Files - .Where(x => includedExtensions.Contains(x.Extension)) - .Where(x => !project.CompileExcluded.Contains(x)) - .Where(x => !project.TestBenches.Contains(x)) - .Select(x => $"./../{x.RelativePath}"); - - yosysArguments.AddRange(properties.GetValueOrDefault("yosysToolchainYosysFlags")?.Split(' ', - StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) ?? []); - yosysArguments.AddRange(includedFiles); - - var execPath = ""; - switch (_settingsService.GetSettingValue(CologneChipConstantService.YosysSourceSettingsKey)) - { - case "CologneChip": - var path = _settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); - execPath = $"{path}/bin/yosys/yosys"; - break; - default: - execPath = "yosys"; - break; - } - - var (success, _) = await _childProcessService.ExecuteShellAsync(execPath, yosysArguments, $"{project.FullPath}/build", - "Running yosys...", AppState.Loading, true, x => - { - if (x.StartsWith("Error:")) - { - _logger.Error(x); - return false; - } - - _outputService.WriteLine(x); - return true; - }); - - - if (!success) { - var ignoreSynthExitCode = ContainerLocator.Container.Resolve().GetSettingValue(CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode); - if (ignoreSynthExitCode) - { - ContainerLocator.Container.Resolve().Warning("The synthesis was terminated with an exit code other than zero"); - ContainerLocator.Container.Resolve().Warning($"Setting '{CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode}' to true"); - ContainerLocator.Container.Resolve().Warning($"Because of this setting, the route and placing tool is started anyway"); - success = true; - } - } - - - var compileTime = DateTime.Now - start; - if (success) - _outputService.WriteLine( - $"==================\n\nSynthesis finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); - else - _outputService.WriteLine( - $"==================\n\nSynthesis failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", - Brushes.Red); - - return success; - } - catch (Exception e) + switch (topLang) { - _logger.Error(e.Message, e); - return false; + case "vhd": + Out.WriteLine("VHDL Synthesis...\n==============="); + return new[] { "-q", "-l", "./synth.log", + "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{topHeader} -e {topName}; " + + $"{yosysSynthTool} -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;" }; + case "v": + Out.WriteLine("Verilog Synthesis...\n=============="); + return new[] { "-p", $"synth_gatemate -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;" }; + default: + throw new NotSupportedException($"Unsupported top language: {topLang}"); } } - public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + protected override (string exe, List args) BuildPrCommand(string topName, string topLang, string ccfFile) { - var start = DateTime.Now; - var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); - var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); - var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); - - List prArguments = ["--device=CCGM1A1","--json", $"{topName}.json", "-o", $"ccf=./../{ccfFile}", "-o", $"out={topName}_impl.txt", "--router=router2"]; - - var success = (await _childProcessService.ExecuteShellAsync("nextpnr-himbaechel", prArguments, - $"{project.FullPath}/build", $"Running P_R...", AppState.Loading, true, null, s => - { - Dispatcher.UIThread.Post(() => { _outputService.WriteLine(s); }); - return true; - })).success; - - var compileTime = DateTime.Now - start; - if (success) - _outputService.WriteLine( - $"==================\n\nPlace and Route finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); - else - _outputService.WriteLine( - $"==================\n\nPlace and Route failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", - Brushes.Red); - - return success; + return ("nextpnr-himbaechel", + new List { + "--device=CCGM1A1", + "--json", $"{topName}.json", + "-o", $"ccf=./../{ccfFile}", + "-o", $"out={topName}_impl.txt", + "--router=router2" + }); } - - public async Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + + public override async Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) { var start = DateTime.Now; - var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); - var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); - var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); - - List prArguments = [$"{topName}_impl.txt", $"{topName}.bit"]; - - var success = (await _childProcessService.ExecuteShellAsync("gmpack", prArguments, - $"{project.FullPath}/build", $"Running P_R...", AppState.Loading, true, null, s => - { - Dispatcher.UIThread.Post(() => { _outputService.WriteLine(s); }); - return true; - })).success; - - var compileTime = DateTime.Now - start; - if (success) - _outputService.WriteLine( - $"==================\n\nPlace and Route finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); - else - _outputService.WriteLine( - $"==================\n\nPlace and Route failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", - Brushes.Red); - + var (topName, _) = SplitTop(project.TopEntity?.Header ?? throw new Exception("TopEntity not set!")); + + var (success, _) = await ExecWithOutput( + "gmpack", + new List { $"{topName}_impl.txt", $"{topName}.bit" }, + $"{project.FullPath}/build", + "Running Pack..."); + + WritePhaseResult("Pack", start, success); return success; } } \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs index 93dcb15..d6b1f15 100644 --- a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs @@ -10,160 +10,31 @@ namespace OneWare.CologneChip.Services; -public class CcProprietaryCompileStrategy : ICologneChipCompileStrategy +public class CcProprietaryCompileStrategy : CcCompileStrategyBase { - private readonly IDockService _dockService; - private readonly IChildProcessService _childProcessService; - private readonly ILogger _logger; - private readonly IOutputService _outputService; - private readonly ISettingsService _settingsService; - - public CcProprietaryCompileStrategy() - { - _dockService = ContainerLocator.Container.Resolve(); - _childProcessService = ContainerLocator.Container.Resolve(); - _logger = ContainerLocator.Container.Resolve(); - _outputService = ContainerLocator.Container.Resolve(); - _settingsService = ContainerLocator.Container.Resolve(); - } - - public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + protected override IEnumerable BuildYosysArgs(string topName, string topLang, string topHeader, string yosysSynthTool) { - try - { - var properties = FpgaSettingsParser.LoadSettings(project, fpgaModel.Fpga.Name); - var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); - - var buildDir = Path.Combine(project.FullPath, "build"); - Directory.CreateDirectory(buildDir); - - _dockService.Show(); - - var start = DateTime.Now; - - var yosysSynthTool = properties.GetValueOrDefault("yosysToolchainYosysSynthTool") ?? - throw new Exception("Yosys Tool not set!"); - - var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); - - List yosysArguments = []; - List includedExtensions = []; - - var ghdlService = ContainerLocator.Container.Resolve(); - - switch (topLanguage) - { - case "vhd": - _outputService.WriteLine("VHDL Synthesis...\n==============="); - yosysArguments = ["-q","-l ./synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{top} -e {topName}; {yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; - includedExtensions = []; - break; - case "v": - _outputService.WriteLine("Verilog Synthesis...\n=============="); - yosysArguments = ["-ql", "./synth.log", "-p", $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v"]; - includedExtensions = [".v", ".sv"]; - break; - } - - var includedFiles = project.Files - .Where(x => includedExtensions.Contains(x.Extension)) - .Where(x => !project.CompileExcluded.Contains(x)) - .Where(x => !project.TestBenches.Contains(x)) - .Select(x => $"./../{x.RelativePath}"); - - yosysArguments.AddRange(properties.GetValueOrDefault("yosysToolchainYosysFlags")?.Split(' ', - StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) ?? []); - yosysArguments.AddRange(includedFiles); - - var execPath = ""; - - switch (ContainerLocator.Container.Resolve() - .GetSetting(CologneChipConstantService.YosysSourceSettingsKey, project)) - { - case "CologneChip": - var path = _settingsService.GetSettingValue(CologneChipConstantService.CcPathSetting); - execPath = $"{path}/bin/yosys/yosys"; - break; - default: - execPath = "yosys"; - break; - } - _logger.Log($"Yosys exec path: {execPath}"); - - var (success, _) = await _childProcessService.ExecuteShellAsync(execPath, yosysArguments, $"{project.FullPath}/build", - "Running yosys...", AppState.Loading, true, x => - { - if (x.StartsWith("Error:")) - { - _logger.Error(x); - return false; - } - - _outputService.WriteLine(x); - return true; - }); - - - if (!success) { - var ignoreSynthExitCode = ContainerLocator.Container.Resolve().GetSettingValue(CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode); - if (ignoreSynthExitCode) - { - ContainerLocator.Container.Resolve().Warning("The synthesis was terminated with an exit code other than zero"); - ContainerLocator.Container.Resolve().Warning($"Setting '{CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode}' to true"); - ContainerLocator.Container.Resolve().Warning($"Because of this setting, the route and placing tool is started anyway"); - success = true; - } - } - - - var compileTime = DateTime.Now - start; - if (success) - _outputService.WriteLine( - $"==================\n\nSynthesis finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); - else - _outputService.WriteLine( - $"==================\n\nSynthesis failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", - Brushes.Red); - - return success; - } - catch (Exception e) + switch (topLang) { - _logger.Error(e.Message, e); - return false; + case "vhd": + Out.WriteLine("VHDL Synthesis...\n==============="); + return new[] { "-q", "-l", "./synth.log", + "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{topHeader} -e {topName}; " + + $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v" }; + case "v": + Out.WriteLine("Verilog Synthesis...\n=============="); + return new[] { "-ql", "./synth.log", + "-p", $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v" }; + default: + throw new NotSupportedException($"Unsupported top language: {topLang}"); } } - public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + protected override (string exe, List args) BuildPrCommand(string topName, string topLang, string ccfFile) { - var start = DateTime.Now; - var top = project.TopEntity?.Header ?? throw new Exception("TopEntity not set!"); - var (topName, topLanguage) = (top.Split('.').First(), top.Split('.').Last()); - var ccfFile = CologneChipSettingsHelper.GetConstraintFile(project); - - List prArguments = ["-i", $"{topName}_synth.v", "-o", topName, $"-ccf ./../{ccfFile} -cCP"]; - - var success = (await _childProcessService.ExecuteShellAsync("p_r", prArguments, - $"{project.FullPath}/build", $"Running P_R...", AppState.Loading, true, null, s => - { - Dispatcher.UIThread.Post(() => { _outputService.WriteLine(s); }); - return true; - })).success; - - var compileTime = DateTime.Now - start; - if (success) - _outputService.WriteLine( - $"==================\n\nPlace and Route finished after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n"); - else - _outputService.WriteLine( - $"==================\n\nPlace and Route failed after {(int)compileTime.TotalMinutes:D2}:{compileTime.Seconds:D2}\n", - Brushes.Red); - - return success; - } - - public Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) - { - return Task.FromResult(true); + return ("p_r", new List { "-i", $"{topName}_synth.v", "-o", topName, $"-ccf ./../{ccfFile} -cCP" }); } + + public override Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + => Task.FromResult(true); } \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CcUtilsService.cs b/src/OneWare.CologneChip/Services/CcUtilsService.cs new file mode 100644 index 0000000..62db2b2 --- /dev/null +++ b/src/OneWare.CologneChip/Services/CcUtilsService.cs @@ -0,0 +1,146 @@ +using OneWare.Essentials.Enums; +using OneWare.Essentials.Models; +using OneWare.Essentials.PackageManager; +using OneWare.Essentials.Services; + +namespace OneWare.CologneChip.Services; + +public class CcUtilsService(IPackageService packageService, ISettingsService settingsService, IApplicationStateService applicationStateService, ICcCustomLogger logger) +{ + private async Task<(bool success, bool needsRestart)> InstallDependenciesAsync() + { + ApplicationProcess checkProc = applicationStateService.AddState("Checking dependencies", AppState.Loading); + + bool restartRequired = false; + bool globalSuccess = true; + + (string id, Version minversion)[] dependencyIDs = + [ + ("OneWare.GhdlExtension", new Version(0, 10, 7)), + ("osscadsuite", new Version(2025, 01, 21)), + ("ghdl", new Version(5, 0, 1)) + ]; + + // Install osscadsuite binary between GHDL plugin and ghdl binary to allow for the addition of the ghdl binary to the store + + foreach ((string dependencyId, Version minVersion) in dependencyIDs) + { + PackageModel? dependencyModel = packageService.Packages.GetValueOrDefault(dependencyId); + Package? dependencyPackage = dependencyModel?.Package; + + if (dependencyPackage == null) + { + logger.Error( + $"Dependency with ID {dependencyId} is not available in the package manager. Please file a bug report if this issue persists"); + + globalSuccess = false; + continue; + } + + if (packageService.Packages.GetValueOrDefault(dependencyId) is + { + Status: PackageStatus.Available + }) + { + bool updatePerformed = true; + + if (settingsService.GetSettingValue(CologneChipConstantService.AutoDownloadBinariesKey)) + { + logger.Log($"Installing \"{dependencyPackage.Name}\"...", true); + + bool localSuccess = false; + + // Try to install the dependency, starting with the latest version + // If the version is not compatible or the download fails, try the previous version + foreach (PackageVersion packageVersion in dependencyPackage.Versions!.Reverse()) + { + // Skip incompatible versions + if (!(await dependencyModel!.CheckCompatibilityAsync(packageVersion)).IsCompatible) + { + continue; + } + + PackageVersion? installedVersion = dependencyModel.InstalledVersion; + + if (installedVersion == packageVersion) + { + logger.Log( + $"Failed to update {dependencyPackage.Name} from version {installedVersion.Version} to version {dependencyPackage.Versions!.Last()}", + true); + + updatePerformed = false; + localSuccess = true; + break; + } + + localSuccess = await dependencyModel.DownloadAsync(packageVersion); + + // Stop trying, if install has been successful + if (localSuccess) + { + break; + } + } + + globalSuccess = globalSuccess && localSuccess; + + if (updatePerformed) + { + if (localSuccess) + { + logger.Log($"Successfully installed \"{dependencyPackage.Name}\".", true); + } + else + { + logger.Error($"Failed to install \"{dependencyPackage.Name}\"."); + } + } + } + else + { + // Log an error if the user has not enabled automatic binary downloads + + logger.Error( + $"Extension \"{dependencyPackage.Name}\" is not installed. Please enable \"Automatically download Binaries\" under the \"Experimental\" settings or download the extension yourself"); + + globalSuccess = false; + } + + if (globalSuccess && updatePerformed) + { + restartRequired = true; + } + } + + // Check whether now the correct version is installed + if (dependencyModel!.Status is PackageStatus.Installed or PackageStatus.UpdateAvailable) + { + if (minVersion.CompareTo(Version.Parse(dependencyModel.InstalledVersion!.Version!)) <= 0) + { + logger.Log( + $"Dependency {dependencyPackage.Id} installed with version {dependencyModel.InstalledVersion.Version} greater than or equal to expected version {minVersion.ToString()}"); + } + else + { + logger.Error( + $"Installed version {dependencyModel.InstalledVersion.Version} for {dependencyPackage.Name} is below the minimum version {minVersion.ToString()}. Please update {dependencyPackage.Name}!"); + + globalSuccess = false; + } + } + else if (dependencyModel.Status is PackageStatus.NeedRestart) + { + restartRequired = true; + } + } + + if (globalSuccess && restartRequired) + { + logger.Log("Dependencies were successfully installed. Please restart OneWare Studio!", true); + } + + applicationStateService.RemoveState(checkProc); + + return (globalSuccess, restartRequired); + } +} \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs index 9c5e952..0a825d8 100644 --- a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs +++ b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs @@ -19,6 +19,8 @@ private CologneChipConstantService() {} public static string CologneChipSettingsUseWsl => "cologneChip_UseWSL"; + public static string AutoDownloadBinariesKey => "cologneChip_AutoDownloadBinaries"; + public static string CologneChipDefaultConstraintFile => "project.ccf"; public const string ToolChainSettingsKey = "cologneChipToolchain"; diff --git a/src/OneWare.CologneChip/Services/ICcCustomLogger.cs b/src/OneWare.CologneChip/Services/ICcCustomLogger.cs new file mode 100644 index 0000000..16e4b2f --- /dev/null +++ b/src/OneWare.CologneChip/Services/ICcCustomLogger.cs @@ -0,0 +1,8 @@ +namespace OneWare.CologneChip.Services; + +public interface ICcCustomLogger +{ + public void Log(string message, bool showOutput = false); + + public void Error(string message, bool showOutput = true); +} \ No newline at end of file From ed5daa3454dbd4b1016119fe7ee3c4ac48c3d314 Mon Sep 17 00:00:00 2001 From: Sebastian Wittlich Date: Mon, 1 Sep 2025 17:14:16 +0200 Subject: [PATCH 6/7] fin #12 + cleanup --- .../OneWareCologneChipModule.cs | 3 +- .../Services/CcCompileStrategyBase.cs | 66 +++++++++++++++---- .../Services/CcCustomLogger.cs | 2 - .../Services/CcNextpnrCompileStrategy.cs | 36 ++++++---- .../Services/CcProprietaryCompileStrategy.cs | 46 +++++++------ .../Services/CologneChipConstantService.cs | 2 +- .../Services/CologneChipService.cs | 3 - .../CologneChipLoaderSettingsViewModel.cs | 4 -- 8 files changed, 108 insertions(+), 54 deletions(-) diff --git a/src/OneWare.CologneChip/OneWareCologneChipModule.cs b/src/OneWare.CologneChip/OneWareCologneChipModule.cs index bbfb2bc..fa1c8ab 100644 --- a/src/OneWare.CologneChip/OneWareCologneChipModule.cs +++ b/src/OneWare.CologneChip/OneWareCologneChipModule.cs @@ -2,7 +2,6 @@ using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Markup.Xaml.Styling; -using Avalonia.Media; using CommunityToolkit.Mvvm.Input; using OneWare.CologneChip.Helpers; using OneWare.CologneChip.Services; @@ -122,7 +121,7 @@ public void OnInitialized(IContainerProvider containerProvider) var projectSettingsService = containerProvider.Resolve(); projectSettingsService.AddProjectSetting(new ProjectSettingBuilder() .WithSetting(new ComboBoxSetting("Place & Route", CologneChipConstantService.ProjectOverrideValue, - CologneChipConstantService.BinarySourcesProject)) + CologneChipConstantService.ToolchainsProject)) .WithCategory("CologneChip") .WithKey(CologneChipConstantService.ToolChainSettingsKey) .Build()); diff --git a/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs b/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs index 26091c3..9666409 100644 --- a/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs +++ b/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs @@ -1,3 +1,7 @@ +using Avalonia.Styling; +using OneWare.Essentials.Models; +using OneWare.GhdlExtension.Services; + namespace OneWare.CologneChip.Services; using Avalonia.Media; @@ -26,8 +30,7 @@ protected CcCompileStrategyBase() Settings = ContainerLocator.Container.Resolve(); } - // === Template: Synth === - public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) + public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) { try { @@ -40,8 +43,14 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f Dock.Show(); var start = DateTime.Now; + + string? preSynthVerilog = null; + if (topLang == "vhd" && !UseEmbeddedGhdl(project)) + { + preSynthVerilog = await PreSynthesizeVhdlToVerilogAsync(project, topName, topHeader); + if (preSynthVerilog is null) return false; + } - // files & flags var included = project.Files .Where(f => GetIncludedExtensions(topLang).Contains(f.Extension)) .Where(f => !project.CompileExcluded.Contains(f)) @@ -54,7 +63,7 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f var yosysSynthTool = properties.GetValueOrDefault("yosysToolchainYosysSynthTool") ?? throw new Exception("Yosys Tool not set!"); - var yosysArgs = BuildYosysArgs(topName, topLang, topHeader, yosysSynthTool) + var yosysArgs = BuildYosysArgs(topName, topLang, topHeader, yosysSynthTool, preSynthVerilog) .Concat(yosysFlags) .Concat(included) .ToList(); @@ -68,7 +77,6 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f "Running yosys..."); success = MaybeIgnoreSynthExitCode(success); - WritePhaseResult("Synthesis", start, success); return success; } @@ -78,8 +86,7 @@ public async Task SynthAsync(UniversalFpgaProjectRoot project, FpgaModel f return false; } } - - // === Template: P&R === + public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) { var start = DateTime.Now; @@ -100,7 +107,6 @@ public async Task PrAsync(UniversalFpgaProjectRoot project, FpgaModel fpga return success; } - // === Template: Pack === public virtual async Task PackAsync(UniversalFpgaProjectRoot project, FpgaModel fpgaModel) { // default: no-op/true @@ -110,7 +116,7 @@ public virtual async Task PackAsync(UniversalFpgaProjectRoot project, Fpga // ---------- Hooks to override ---------- protected abstract IEnumerable BuildYosysArgs( - string topName, string topLang, string topHeader, string yosysSynthTool); + string topName, string topLang, string topHeader, string yosysSynthTool, string? preSynthVerilog); protected abstract (string exe, List args) BuildPrCommand( string topName, string topLang, string ccfFile); @@ -134,7 +140,16 @@ protected virtual string ResolveYosysPath(UniversalFpgaProjectRoot project) } // ---------- Utilities ---------- - + + protected virtual bool UseEmbeddedGhdl(UniversalFpgaProjectRoot project) + { + var src = ContainerLocator.Container.Resolve() + .GetSetting(CologneChipConstantService.YosysSourceSettingsKey, project); + return src == "CologneChip"; + } + + protected virtual string ResolveGhdlPath() => "ghdl"; + protected static (string name, string lang) SplitTop(string header) { var parts = header.Split('.'); @@ -173,7 +188,36 @@ protected bool MaybeIgnoreSynthExitCode(bool success) return true; }); } - + + protected async Task PreSynthesizeVhdlToVerilogAsync( + UniversalFpgaProjectRoot project, string topName, string topHeader) + { + var buildDir = Path.Combine(project.FullPath, "build"); + + var vhdlFiles = project.Files + .Where(f => GetVhdlExtensions().Contains(f.Extension)) + .Where(f => !project.CompileExcluded.Contains(f)) + .Where(f => !project.TestBenches.Contains(f)) + .Select(f => $"./../{f.RelativePath}") + .ToList(); + + if (vhdlFiles.Count == 0) + { + Log.Error("No VHDL sources found for external GHDL synthesis."); + return null; + } + + if (project.TopEntity is IProjectFile projectFile) + { + await ContainerLocator.Container.Resolve().SynthAsync(projectFile,"verilog", "build" ); + return $"{topName}.v"; + } + + throw new Exception($"Could not find matching operation for object type: {project.GetType().Name}"); + } + + protected virtual IEnumerable GetVhdlExtensions() => new[] { ".vhd", ".vhdl" }; + protected void WritePhaseResult(string phase, DateTime start, bool success) { var t = DateTime.Now - start; diff --git a/src/OneWare.CologneChip/Services/CcCustomLogger.cs b/src/OneWare.CologneChip/Services/CcCustomLogger.cs index caba9c1..b8f393d 100644 --- a/src/OneWare.CologneChip/Services/CcCustomLogger.cs +++ b/src/OneWare.CologneChip/Services/CcCustomLogger.cs @@ -1,9 +1,7 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.Primitives; using Avalonia.Media; using OneWare.Essentials.Services; -using TextMateSharp.Themes; namespace OneWare.CologneChip.Services; public class CcCustomLogger(ILogger logger) : ICcCustomLogger diff --git a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs index eac1468..5c7e97c 100644 --- a/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/CcNextpnrCompileStrategy.cs @@ -11,21 +11,35 @@ namespace OneWare.CologneChip.Services; public class CcNextpnrCompileStrategy : CcCompileStrategyBase { - protected override IEnumerable BuildYosysArgs(string topName, string topLang, string topHeader, string yosysSynthTool) + protected override IEnumerable BuildYosysArgs( + string topName, string topLang, string topHeader, string yosysSynthTool, string? preSynthVerilog) { - switch (topLang) + if (topLang == "vhd") { - case "vhd": - Out.WriteLine("VHDL Synthesis...\n==============="); - return new[] { "-q", "-l", "./synth.log", + if (preSynthVerilog is not null) + { + Out.WriteLine("VHDL Synthesis (external GHDL → Verilog)...\n==============="); + return new[] { + "-ql", "./synth.log", + "-p", $"read_verilog {preSynthVerilog}; " + + $"synth_gatemate -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;" + }; + } + else + { + Out.WriteLine("VHDL Synthesis (embedded GHDL)...\n==============="); + return new[] { + "-ql", "./synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{topHeader} -e {topName}; " + - $"{yosysSynthTool} -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;" }; - case "v": - Out.WriteLine("Verilog Synthesis...\n=============="); - return new[] { "-p", $"synth_gatemate -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;" }; - default: - throw new NotSupportedException($"Unsupported top language: {topLang}"); + $"{yosysSynthTool} -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;" + }; + } } + + Out.WriteLine("Verilog Synthesis...\n=============="); + return new[] { + "-p", $"synth_gatemate -nomx8 -top {topName} -luttree -nomult; write_json {topName}.json;" + }; } protected override (string exe, List args) BuildPrCommand(string topName, string topLang, string ccfFile) diff --git a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs index d6b1f15..8553d22 100644 --- a/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs +++ b/src/OneWare.CologneChip/Services/CcProprietaryCompileStrategy.cs @@ -1,33 +1,39 @@ -using Avalonia.Media; -using Avalonia.Threading; -using OneWare.CologneChip.Helpers; -using OneWare.Essentials.Enums; -using OneWare.Essentials.Services; -using OneWare.GhdlExtension.Services; using OneWare.UniversalFpgaProjectSystem.Models; -using OneWare.UniversalFpgaProjectSystem.Parser; -using Prism.Ioc; namespace OneWare.CologneChip.Services; public class CcProprietaryCompileStrategy : CcCompileStrategyBase { - protected override IEnumerable BuildYosysArgs(string topName, string topLang, string topHeader, string yosysSynthTool) + protected override IEnumerable BuildYosysArgs( + string topName, string topLang, string topHeader, string yosysSynthTool, string? preSynthVerilog) { - switch (topLang) + if (topLang == "vhd") { - case "vhd": - Out.WriteLine("VHDL Synthesis...\n==============="); - return new[] { "-q", "-l", "./synth.log", + if (preSynthVerilog is not null) + { + Out.WriteLine("VHDL Synthesis (external GHDL → Verilog)...\n==============="); + return new[] { + "-ql", "./synth.log", + "-p", $"read_verilog {preSynthVerilog}; " + + $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v" + }; + } + else + { + Out.WriteLine("VHDL Synthesis (embedded GHDL)...\n==============="); + return new[] { + "-ql", "./synth.log", "-p", $"ghdl --warn-no-binding -C --ieee=synopsys ./../{topHeader} -e {topName}; " + - $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v" }; - case "v": - Out.WriteLine("Verilog Synthesis...\n=============="); - return new[] { "-ql", "./synth.log", - "-p", $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v" }; - default: - throw new NotSupportedException($"Unsupported top language: {topLang}"); + $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v" + }; + } } + + Out.WriteLine("Verilog Synthesis...\n=============="); + return new[] { + "-ql", "./synth.log", + "-p", $"{yosysSynthTool} -nomx8 -top {topName} -vlog {topName}_synth.v" + }; } protected override (string exe, List args) BuildPrCommand(string topName, string topLang, string ccfFile) diff --git a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs index 0a825d8..5ca66d9 100644 --- a/src/OneWare.CologneChip/Services/CologneChipConstantService.cs +++ b/src/OneWare.CologneChip/Services/CologneChipConstantService.cs @@ -25,7 +25,7 @@ private CologneChipConstantService() {} public const string ToolChainSettingsKey = "cologneChipToolchain"; public static readonly string[] Toolchains = ["p_r", "nextpnr"]; - public static readonly string[] ToolchainsGlobal = ["p_r", "nextpnr", ProjectOverrideValue]; + public static readonly string[] ToolchainsProject = ["p_r", "nextpnr", ProjectOverrideValue]; public const string ToolChainDefault = "nextpnr"; public const string ProjectOverrideValue = "Global"; diff --git a/src/OneWare.CologneChip/Services/CologneChipService.cs b/src/OneWare.CologneChip/Services/CologneChipService.cs index 5933901..27414b6 100644 --- a/src/OneWare.CologneChip/Services/CologneChipService.cs +++ b/src/OneWare.CologneChip/Services/CologneChipService.cs @@ -1,11 +1,8 @@ using Avalonia.Media; -using Avalonia.Threading; using OneWare.CologneChip.Helpers; -using OneWare.Essentials.Enums; using OneWare.Essentials.Models; using OneWare.Essentials.Services; using OneWare.UniversalFpgaProjectSystem.Models; -using OneWare.UniversalFpgaProjectSystem.Parser; using Prism.Ioc; namespace OneWare.CologneChip.Services; diff --git a/src/OneWare.CologneChip/ViewModels/CologneChipLoaderSettingsViewModel.cs b/src/OneWare.CologneChip/ViewModels/CologneChipLoaderSettingsViewModel.cs index daf64fa..8538967 100644 --- a/src/OneWare.CologneChip/ViewModels/CologneChipLoaderSettingsViewModel.cs +++ b/src/OneWare.CologneChip/ViewModels/CologneChipLoaderSettingsViewModel.cs @@ -1,13 +1,9 @@ -using System.Reactive.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using DynamicData.Binding; using OneWare.CologneChip.Services; using OneWare.Essentials.Controls; using OneWare.Essentials.Models; using OneWare.Essentials.ViewModels; using OneWare.Settings.ViewModels; -using OneWare.Settings.ViewModels.SettingTypes; using OneWare.UniversalFpgaProjectSystem.Fpga; using OneWare.UniversalFpgaProjectSystem.Models; using OneWare.UniversalFpgaProjectSystem.Parser; From 769c2cec0d261611d6806227d5d85aee1c3f8c8c Mon Sep 17 00:00:00 2001 From: Sebastian Wittlich Date: Tue, 7 Oct 2025 13:13:36 +0200 Subject: [PATCH 7/7] Release 0.7.1 --- Release.md | 6 ++++++ oneware-extension.json | 2 +- src/OneWare.CologneChip/OneWare.CologneChip.csproj | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Release.md b/Release.md index 3cd972d..db62ed1 100644 --- a/Release.md +++ b/Release.md @@ -1,5 +1,11 @@ # OneWare.CologneChip +## Release 0.7.1 +- nextpnr support (OSSCAD Suite needed) +- Global and project support for changing the toolchain +- Fixed some bugs +- Removed Release 0.7 from the oneware-extension.json (faulty version) + ## Release 0.7 - Update Dependencies for OW Studio Version 21.4.0 diff --git a/oneware-extension.json b/oneware-extension.json index f1b0fe3..ec368d9 100644 --- a/oneware-extension.json +++ b/oneware-extension.json @@ -77,7 +77,7 @@ ] }, { - "version": "0.7", + "version": "0.7.1", "targets": [ { "target": "all" diff --git a/src/OneWare.CologneChip/OneWare.CologneChip.csproj b/src/OneWare.CologneChip/OneWare.CologneChip.csproj index 6aeea53..e02900a 100644 --- a/src/OneWare.CologneChip/OneWare.CologneChip.csproj +++ b/src/OneWare.CologneChip/OneWare.CologneChip.csproj @@ -1,7 +1,7 @@  - 0.8-SNAPSHOT + 0.7.1 net9.0 enable enable